From 5fcd32c86413df81b36be6fed162d3da5c7556e2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 8 Oct 2019 15:13:33 +0200 Subject: Make twotextureproviders work on the rhi rendering path Involves porting away from the ill-fated QSGSimpleMaterial. This marks the first appearance of QSGMaterialRhiShader in any of the examples. Task-number: QTBUG-79086 Change-Id: I464e2bd06a6b5b19775b1b35265bb843979ec4b4 Reviewed-by: Paul Olav Tvete --- .../quick/scenegraph/twotextureproviders/main.qml | 17 +- .../twotextureproviders/shaders/+qsb/checker.frag | Bin 0 -> 1824 bytes .../shaders/+qsb/xorblender.frag | Bin 0 -> 1941 bytes .../shaders/+qsb/xorblender.vert | Bin 0 -> 1815 bytes .../twotextureproviders/shaders/checker.frag | 14 ++ .../twotextureproviders/shaders/checker_rhi.frag | 25 +++ .../twotextureproviders/shaders/xorblender.frag | 12 ++ .../twotextureproviders/shaders/xorblender.vert | 12 ++ .../shaders/xorblender_rhi.frag | 20 ++ .../shaders/xorblender_rhi.vert | 19 ++ .../twotextureproviders/twotextureproviders.qrc | 8 + .../scenegraph/twotextureproviders/xorblender.cpp | 220 +++++++++++++++------ 12 files changed, 274 insertions(+), 73 deletions(-) create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/+qsb/checker.frag create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.frag create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.vert create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/checker.frag create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/checker_rhi.frag create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/xorblender.vert create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.frag create mode 100644 examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.vert (limited to 'examples/quick/scenegraph/twotextureproviders') diff --git a/examples/quick/scenegraph/twotextureproviders/main.qml b/examples/quick/scenegraph/twotextureproviders/main.qml index 296df766a1..b9e516f4b7 100644 --- a/examples/quick/scenegraph/twotextureproviders/main.qml +++ b/examples/quick/scenegraph/twotextureproviders/main.qml @@ -64,20 +64,9 @@ Item { property size pixelSize: Qt.size(width / tileSize, height / tileSize); - fragmentShader: - " - uniform lowp vec4 color1; - uniform lowp vec4 color2; - uniform highp vec2 pixelSize; - varying highp vec2 qt_TexCoord0; - void main() { - highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize)); - if (tc.x != tc.y) - gl_FragColor = color1; - else - gl_FragColor = color2; - } - " + // Will automatically pick either checker.frag or +qsb/checker.frag + // thanks to file selectors. + fragmentShader: "qrc:/scenegraph/twotextureproviders/shaders/checker.frag" } width: 320 diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/checker.frag b/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/checker.frag new file mode 100644 index 0000000000..edcfad488b Binary files /dev/null and b/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/checker.frag differ diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.frag b/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.frag new file mode 100644 index 0000000000..7a5280ba08 Binary files /dev/null and b/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.frag differ diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.vert b/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.vert new file mode 100644 index 0000000000..d643c7be6a Binary files /dev/null and b/examples/quick/scenegraph/twotextureproviders/shaders/+qsb/xorblender.vert differ diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/checker.frag b/examples/quick/scenegraph/twotextureproviders/shaders/checker.frag new file mode 100644 index 0000000000..044b3bad58 --- /dev/null +++ b/examples/quick/scenegraph/twotextureproviders/shaders/checker.frag @@ -0,0 +1,14 @@ +uniform lowp vec4 color1; +uniform lowp vec4 color2; +uniform highp vec2 pixelSize; + +varying highp vec2 qt_TexCoord0; + +void main() +{ + highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize)); + if (tc.x != tc.y) + gl_FragColor = color1; + else + gl_FragColor = color2; +} diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/checker_rhi.frag b/examples/quick/scenegraph/twotextureproviders/shaders/checker_rhi.frag new file mode 100644 index 0000000000..0932bc8c37 --- /dev/null +++ b/examples/quick/scenegraph/twotextureproviders/shaders/checker_rhi.frag @@ -0,0 +1,25 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + // preamble as required by ShaderEffect + mat4 qt_Matrix; + float qt_Opacity; + + // our custom members, connected automatically to QML object properties + vec4 color1; + vec4 color2; + vec2 pixelSize; +} ubuf; + +void main() +{ + vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * ubuf.pixelSize)); + if (tc.x != tc.y) + fragColor = ubuf.color1; + else + fragColor = ubuf.color2; +} diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag new file mode 100644 index 0000000000..f67735312d --- /dev/null +++ b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.frag @@ -0,0 +1,12 @@ +uniform lowp float qt_Opacity; +uniform lowp sampler2D uSource1; +uniform lowp sampler2D uSource2; + +varying highp vec2 vTexCoord; + +void main() +{ + lowp vec4 p1 = texture2D(uSource1, vTexCoord); + lowp vec4 p2 = texture2D(uSource2, vTexCoord); + gl_FragColor = (p1 * (1.0 - p2.a) + p2 * (1.0 - p1.a)) * qt_Opacity; +} diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.vert b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.vert new file mode 100644 index 0000000000..ac9f1364d6 --- /dev/null +++ b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender.vert @@ -0,0 +1,12 @@ +attribute highp vec4 aVertex; +attribute highp vec2 aTexCoord; + +uniform highp mat4 qt_Matrix; + +varying highp vec2 vTexCoord; + +void main() +{ + gl_Position = qt_Matrix * aVertex; + vTexCoord = aTexCoord; +} diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.frag b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.frag new file mode 100644 index 0000000000..bc68160c1f --- /dev/null +++ b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.frag @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in vec2 vTexCoord; + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +} ubuf; + +layout(binding = 1) uniform sampler2D uSource1; +layout(binding = 2) uniform sampler2D uSource2; + +void main() +{ + lowp vec4 p1 = texture(uSource1, vTexCoord); + lowp vec4 p2 = texture(uSource2, vTexCoord); + fragColor = (p1 * (1.0 - p2.a) + p2 * (1.0 - p1.a)) * ubuf.qt_Opacity; +} diff --git a/examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.vert b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.vert new file mode 100644 index 0000000000..41000bde04 --- /dev/null +++ b/examples/quick/scenegraph/twotextureproviders/shaders/xorblender_rhi.vert @@ -0,0 +1,19 @@ +#version 440 + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec2 aTexCoord; + +layout(location = 0) out vec2 vTexCoord; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +} ubuf; + +out gl_PerVertex { vec4 gl_Position; }; + +void main() +{ + gl_Position = ubuf.qt_Matrix * aVertex; + vTexCoord = aTexCoord; +} diff --git a/examples/quick/scenegraph/twotextureproviders/twotextureproviders.qrc b/examples/quick/scenegraph/twotextureproviders/twotextureproviders.qrc index 1424333ce5..8022a77a1e 100644 --- a/examples/quick/scenegraph/twotextureproviders/twotextureproviders.qrc +++ b/examples/quick/scenegraph/twotextureproviders/twotextureproviders.qrc @@ -1,5 +1,13 @@ main.qml + + shaders/checker.frag + shaders/xorblender.vert + shaders/xorblender.frag + + shaders/+qsb/checker.frag + shaders/+qsb/xorblender.vert + shaders/+qsb/xorblender.frag diff --git a/examples/quick/scenegraph/twotextureproviders/xorblender.cpp b/examples/quick/scenegraph/twotextureproviders/xorblender.cpp index 8d7597addf..d5881b9adc 100644 --- a/examples/quick/scenegraph/twotextureproviders/xorblender.cpp +++ b/examples/quick/scenegraph/twotextureproviders/xorblender.cpp @@ -55,7 +55,7 @@ #include #include -#include +#include #include #include #include @@ -67,64 +67,170 @@ * a custom material. */ -struct XorBlendState { - QSGTexture *texture1; - QSGTexture *texture2; +class XorBlendMaterial : public QSGMaterial +{ +public: + XorBlendMaterial(); + QSGMaterialType *type() const override; + QSGMaterialShader *createShader() const override; + int compare(const QSGMaterial *other) const override; + + struct { + QSGTexture *texture1 = nullptr; + QSGTexture *texture2 = nullptr; + } state; }; -class XorBlendShader : public QSGSimpleMaterialShader +class XorBlendShader : public QSGMaterialShader // for when the scenegraph is using OpenGL directly { - QSG_DECLARE_SIMPLE_SHADER(XorBlendShader, XorBlendState) public: + XorBlendShader(); + void initialize() override; + char const *const *attributeNames() const override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; - const char *vertexShader() const override { - return - "attribute highp vec4 aVertex; \n" - "attribute highp vec2 aTexCoord; \n" - "uniform highp mat4 qt_Matrix; \n" - "varying highp vec2 vTexCoord; \n" - "void main() { \n" - " gl_Position = qt_Matrix * aVertex; \n" - " vTexCoord = aTexCoord; \n" - "}"; - } +private: + int m_matrix_id; + int m_opacity_id; +}; - const char *fragmentShader() const override { - return - "uniform lowp float qt_Opacity; \n" - "uniform lowp sampler2D uSource1; \n" - "uniform lowp sampler2D uSource2; \n" - "varying highp vec2 vTexCoord; \n" - "void main() { \n" - " lowp vec4 p1 = texture2D(uSource1, vTexCoord); \n" - " lowp vec4 p2 = texture2D(uSource2, vTexCoord); \n" - " gl_FragColor = (p1 * (1.0 - p2.a) + p2 * (1.0 - p1.a)) * qt_Opacity; \n" - "}"; - } +class XorBlendRhiShader : public QSGMaterialRhiShader // for when the scenegraph is using QRhi +{ +public: + XorBlendRhiShader(); + bool updateUniformData(RenderState &state, + QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; + void updateSampledImage(RenderState &state, int binding, QSGTexture **texture, + QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; +}; + +XorBlendMaterial::XorBlendMaterial() +{ + setFlag(SupportsRhiShader); + setFlag(Blending); +} + +QSGMaterialShader *XorBlendMaterial::createShader() const +{ + if (flags().testFlag(RhiShaderWanted)) + return new XorBlendRhiShader; + else + return new XorBlendShader; +} + +QSGMaterialType *XorBlendMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +int XorBlendMaterial::compare(const QSGMaterial *o) const +{ + Q_ASSERT(o && type() == o->type()); + const XorBlendMaterial *other = static_cast(o); + + if (!state.texture1 || !other->state.texture1) + return state.texture1 ? 1 : -1; + + if (!state.texture2 || !other->state.texture2) + return state.texture2 ? -1 : 1; + + if (int diff = state.texture1->comparisonKey() - other->state.texture1->comparisonKey()) + return diff; + + if (int diff = state.texture2->comparisonKey() - other->state.texture2->comparisonKey()) + return diff; - QList attributes() const override { - return QList() << "aVertex" << "aTexCoord"; + return 0; +} + +XorBlendShader::XorBlendShader() +{ + setShaderSourceFile(QOpenGLShader::Vertex, QLatin1String(":/scenegraph/twotextureproviders/shaders/xorblender.vert")); + setShaderSourceFile(QOpenGLShader::Fragment, QLatin1String(":/scenegraph/twotextureproviders/shaders/xorblender.frag")); +} + +void XorBlendShader::initialize() +{ + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_opacity_id = program()->uniformLocation("qt_Opacity"); + // The texture units never change, only the textures we bind to them so + // we set these once and for all here. + program()->setUniformValue("uSource1", 0); // GL_TEXTURE0 + program()->setUniformValue("uSource2", 1); // GL_TEXTURE1 +} + +char const *const *XorBlendShader::attributeNames() const +{ + static char const *const attr[] = { "aVertex", "aTexCoord", nullptr }; + return attr; +} + +void XorBlendShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) +{ + XorBlendMaterial *material = static_cast(newEffect); + + if (state.isMatrixDirty()) + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + + if (state.isOpacityDirty()) + program()->setUniformValue(m_opacity_id, state.opacity()); + + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + // We bind the textures in inverse order so that we leave the updateState + // function with GL_TEXTURE0 as the active texture unit. This is maintain + // the "contract" that updateState should not mess up the GL state beyond + // what is needed for this material. + f->glActiveTexture(GL_TEXTURE1); + material->state.texture2->bind(); + f->glActiveTexture(GL_TEXTURE0); + material->state.texture1->bind(); +} + +XorBlendRhiShader::XorBlendRhiShader() +{ + setShaderFileName(VertexStage, QLatin1String(":/scenegraph/twotextureproviders/shaders/+qsb/xorblender.vert")); + setShaderFileName(FragmentStage, QLatin1String(":/scenegraph/twotextureproviders/shaders/+qsb/xorblender.frag")); +} + +bool XorBlendRhiShader::updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *) +{ + bool changed = false; + QByteArray *buf = state.uniformData(); + Q_ASSERT(buf->size() >= 68); + + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.combinedMatrix(); + memcpy(buf->data(), m.constData(), 64); + changed = true; } - void updateState(const XorBlendState *state, const XorBlendState *) override { - QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - // We bind the textures in inverse order so that we leave the updateState - // function with GL_TEXTURE0 as the active texture unit. This is maintain - // the "contract" that updateState should not mess up the GL state beyond - // what is needed for this material. - f->glActiveTexture(GL_TEXTURE1); - state->texture2->bind(); - f->glActiveTexture(GL_TEXTURE0); - state->texture1->bind(); + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(buf->data() + 64, &opacity, 4); + changed = true; } - void resolveUniforms() override { - // The texture units never change, only the texturess we bind to them so - // we set these once and for all here. - program()->setUniformValue("uSource1", 0); // GL_TEXTURE0 - program()->setUniformValue("uSource2", 1); // GL_TEXTURE1 + return changed; +} + +void XorBlendRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture, + QSGMaterial *newMaterial, QSGMaterial *) +{ + Q_UNUSED(state); + + XorBlendMaterial *mat = static_cast(newMaterial); + switch (binding) { // the binding for the sampler2Ds in the fragment shader + case 1: + *texture = mat->state.texture1; + break; + case 2: + *texture = mat->state.texture2; + break; + default: + return; } -}; +} /* The rendering is split into two nodes. The top-most node is not actually * rendering anything, but is responsible for managing the texture providers. @@ -148,10 +254,7 @@ public: setFlag(QSGNode::UsePreprocess, true); // Set up material so it is all set for later.. - m_material = XorBlendShader::createMaterial(); - m_material->state()->texture1 = nullptr; - m_material->state()->texture2 = nullptr; - m_material->setFlag(QSGMaterial::Blending); + m_material = new XorBlendMaterial; m_node.setMaterial(m_material); m_node.setFlag(QSGNode::OwnsMaterial); @@ -166,25 +269,24 @@ public: } void preprocess() override { - XorBlendState *state = m_material->state(); // Update the textures from the providers, calling into QSGDynamicTexture if required if (m_provider1) { - state->texture1 = m_provider1->texture(); - if (QSGDynamicTexture *dt1 = qobject_cast(state->texture1)) + m_material->state.texture1 = m_provider1->texture(); + if (QSGDynamicTexture *dt1 = qobject_cast(m_material->state.texture1)) dt1->updateTexture(); } if (m_provider2) { - state->texture2 = m_provider2->texture(); - if (QSGDynamicTexture *dt2 = qobject_cast(state->texture2)) + m_material->state.texture2 = m_provider2->texture(); + if (QSGDynamicTexture *dt2 = qobject_cast(m_material->state.texture2)) dt2->updateTexture(); } // Remove node from the scene graph if it is there and either texture is missing... - if (m_node.parent() && (!state->texture1 || !state->texture2)) + if (m_node.parent() && (!m_material->state.texture1 || !m_material->state.texture2)) removeChildNode(&m_node); // Add it if it is not already there and both textures are present.. - else if (!m_node.parent() && state->texture1 && state->texture2) + else if (!m_node.parent() && m_material->state.texture1 && m_material->state.texture2) appendChildNode(&m_node); } @@ -206,7 +308,7 @@ public slots: private: QRectF m_rect; - QSGSimpleMaterial *m_material; + XorBlendMaterial *m_material; QSGGeometryNode m_node; QPointer m_provider1; QPointer m_provider2; -- cgit v1.2.3