diff options
author | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2016-04-26 12:47:59 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2016-04-29 09:36:36 +0000 |
commit | 96cdc5be2aa0cf87033591cfd76661291b3f6933 (patch) | |
tree | feac67a35952fcaf9b64db0a4b227957852b66d0 /src/plugins | |
parent | e88e2940598086b57e6c844afa2eca4153d0f528 (diff) |
D3D12: Long Live Wobble!
D3D12 shader effect node.
Cull mode, atlas (qt_SubRect_*) support, and some nice-to-haves are
currently missing. The built-in shaders won't yet work due to not sending
'source' down the stack so both have to be application-supplied.
Nonetheless..the wobble test works!
Change-Id: If4cd0143fa5794a8d5f89b576ffcfb084efeb343
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/plugins')
6 files changed, 715 insertions, 22 deletions
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h index 8a475ddb77..6786d8fc06 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h @@ -66,7 +66,7 @@ public: QSGMaterialType *type() const override; int compare(const QSGMaterial *other) const override; - virtual int constantBufferSize() const override; + int constantBufferSize() const override; void preparePipeline(QSGD3D12PipelineState *pipelineState) override; UpdateResults updatePipeline(const RenderState &state, QSGD3D12PipelineState *pipelineState, @@ -84,7 +84,7 @@ public: QSGMaterialType *type() const override; int compare(const QSGMaterial *other) const override; - virtual int constantBufferSize() const override; + int constantBufferSize() const override; void preparePipeline(QSGD3D12PipelineState *pipelineState) override; UpdateResults updatePipeline(const RenderState &state, QSGD3D12PipelineState *pipelineState, @@ -101,7 +101,7 @@ public: QSGMaterialType *type() const override; int compare(const QSGMaterial *other) const override; - virtual int constantBufferSize() const override; + int constantBufferSize() const override; void preparePipeline(QSGD3D12PipelineState *pipelineState) override; UpdateResults updatePipeline(const RenderState &state, QSGD3D12PipelineState *pipelineState, @@ -141,7 +141,7 @@ public: QSGMaterialType *type() const override; int compare(const QSGMaterial *other) const override; - virtual int constantBufferSize() const override; + int constantBufferSize() const override; void preparePipeline(QSGD3D12PipelineState *pipelineState) override; UpdateResults updatePipeline(const RenderState &state, QSGD3D12PipelineState *pipelineState, @@ -189,7 +189,7 @@ public: QSGMaterialType *type() const override; int compare(const QSGMaterial *other) const override; - virtual int constantBufferSize() const override; + int constantBufferSize() const override; void preparePipeline(QSGD3D12PipelineState *pipelineState) override; UpdateResults updatePipeline(const RenderState &state, QSGD3D12PipelineState *pipelineState, diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 2c05ce01c6..83ba443800 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -41,6 +41,7 @@ #include "qsgd3d12engine_p.h" #include "qsgd3d12context_p.h" #include "qsgd3d12rendercontext_p.h" +#include "qsgd3d12shadereffectnode_p.h" #include <private/qsgrenderer_p.h> #include <private/qquickwindow_p.h> #include <private/qquickprofiler_p.h> @@ -336,6 +337,7 @@ bool QSGD3D12RenderThread::event(QEvent *e) engine->waitGPU(); // Bye bye nodes... wd->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); } rc->invalidate(); QCoreApplication::processEvents(); @@ -486,6 +488,7 @@ void QSGD3D12RenderThread::sync(bool inExpose) if (Q_UNLIKELY(debug_loop())) qDebug("RT - sync - device was lost, resetting scenegraph"); QQuickWindowPrivate::get(exposedWindow)->cleanupNodesOnShutdown(); + QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache(); rc->invalidate(); } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index 49f517f877..a94cdf2827 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -42,10 +42,14 @@ #include "qsgd3d12engine_p.h" #include <QtCore/qfile.h> #include <QtQml/qqmlfile.h> +#include <qsgtextureprovider.h> #include <d3d12shader.h> #include <d3dcompiler.h> +#include "vs_shadereffectdefault.hlslh" +#include "ps_shadereffectdefault.hlslh" + QT_BEGIN_NAMESPACE // NOTE: Avoid categorized logging. It is slow. @@ -56,19 +60,432 @@ QT_BEGIN_NAMESPACE DECLARE_DEBUG_VAR(render) +void QSGD3D12ShaderLinker::reset(const QByteArray &vertBlob, const QByteArray &fragBlob) +{ + Q_ASSERT(!vertBlob.isEmpty() && !fragBlob.isEmpty()); + vs = vertBlob; + fs = fragBlob; + + error = false; + + constantBufferSize = 0; + constants.clear(); + samplers.clear(); + textures.clear(); +} + +void QSGD3D12ShaderLinker::feedVertexInput(const QSGShaderEffectNode::ShaderData &shader) +{ + bool foundPos = false, foundTexCoord = false; + + for (const auto &ip : qAsConst(shader.shaderInfo.inputParameters)) { + if (ip.semanticName == QByteArrayLiteral("POSITION")) + foundPos = true; + else if (ip.semanticName == QByteArrayLiteral("TEXCOORD")) + foundTexCoord = true; + } + + if (!foundPos) { + qWarning("ShaderEffect: No POSITION input found."); + error = true; + } + if (!foundTexCoord) { + qWarning("ShaderEffect: No TEXCOORD input found."); + error = true; + } + + // Nothing else to do here, the QSGGeometry::AttributeSet decides anyway + // and that is already generated by QQuickShaderEffectMesh via + // QSGGeometry::defaultAttributes_TexturedPoint2D() and has the semantics + // so it will just work. +} + +void QSGD3D12ShaderLinker::feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices) +{ + Q_ASSERT(shader.shaderInfo.variables.count() == shader.varData.count()); + if (!dirtyIndices) { + constantBufferSize = qMax(constantBufferSize, shader.shaderInfo.constantDataSize); + for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) { + const auto &var(shader.shaderInfo.variables.at(i)); + if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Constant) { + const auto &vd(shader.varData.at(i)); + Constant c; + c.size = var.size; + c.specialType = vd.specialType; + c.value = vd.value; + constants[var.offset] = c; + } + } + } else { + for (int idx : *dirtyIndices) + constants[shader.shaderInfo.variables.at(idx).offset].value = shader.varData.at(idx).value; + } +} + +void QSGD3D12ShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &shader) +{ + for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) { + const auto &var(shader.shaderInfo.variables.at(i)); + if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) { + const auto &vd(shader.varData.at(i)); + Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Unused); + samplers.insert(var.bindPoint); + } + } +} + +void QSGD3D12ShaderLinker::feedTextures(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices) +{ + if (!dirtyIndices) { + for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) { + const auto &var(shader.shaderInfo.variables.at(i)); + const auto &vd(shader.varData.at(i)); + if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Texture) { + Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source); + textures.insert(var.bindPoint, vd.value); + } + } + } else { + for (int idx : *dirtyIndices) + textures.insert(shader.shaderInfo.variables.at(idx).bindPoint, shader.varData.at(idx).value); + } +} + +void QSGD3D12ShaderLinker::dump() +{ + if (error) { + qDebug() << "Failed to generate program data"; + return; + } + qDebug() << "Combined shader data" << vs.size() << fs.size() << "cbuffer size" << constantBufferSize; + qDebug() << " - constants" << constants; + qDebug() << " - samplers" << samplers; + qDebug() << " - textures" << textures; +} + +QDebug operator<<(QDebug debug, const QSGD3D12ShaderLinker::Constant &c) +{ + QDebugStateSaver saver(debug); + debug.space(); + debug << "size" << c.size; + if (c.specialType != QSGShaderEffectNode::VariableData::None) + debug << "special" << c.specialType; + else + debug << "value" << c.value; + return debug; +} + +QSGD3D12ShaderEffectMaterial::QSGD3D12ShaderEffectMaterial(QSGD3D12ShaderEffectNode *node) + : node(node) +{ + setFlag(Blending | RequiresFullMatrix, true); // may be changed in sync() +} + +struct QSGD3D12ShaderMaterialTypeCache +{ + QSGMaterialType *get(const QByteArray &vs, const QByteArray &fs); + void reset() { qDeleteAll(m_types); m_types.clear(); } + + struct Key { + QByteArray blob[2]; + Key() { } + Key(const QByteArray &vs, const QByteArray &fs) { blob[0] = vs; blob[1] = fs; } + bool operator==(const Key &other) const { + return blob[0] == other.blob[0] && blob[1] == other.blob[1]; + } + }; + QHash<Key, QSGMaterialType *> m_types; +}; + +uint qHash(const QSGD3D12ShaderMaterialTypeCache::Key &key, uint seed = 0) +{ + uint hash = seed; + for (int i = 0; i < 2; ++i) + hash = hash * 31337 + qHash(key.blob[i]); + return hash; +} + +QSGMaterialType *QSGD3D12ShaderMaterialTypeCache::get(const QByteArray &vs, const QByteArray &fs) +{ + const Key k(vs, fs); + if (m_types.contains(k)) + return m_types.value(k); + + QSGMaterialType *t = new QSGMaterialType; + m_types.insert(k, t); + return t; +} + +Q_GLOBAL_STATIC(QSGD3D12ShaderMaterialTypeCache, shaderMaterialTypeCache) + +void QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache() +{ + shaderMaterialTypeCache()->reset(); +} + +QSGMaterialType *QSGD3D12ShaderEffectMaterial::type() const +{ + return mtype; +} + +int QSGD3D12ShaderEffectMaterial::compare(const QSGMaterial *other) const +{ + Q_ASSERT(other && type() == other->type()); + const QSGD3D12ShaderEffectMaterial *o = static_cast<const QSGD3D12ShaderEffectMaterial *>(other); + + if (int diff = cullMode - o->cullMode) + return diff; + + if (int diff = textureProviders.count() - o->textureProviders.count()) + return diff; + + if (linker.constants != o->linker.constants) + return 1; + + for (int i = 0; i < textureProviders.count(); ++i) { + QSGTextureProvider *tp1 = textureProviders.at(i); + QSGTextureProvider *tp2 = o->textureProviders.at(i); + if (!tp1 || !tp2) + return tp1 == tp2 ? 0 : 1; + QSGTexture *t1 = tp1->texture(); + QSGTexture *t2 = tp2->texture(); + if (!t1 || !t2) + return t1 == t2 ? 0 : 1; + if (int diff = t1->textureId() - t2->textureId()) + return diff; + } + + return 0; +} + +int QSGD3D12ShaderEffectMaterial::constantBufferSize() const +{ + return linker.constantBufferSize; +} + +void QSGD3D12ShaderEffectMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState) +{ + pipelineState->shaders.vs = reinterpret_cast<const quint8 *>(linker.vs.constData()); + pipelineState->shaders.vsSize = linker.vs.size(); + pipelineState->shaders.ps = reinterpret_cast<const quint8 *>(linker.fs.constData()); + pipelineState->shaders.psSize = linker.fs.size(); + + pipelineState->shaders.rootSig.textureViews.resize(textureProviders.count()); +} + +static inline QColor qsg_premultiply_color(const QColor &c) +{ + return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF()); +} + +QSGD3D12Material::UpdateResults QSGD3D12ShaderEffectMaterial::updatePipeline(const RenderState &state, + QSGD3D12PipelineState *, + ExtraState *, + quint8 *constantBuffer) +{ + QSGD3D12Material::UpdateResults r = 0; + quint8 *p = constantBuffer; + + for (auto it = linker.constants.constBegin(), itEnd = linker.constants.constEnd(); it != itEnd; ++it) { + quint8 *dst = p + it.key(); + const QSGD3D12ShaderLinker::Constant &c(it.value()); + if (c.specialType == QSGShaderEffectNode::VariableData::Opacity) { + if (state.isOpacityDirty()) { + const float f = state.opacity(); + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, &f, sizeof(f)); + r |= UpdatedConstantBuffer; + } + } else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) { + if (state.isMatrixDirty()) { + const int sz = 16 * sizeof(float); + Q_ASSERT(sz == c.size); + memcpy(dst, state.combinedMatrix().constData(), sz); + r |= UpdatedConstantBuffer; + } + } else if (c.specialType == QSGShaderEffectNode::VariableData::None) { + r |= UpdatedConstantBuffer; + switch (c.value.type()) { + case QMetaType::QColor: { + const QColor v = qsg_premultiply_color(qvariant_cast<QColor>(c.value)); + const float f[4] = { float(v.redF()), float(v.greenF()), float(v.blueF()), float(v.alphaF()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::Float: { + const float f = qvariant_cast<float>(c.value); + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, &f, sizeof(f)); + break; + } + case QMetaType::Double: { + const float f = float(qvariant_cast<double>(c.value)); + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, &f, sizeof(f)); + break; + } + case QMetaType::Int: { + const int i = c.value.toInt(); + Q_ASSERT(sizeof(i) == c.size); + memcpy(dst, &i, sizeof(i)); + break; + } + case QMetaType::Bool: { + const bool b = c.value.toBool(); + Q_ASSERT(sizeof(b) == c.size); + memcpy(dst, &b, sizeof(b)); + break; + } + case QMetaType::QTransform: { // float3x3 + const QTransform v = qvariant_cast<QTransform>(c.value); + const float m[3][3] = { + { float(v.m11()), float(v.m12()), float(v.m13()) }, + { float(v.m21()), float(v.m22()), float(v.m23()) }, + { float(v.m31()), float(v.m32()), float(v.m33()) } + }; + Q_ASSERT(sizeof(m) == c.size); + memcpy(dst, m[0], sizeof(m)); + break; + } + case QMetaType::QSize: + case QMetaType::QSizeF: { // float2 + const QSizeF v = c.value.toSizeF(); + const float f[2] = { float(v.width()), float(v.height()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::QPoint: + case QMetaType::QPointF: { // float2 + const QPointF v = c.value.toPointF(); + const float f[2] = { float(v.x()), float(v.y()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::QRect: + case QMetaType::QRectF: { // float4 + const QRectF v = c.value.toRectF(); + const float f[4] = { float(v.x()), float(v.y()), float(v.width()), float(v.height()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::QVector2D: { // float2 + const QVector2D v = qvariant_cast<QVector2D>(c.value); + const float f[2] = { float(v.x()), float(v.y()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::QVector3D: { // float3 + const QVector3D v = qvariant_cast<QVector3D>(c.value); + const float f[3] = { float(v.x()), float(v.y()), float(v.z()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::QVector4D: { // float4 + const QVector4D v = qvariant_cast<QVector4D>(c.value); + const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.w()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::QQuaternion: { // float4 + const QQuaternion v = qvariant_cast<QQuaternion>(c.value); + const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.scalar()) }; + Q_ASSERT(sizeof(f) == c.size); + memcpy(dst, f, sizeof(f)); + break; + } + case QMetaType::QMatrix4x4: { // float4x4 + const QMatrix4x4 v = qvariant_cast<QMatrix4x4>(c.value); + const int sz = 16 * sizeof(float); + Q_ASSERT(sz == c.size); + memcpy(dst, v.constData(), sz); + break; + } + default: + break; + } + } + } + + for (QSGTextureProvider *tp : textureProviders) { + if (tp) { + if (QSGTexture *t = tp->texture()) + t->bind(); + } + // ### have a dummy in case the texture provider is null + } + + // ### cull mode + + return r; +} + +void QSGD3D12ShaderEffectMaterial::updateTextureProviders(bool layoutChange) +{ + if (layoutChange) { + for (QSGTextureProvider *tp : textureProviders) { + if (tp) { + QObject::disconnect(tp, SIGNAL(textureChanged()), node, + SLOT(handleTextureChange())); + QObject::disconnect(tp, SIGNAL(destroyed(QObject*)), node, + SLOT(handleTextureProviderDestroyed(QObject*))); + } + } + + textureProviders.fill(nullptr, linker.textures.count()); + } + + for (auto it = linker.textures.constBegin(), itEnd = linker.textures.constEnd(); it != itEnd; ++it) { + const int bindPoint = it.key(); + // Now that the linker has merged the textures, we can switch over to a + // simple vector indexed by the binding point for textureProviders. + Q_ASSERT(bindPoint >= 0 && bindPoint < textureProviders.count()); + QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(it.value())); + QSGTextureProvider *newProvider = source && source->isTextureProvider() ? source->textureProvider() : nullptr; + QSGTextureProvider *&activeProvider(textureProviders[bindPoint]); + if (newProvider != activeProvider) { + if (activeProvider) { + QObject::disconnect(activeProvider, SIGNAL(textureChanged()), node, + SLOT(handleTextureChange())); + QObject::disconnect(activeProvider, SIGNAL(destroyed(QObject*)), node, + SLOT(handleTextureProviderDestroyed(QObject*))); + } + if (newProvider) { + Q_ASSERT_X(newProvider->thread() == QThread::currentThread(), + "QSGD3D12ShaderEffectMaterial::updateTextureProviders", + "Texture provider must belong to the rendering thread"); + QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(handleTextureChange())); + QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, + SLOT(handleTextureProviderDestroyed(QObject*))); + } else { + const char *typeName = source ? source->metaObject()->className() : it.value().typeName(); + qWarning("ShaderEffect: Texture t%d is not assigned a valid texture provider (%s).", + bindPoint, typeName); + } + activeProvider = newProvider; + } + } +} + QSGD3D12ShaderEffectNode::QSGD3D12ShaderEffectNode(QSGD3D12RenderContext *rc, QSGD3D12GuiThreadShaderEffectManager *mgr) : QSGShaderEffectNode(mgr), m_rc(rc), - m_mgr(mgr) + m_mgr(mgr), + m_material(this) { - // ### no material yet, it will just crash - //setMaterial(&m_material); + setFlag(UsePreprocess, true); + setMaterial(&m_material); } QRectF QSGD3D12ShaderEffectNode::normalizedTextureSubRect() const { - return QRectF(0, 0, 1, 1); - // ### + return QRectF(0, 1, 1, -1); // ### } void QSGD3D12ShaderEffectNode::sync(SyncData *syncData) @@ -76,7 +493,170 @@ void QSGD3D12ShaderEffectNode::sync(SyncData *syncData) if (Q_UNLIKELY(debug_render())) qDebug() << "shadereffect node sync" << syncData->dirty; - // ### + if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) { + m_material.setFlag(QSGMaterial::Blending, syncData->blending); + markDirty(QSGNode::DirtyMaterial); + } + + if (m_material.cullMode != syncData->cullMode) { + m_material.cullMode = syncData->cullMode; + markDirty(QSGNode::DirtyMaterial); + } + + if (syncData->dirty & QSGShaderEffectNode::DirtyShaders) { + QByteArray vertBlob, fragBlob; + + m_material.hasCustomVertexShader = syncData->vertex.shader->valid; + if (m_material.hasCustomVertexShader) { + vertBlob = syncData->vertex.shader->shaderInfo.blob; + } else { + vertBlob = QByteArray::fromRawData(reinterpret_cast<const char *>(g_VS_DefaultShaderEffect), + sizeof(g_VS_DefaultShaderEffect)); + } + + m_material.hasCustomFragmentShader = syncData->fragment.shader->valid; + if (m_material.hasCustomFragmentShader) { + fragBlob = syncData->fragment.shader->shaderInfo.blob; + } else { + fragBlob = QByteArray::fromRawData(reinterpret_cast<const char *>(g_PS_DefaultShaderEffect), + sizeof(g_PS_DefaultShaderEffect)); + } + + m_material.mtype = shaderMaterialTypeCache()->get(vertBlob, fragBlob); + m_material.linker.reset(vertBlob, fragBlob); + + if (m_material.hasCustomVertexShader) { + m_material.linker.feedVertexInput(*syncData->vertex.shader); + m_material.linker.feedConstants(*syncData->vertex.shader); + m_material.linker.feedSamplers(*syncData->vertex.shader); + m_material.linker.feedTextures(*syncData->vertex.shader); + } else { + QSGShaderEffectNode::ShaderData defaultSD; + defaultSD.valid = true; + defaultSD.shaderInfo.blob = vertBlob; + defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex; + + QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter ip; + ip.semanticName = QByteArrayLiteral("POSITION"); + defaultSD.shaderInfo.inputParameters.append(ip); + ip.semanticName = QByteArrayLiteral("TEXCOORD"); + defaultSD.shaderInfo.inputParameters.append(ip); + + // { float4x4 qt_Matrix; float qt_Opacity; } where only the matrix is used + QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v; + v.name = QByteArrayLiteral("qt_Matrix"); + v.offset = 0; + v.size = 16 * sizeof(float); + defaultSD.shaderInfo.variables.append(v); + QSGShaderEffectNode::VariableData vd; + vd.specialType = QSGShaderEffectNode::VariableData::Matrix; + defaultSD.varData.append(vd); + + defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float); + + m_material.linker.feedVertexInput(defaultSD); + m_material.linker.feedConstants(defaultSD); + } + + if (m_material.hasCustomFragmentShader) { + m_material.linker.feedConstants(*syncData->fragment.shader); + m_material.linker.feedSamplers(*syncData->fragment.shader); + m_material.linker.feedTextures(*syncData->fragment.shader); + } else { + QSGShaderEffectNode::ShaderData defaultSD; + defaultSD.valid = true; + defaultSD.shaderInfo.blob = fragBlob; + defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment; + + // { float4x4 qt_Matrix; float qt_Opacity; } where only the opacity is used + QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v; + v.name = QByteArrayLiteral("qt_Opacity"); + v.offset = 16 * sizeof(float); + v.size = sizeof(float); + defaultSD.shaderInfo.variables.append(v); + QSGShaderEffectNode::VariableData vd; + vd.specialType = QSGShaderEffectNode::VariableData::Opacity; + defaultSD.varData.append(vd); + + v.name = QByteArrayLiteral("source"); + v.bindPoint = 0; + v.type = QSGGuiThreadShaderEffectManager::ShaderInfo::Texture; + defaultSD.shaderInfo.variables.append(v); + vd.specialType = QSGShaderEffectNode::VariableData::Source; + defaultSD.varData.append(vd); + + v.name = QByteArrayLiteral("sourceSampler"); + v.bindPoint = 0; + v.type = QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler; + defaultSD.shaderInfo.variables.append(v); + vd.specialType = QSGShaderEffectNode::VariableData::Unused; + defaultSD.varData.append(vd); + + defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float); + + m_material.linker.feedConstants(defaultSD); + m_material.linker.feedSamplers(defaultSD); + m_material.linker.feedTextures(defaultSD); + } + + m_material.updateTextureProviders(true); + markDirty(QSGNode::DirtyMaterial); + + if (Q_UNLIKELY(debug_render())) + m_material.linker.dump(); + } else { + if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) { + if (!syncData->vertex.dirtyConstants->isEmpty()) + m_material.linker.feedConstants(*syncData->vertex.shader, syncData->vertex.dirtyConstants); + if (!syncData->fragment.dirtyConstants->isEmpty()) + m_material.linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants); + markDirty(QSGNode::DirtyMaterial); + if (Q_UNLIKELY(debug_render())) + m_material.linker.dump(); + } + + if (syncData->dirty & QSGShaderEffectNode::DirtyShaderTexture) { + if (!syncData->vertex.dirtyTextures->isEmpty()) + m_material.linker.feedTextures(*syncData->vertex.shader, syncData->vertex.dirtyTextures); + if (!syncData->fragment.dirtyTextures->isEmpty()) + m_material.linker.feedTextures(*syncData->fragment.shader, syncData->fragment.dirtyTextures); + m_material.updateTextureProviders(false); + markDirty(QSGNode::DirtyMaterial); + if (Q_UNLIKELY(debug_render())) + m_material.linker.dump(); + } + } + + if (bool(m_material.flags() & QSGMaterial::RequiresFullMatrix) != m_material.hasCustomVertexShader) { + m_material.setFlag(QSGMaterial::RequiresFullMatrix, m_material.hasCustomVertexShader); + markDirty(QSGNode::DirtyMaterial); + } + + // ### texture subrect +} + +void QSGD3D12ShaderEffectNode::handleTextureChange() +{ + markDirty(QSGNode::DirtyMaterial); + emit m_mgr->textureChanged(); +} + +void QSGD3D12ShaderEffectNode::handleTextureProviderDestroyed(QObject *object) +{ + for (QSGTextureProvider *&tp : m_material.textureProviders) { + if (tp == object) + tp = nullptr; + } +} + +void QSGD3D12ShaderEffectNode::preprocess() +{ + for (QSGTextureProvider *tp : m_material.textureProviders) { + if (tp) { + if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(tp->texture())) + texture->updateTexture(); + } + } } QSGGuiThreadShaderEffectManager::ShaderType QSGD3D12GuiThreadShaderEffectManager::shaderType() const @@ -163,6 +743,8 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, Shader const int cbufferCount = shaderDesc.ConstantBuffers; const int boundResCount = shaderDesc.BoundResources; + result->constantDataSize = 0; + if (ieCount < 1) { qWarning("Invalid shader: Not enough input parameters (%d)", ieCount); return false; @@ -213,6 +795,7 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, Shader continue; } gotCBuffer = true; + result->constantDataSize = bufDesc.Size; for (uint cbIdx = 0; cbIdx < bufDesc.Variables; ++cbIdx) { ID3D12ShaderReflectionVariable *cvar = cbuf->GetVariableByIndex(cbIdx); D3D12_SHADER_VARIABLE_DESC varDesc; @@ -220,6 +803,9 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, Shader qWarning("D3D reflection: Failed to query constant buffer variable %d", cbIdx); return false; } + // we report the full size of the buffer but only return variables that are actually used by this shader + if (!(varDesc.uFlags & D3D_SVF_USED)) + continue; ShaderInfo::Variable v; v.type = ShaderInfo::Constant; v.name = QByteArray(varDesc.Name); @@ -270,14 +856,8 @@ bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, Shader } if (Q_UNLIKELY(debug_render())) { - for (int i = 0; i < result->inputParameters.count(); ++i) { - const ShaderInfo::InputParameter &p(result->inputParameters.at(i)); - qDebug() << "input" << i << p; - } - for (int i = 0; i < result->variables.count(); ++i) { - const ShaderInfo::Variable &v(result->variables.at(i)); - qDebug() << "var" << i << v; - } + qDebug() << "Input:" << result->inputParameters; + qDebug() << "Variables:" << result->variables << "cbuffer size" << result->constantDataSize; } return true; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h index f88e028b35..d58233481a 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h @@ -52,24 +52,96 @@ // #include <private/qsgadaptationlayer_p.h> -#include "qsgd3d12builtinmaterials_p.h" +#include "qsgd3d12material_p.h" QT_BEGIN_NAMESPACE class QSGD3D12RenderContext; class QSGD3D12GuiThreadShaderEffectManager; +class QSGD3D12ShaderEffectNode; -class QSGD3D12ShaderEffectNode : public QSGShaderEffectNode +class QSGD3D12ShaderLinker { public: + void reset(const QByteArray &vertBlob, const QByteArray &fragBlob); + + void feedVertexInput(const QSGShaderEffectNode::ShaderData &shader); + void feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr); + void feedSamplers(const QSGShaderEffectNode::ShaderData &shader); + void feedTextures(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr); + + void dump(); + + struct Constant { + uint size; + QSGShaderEffectNode::VariableData::SpecialType specialType; + QVariant value; + bool operator==(const Constant &other) const { + return size == other.size && specialType == other.specialType + && (specialType == QSGShaderEffectNode::VariableData::None ? value == other.value : true); + } + }; + + bool error; + QByteArray vs; + QByteArray fs; + uint constantBufferSize; + QHash<uint, Constant> constants; // offset -> Constant + QSet<int> samplers; // bindpoint + QHash<int, QVariant> textures; // bindpoint -> value (source ref) +}; + +QDebug operator<<(QDebug debug, const QSGD3D12ShaderLinker::Constant &c); + +class QSGD3D12ShaderEffectMaterial : public QSGD3D12Material +{ +public: + QSGD3D12ShaderEffectMaterial(QSGD3D12ShaderEffectNode *node); + + QSGMaterialType *type() const override; + int compare(const QSGMaterial *other) const override; + + int constantBufferSize() const override; + void preparePipeline(QSGD3D12PipelineState *pipelineState) override; + UpdateResults updatePipeline(const RenderState &state, + QSGD3D12PipelineState *pipelineState, + ExtraState *extraState, + quint8 *constantBuffer) override; + + void updateTextureProviders(bool layoutChange); + + QSGD3D12ShaderEffectNode *node; + bool valid = false; + QSGShaderEffectNode::CullMode cullMode = QSGShaderEffectNode::NoCulling; + bool hasCustomVertexShader = false; + bool hasCustomFragmentShader = false; + QSGD3D12ShaderLinker linker; + QSGMaterialType *mtype = nullptr; + QVector<QSGTextureProvider *> textureProviders; +}; + +class QSGD3D12ShaderEffectNode : public QObject, public QSGShaderEffectNode +{ + Q_OBJECT + +public: QSGD3D12ShaderEffectNode(QSGD3D12RenderContext *rc, QSGD3D12GuiThreadShaderEffectManager *mgr); QRectF normalizedTextureSubRect() const override; void sync(SyncData *syncData) override; + static void cleanupMaterialTypeCache(); + + void preprocess() override; + +private Q_SLOTS: + void handleTextureChange(); + void handleTextureProviderDestroyed(QObject *object); + private: QSGD3D12RenderContext *m_rc; QSGD3D12GuiThreadShaderEffectManager *m_mgr; + QSGD3D12ShaderEffectMaterial m_material; }; class QSGD3D12GuiThreadShaderEffectManager : public QSGGuiThreadShaderEffectManager diff --git a/src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl b/src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl new file mode 100644 index 0000000000..94672d6267 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl @@ -0,0 +1,27 @@ +cbuffer ConstantBuffer : register(b0) +{ + float4x4 qt_Matrix; + float qt_Opacity; +}; + +struct PSInput +{ + float4 position : SV_POSITION; + float2 coord : TEXCOORD0; +}; + +Texture2D source : register(t0); +SamplerState sourceSampler : register(s0); + +PSInput VS_DefaultShaderEffect(float4 position : POSITION, float2 coord : TEXCOORD0) +{ + PSInput result; + result.position = mul(qt_Matrix, position); + result.coord = coord; + return result; +} + +float4 PS_DefaultShaderEffect(PSInput input) : SV_TARGET +{ + return source.Sample(sourceSampler, input.coord) * qt_Opacity; +} diff --git a/src/plugins/scenegraph/d3d12/shaders/shaders.pri b/src/plugins/scenegraph/d3d12/shaders/shaders.pri index 02786e2606..296e119cb2 100644 --- a/src/plugins/scenegraph/d3d12/shaders/shaders.pri +++ b/src/plugins/scenegraph/d3d12/shaders/shaders.pri @@ -88,6 +88,16 @@ outlinedtext_pshader.header = ps_outlinedtext.hlslh outlinedtext_pshader.entry = PS_OutlinedText outlinedtext_pshader.type = ps_5_0 +shadereffectdefault_VSPS = $$PWD/shadereffectdefault.hlsl +shadereffectdefault_vshader.input = shadereffectdefault_VSPS +shadereffectdefault_vshader.header = vs_shadereffectdefault.hlslh +shadereffectdefault_vshader.entry = VS_DefaultShaderEffect +shadereffectdefault_vshader.type = vs_5_0 +shadereffectdefault_pshader.input = shadereffectdefault_VSPS +shadereffectdefault_pshader.header = ps_shadereffectdefault.hlslh +shadereffectdefault_pshader.entry = PS_DefaultShaderEffect +shadereffectdefault_pshader.type = ps_5_0 + HLSL_SHADERS = \ vertexcolor_vshader vertexcolor_pshader \ stencilclip_vshader stencilclip_pshader \ @@ -96,6 +106,7 @@ HLSL_SHADERS = \ smoothtexture_vshader smoothtexture_pshader \ mipmapgen_cshader \ textmask_vshader textmask_pshader24 textmask_pshader32 textmask_pshader8 \ - styledtext_vshader styledtext_pshader outlinedtext_vshader outlinedtext_pshader + styledtext_vshader styledtext_pshader outlinedtext_vshader outlinedtext_pshader \ + shadereffectdefault_vshader shadereffectdefault_pshader load(hlsl_bytecode_header) |