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 | |
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')
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h | 10 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp | 3 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp | 608 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h | 76 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl | 27 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/shaders/shaders.pri | 13 | ||||
-rw-r--r-- | src/quick/items/qquickgenericshadereffect.cpp | 27 | ||||
-rw-r--r-- | src/quick/items/qquickgenericshadereffect_p.h | 2 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgadaptationlayer.cpp | 15 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgadaptationlayer_p.h | 29 |
10 files changed, 769 insertions, 41 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) diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp index 769c408672..0e88868aea 100644 --- a/src/quick/items/qquickgenericshadereffect.cpp +++ b/src/quick/items/qquickgenericshadereffect.cpp @@ -80,7 +80,7 @@ void QQuickGenericShaderEffect::setFragmentShader(const QByteArray &src) return; m_fragShader = src; - m_dirty |= QSGShaderEffectNode::DirtyShaderFragment; + m_dirty |= QSGShaderEffectNode::DirtyShaders; if (m_item->isComponentComplete()) updateShader(Fragment, src); @@ -95,7 +95,7 @@ void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src) return; m_vertShader = src; - m_dirty |= QSGShaderEffectNode::DirtyShaderVertex; + m_dirty |= QSGShaderEffectNode::DirtyShaders; if (m_item->isComponentComplete()) updateShader(Vertex, src); @@ -264,9 +264,7 @@ QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQui if (!node) { QSGRenderContext *rc = QQuickWindowPrivate::get(m_item->window())->context; node = rc->sceneGraphContext()->createShaderEffectNode(rc, mgr); - m_dirty = QSGShaderEffectNode::DirtyShaderVertex | QSGShaderEffectNode::DirtyShaderFragment - | QSGShaderEffectNode::DirtyShaderConstant | QSGShaderEffectNode::DirtyShaderTexture - | QSGShaderEffectNode::DirtyShaderGeometry | QSGShaderEffectNode::DirtyShaderMesh; + m_dirty = QSGShaderEffectNode::DirtyShaderAll; } // Dirty mesh and geometry are handled here, the rest is passed on to the node. @@ -295,12 +293,20 @@ QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQui sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode); sd.blending = m_blending; sd.supportsAtlasTextures = m_supportsAtlasTextures; - sd.vertexShader = (m_dirty & QSGShaderEffectNode::DirtyShaderVertex) ? &m_shaders[Vertex] : nullptr; - sd.fragmentShader = (m_dirty & QSGShaderEffectNode::DirtyShaderFragment) ? &m_shaders[Fragment] : nullptr; + sd.vertex.shader = &m_shaders[Vertex]; + sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex]; + sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex]; + sd.fragment.shader = &m_shaders[Fragment]; + sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment]; + sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment]; node->sync(&sd); m_dirty = 0; + for (int i = 0; i < NShader; ++i) { + m_dirtyConstants[i].clear(); + m_dirtyTextures[i].clear(); + } return node; } @@ -396,6 +402,7 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray // For file-based shader source/bytecode this is where the data is pulled // in from the file. QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo; + // ### this will need some sort of caching mechanism if (!mgr->reflect(src, &shaderInfo)) { qWarning("ShaderEffect: shader reflection failed for %s", src.constData()); m_shaders[shaderType].valid = false; @@ -416,7 +423,7 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects(); // Hook up the signals to get notified about changes for properties that - // correspond to variables in the shader. + // correspond to variables in the shader. Store also the values. for (int i = 0; i < varCount; ++i) { const auto &v(shaderInfo.variables.at(i)); QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]); @@ -433,7 +440,7 @@ void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray // The value of a property corresponding to a sampler is the source // item ref, unless there are separate texture objects in which case - // the sampler is ignored. + // the sampler is ignored (here). if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) { if (texturesSeparate) { vd.specialType = QSGShaderEffectNode::VariableData::Unused; @@ -532,10 +539,12 @@ void QQuickGenericShaderEffect::propertyChanged(int mappedId) } m_dirty |= QSGShaderEffectNode::DirtyShaderTexture; + m_dirtyTextures[type].insert(idx); } else { vd.value = m_item->property(v.name.constData()); m_dirty |= QSGShaderEffectNode::DirtyShaderConstant; + m_dirtyConstants[type].insert(idx); } m_item->update(); diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h index bc90b493ca..5f6652ec32 100644 --- a/src/quick/items/qquickgenericshadereffect_p.h +++ b/src/quick/items/qquickgenericshadereffect_p.h @@ -132,6 +132,8 @@ private: QSGShaderEffectNode::ShaderData m_shaders[NShader]; QSGShaderEffectNode::DirtyShaderFlags m_dirty; + QSet<int> m_dirtyConstants[NShader]; + QSet<int> m_dirtyTextures[NShader]; struct SignalMapper { SignalMapper() : mapper(nullptr), active(false) { } diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index b0259c50b0..50986e2528 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -522,11 +522,16 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p) { - debug << p.semanticName << p.semanticIndex; + QDebugStateSaver saver(debug); + debug.space(); + debug << p.semanticName << "semindex" << p.semanticIndex; return debug; } + QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v) { + QDebugStateSaver saver(debug); + debug.space(); debug << v.name; switch (v.type) { case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant: @@ -543,6 +548,14 @@ QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInf } return debug; } + +QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd) +{ + QDebugStateSaver saver(debug); + debug.space(); + debug << vd.specialType; + return debug; +} #endif QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 5155cdd719..c06e681d30 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -289,6 +289,7 @@ public: Type type; QVector<InputParameter> inputParameters; QVector<Variable> variables; + uint constantDataSize; }; virtual bool reflect(const QByteArray &src, ShaderInfo *result) = 0; @@ -307,12 +308,13 @@ class Q_QUICK_PRIVATE_EXPORT QSGShaderEffectNode : public QSGVisitableNode { public: enum DirtyShaderFlag { - DirtyShaderVertex = 0x01, - DirtyShaderFragment = 0x02, - DirtyShaderConstant = 0x04, - DirtyShaderTexture = 0x08, - DirtyShaderGeometry = 0x10, - DirtyShaderMesh = 0x20 + DirtyShaders = 0x01, + DirtyShaderConstant = 0x02, + DirtyShaderTexture = 0x04, + DirtyShaderGeometry = 0x08, + DirtyShaderMesh = 0x10, + + DirtyShaderAll = 0xFF }; Q_DECLARE_FLAGS(DirtyShaderFlags, DirtyShaderFlag) @@ -323,7 +325,7 @@ public: }; struct VariableData { - enum SpecialType { None, Unused, SubRect, Opacity, Matrix, Source }; + enum SpecialType { None, Unused, Source, SubRect, Opacity, Matrix }; QVariant value; SpecialType specialType; @@ -341,8 +343,13 @@ public: CullMode cullMode; bool blending; bool supportsAtlasTextures; - ShaderData *vertexShader; - ShaderData *fragmentShader; + struct ShaderSyncData { + const ShaderData *shader; + const QSet<int> *dirtyConstants; + const QSet<int> *dirtyTextures; + }; + ShaderSyncData vertex; + ShaderSyncData fragment; }; // Each ShaderEffect item has one node (render thread) and one manager (gui thread). @@ -356,6 +363,10 @@ public: Q_DECLARE_OPERATORS_FOR_FLAGS(QSGShaderEffectNode::DirtyShaderFlags) +#ifndef QT_NO_DEBUG_STREAM +Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd); +#endif + class Q_QUICK_PRIVATE_EXPORT QSGGlyphNode : public QSGVisitableNode { public: |