From 7b130535cf590b310f23c8167986588d3982ad20 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 7 Aug 2019 15:27:24 +0200 Subject: Implement QSGRenderNode for the rhi path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation is also refined and extended. Revise the rendernode example as well: make it possible to test and demonstrate both scissor and stencil based clipping. An implementation of the triangle for another graphics API should follow in a separate patch at a later point. For now only OpenGL is supported in combination with the RHI. Change-Id: I7ef9f6c6e0b44f1bdf44c9266ea3fa4736367a5d Reviewed-by: Christian Strømme --- .../scenegraph/rendernode/customrenderitem.cpp | 28 +++++--- .../quick/scenegraph/rendernode/d3d12renderer.cpp | 6 +- examples/quick/scenegraph/rendernode/main.qml | 81 +++++++++++++++++----- .../quick/scenegraph/rendernode/openglrenderer.cpp | 20 +++++- 4 files changed, 104 insertions(+), 31 deletions(-) (limited to 'examples') diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp index 8f248e2ecb..9dbe8d86a0 100644 --- a/examples/quick/scenegraph/rendernode/customrenderitem.cpp +++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp @@ -71,23 +71,29 @@ QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) if (!ri) return nullptr; switch (ri->graphicsApi()) { - case QSGRendererInterface::OpenGL: + case QSGRendererInterface::OpenGL: + Q_FALLTHROUGH(); + case QSGRendererInterface::OpenGLRhi: #if QT_CONFIG(opengl) - n = new OpenGLRenderNode(this); - break; + n = new OpenGLRenderNode(this); #endif - case QSGRendererInterface::Direct3D12: + break; + + case QSGRendererInterface::Direct3D12: // ### Qt 6: remove #if QT_CONFIG(d3d12) - n = new D3D12RenderNode(this); - break; + n = new D3D12RenderNode(this); #endif - case QSGRendererInterface::Software: - n = new SoftwareRenderNode(this); - break; + break; + + case QSGRendererInterface::Software: + n = new SoftwareRenderNode(this); + break; - default: - return nullptr; + default: + break; } + if (!n) + qWarning("QSGRendererInterface reports unknown graphics API %d", ri->graphicsApi()); } return n; diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp index df0e29eb67..878b022950 100644 --- a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp +++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp @@ -54,6 +54,8 @@ #include #include +// ### Qt 6: remove + #if QT_CONFIG(d3d12) D3D12RenderNode::D3D12RenderNode(QQuickItem *item) @@ -166,8 +168,8 @@ void D3D12RenderNode::init() psoDesc.RasterizerState = rastDesc; psoDesc.BlendState = blendDesc; // No depth. The correct stacking of the item is ensured by the projection matrix. - // Do not bother with stencil since we do not apply clipping in the - // example. If clipping is desired, render() needs to set a different PSO + // Note that this does not support clipping. + // If clipping is desired, render() needs to set a different PSO // with stencil enabled whenever the RenderState indicates so. psoDesc.SampleMask = UINT_MAX; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; diff --git a/examples/quick/scenegraph/rendernode/main.qml b/examples/quick/scenegraph/rendernode/main.qml index d0ba4a4669..85060261c5 100644 --- a/examples/quick/scenegraph/rendernode/main.qml +++ b/examples/quick/scenegraph/rendernode/main.qml @@ -53,23 +53,55 @@ import SceneGraphRendering 2.0 Item { Rectangle { + id: bg anchors.fill: parent gradient: Gradient { GradientStop { position: 0; color: "steelblue" } GradientStop { position: 1; color: "black" } } - CustomRenderItem { - id: renderer + MouseArea { anchors.fill: parent - anchors.margins: 10 + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if (mouse.button === Qt.LeftButton) { + clipper.clip = !clipper.clip + } else if (mouse.button === Qt.RightButton) { + nonRectClipAnim.running = !nonRectClipAnim.running + if (!nonRectClipAnim.running) + clipper.rotation = 0; + } + } + } + + Rectangle { + id: clipper + width: parent.width / 2 + height: parent.height / 2 + anchors.centerIn: parent + border.color: "yellow" + border.width: 2 + color: "transparent" + NumberAnimation on rotation { + id: nonRectClipAnim + from: 0; to: 360; duration: 5000; loops: Animation.Infinite + running: false + } + + CustomRenderItem { + id: renderer + width: bg.width - 20 + height: bg.height - 20 + x: -clipper.x + 10 + y: -clipper.y + 10 - transform: [ - Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; }, - Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 }, - Scale { id: scale; }, - Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 } - ] + transform: [ + Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; }, + Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 }, + Scale { id: scale; }, + Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 } + ] + } } SequentialAnimation { @@ -94,17 +126,34 @@ Item { Text { id: label - anchors.bottom: renderer.bottom - anchors.left: renderer.left - anchors.right: renderer.right + anchors.bottom: parent.bottom + anchors.left: parent.left anchors.margins: 20 + color: "yellow" wrapMode: Text.WordWrap property int api: GraphicsInfo.api - text: "Custom rendering via the graphics API " - + (api === GraphicsInfo.OpenGL ? "OpenGL" - : api === GraphicsInfo.Direct3D12 ? "Direct3D 12" - : api === GraphicsInfo.Software ? "Software" : "") + text: { + var apiStr; + switch (api) { + case GraphicsInfo.OpenGL: apiStr = "OpenGL (direct)"; break; + case GraphicsInfo.Direct3D12: apiStr = "Direct3D 12 (direct)"; break; + case GraphicsInfo.Software: apiStr = "Software (QPainter)"; break; + case GraphicsInfo.OpenGLRhi: apiStr = "OpenGL (RHI)"; break; + // the example has no other QSGRenderNode subclasses + default: apiStr = ""; break; + } + "Custom rendering via the graphics API " + apiStr + + "\nLeft click to toggle clipping to yellow rect" + + "\nRight click to rotate (can be used to exercise stencil clip instead of scissor)" + } + } + + Text { + id: label2 + anchors.top: parent.top + anchors.right: parent.right color: "yellow" + text: "Clip: " + (clipper.clip ? "ON" : "OFF") + " Rotation: " + (nonRectClipAnim.running ? "ON" : "OFF") } } } diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp index 3c68830db6..5e6b9e3656 100644 --- a/examples/quick/scenegraph/rendernode/openglrenderer.cpp +++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp @@ -148,17 +148,33 @@ void OpenGLRenderNode::render(const RenderState *state) m_program->enableAttributeArray(0); m_program->enableAttributeArray(1); - // Note that clipping (scissor or stencil) is ignored in this example. + // We are prepared both for the legacy (direct OpenGL) and the modern + // (abstracted by RHI) OpenGL scenegraph. So set all the states that are + // important to us. + + f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); f->glEnable(GL_BLEND); f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + // Clip support. + if (state->scissorEnabled()) { + f->glEnable(GL_SCISSOR_TEST); + const QRect r = state->scissorRect(); // already bottom-up + f->glScissor(r.x(), r.y(), r.width(), r.height()); + } + if (state->stencilEnabled()) { + f->glEnable(GL_STENCIL_TEST); + f->glStencilFunc(GL_EQUAL, state->stencilValue(), 0xFF); + f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + } + f->glDrawArrays(GL_TRIANGLES, 0, 3); } QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const { - return BlendState; + return BlendState | ScissorState | StencilState; } QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const -- cgit v1.2.3