From a86a0e9a79ca9d984f07a202c62a46b75956f73e Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 1 Mar 2016 16:51:38 +0100 Subject: D3D12: Add support for clipping via scissor and stencil Change-Id: Ie55b9a2fd39d9e11321454a34a21c2a52ff1d23a Reviewed-by: Andy Nichols --- .../adaptations/d3d12/qsgd3d12engine.cpp | 98 +++++++---- .../adaptations/d3d12/qsgd3d12engine_p.h | 59 +++++-- .../adaptations/d3d12/qsgd3d12engine_p_p.h | 6 +- .../adaptations/d3d12/qsgd3d12rectanglenode.cpp | 5 + .../adaptations/d3d12/qsgd3d12rectanglenode_p.h | 1 + .../adaptations/d3d12/qsgd3d12renderer.cpp | 186 +++++++++++++++++++-- .../adaptations/d3d12/qsgd3d12renderer_p.h | 8 + .../adaptations/d3d12/shaders/shaders.pri | 15 +- .../adaptations/d3d12/shaders/stencilclip.hlsl | 26 +++ src/quick/scenegraph/qsgdefaultrectanglenode.cpp | 3 + src/quick/scenegraph/qsgdefaultrectanglenode_p.h | 1 + tests/manual/nodetypes/Rects.qml | 126 ++++++++++++++ tests/manual/nodetypes/main.qml | 69 +------- tests/manual/nodetypes/nodetypes.cpp | 4 + tests/manual/nodetypes/nodetypes.pro | 2 +- tests/manual/nodetypes/nodetypes.qrc | 1 + 16 files changed, 479 insertions(+), 131 deletions(-) create mode 100644 src/quick/scenegraph/adaptations/d3d12/shaders/stencilclip.hlsl create mode 100644 tests/manual/nodetypes/Rects.qml diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp index c26bcfdd9e..6f78d740c3 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine.cpp @@ -311,9 +311,14 @@ void QSGD3D12Engine::queueClearRenderTarget(const QColor &color) d->queueClearRenderTarget(color); } -void QSGD3D12Engine::queueClearDepthStencil(float depthValue, quint8 stencilValue) +void QSGD3D12Engine::queueClearDepthStencil(float depthValue, quint8 stencilValue, ClearFlags which) { - d->queueClearDepthStencil(depthValue, stencilValue); + d->queueClearDepthStencil(depthValue, stencilValue, which); +} + +void QSGD3D12Engine::queueSetStencilRef(quint32 ref) +{ + d->queueSetStencilRef(ref); } void QSGD3D12Engine::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride, @@ -537,7 +542,7 @@ DXGI_SAMPLE_DESC QSGD3D12EnginePrivate::makeSampleDesc(DXGI_FORMAT format, int s ID3D12Resource *QSGD3D12EnginePrivate::createDepthStencil(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size, int samples) { D3D12_CLEAR_VALUE depthClearValue = {}; - depthClearValue.Format = DXGI_FORMAT_D32_FLOAT; + depthClearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthClearValue.DepthStencil.Depth = 1.0f; depthClearValue.DepthStencil.Stencil = 0; @@ -550,7 +555,7 @@ ID3D12Resource *QSGD3D12EnginePrivate::createDepthStencil(D3D12_CPU_DESCRIPTOR_H bufDesc.Height = size.height(); bufDesc.DepthOrArraySize = 1; bufDesc.MipLevels = 1; - bufDesc.Format = DXGI_FORMAT_D32_FLOAT; + bufDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; bufDesc.SampleDesc = makeSampleDesc(bufDesc.Format, samples); bufDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; bufDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; @@ -563,7 +568,7 @@ ID3D12Resource *QSGD3D12EnginePrivate::createDepthStencil(D3D12_CPU_DESCRIPTOR_H } D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {}; - depthStencilDesc.Format = DXGI_FORMAT_D32_FLOAT; + depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.ViewDimension = bufDesc.SampleDesc.Count <= 1 ? D3D12_DSV_DIMENSION_TEXTURE2D : D3D12_DSV_DIMENSION_TEXTURE2DMS; device->CreateDepthStencilView(resource, &depthStencilDesc, viewHandle); @@ -717,6 +722,9 @@ void QSGD3D12EnginePrivate::beginFrame() commandList->Reset(commandAllocator.Get(), nullptr); transitionResource(backBufferRT(), commandList.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET); + + m_drawingMode = QSGGeometry::DrawingMode(-1); + m_indexBufferSet = false; } void QSGD3D12EnginePrivate::updateBuffer(StagingBufferRef *br, ID3D12Resource *r, const char *dbgstr) @@ -842,12 +850,12 @@ void QSGD3D12EnginePrivate::setPipelineState(const QSGD3D12PipelineState &pipeli D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD, D3D12_BLEND_INV_DEST_ALPHA, D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD, D3D12_LOGIC_OP_NOOP, - D3D12_COLOR_WRITE_ENABLE_ALL + UINT8(pipelineState.colorWrite ? D3D12_COLOR_WRITE_ENABLE_ALL : 0) }; blendDesc.RenderTarget[0] = premulBlendDesc; } else { D3D12_RENDER_TARGET_BLEND_DESC noBlendDesc = {}; - noBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + noBlendDesc.RenderTargetWriteMask = pipelineState.colorWrite ? D3D12_COLOR_WRITE_ENABLE_ALL : 0; blendDesc.RenderTarget[0] = noBlendDesc; } psoDesc.BlendState = blendDesc; @@ -855,13 +863,22 @@ void QSGD3D12EnginePrivate::setPipelineState(const QSGD3D12PipelineState &pipeli psoDesc.DepthStencilState.DepthEnable = pipelineState.depthEnable; psoDesc.DepthStencilState.DepthWriteMask = pipelineState.depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC(pipelineState.depthFunc); + psoDesc.DepthStencilState.StencilEnable = pipelineState.stencilEnable; - // ### stencil stuff + psoDesc.DepthStencilState.StencilReadMask = psoDesc.DepthStencilState.StencilWriteMask = 0xFF; + D3D12_DEPTH_STENCILOP_DESC stencilOpDesc = { + D3D12_STENCIL_OP(pipelineState.stencilFailOp), + D3D12_STENCIL_OP(pipelineState.stencilDepthFailOp), + D3D12_STENCIL_OP(pipelineState.stencilPassOp), + D3D12_COMPARISON_FUNC(pipelineState.stencilFunc) + }; + psoDesc.DepthStencilState.FrontFace = psoDesc.DepthStencilState.BackFace = stencilOpDesc; + psoDesc.SampleMask = UINT_MAX; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE(pipelineState.topologyType); psoDesc.NumRenderTargets = 1; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; - psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT; + psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; psoDesc.SampleDesc.Count = 1; HRESULT hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pso)); @@ -905,13 +922,13 @@ void QSGD3D12EnginePrivate::markConstantBufferDirty(int offset, int size) void QSGD3D12EnginePrivate::queueViewport(const QRect &rect) { - const D3D12_VIEWPORT viewport = { 0, 0, float(rect.width()), float(rect.height()), 0, 1 }; + const D3D12_VIEWPORT viewport = { float(rect.x()), float(rect.y()), float(rect.width()), float(rect.height()), 0, 1 }; commandList->RSSetViewports(1, &viewport); } void QSGD3D12EnginePrivate::queueScissor(const QRect &rect) { - const D3D12_RECT scissorRect = { 0, 0, rect.width() - 1, rect.height() - 1 }; + const D3D12_RECT scissorRect = { rect.left(), rect.top(), rect.right(), rect.bottom() }; commandList->RSSetScissorRects(1, &scissorRect); } @@ -928,9 +945,14 @@ void QSGD3D12EnginePrivate::queueClearRenderTarget(const QColor &color) commandList->ClearRenderTargetView(backBufferRTV(), clearColor, 0, nullptr); } -void QSGD3D12EnginePrivate::queueClearDepthStencil(float depthValue, quint8 stencilValue) +void QSGD3D12EnginePrivate::queueClearDepthStencil(float depthValue, quint8 stencilValue, QSGD3D12Engine::ClearFlags which) { - commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, depthValue, stencilValue, 0, nullptr); + commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAGS(int(which)), depthValue, stencilValue, 0, nullptr); +} + +void QSGD3D12EnginePrivate::queueSetStencilRef(quint32 ref) +{ + commandList->OMSetStencilRef(ref); } void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride, @@ -997,33 +1019,37 @@ void QSGD3D12EnginePrivate::queueDraw(QSGGeometry::DrawingMode mode, int count, Q_ASSERT(indexBuffer || startIndexIndex < 0); - D3D_PRIMITIVE_TOPOLOGY topology; - switch (mode) { - case QSGGeometry::DrawPoints: - topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST; - break; - case QSGGeometry::DrawLines: - topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; - break; - case QSGGeometry::DrawLineStrip: - topology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; - break; - case QSGGeometry::DrawTriangles: - topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - break; - case QSGGeometry::DrawTriangleStrip: - topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; - break; - default: - qFatal("Unsupported drawing mode 0x%x", mode); - break; + if (mode != m_drawingMode) { + D3D_PRIMITIVE_TOPOLOGY topology; + switch (mode) { + case QSGGeometry::DrawPoints: + topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST; + break; + case QSGGeometry::DrawLines: + topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; + break; + case QSGGeometry::DrawLineStrip: + topology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; + break; + case QSGGeometry::DrawTriangles: + topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + break; + case QSGGeometry::DrawTriangleStrip: + topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + break; + default: + qFatal("Unsupported drawing mode 0x%x", mode); + break; + } + commandList->IASetPrimitiveTopology(topology); + m_drawingMode = mode; } - commandList->IASetPrimitiveTopology(topology); - + // must be set after the topology commandList->IASetVertexBuffers(0, 1, &vbv); - if (startIndexIndex >= 0) { + if (startIndexIndex >= 0 && !m_indexBufferSet) { + m_indexBufferSet = true; D3D12_INDEX_BUFFER_VIEW ibv; ibv.BufferLocation = indexBuffer->GetGPUVirtualAddress(); ibv.SizeInBytes = indexData.size; diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h index 52ca80a002..a03be4b884 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p.h @@ -130,15 +130,26 @@ struct QSGD3D12PipelineState CullBack }; - enum DepthFunc { - DepthNever = 1, - DepthLess, - DepthEqual, - DepthLessEqual, - DepthGreater, - DepthNotEqual, - DepthGreaterEqual, - DepthAlways + enum CompareFunc { + CompareNever = 1, + CompareLess, + CompareEqual, + CompareLessEqual, + CompareGreater, + CompareNotEqual, + CompareGreaterEqual, + CompareAlways + }; + + enum StencilOp { + StencilKeep = 1, + StencilZero, + StencilReplace, + StencilIncrSat, + StencilDecrSat, + StencilInvert, + StencilIncr, + StencilDescr }; enum TopologyType { @@ -153,12 +164,16 @@ struct QSGD3D12PipelineState CullMode cullMode = CullNone; bool frontCCW = true; + bool colorWrite = true; bool premulBlend = false; // == GL_ONE, GL_ONE_MINUS_SRC_ALPHA bool depthEnable = true; - DepthFunc depthFunc = DepthLess; + CompareFunc depthFunc = CompareLess; bool depthWrite = true; bool stencilEnable = false; - // ### stencil stuff + CompareFunc stencilFunc = CompareEqual; + StencilOp stencilFailOp = StencilKeep; + StencilOp stencilDepthFailOp = StencilKeep; + StencilOp stencilPassOp = StencilKeep; TopologyType topologyType = TopologyTypeTriangle; bool operator==(const QSGD3D12PipelineState &other) const { @@ -166,11 +181,16 @@ struct QSGD3D12PipelineState && inputElements == other.inputElements && cullMode == other.cullMode && frontCCW == other.frontCCW + && colorWrite == other.colorWrite && premulBlend == other.premulBlend && depthEnable == other.depthEnable && depthFunc == other.depthFunc && depthWrite == other.depthWrite && stencilEnable == other.stencilEnable + && stencilFunc == other.stencilFunc + && stencilFailOp == other.stencilFailOp + && stencilDepthFailOp == other.stencilDepthFailOp + && stencilPassOp == other.stencilPassOp && topologyType == other.topologyType; } }; @@ -178,8 +198,10 @@ struct QSGD3D12PipelineState inline uint qHash(const QSGD3D12PipelineState &key, uint seed = 0) { return qHash(key.shaders, seed) + qHash(key.inputElements, seed) - + key.cullMode + key.frontCCW + key.premulBlend + key.depthEnable - + key.depthFunc + key.depthWrite + key.stencilEnable + key.topologyType; + + key.cullMode + key.frontCCW + key.colorWrite + key.premulBlend + key.depthEnable + + key.depthFunc + key.depthWrite + key.stencilEnable + key.stencilFunc + + key.stencilFailOp + key.stencilDepthFailOp + key.stencilPassOp + + key.topologyType; } class QSGD3D12Engine @@ -202,11 +224,18 @@ public: void setConstantBuffer(const quint8 *data, int size); void markConstantBufferDirty(int offset, int size); + enum ClearFlag { + ClearDepth = 0x1, + ClearStencil = 0x2 + }; + Q_DECLARE_FLAGS(ClearFlags, ClearFlag) + void queueViewport(const QRect &rect); void queueScissor(const QRect &rect); void queueSetRenderTarget(); void queueClearRenderTarget(const QColor &color); - void queueClearDepthStencil(float depthValue, quint8 stencilValue); + void queueClearDepthStencil(float depthValue, quint8 stencilValue, ClearFlags which); + void queueSetStencilRef(quint32 ref); void queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride, int cboOffset, @@ -223,6 +252,8 @@ private: Q_DISABLE_COPY(QSGD3D12Engine) }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::ClearFlags) + QT_END_NAMESPACE #endif // QSGD3D12ENGINE_P_H diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h index fd72c4f064..6dd95e725e 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12engine_p_p.h @@ -156,7 +156,8 @@ public: void queueScissor(const QRect &rect); void queueSetRenderTarget(); void queueClearRenderTarget(const QColor &color); - void queueClearDepthStencil(float depthValue, quint8 stencilValue); + void queueClearDepthStencil(float depthValue, quint8 stencilValue, QSGD3D12Engine::ClearFlags which); + void queueSetStencilRef(quint32 ref); void queueDraw(QSGGeometry::DrawingMode mode, int count, int vboOffset, int vboStride, int cboOffset, @@ -223,6 +224,9 @@ private: QHash > m_psoCache; ComPtr m_rootSig; + + QSGGeometry::DrawingMode m_drawingMode; + bool m_indexBufferSet; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp index 5956a06e2f..4a537731ce 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode.cpp @@ -46,6 +46,11 @@ QSGD3D12RectangleNode::QSGD3D12RectangleNode() setMaterial(&m_material); } +bool QSGD3D12RectangleNode::supportsAntialiasing() const +{ + return false; // ### +} + void QSGD3D12RectangleNode::updateMaterialAntialiasing() { //if (m_antialiasing) diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h index 464dc763a6..dfabb4f832 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12rectanglenode_p.h @@ -62,6 +62,7 @@ public: QSGD3D12RectangleNode(); private: + bool supportsAntialiasing() const; void updateMaterialAntialiasing() override; void updateMaterialBlending(QSGNode::DirtyState *state) override; diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp index 2e55f7ec0e..521cefc7c1 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer.cpp @@ -41,6 +41,11 @@ #include "qsgd3d12rendercontext_p.h" #include +#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()) @@ -64,7 +69,7 @@ QSGD3D12Renderer::QSGD3D12Renderer(QSGRenderContext *context) m_renderList(16), m_vboData(1024), m_iboData(256), - m_cboData(256) + m_cboData(4096) { setNodeUpdater(new DummyUpdater); } @@ -174,12 +179,15 @@ void QSGD3D12Renderer::buildRenderList(QSGNode *node, QSGClipNode *clip) memcpy(m_iboData.data() + e.iboOffset, g->indexData(), indexSize); } + e.cboOffset = m_cboData.size(); if (node->type() == QSGNode::GeometryNodeType) { QSGD3D12Material *m = static_cast(static_cast(node)->activeMaterial()); - e.cboOffset = m_cboData.size(); e.cboSize = m->constantBufferSize(); - m_cboData.resize(m_cboData.size() + e.cboSize); + } 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); @@ -198,6 +206,8 @@ void QSGD3D12Renderer::render() m_engine = rc->engine(); m_engine->beginFrame(); + m_activeScissorRect = QRect(); + if (m_rebuild) { m_rebuild = false; @@ -211,7 +221,7 @@ void QSGD3D12Renderer::render() m_iboData.reset(); m_cboData.reset(); - buildRenderList(rootNode(), 0); + buildRenderList(rootNode(), nullptr); m_engine->setVertexBuffer(m_vboData.data(), m_vboData.size()); m_engine->setIndexBuffer(m_iboData.data(), m_iboData.size()); @@ -343,13 +353,10 @@ void QSGD3D12Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) void QSGD3D12Renderer::renderElements() { - QRect r = viewportRect(); - r.setY(deviceRect().bottom() - r.bottom()); - m_engine->queueViewport(r); - m_engine->queueScissor(r); + m_engine->queueViewport(viewportRect()); m_engine->queueSetRenderTarget(); m_engine->queueClearRenderTarget(clearColor()); - m_engine->queueClearDepthStencil(1, 0); + m_engine->queueClearDepthStencil(1, 0, QSGD3D12Engine::ClearDepth | QSGD3D12Engine::ClearStencil); m_pipelineState.premulBlend = false; m_pipelineState.depthEnable = true; @@ -452,12 +459,26 @@ void QSGD3D12Renderer::renderElement(int elementIndex) if (updRes.testFlag(QSGD3D12Material::UpdatedConstantBuffer)) m_engine->markConstantBufferDirty(e.cboOffset, e.cboSize); - // Input element layout - m_pipelineState.inputElements.resize(g->attributeCount()); + setInputLayout(g, &m_pipelineState); + + m_lastMaterialType = m->type(); + + setupClipping(gn, elementIndex); + + // ### line width / point size ?? + + m_engine->setPipelineState(m_pipelineState); + + queueDrawCall(g, e); +} + +void QSGD3D12Renderer::setInputLayout(const QSGGeometry *g, QSGD3D12PipelineState *pipelineState) +{ + pipelineState->inputElements.resize(g->attributeCount()); const QSGGeometry::Attribute *attrs = g->attributes(); quint32 offset = 0; for (int i = 0; i < g->attributeCount(); ++i) { - QSGD3D12InputElement &ie(m_pipelineState.inputElements[i]); + QSGD3D12InputElement &ie(pipelineState->inputElements[i]); static const char *semanticNames[] = { "UNKNOWN", "POSITION", "COLOR", "TEXCOORD" }; Q_ASSERT(attrs[i].semantic >= 1 && attrs[i].semantic < _countof(semanticNames)); const int tupleSize = attrs[i].tupleSize; @@ -471,13 +492,10 @@ void QSGD3D12Renderer::renderElement(int elementIndex) // There is one buffer with interleaved data so the slot is always 0. ie.slot = 0; } +} - m_lastMaterialType = m->type(); - - // ### line width / point size ?? - - m_engine->setPipelineState(m_pipelineState); - +void QSGD3D12Renderer::queueDrawCall(const QSGGeometry *g, const QSGD3D12Renderer::Element &e) +{ if (e.iboOffset >= 0) { const QSGGeometry::Type indexType = QSGGeometry::Type(g->indexType()); const QSGD3D12Format indexFormat = QSGD3D12Engine::toDXGIFormat(indexType); @@ -492,4 +510,136 @@ void QSGD3D12Renderer::renderElement(int elementIndex) } } +void QSGD3D12Renderer::setupClipping(const QSGGeometryNode *gn, int elementIndex) +{ + const QSGClipNode *clip = gn->clipList(); + + const QRect devRect = deviceRect(); + QRect scissorRect; + enum ClipType { + ClipScissor = 0x1, + ClipStencil = 0x2 + }; + 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 + 1, iy2 - iy1 + 1); + clipTypes |= ClipScissor; + } else { + scissorRect &= QRect(ix1, devRect.height() - iy2, ix2 - ix1 + 1, iy2 - iy1 + 1); + } + } 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); + } else { + m_pipelineState.stencilEnable = false; + } +} + +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 == GL_FLOAT); + + setInputLayout(g, &sps); + m_engine->setPipelineState(sps); + + Q_ASSERT(ce.cboSize > 0); + quint8 *p = m_cboData.data() + ce.cboOffset; + memcpy(p, m.constData(), 16 * sizeof(float)); + m_engine->markConstantBufferDirty(ce.cboOffset, ce.cboSize); + + queueDrawCall(g, ce); + + ++stencilValue; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h index e637dbbb9c..8b430e1f1b 100644 --- a/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h +++ b/src/quick/scenegraph/adaptations/d3d12/qsgd3d12renderer_p.h @@ -73,6 +73,10 @@ private: void buildRenderList(QSGNode *node, QSGClipNode *clip); void renderElements(); void renderElement(int elementIndex); + void setInputLayout(const QSGGeometry *g, QSGD3D12PipelineState *pipelineState); + void setupClipping(const QSGGeometryNode *gn, int elementIndex); + void setScissor(const QRect &r); + void renderStencilClip(const QSGClipNode *clip, int elementIndex, const QMatrix4x4 &m, quint32 &stencilValue); struct Element { QSGBasicGeometryNode *node = nullptr; @@ -84,6 +88,8 @@ private: bool cboPrepared = false; }; + void queueDrawCall(const QSGGeometry *g, const Element &e); + QSet m_dirtyTransformNodes; QSet m_dirtyOpacityNodes; QBitArray m_opaqueElements; @@ -100,6 +106,8 @@ private: typedef QHash NodeDirtyMap; NodeDirtyMap m_nodeDirtyMap; + + QRect m_activeScissorRect; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri index 6fc591cb33..e7ea6cdde5 100644 --- a/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri +++ b/src/quick/scenegraph/adaptations/d3d12/shaders/shaders.pri @@ -8,5 +8,18 @@ vertexcolor_pshader.header = ps_vertexcolor.hlslh vertexcolor_pshader.entry = PS_VertexColor vertexcolor_pshader.type = ps_5_0 -HLSL_SHADERS = vertexcolor_vshader vertexcolor_pshader +stencilclip_VSPS = $$PWD/stencilclip.hlsl +stencilclip_vshader.input = stencilclip_VSPS +stencilclip_vshader.header = vs_stencilclip.hlslh +stencilclip_vshader.entry = VS_StencilClip +stencilclip_vshader.type = vs_5_0 +stencilclip_pshader.input = stencilclip_VSPS +stencilclip_pshader.header = ps_stencilclip.hlslh +stencilclip_pshader.entry = PS_StencilClip +stencilclip_pshader.type = ps_5_0 + +HLSL_SHADERS = \ + vertexcolor_vshader vertexcolor_pshader \ + stencilclip_vshader stencilclip_pshader + load(hlsl_bytecode_header) diff --git a/src/quick/scenegraph/adaptations/d3d12/shaders/stencilclip.hlsl b/src/quick/scenegraph/adaptations/d3d12/shaders/stencilclip.hlsl new file mode 100644 index 0000000000..9aff84d261 --- /dev/null +++ b/src/quick/scenegraph/adaptations/d3d12/shaders/stencilclip.hlsl @@ -0,0 +1,26 @@ +struct VSInput +{ + float4 position : POSITION; +}; + +cbuffer ConstantBuffer : register(b0) +{ + float4x4 mvp; +}; + +struct PSInput +{ + float4 position : SV_POSITION; +}; + +PSInput VS_StencilClip(VSInput input) +{ + PSInput result; + result.position = mul(mvp, input.position); + return result; +} + +float4 PS_StencilClip(PSInput input) : SV_TARGET +{ + return float4(0.81, 0.83, 0.12, 1.0); // Trolltech green ftw! +} diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp index 9a219ece63..9e78847667 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp +++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp @@ -258,6 +258,9 @@ void QSGDefaultNoMaterialRectangleNode::setRadius(qreal radius) void QSGDefaultNoMaterialRectangleNode::setAntialiasing(bool antialiasing) { + if (!supportsAntialiasing()) + return; + if (antialiasing == m_antialiasing) return; m_antialiasing = antialiasing; diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h index 61bf4fb4ae..f5f3c465b7 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h @@ -88,6 +88,7 @@ public: void update() override; protected: + virtual bool supportsAntialiasing() const { return true; } virtual void updateMaterialAntialiasing() = 0; virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0; diff --git a/tests/manual/nodetypes/Rects.qml b/tests/manual/nodetypes/Rects.qml new file mode 100644 index 0000000000..a6eeb375d4 --- /dev/null +++ b/tests/manual/nodetypes/Rects.qml @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + Rectangle { + width: 100 + height: 100 + anchors.centerIn: parent + color: "red" + NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; } + + Rectangle { + color: "gray" + width: 50 + height: 50 + anchors.centerIn: parent + + SequentialAnimation on opacity { + loops: Animation.Infinite + NumberAnimation { + from: 1.0 + to: 0.0 + duration: 4000 + } + NumberAnimation { + from: 0.0 + to: 1.0 + duration: 4000 + easing.type: Easing.InOutQuad + } + } + } + } + + Rectangle { + color: "green" + width: 100 + height: 200 + x: 0 + y: 0 + + NumberAnimation on x { + from: 0 + to: 300 + duration: 5000 + } + NumberAnimation on y { + from: 0 + to: 50 + duration: 2000 + } + + clip: true + Rectangle { + color: "lightGreen" + width: 50 + height: 50 + x: 75 + y: 175 + } + } + + Rectangle { + color: "blue" + width: 200 + height: 100 + x: 100 + y: 300 + radius: 16 + border.color: "red" + border.width: 4 + + SequentialAnimation on y { + loops: Animation.Infinite + NumberAnimation { + from: 300 + to: 500 + duration: 7000 + } + NumberAnimation { + from: 500 + to: 300 + duration: 3000 + } + } + } +} diff --git a/tests/manual/nodetypes/main.qml b/tests/manual/nodetypes/main.qml index 81d1e63ead..8e816fac28 100644 --- a/tests/manual/nodetypes/main.qml +++ b/tests/manual/nodetypes/main.qml @@ -41,69 +41,18 @@ import QtQuick 2.0 Item { - Rectangle { - width: 100 - height: 100 - anchors.centerIn: parent - color: "red" - NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; } + focus: true - Rectangle { - color: "gray" - width: 50 - height: 50 - anchors.centerIn: parent - - SequentialAnimation on opacity { - loops: Animation.Infinite - NumberAnimation { - from: 1.0 - to: 0.0 - duration: 4000 - } - NumberAnimation { - from: 0.0 - to: 1.0 - duration: 4000 - easing.type: Easing.InOutQuad - } - } - } - } - - Rectangle { - color: "green" - width: 100 - height: 200 - x: 0 - y: 0 - - NumberAnimation on x { - from: 0 - to: 300 - duration: 10000 - } + Loader { + anchors.fill: parent + id: loader } - Rectangle { - color: "blue" - width: 200 - height: 100 - x: 100 - y: 300 + Keys.onPressed: { + if (event.key === Qt.Key_S) + loader.source = ""; - SequentialAnimation on y { - loops: Animation.Infinite - NumberAnimation { - from: 300 - to: 500 - duration: 7000 - } - NumberAnimation { - from: 500 - to: 300 - duration: 3000 - } - } + if (event.key === Qt.Key_R) + loader.source = "qrc:/Rects.qml"; } } diff --git a/tests/manual/nodetypes/nodetypes.cpp b/tests/manual/nodetypes/nodetypes.cpp index 506a202ec7..f1ad15a92d 100644 --- a/tests/manual/nodetypes/nodetypes.cpp +++ b/tests/manual/nodetypes/nodetypes.cpp @@ -47,6 +47,10 @@ int main(int argc, char **argv) QGuiApplication app(argc, argv); + qDebug("Available tests:"); + qDebug(" [R] - Rectangles"); + qDebug("\nPress S to stop the currently running test\n"); + QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); view.resize(1024, 768); diff --git a/tests/manual/nodetypes/nodetypes.pro b/tests/manual/nodetypes/nodetypes.pro index c0b258d9ae..4ac026fc41 100644 --- a/tests/manual/nodetypes/nodetypes.pro +++ b/tests/manual/nodetypes/nodetypes.pro @@ -4,4 +4,4 @@ SOURCES += nodetypes.cpp RESOURCES += nodetypes.qrc -OTHER_FILES += main.qml +OTHER_FILES += main.qml Rects.qml diff --git a/tests/manual/nodetypes/nodetypes.qrc b/tests/manual/nodetypes/nodetypes.qrc index 5f6483ac33..ce582ab75a 100644 --- a/tests/manual/nodetypes/nodetypes.qrc +++ b/tests/manual/nodetypes/nodetypes.qrc @@ -1,5 +1,6 @@ main.qml + Rects.qml -- cgit v1.2.3