diff options
Diffstat (limited to 'src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp')
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp | 785 |
1 files changed, 0 insertions, 785 deletions
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp deleted file mode 100644 index c38c616ae6..0000000000 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp +++ /dev/null @@ -1,785 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsgd3d12renderer_p.h" -#include "qsgd3d12rendercontext_p.h" -#include <private/qsgnodeupdater_p.h> -#include <private/qsgrendernode_p.h> - -#include "vs_stencilclip.hlslh" -#include "ps_stencilclip.hlslh" - -//#define I_LIKE_STENCIL - -QT_BEGIN_NAMESPACE - -#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling()) - -// NOTE: Avoid categorized logging. It is slow. - -#define DECLARE_DEBUG_VAR(variable) \ - static bool debug_ ## variable() \ - { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; } - -DECLARE_DEBUG_VAR(build) -DECLARE_DEBUG_VAR(change) -DECLARE_DEBUG_VAR(render) - -class DummyUpdater : public QSGNodeUpdater -{ -public: - void updateState(QSGNode *) { }; -}; - -QSGD3D12Renderer::QSGD3D12Renderer(QSGRenderContext *context) - : QSGRenderer(context), - m_vboData(1024), - m_iboData(256), - m_cboData(4096), - m_renderList(16) -{ - setNodeUpdater(new DummyUpdater); -} - -QSGD3D12Renderer::~QSGD3D12Renderer() -{ - if (m_engine) { - m_engine->releaseBuffer(m_vertexBuf); - m_engine->releaseBuffer(m_indexBuf); - m_engine->releaseBuffer(m_constantBuf); - } -} - -void QSGD3D12Renderer::renderScene(GLuint fboId) -{ - m_renderTarget = fboId; - - struct DummyBindable : public QSGBindable { - void bind() const { } - } bindable; - - QSGRenderer::renderScene(bindable); // calls back render() -} - -// Search through the node set and remove nodes that are descendants of other -// nodes in the same set. -static QSet<QSGNode *> qsg_removeDescendants(const QSet<QSGNode *> &nodes, QSGRootNode *root) -{ - QSet<QSGNode *> result = nodes; - for (QSGNode *node : nodes) { - QSGNode *n = node; - while (n != root) { - if (n != node && result.contains(n)) { - result.remove(node); - break; - } - n = n->parent(); - } - } - return result; -} - -void QSGD3D12Renderer::updateMatrices(QSGNode *node, QSGTransformNode *xform) -{ - if (node->isSubtreeBlocked()) - return; - - if (node->type() == QSGNode::TransformNodeType) { - QSGTransformNode *tn = static_cast<QSGTransformNode *>(node); - if (xform) - tn->setCombinedMatrix(xform->combinedMatrix() * tn->matrix()); - else - tn->setCombinedMatrix(tn->matrix()); - QSGNODE_TRAVERSE(node) - updateMatrices(child, tn); - } else { - if (node->type() == QSGNode::GeometryNodeType || node->type() == QSGNode::ClipNodeType) { - m_nodeDirtyMap[node] |= QSGD3D12MaterialRenderState::DirtyMatrix; - QSGBasicGeometryNode *gnode = static_cast<QSGBasicGeometryNode *>(node); - const QMatrix4x4 *newMatrix = xform ? &xform->combinedMatrix() : nullptr; - // NB the newMatrix ptr is usually the same as before as it just - // references the transform node's own matrix. - gnode->setRendererMatrix(newMatrix); - } - QSGNODE_TRAVERSE(node) - updateMatrices(child, xform); - } -} - -void QSGD3D12Renderer::updateOpacities(QSGNode *node, float inheritedOpacity) -{ - if (node->isSubtreeBlocked()) - return; - - if (node->type() == QSGNode::OpacityNodeType) { - QSGOpacityNode *on = static_cast<QSGOpacityNode *>(node); - float combined = inheritedOpacity * on->opacity(); - on->setCombinedOpacity(combined); - QSGNODE_TRAVERSE(node) - updateOpacities(child, combined); - } else { - if (node->type() == QSGNode::GeometryNodeType) { - m_nodeDirtyMap[node] |= QSGD3D12MaterialRenderState::DirtyOpacity; - QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node); - gn->setInheritedOpacity(inheritedOpacity); - } - QSGNODE_TRAVERSE(node) - updateOpacities(child, inheritedOpacity); - } -} - -void QSGD3D12Renderer::buildRenderList(QSGNode *node, QSGClipNode *clip) -{ - if (node->isSubtreeBlocked()) - return; - - if (node->type() == QSGNode::GeometryNodeType || node->type() == QSGNode::ClipNodeType) { - QSGBasicGeometryNode *gn = static_cast<QSGBasicGeometryNode *>(node); - QSGGeometry *g = gn->geometry(); - - Element e; - e.node = gn; - - if (g->vertexCount() > 0) { - e.vboOffset = m_vboData.size(); - const int vertexSize = g->sizeOfVertex() * g->vertexCount(); - m_vboData.resize(m_vboData.size() + vertexSize); - memcpy(m_vboData.data() + e.vboOffset, g->vertexData(), vertexSize); - } - - if (g->indexCount() > 0) { - e.iboOffset = m_iboData.size(); - e.iboStride = g->sizeOfIndex(); - const int indexSize = e.iboStride * g->indexCount(); - m_iboData.resize(m_iboData.size() + indexSize); - memcpy(m_iboData.data() + e.iboOffset, g->indexData(), indexSize); - } - - e.cboOffset = m_cboData.size(); - if (node->type() == QSGNode::GeometryNodeType) { - QSGD3D12Material *m = static_cast<QSGD3D12Material *>(static_cast<QSGGeometryNode *>(node)->activeMaterial()); - e.cboSize = m->constantBufferSize(); - } else { - // Stencil-based clipping needs a 4x4 matrix. - e.cboSize = QSGD3D12Engine::alignedConstantBufferSize(16 * sizeof(float)); - } - m_cboData.resize(m_cboData.size() + e.cboSize); - - m_renderList.add(e); - - gn->setRendererClipList(clip); - if (node->type() == QSGNode::ClipNodeType) - clip = static_cast<QSGClipNode *>(node); - } else if (node->type() == QSGNode::RenderNodeType) { - QSGRenderNode *rn = static_cast<QSGRenderNode *>(node); - Element e; - e.node = rn; - m_renderList.add(e); - } - - QSGNODE_TRAVERSE(node) - buildRenderList(child, clip); -} - -void QSGD3D12Renderer::render() -{ - QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(context()); - m_engine = rc->engine(); - if (!m_layerRenderer) - m_engine->beginFrame(); - else - m_engine->beginLayer(); - - m_activeScissorRect = QRect(); - - if (m_rebuild) { - m_rebuild = false; - - m_dirtyTransformNodes.clear(); - m_dirtyTransformNodes.insert(rootNode()); - m_dirtyOpacityNodes.clear(); - m_dirtyOpacityNodes.insert(rootNode()); - - m_renderList.reset(); - m_vboData.reset(); - m_iboData.reset(); - m_cboData.reset(); - - buildRenderList(rootNode(), nullptr); - - if (!m_vertexBuf) - m_vertexBuf = m_engine->genBuffer(); - m_engine->resetBuffer(m_vertexBuf, m_vboData.data(), m_vboData.size()); - - if (!m_constantBuf) - m_constantBuf = m_engine->genBuffer(); - m_engine->resetBuffer(m_constantBuf, m_cboData.data(), m_cboData.size()); - - if (m_iboData.size()) { - if (!m_indexBuf) - m_indexBuf = m_engine->genBuffer(); - m_engine->resetBuffer(m_indexBuf, m_iboData.data(), m_iboData.size()); - } else if (m_indexBuf) { - m_engine->releaseBuffer(m_indexBuf); - m_indexBuf = 0; - } - - if (Q_UNLIKELY(debug_build())) { - qDebug("renderList: %d elements in total", m_renderList.size()); - for (int i = 0; i < m_renderList.size(); ++i) { - const Element &e = m_renderList.at(i); - qDebug() << " - " << e.vboOffset << e.iboOffset << e.cboOffset << e.cboSize << e.node; - } - } - } - - const QRect devRect = deviceRect(); - m_projectionChangedDueToDeviceSize = devRect != m_lastDeviceRect; - if (m_projectionChangedDueToDeviceSize) - m_lastDeviceRect = devRect; - - if (m_dirtyTransformNodes.size()) { - const QSet<QSGNode *> subTreeRoots = qsg_removeDescendants(m_dirtyTransformNodes, rootNode()); - for (QSGNode *node : subTreeRoots) { - // First find the parent transform so we have the accumulated - // matrix up until this point. - QSGTransformNode *xform = 0; - QSGNode *n = node; - if (n->type() == QSGNode::TransformNodeType) - n = node->parent(); - while (n != rootNode() && n->type() != QSGNode::TransformNodeType) - n = n->parent(); - if (n != rootNode()) - xform = static_cast<QSGTransformNode *>(n); - - // Then update in the subtree - updateMatrices(node, xform); - } - } - - if (m_dirtyOpacityNodes.size()) { - const QSet<QSGNode *> subTreeRoots = qsg_removeDescendants(m_dirtyOpacityNodes, rootNode()); - for (QSGNode *node : subTreeRoots) { - float opacity = 1.0f; - QSGNode *n = node; - if (n->type() == QSGNode::OpacityNodeType) - n = node->parent(); - while (n != rootNode() && n->type() != QSGNode::OpacityNodeType) - n = n->parent(); - if (n != rootNode()) - opacity = static_cast<QSGOpacityNode *>(n)->combinedOpacity(); - - updateOpacities(node, opacity); - } - m_dirtyOpaqueElements = true; - } - - if (m_dirtyOpaqueElements) { - m_dirtyOpaqueElements = false; - m_opaqueElements.clear(); - m_opaqueElements.resize(m_renderList.size()); - for (int i = 0; i < m_renderList.size(); ++i) { - const Element &e = m_renderList.at(i); - if (e.node->type() == QSGNode::GeometryNodeType) { - const QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(e.node); - if (gn->inheritedOpacity() > 0.999f && ((gn->activeMaterial()->flags() & QSGMaterial::Blending) == 0)) - m_opaqueElements.setBit(i); - } - // QSGRenderNodes are always treated as non-opaque - } - } - - // Build pipeline state and draw calls. - renderElements(); - - m_dirtyTransformNodes.clear(); - m_dirtyOpacityNodes.clear(); - m_dirtyOpaqueElements = false; - m_nodeDirtyMap.clear(); - - // Finalize buffers and execute commands. - if (!m_layerRenderer) - m_engine->endFrame(); - else - m_engine->endLayer(); -} - -void QSGD3D12Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) -{ - // note that with DirtyNodeRemoved the window and all the graphics engine may already be gone - - if (Q_UNLIKELY(debug_change())) { - QDebug debug = qDebug(); - debug << "dirty:"; - if (state & QSGNode::DirtyGeometry) - debug << "Geometry"; - if (state & QSGNode::DirtyMaterial) - debug << "Material"; - if (state & QSGNode::DirtyMatrix) - debug << "Matrix"; - if (state & QSGNode::DirtyNodeAdded) - debug << "Added"; - if (state & QSGNode::DirtyNodeRemoved) - debug << "Removed"; - if (state & QSGNode::DirtyOpacity) - debug << "Opacity"; - if (state & QSGNode::DirtySubtreeBlocked) - debug << "SubtreeBlocked"; - if (state & QSGNode::DirtyForceUpdate) - debug << "ForceUpdate"; - - // when removed, some parts of the node could already have been destroyed - // so don't debug it out. - if (state & QSGNode::DirtyNodeRemoved) - debug << (void *) node << node->type(); - else - debug << node; - } - - if (state & (QSGNode::DirtyNodeAdded - | QSGNode::DirtyNodeRemoved - | QSGNode::DirtySubtreeBlocked - | QSGNode::DirtyGeometry - | QSGNode::DirtyForceUpdate)) - m_rebuild = true; - - if (state & QSGNode::DirtyMatrix) - m_dirtyTransformNodes << node; - - if (state & QSGNode::DirtyOpacity) - m_dirtyOpacityNodes << node; - - if (state & QSGNode::DirtyMaterial) - m_dirtyOpaqueElements = true; - - QSGRenderer::nodeChanged(node, state); -} - -void QSGD3D12Renderer::renderElements() -{ - m_engine->queueSetRenderTarget(m_renderTarget); - m_engine->queueViewport(viewportRect()); - m_engine->queueClearRenderTarget(clearColor()); - m_engine->queueClearDepthStencil(1, 0, QSGD3D12Engine::ClearDepth | QSGD3D12Engine::ClearStencil); - - m_pipelineState.blend = m_freshPipelineState.blend = QSGD3D12PipelineState::BlendNone; - m_pipelineState.depthEnable = m_freshPipelineState.depthEnable = true; - m_pipelineState.depthWrite = m_freshPipelineState.depthWrite = true; - - // First do opaque... - // The algorithm is quite simple. We traverse the list back-to-front, and - // for every item we start a second traversal and draw all elements which - // have identical material. Then we clear the bit for this in the rendered - // list so we don't draw it again when we come to that index. - QBitArray rendered = m_opaqueElements; - for (int i = m_renderList.size() - 1; i >= 0; --i) { - if (rendered.testBit(i)) { - renderElement(i); - for (int j = i - 1; j >= 0; --j) { - if (rendered.testBit(j)) { - const QSGGeometryNode *gni = static_cast<QSGGeometryNode *>(m_renderList.at(i).node); - const QSGGeometryNode *gnj = static_cast<QSGGeometryNode *>(m_renderList.at(j).node); - if (gni->clipList() == gnj->clipList() - && gni->inheritedOpacity() == gnj->inheritedOpacity() - && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode() - && gni->geometry()->attributes() == gnj->geometry()->attributes()) { - const QSGMaterial *ami = gni->activeMaterial(); - const QSGMaterial *amj = gnj->activeMaterial(); - if (ami->type() == amj->type() - && ami->flags() == amj->flags() - && ami->compare(amj) == 0) { - renderElement(j); - rendered.clearBit(j); - } - } - } - } - } - } - - m_pipelineState.blend = m_freshPipelineState.blend = QSGD3D12PipelineState::BlendPremul; - m_pipelineState.depthWrite = m_freshPipelineState.depthWrite = false; - - // ...then the alpha ones - for (int i = 0; i < m_renderList.size(); ++i) { - if ((m_renderList.at(i).node->type() == QSGNode::GeometryNodeType && !m_opaqueElements.testBit(i)) - || m_renderList.at(i).node->type() == QSGNode::RenderNodeType) - renderElement(i); - } -} - -struct RenderNodeState : public QSGRenderNode::RenderState -{ - const QMatrix4x4 *projectionMatrix() const override { return m_projectionMatrix; } - QRect scissorRect() const override { return m_scissorRect; } - bool scissorEnabled() const override { return m_scissorEnabled; } - int stencilValue() const override { return m_stencilValue; } - bool stencilEnabled() const override { return m_stencilEnabled; } - const QRegion *clipRegion() const override { return nullptr; } - - const QMatrix4x4 *m_projectionMatrix; - QRect m_scissorRect; - bool m_scissorEnabled; - int m_stencilValue; - bool m_stencilEnabled; -}; - -void QSGD3D12Renderer::renderElement(int elementIndex) -{ - Element &e = m_renderList.at(elementIndex); - Q_ASSERT(e.node->type() == QSGNode::GeometryNodeType || e.node->type() == QSGNode::RenderNodeType); - - if (e.node->type() == QSGNode::RenderNodeType) { - renderRenderNode(static_cast<QSGRenderNode *>(e.node), elementIndex); - return; - } - - if (e.vboOffset < 0) - return; - - Q_ASSERT(e.cboOffset >= 0); - - const QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(e.node); - if (Q_UNLIKELY(debug_render())) - qDebug() << "renderElement:" << elementIndex << gn << e.vboOffset << e.iboOffset << gn->inheritedOpacity() << gn->clipList(); - - if (gn->inheritedOpacity() < 0.001f) // pretty much invisible, don't draw it - return; - - // Update the QSGRenderer members which the materials will access. - m_current_projection_matrix = projectionMatrix(); - const float scale = 1.0 / m_renderList.size(); - m_current_projection_matrix(2, 2) = scale; - m_current_projection_matrix(2, 3) = 1.0f - (elementIndex + 1) * scale; - m_current_model_view_matrix = gn->matrix() ? *gn->matrix() : QMatrix4x4(); - m_current_determinant = m_current_model_view_matrix.determinant(); - m_current_opacity = gn->inheritedOpacity(); - - const QSGGeometry *g = gn->geometry(); - QSGD3D12Material *m = static_cast<QSGD3D12Material *>(gn->activeMaterial()); - - if (m->type() != m_lastMaterialType) { - m_pipelineState = m_freshPipelineState; - m->preparePipeline(&m_pipelineState); - } - - QSGD3D12MaterialRenderState::DirtyStates dirtyState = m_nodeDirtyMap.value(e.node); - - // After a rebuild everything in the cbuffer has to be updated. - if (!e.cboPrepared) { - e.cboPrepared = true; - dirtyState = QSGD3D12MaterialRenderState::DirtyAll; - } - - // DirtyMatrix does not include projection matrix changes that can arise - // due to changing the render target's size (and there is no rebuild). - // Accommodate for this. - if (m_projectionChangedDueToDeviceSize) - dirtyState |= QSGD3D12MaterialRenderState::DirtyMatrix; - - quint8 *cboPtr = nullptr; - if (e.cboSize > 0) - cboPtr = m_cboData.data() + e.cboOffset; - - if (Q_UNLIKELY(debug_render())) - qDebug() << "dirty state for" << e.node << "is" << dirtyState; - - QSGD3D12Material::ExtraState extraState; - QSGD3D12Material::UpdateResults updRes = m->updatePipeline(state(dirtyState), - &m_pipelineState, - &extraState, - cboPtr); - - if (updRes.testFlag(QSGD3D12Material::UpdatedConstantBuffer)) - m_engine->markBufferDirty(m_constantBuf, e.cboOffset, e.cboSize); - - if (updRes.testFlag(QSGD3D12Material::UpdatedBlendFactor)) - m_engine->queueSetBlendFactor(extraState.blendFactor); - - setInputLayout(g, &m_pipelineState); - - m_lastMaterialType = m->type(); - - setupClipping(gn->clipList(), elementIndex); - - // ### Lines and points with sizes other than 1 have to be implemented in some other way. Just ignore for now. - if (g->drawingMode() == QSGGeometry::DrawLineStrip || g->drawingMode() == QSGGeometry::DrawLines) { - if (g->lineWidth() != 1.0f) - qWarning("QSGD3D12Renderer: Line widths other than 1 are not supported by this renderer"); - } else if (g->drawingMode() == QSGGeometry::DrawPoints) { - if (g->lineWidth() != 1.0f) - qWarning("QSGD3D12Renderer: Point sprites are not supported by this renderer"); - } - - m_engine->finalizePipeline(m_pipelineState); - - queueDrawCall(g, e); -} - -void QSGD3D12Renderer::setInputLayout(const QSGGeometry *g, QSGD3D12PipelineState *pipelineState) -{ - pipelineState->inputElementCount = g->attributeCount(); - const QSGGeometry::Attribute *attrs = g->attributes(); - quint32 offset = 0; - for (int i = 0; i < g->attributeCount(); ++i) { - QSGD3D12InputElement &ie(pipelineState->inputElements[i]); - static const char *semanticNames[] = { "UNKNOWN", "POSITION", "COLOR", "TEXCOORD", "TEXCOORD", "TEXCOORD" }; - static const int semanticIndices[] = { 0, 0, 0, 0, 1, 2 }; - const int semantic = attrs[i].attributeType; - Q_ASSERT(semantic >= 1 && semantic < _countof(semanticNames)); - const int tupleSize = attrs[i].tupleSize; - ie.semanticName = semanticNames[semantic]; - ie.semanticIndex = semanticIndices[semantic]; - ie.offset = offset; - int bytesPerTuple = 0; - ie.format = QSGD3D12Engine::toDXGIFormat(QSGGeometry::Type(attrs[i].type), tupleSize, &bytesPerTuple); - if (ie.format == FmtUnknown) - qFatal("QSGD3D12Renderer: unsupported tuple size for attribute type 0x%x", attrs[i].type); - offset += bytesPerTuple; - // There is one buffer with interleaved data so the slot is always 0. - ie.slot = 0; - } -} - -void QSGD3D12Renderer::queueDrawCall(const QSGGeometry *g, const QSGD3D12Renderer::Element &e) -{ - QSGD3D12Engine::DrawParams dp; - dp.mode = QSGGeometry::DrawingMode(g->drawingMode()); - dp.vertexBuf = m_vertexBuf; - dp.constantBuf = m_constantBuf; - dp.vboOffset = e.vboOffset; - dp.vboSize = g->vertexCount() * g->sizeOfVertex(); - dp.vboStride = g->sizeOfVertex(); - dp.cboOffset = e.cboOffset; - - if (e.iboOffset >= 0) { - const QSGGeometry::Type indexType = QSGGeometry::Type(g->indexType()); - const QSGD3D12Format indexFormat = QSGD3D12Engine::toDXGIFormat(indexType); - if (indexFormat == FmtUnknown) - qFatal("QSGD3D12Renderer: unsupported index type 0x%x", indexType); - dp.count = g->indexCount(); - dp.indexBuf = m_indexBuf; - dp.startIndexIndex = e.iboOffset / e.iboStride; - dp.indexFormat = indexFormat; - } else { - dp.count = g->vertexCount(); - } - - m_engine->queueDraw(dp); -} - -void QSGD3D12Renderer::setupClipping(const QSGClipNode *clip, int elementIndex) -{ - const QRect devRect = deviceRect(); - QRect scissorRect; - int clipTypes = 0; - quint32 stencilValue = 0; - - while (clip) { - QMatrix4x4 m = projectionMatrix(); - if (clip->matrix()) - m *= *clip->matrix(); - -#ifndef I_LIKE_STENCIL - const bool isRectangleWithNoPerspective = clip->isRectangular() - && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1)); - const bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0)); - const bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1)); - - if (isRectangleWithNoPerspective && (noRotate || isRotate90)) { - QRectF bbox = clip->clipRect(); - float invW = 1.0f / m(3, 3); - float fx1, fy1, fx2, fy2; - if (noRotate) { - fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW; - fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW; - fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW; - fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW; - } else { - Q_ASSERT(isRotate90); - fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW; - fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW; - fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW; - fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW; - } - - if (fx1 > fx2) - qSwap(fx1, fx2); - if (fy1 > fy2) - qSwap(fy1, fy2); - - int ix1 = qRound((fx1 + 1) * devRect.width() * 0.5f); - int iy1 = qRound((fy1 + 1) * devRect.height() * 0.5f); - int ix2 = qRound((fx2 + 1) * devRect.width() * 0.5f); - int iy2 = qRound((fy2 + 1) * devRect.height() * 0.5f); - - if (!(clipTypes & ClipScissor)) { - scissorRect = QRect(ix1, devRect.height() - iy2, ix2 - ix1, iy2 - iy1); - clipTypes |= ClipScissor; - } else { - scissorRect &= QRect(ix1, devRect.height() - iy2, ix2 - ix1, iy2 - iy1); - } - } else -#endif - { - clipTypes |= ClipStencil; - renderStencilClip(clip, elementIndex, m, stencilValue); - } - - clip = clip->clipList(); - } - - setScissor((clipTypes & ClipScissor) ? scissorRect : viewportRect()); - - if (clipTypes & ClipStencil) { - m_pipelineState.stencilEnable = true; - m_engine->queueSetStencilRef(stencilValue); - m_currentStencilValue = stencilValue; - } else { - m_pipelineState.stencilEnable = false; - m_currentStencilValue = 0; - } - - m_currentClipTypes = clipTypes; -} - -void QSGD3D12Renderer::setScissor(const QRect &r) -{ - if (m_activeScissorRect == r) - return; - - m_activeScissorRect = r; - m_engine->queueScissor(r); -} - -void QSGD3D12Renderer::renderStencilClip(const QSGClipNode *clip, int elementIndex, - const QMatrix4x4 &m, quint32 &stencilValue) -{ - QSGD3D12PipelineState sps; - sps.shaders.vs = g_VS_StencilClip; - sps.shaders.vsSize = sizeof(g_VS_StencilClip); - sps.shaders.ps = g_PS_StencilClip; - sps.shaders.psSize = sizeof(g_PS_StencilClip); - - m_engine->queueClearDepthStencil(1, 0, QSGD3D12Engine::ClearStencil); - sps.stencilEnable = true; - sps.colorWrite = false; - sps.depthWrite = false; - - sps.stencilFunc = QSGD3D12PipelineState::CompareEqual; - sps.stencilFailOp = QSGD3D12PipelineState::StencilKeep; - sps.stencilDepthFailOp = QSGD3D12PipelineState::StencilKeep; - sps.stencilPassOp = QSGD3D12PipelineState::StencilIncr; - - m_engine->queueSetStencilRef(stencilValue); - - int clipIndex = elementIndex; - while (m_renderList.at(--clipIndex).node != clip) { - Q_ASSERT(clipIndex >= 0); - } - const Element &ce = m_renderList.at(clipIndex); - Q_ASSERT(ce.node == clip); - - const QSGGeometry *g = clip->geometry(); - Q_ASSERT(g->attributeCount() == 1); - Q_ASSERT(g->attributes()[0].tupleSize == 2); - Q_ASSERT(g->attributes()[0].type == QSGGeometry::FloatType); - - setInputLayout(g, &sps); - m_engine->finalizePipeline(sps); - - Q_ASSERT(ce.cboSize > 0); - quint8 *p = m_cboData.data() + ce.cboOffset; - memcpy(p, m.constData(), 16 * sizeof(float)); - m_engine->markBufferDirty(m_constantBuf, ce.cboOffset, ce.cboSize); - - queueDrawCall(g, ce); - - ++stencilValue; -} - -void QSGD3D12Renderer::renderRenderNode(QSGRenderNode *node, int elementIndex) -{ - QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(node); - RenderNodeState state; - - setupClipping(rd->m_clip_list, elementIndex); - - QMatrix4x4 pm = projectionMatrix(); - state.m_projectionMatrix = ± - state.m_scissorEnabled = m_currentClipTypes & ClipScissor; - state.m_stencilEnabled = m_currentClipTypes & ClipStencil; - state.m_scissorRect = m_activeScissorRect; - state.m_stencilValue = m_currentStencilValue; - - // ### rendernodes do not have the QSGBasicGeometryNode infrastructure - // for storing combined matrices, opacity and such, but perhaps they should. - QSGNode *xform = node->parent(); - QSGNode *root = rootNode(); - QMatrix4x4 modelview; - while (xform != root) { - if (xform->type() == QSGNode::TransformNodeType) { - modelview *= static_cast<QSGTransformNode *>(xform)->combinedMatrix(); - break; - } - xform = xform->parent(); - } - rd->m_matrix = &modelview; - - QSGNode *opacity = node->parent(); - rd->m_opacity = 1.0; - while (opacity != rootNode()) { - if (opacity->type() == QSGNode::OpacityNodeType) { - rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity(); - break; - } - opacity = opacity->parent(); - } - - node->render(&state); - - m_engine->invalidateCachedFrameState(); - // For simplicity, reset viewport, scissor, blend factor, stencil ref when - // any of them got changed. This will likely be rare so skip these otherwise. - // Render target, pipeline state, draw call related stuff will be reset always. - const bool restoreMinimal = node->changedStates() == 0; - m_engine->restoreFrameState(restoreMinimal); -} - -QT_END_NAMESPACE |