diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 6b3a4bd7c3f..cd001d9ed02 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -78,8 +78,10 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") \l{https://learn.microsoft.com/en-us/windows/win32/direct3darticles/directx-warp}{software adapter} is still an option. - \li Direct3D 12.0 or newer, with Shader Model 5.0 or newer. The D3D12 - device is by default created with specifying a minimum feature level of + \li Direct3D 12 on Windows 10 version 1703 and newer, with Shader Model 5.0 + or newer. Qt requires ID3D12Device2 to be present, hence the requirement + for at least version 1703 of Windows 10. The D3D12 device is by default + created with specifying a minimum feature level of \c{D3D_FEATURE_LEVEL_11_0}. \li Metal 1.2 or newer. @@ -976,7 +978,9 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") typically supported. When reported as supported, creating a QRhiTextureRenderTarget with a QRhiColorAttachment that references a texture array and has \l{QRhiColorAttachment::setMultiViewCount()}{multiViewCount} - set enables recording a render pass that uses multiview rendering. Note that + set enables recording a render pass that uses multiview rendering. In addition, + any QRhiGraphicsPipeline used in that render pass must have + \l{QRhiGraphicsPipeline::setMultiViewCount()}{the same view count set}. Note that multiview is only available in combination with 2D texture arrays. It cannot be used to optimize the rendering into individual textures (e.g. two, for the left and right eyes). Rather, the target of a multiview render pass is @@ -6772,6 +6776,26 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const \sa QRhi::NonFillPolygonMode */ +/*! + \fn int QRhiGraphicsPipeline::multiViewCount() const + \return the view count. The default is 0, indicating no multiview rendering. + \since 6.7 + */ + +/*! + \fn void QRhiGraphicsPipeline::setMultiViewCount(int count) + Sets the view \a count for multiview rendering. The default is 0, + indicating no multiview rendering. + \a count must be 2 or larger to trigger multiview rendering. + + Multiview is only available when the \l{QRhi::MultiView}{MultiView feature} + is reported as supported. The render target must be a 2D texture array, and + the color attachment for the render target must have the same \a count set. + + \since 6.7 + \sa QRhi::MultiView, QRhiColorAttachment::setMultiViewCount() + */ + /*! \class QRhiSwapChain \inmodule QtGui @@ -8088,9 +8112,17 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh #endif case D3D12: #ifdef Q_OS_WIN +#ifdef QRHI_D3D12_AVAILABLE r->d = new QRhiD3D12(static_cast(params), static_cast(importDevice)); break; +#else + qWarning("Qt was built without Direct3D 12 support. " + "This is likely due to having ancient SDK headers (such as d3d12.h) in the Qt build environment. " + "Rebuild Qt with an SDK supporting D3D12 features introduced in Windows 10 version 1703, " + "or use an MSVC build as those typically are built with more up-to-date SDKs."); + break; +#endif #else qWarning("This platform has no Direct3D 12 support"); break; diff --git a/src/gui/rhi/qrhi.h b/src/gui/rhi/qrhi.h index 585bead7b39..91a1c646a38 100644 --- a/src/gui/rhi/qrhi.h +++ b/src/gui/rhi/qrhi.h @@ -1441,6 +1441,9 @@ public: PolygonMode polygonMode() const {return m_polygonMode; } void setPolygonMode(PolygonMode mode) {m_polygonMode = mode; } + int multiViewCount() const { return m_multiViewCount; } + void setMultiViewCount(int count) { m_multiViewCount = count; } + virtual bool create() = 0; protected: @@ -1464,6 +1467,7 @@ protected: float m_slopeScaledDepthBias = 0.0f; int m_patchControlPointCount = 3; PolygonMode m_polygonMode = Fill; + int m_multiViewCount = 0; QVarLengthArray m_shaderStages; QRhiVertexInputLayout m_vertexInputLayout; QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr; diff --git a/src/gui/rhi/qrhi_platform.h b/src/gui/rhi/qrhi_platform.h index 30676d0da6a..3bf65c2e96f 100644 --- a/src/gui/rhi/qrhi_platform.h +++ b/src/gui/rhi/qrhi_platform.h @@ -145,7 +145,7 @@ struct Q_GUI_EXPORT QRhiD3D12NativeHandles : public QRhiNativeHandles struct Q_GUI_EXPORT QRhiD3D12CommandBufferNativeHandles : public QRhiNativeHandles { - void *commandList = nullptr; // ID3D12GraphicsCommandList + void *commandList = nullptr; // ID3D12GraphicsCommandList1 }; #endif // WIN/QDOC diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp index 55e2d626e02..e4f519cb259 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -19,6 +19,8 @@ #define QRHI_D3D12_HAS_OLD_PIX #endif +#ifdef __ID3D12Device2_INTERFACE_DEFINED__ + QT_BEGIN_NAMESPACE /* @@ -108,7 +110,7 @@ QT_BEGIN_NAMESPACE /*! \class QRhiD3D12CommandBufferNativeHandles \inmodule QtGui - \brief Holds the ID3D12GraphicsCommandList object that is backing a QRhiCommandBuffer. + \brief Holds the ID3D12GraphicsCommandList1 object that is backing a QRhiCommandBuffer. \note The command list object is only guaranteed to be valid, and in recording state, while recording a frame. That is, between a @@ -132,8 +134,14 @@ QRhiD3D12::QRhiD3D12(QRhiD3D12InitParams *params, QRhiD3D12NativeHandles *import debugLayer = params->enableDebugLayer; if (importParams) { if (importParams->dev) { - dev = reinterpret_cast(importParams->dev); - importedDevice = true; + ID3D12Device *d3d12Device = reinterpret_cast(importParams->dev); + if (SUCCEEDED(d3d12Device->QueryInterface(__uuidof(ID3D12Device2), reinterpret_cast(&dev)))) { + // get rid of the ref added by QueryInterface + d3d12Device->Release(); + importedDevice = true; + } else { + qWarning("ID3D12Device2 not supported, cannot import device"); + } } if (importParams->commandQueue) { cmdQueue = reinterpret_cast(importParams->commandQueue); @@ -267,7 +275,7 @@ bool QRhiD3D12::create(QRhi::Flags flags) hr = D3D12CreateDevice(activeAdapter, minimumFeatureLevel, - __uuidof(ID3D12Device), + __uuidof(ID3D12Device2), reinterpret_cast(&dev)); if (FAILED(hr)) { qWarning("Failed to create D3D12 device: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -402,6 +410,10 @@ bool QRhiD3D12::create(QRhi::Flags flags) return false; } + D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {}; + if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3)))) + caps.multiView = options3.ViewInstancingTier != D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED; + deviceLost = false; offscreenActive = false; @@ -639,7 +651,7 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const case QRhi::ThreeDimensionalTextureMipmaps: return false; // we generate mipmaps ourselves with compute and this is not implemented case QRhi::MultiView: - return false; + return caps.multiView; } return false; } @@ -796,6 +808,9 @@ void QRhiD3D12::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline } cbD->cmdList->IASetPrimitiveTopology(psD->topology); + + if (psD->viewInstanceMask) + cbD->cmdList->SetViewInstanceMask(psD->viewInstanceMask); } } @@ -1465,7 +1480,7 @@ QRhi::FrameOpResult QRhiD3D12::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame barrierGen.addTransitionBarrier(backBufferResourceHandle, D3D12_RESOURCE_STATE_PRESENT); barrierGen.enqueueBufferedTransitionBarriers(cbD); - ID3D12GraphicsCommandList *cmdList = cbD->cmdList; + ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList; HRESULT hr = cmdList->Close(); if (FAILED(hr)) { qWarning("Failed to close command list: %s", @@ -1562,7 +1577,7 @@ QRhi::FrameOpResult QRhiD3D12::endOffscreenFrame(QRhi::EndFrameFlags flags) offscreenActive = false; QD3D12CommandBuffer *cbD = offscreenCb[currentFrameSlot]; - ID3D12GraphicsCommandList *cmdList = cbD->cmdList; + ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList; HRESULT hr = cmdList->Close(); if (FAILED(hr)) { qWarning("Failed to close command list: %s", @@ -1603,7 +1618,7 @@ QRhi::FrameOpResult QRhiD3D12::finish() Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass); - ID3D12GraphicsCommandList *cmdList = cbD->cmdList; + ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList; HRESULT hr = cmdList->Close(); if (FAILED(hr)) { qWarning("Failed to close command list: %s", @@ -2912,7 +2927,7 @@ DXGI_SAMPLE_DESC QRhiD3D12::effectiveSampleCount(int sampleCount, DXGI_FORMAT fo return desc; } -bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList **cmdList) +bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList) { ID3D12CommandAllocator *cmdAlloc = cmdAllocators[currentFrameSlot]; if (!*cmdList) { @@ -2920,7 +2935,7 @@ bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList ** D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, - __uuidof(ID3D12GraphicsCommandList), + __uuidof(ID3D12GraphicsCommandList1), reinterpret_cast(cmdList)); if (FAILED(hr)) { qWarning("Failed to create command list: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -4412,19 +4427,21 @@ bool QD3D12TextureRenderTarget::create() qWarning("Could not look up texture handle for render target"); return false; } + const bool isMultiView = it->multiViewCount() >= 2; + UINT layerCount = isMultiView ? UINT(it->multiViewCount()) : 1; D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags()); if (texD->flags().testFlag(QRhiTexture::CubeMap)) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture2DArray.ArraySize = 1; + rtvDesc.Texture2DArray.ArraySize = layerCount; } else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) { if (texD->flags().testFlag(QRhiTexture::TextureArray)) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY; rtvDesc.Texture1DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture1DArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture1DArray.ArraySize = 1; + rtvDesc.Texture1DArray.ArraySize = layerCount; } else { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; rtvDesc.Texture1D.MipSlice = UINT(colorAtt.level()); @@ -4433,18 +4450,18 @@ bool QD3D12TextureRenderTarget::create() if (texD->sampleDesc.Count > 1) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; rtvDesc.Texture2DMSArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture2DMSArray.ArraySize = 1; + rtvDesc.Texture2DMSArray.ArraySize = layerCount; } else { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture2DArray.FirstArraySlice = UINT(colorAtt.layer()); - rtvDesc.Texture2DArray.ArraySize = 1; + rtvDesc.Texture2DArray.ArraySize = layerCount; } } else if (texD->flags().testFlag(QRhiTexture::ThreeDimensional)) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; rtvDesc.Texture3D.MipSlice = UINT(colorAtt.level()); rtvDesc.Texture3D.FirstWSlice = UINT(colorAtt.layer()); - rtvDesc.Texture3D.WSize = 1; + rtvDesc.Texture3D.WSize = layerCount; } else { if (texD->sampleDesc.Count > 1) { rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; @@ -5257,83 +5274,26 @@ bool QD3D12GraphicsPipeline::create() QD3D12RenderPassDescriptor *rpD = QRHI_RES(QD3D12RenderPassDescriptor, m_renderPassDesc); const DXGI_SAMPLE_DESC sampleDesc = rhiD->effectiveSampleCount(m_sampleCount, DXGI_FORMAT(rpD->colorFormat[0])); - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.pRootSignature = rootSig; - for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) { - const int d3dStage = qd3d12_stage(shaderStage.type()); - switch (d3dStage) { - case VS: - psoDesc.VS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.VS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case HS: - psoDesc.HS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.HS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case DS: - psoDesc.DS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.DS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case GS: - psoDesc.GS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.GS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - case PS: - psoDesc.PS.pShaderBytecode = shaderBytecode[d3dStage].constData(); - psoDesc.PS.BytecodeLength = shaderBytecode[d3dStage].size(); - break; - default: - Q_UNREACHABLE(); - break; - } - } + struct { + QD3D12PipelineStateSubObject rootSig; + QD3D12PipelineStateSubObject inputLayout; + QD3D12PipelineStateSubObject primitiveTopology; + QD3D12PipelineStateSubObject VS; + QD3D12PipelineStateSubObject HS; + QD3D12PipelineStateSubObject DS; + QD3D12PipelineStateSubObject GS; + QD3D12PipelineStateSubObject PS; + QD3D12PipelineStateSubObject rasterizerState; + QD3D12PipelineStateSubObject depthStencilState; + QD3D12PipelineStateSubObject blendState; + QD3D12PipelineStateSubObject rtFormats; + QD3D12PipelineStateSubObject dsFormat; + QD3D12PipelineStateSubObject sampleDesc; + QD3D12PipelineStateSubObject sampleMask; + QD3D12PipelineStateSubObject viewInstancingDesc; + } stream; - psoDesc.BlendState.IndependentBlendEnable = m_targetBlends.count() > 1; - for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) { - const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]); - D3D12_RENDER_TARGET_BLEND_DESC blend = {}; - blend.BlendEnable = b.enable; - blend.SrcBlend = toD3DBlendFactor(b.srcColor, true); - blend.DestBlend = toD3DBlendFactor(b.dstColor, true); - blend.BlendOp = toD3DBlendOp(b.opColor); - blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false); - blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false); - blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha); - blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite); - psoDesc.BlendState.RenderTarget[i] = blend; - } - if (m_targetBlends.isEmpty()) { - D3D12_RENDER_TARGET_BLEND_DESC blend = {}; - blend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; - psoDesc.BlendState.RenderTarget[0] = blend; - } - - psoDesc.SampleMask = 0xFFFFFFFF; - - psoDesc.RasterizerState.FillMode = toD3DFillMode(m_polygonMode); - psoDesc.RasterizerState.CullMode = toD3DCullMode(m_cullMode); - psoDesc.RasterizerState.FrontCounterClockwise = m_frontFace == CCW; - psoDesc.RasterizerState.DepthBias = m_depthBias; - psoDesc.RasterizerState.SlopeScaledDepthBias = m_slopeScaledDepthBias; - psoDesc.RasterizerState.DepthClipEnable = TRUE; - psoDesc.RasterizerState.MultisampleEnable = sampleDesc.Count > 1; - - psoDesc.DepthStencilState.DepthEnable = m_depthTest; - psoDesc.DepthStencilState.DepthWriteMask = m_depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; - psoDesc.DepthStencilState.DepthFunc = toD3DCompareOp(m_depthOp); - psoDesc.DepthStencilState.StencilEnable = m_stencilTest; - if (m_stencilTest) { - psoDesc.DepthStencilState.StencilReadMask = UINT8(m_stencilReadMask); - psoDesc.DepthStencilState.StencilWriteMask = UINT8(m_stencilWriteMask); - psoDesc.DepthStencilState.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp); - psoDesc.DepthStencilState.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp); - psoDesc.DepthStencilState.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp); - psoDesc.DepthStencilState.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp); - psoDesc.DepthStencilState.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp); - psoDesc.DepthStencilState.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp); - psoDesc.DepthStencilState.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp); - psoDesc.DepthStencilState.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp); - } + stream.rootSig.object = rootSig; QVarLengthArray inputDescs; QByteArrayList matrixSliceSemantics; @@ -5371,24 +5331,113 @@ bool QD3D12GraphicsPipeline::create() inputDescs.append(desc); } } - if (!inputDescs.isEmpty()) { - psoDesc.InputLayout.pInputElementDescs = inputDescs.constData(); - psoDesc.InputLayout.NumElements = inputDescs.count(); - } - psoDesc.PrimitiveTopologyType = toD3DTopologyType(m_topology); + stream.inputLayout.object.NumElements = inputDescs.count(); + stream.inputLayout.object.pInputElementDescs = inputDescs.isEmpty() ? nullptr : inputDescs.constData(); + + stream.primitiveTopology.object = toD3DTopologyType(m_topology); topology = toD3DTopology(m_topology, m_patchControlPointCount); - psoDesc.NumRenderTargets = rpD->colorAttachmentCount; + for (const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) { + const int d3dStage = qd3d12_stage(shaderStage.type()); + switch (d3dStage) { + case VS: + stream.VS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.VS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case HS: + stream.HS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.HS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case DS: + stream.DS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.DS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case GS: + stream.GS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.GS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + case PS: + stream.PS.object.pShaderBytecode = shaderBytecode[d3dStage].constData(); + stream.PS.object.BytecodeLength = shaderBytecode[d3dStage].size(); + break; + default: + Q_UNREACHABLE(); + break; + } + } + + stream.rasterizerState.object.FillMode = toD3DFillMode(m_polygonMode); + stream.rasterizerState.object.CullMode = toD3DCullMode(m_cullMode); + stream.rasterizerState.object.FrontCounterClockwise = m_frontFace == CCW; + stream.rasterizerState.object.DepthBias = m_depthBias; + stream.rasterizerState.object.SlopeScaledDepthBias = m_slopeScaledDepthBias; + stream.rasterizerState.object.DepthClipEnable = TRUE; + stream.rasterizerState.object.MultisampleEnable = sampleDesc.Count > 1; + + stream.depthStencilState.object.DepthEnable = m_depthTest; + stream.depthStencilState.object.DepthWriteMask = m_depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; + stream.depthStencilState.object.DepthFunc = toD3DCompareOp(m_depthOp); + stream.depthStencilState.object.StencilEnable = m_stencilTest; + if (m_stencilTest) { + stream.depthStencilState.object.StencilReadMask = UINT8(m_stencilReadMask); + stream.depthStencilState.object.StencilWriteMask = UINT8(m_stencilWriteMask); + stream.depthStencilState.object.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp); + stream.depthStencilState.object.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp); + stream.depthStencilState.object.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp); + stream.depthStencilState.object.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp); + stream.depthStencilState.object.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp); + stream.depthStencilState.object.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp); + stream.depthStencilState.object.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp); + stream.depthStencilState.object.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp); + } + + stream.blendState.object.IndependentBlendEnable = m_targetBlends.count() > 1; + for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) { + const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]); + D3D12_RENDER_TARGET_BLEND_DESC blend = {}; + blend.BlendEnable = b.enable; + blend.SrcBlend = toD3DBlendFactor(b.srcColor, true); + blend.DestBlend = toD3DBlendFactor(b.dstColor, true); + blend.BlendOp = toD3DBlendOp(b.opColor); + blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha, false); + blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha, false); + blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha); + blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite); + stream.blendState.object.RenderTarget[i] = blend; + } + if (m_targetBlends.isEmpty()) { + D3D12_RENDER_TARGET_BLEND_DESC blend = {}; + blend.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + stream.blendState.object.RenderTarget[0] = blend; + } + + stream.rtFormats.object.NumRenderTargets = rpD->colorAttachmentCount; for (int i = 0; i < rpD->colorAttachmentCount; ++i) - psoDesc.RTVFormats[i] = DXGI_FORMAT(rpD->colorFormat[i]); - psoDesc.DSVFormat = rpD->hasDepthStencil ? DXGI_FORMAT(rpD->dsFormat) : DXGI_FORMAT_UNKNOWN; - psoDesc.SampleDesc = sampleDesc; + stream.rtFormats.object.RTFormats[i] = DXGI_FORMAT(rpD->colorFormat[i]); + + stream.dsFormat.object = rpD->hasDepthStencil ? DXGI_FORMAT(rpD->dsFormat) : DXGI_FORMAT_UNKNOWN; + + stream.sampleDesc.object = sampleDesc; + + stream.sampleMask.object = 0xFFFFFFFF; + + viewInstanceMask = 0; + const bool isMultiView = m_multiViewCount >= 2; + stream.viewInstancingDesc.object.ViewInstanceCount = isMultiView ? m_multiViewCount : 0; + QVarLengthArray viewInstanceLocations; + if (isMultiView) { + for (int i = 0; i < m_multiViewCount; ++i) { + viewInstanceMask |= (1 << i); + viewInstanceLocations.append({ 0, UINT(i) }); + } + stream.viewInstancingDesc.object.pViewInstanceLocations = viewInstanceLocations.constData(); + } + + const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream }; ID3D12PipelineState *pso = nullptr; - HRESULT hr = rhiD->dev->CreateGraphicsPipelineState(&psoDesc, - __uuidof(ID3D12PipelineState), - reinterpret_cast(&pso)); + HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast(&pso)); if (FAILED(hr)) { qWarning("Failed to create graphics pipeline state: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -5487,14 +5536,16 @@ bool QD3D12ComputePipeline::create() return false; } - D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.pRootSignature = rootSig; - psoDesc.CS.pShaderBytecode = shaderBytecode.constData(); - psoDesc.CS.BytecodeLength = shaderBytecode.size(); + struct { + QD3D12PipelineStateSubObject rootSig; + QD3D12PipelineStateSubObject CS; + } stream; + stream.rootSig.object = rootSig; + stream.CS.object.pShaderBytecode = shaderBytecode.constData(); + stream.CS.object.BytecodeLength = shaderBytecode.size(); + const D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = { sizeof(stream), &stream }; ID3D12PipelineState *pso = nullptr; - HRESULT hr = rhiD->dev->CreateComputePipelineState(&psoDesc, - __uuidof(ID3D12PipelineState), - reinterpret_cast(&pso)); + HRESULT hr = rhiD->dev->CreatePipelineState(&streamDesc, __uuidof(ID3D12PipelineState), reinterpret_cast(&pso)); if (FAILED(hr)) { qWarning("Failed to create compute pipeline state: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -6141,3 +6192,5 @@ bool QD3D12SwapChain::createOrResize() } QT_END_NAMESPACE + +#endif // __ID3D12Device2_INTERFACE_DEFINED__ diff --git a/src/gui/rhi/qrhid3d12_p.h b/src/gui/rhi/qrhid3d12_p.h index a6954d279cf..bfc2f530dbd 100644 --- a/src/gui/rhi/qrhid3d12_p.h +++ b/src/gui/rhi/qrhid3d12_p.h @@ -30,6 +30,15 @@ #include "D3D12MemAlloc.h" +// ID3D12Device2 and ID3D12GraphicsCommandList1 and types and enums introduced +// with those are hard requirements now. These should be declared in any +// moderately recent d3d12.h, but if it is an SDK from before Windows 10 +// version 1703 then these types could be missing. In the absence of other +// options, handle this by skipping all the code and making QRhi::create() fail +// in such builds. +#ifdef __ID3D12Device2_INTERFACE_DEFINED__ +#define QRHI_D3D12_AVAILABLE + QT_BEGIN_NAMESPACE static const int QD3D12_FRAMES_IN_FLIGHT = 2; @@ -863,6 +872,7 @@ struct QD3D12GraphicsPipeline : public QRhiGraphicsPipeline QD3D12ObjectHandle rootSigHandle; std::array stageData; D3D12_PRIMITIVE_TOPOLOGY topology; + UINT viewInstanceMask = 0; uint generation = 0; friend class QRhiD3D12; }; @@ -889,7 +899,7 @@ struct QD3D12CommandBuffer : public QRhiCommandBuffer const QRhiNativeHandles *nativeHandles(); - ID3D12GraphicsCommandList *cmdList = nullptr; // not owned + ID3D12GraphicsCommandList1 *cmdList = nullptr; // not owned QRhiD3D12CommandBufferNativeHandles nativeHandlesStruct; enum PassType { @@ -984,12 +994,19 @@ struct QD3D12SwapChain : public QRhiSwapChain ID3D12Fence *fence = nullptr; HANDLE fenceEvent = nullptr; UINT64 fenceCounter = 0; - ID3D12GraphicsCommandList *cmdList = nullptr; + ID3D12GraphicsCommandList1 *cmdList = nullptr; } frameRes[QD3D12_FRAMES_IN_FLIGHT]; int currentFrameSlot = 0; // index in frameRes }; +template +struct alignas(void*) QD3D12PipelineStateSubObject +{ + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE type = Type; + T object = {}; +}; + class QRhiD3D12 : public QRhiImplementation { public: @@ -1111,7 +1128,7 @@ public: void waitGpu(); DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount, DXGI_FORMAT format) const; bool ensureDirectCompositionDevice(); - bool startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList **cmdList); + bool startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList); void enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates); void finishActiveReadbacks(bool forced = false); bool ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h, @@ -1122,7 +1139,7 @@ public: void bindShaderVisibleHeaps(QD3D12CommandBuffer *cbD); bool debugLayer = false; - ID3D12Device *dev = nullptr; + ID3D12Device2 *dev = nullptr; D3D_FEATURE_LEVEL minimumFeatureLevel = D3D_FEATURE_LEVEL(0); LUID adapterLuid = {}; bool importedDevice = false; @@ -1187,8 +1204,14 @@ public: const QRhiShaderResourceBinding::Data::StorageImageData &d, QD3D12ShaderResourceVisitor::StorageOp op, int shaderRegister); + + struct { + bool multiView = false; + } caps; }; QT_END_NAMESPACE +#endif // __ID3D12Device2_INTERFACE_DEFINED__ + #endif diff --git a/tests/manual/rhi/multiview/buildshaders.bat b/tests/manual/rhi/multiview/buildshaders.bat index 50ef1940585..0499bf032cf 100644 --- a/tests/manual/rhi/multiview/buildshaders.bat +++ b/tests/manual/rhi/multiview/buildshaders.bat @@ -1,4 +1,4 @@ -qsb --view-count 2 --glsl "300 es,330" --hlsl 61 multiview.vert -o multiview.vert.qsb -qsb --glsl "300 es,330" --hlsl 61 multiview.frag -o multiview.frag.qsb -qsb --glsl "300 es,330" --hlsl 61 texture.vert -o texture.vert.qsb -qsb --glsl "300 es,330" --hlsl 61 texture.frag -o texture.frag.qsb +qsb --view-count 2 --glsl "300 es,330" --hlsl 61 -c multiview.vert -o multiview.vert.qsb +qsb --glsl "300 es,330" --hlsl 61 -c multiview.frag -o multiview.frag.qsb +qsb --glsl "300 es,330" --hlsl 61 -c texture.vert -o texture.vert.qsb +qsb --glsl "300 es,330" --hlsl 61 -c texture.frag -o texture.frag.qsb diff --git a/tests/manual/rhi/multiview/multiview.cpp b/tests/manual/rhi/multiview/multiview.cpp index f4a5db3911e..80841d2084f 100644 --- a/tests/manual/rhi/multiview/multiview.cpp +++ b/tests/manual/rhi/multiview/multiview.cpp @@ -64,7 +64,7 @@ void Window::customInit() QRhiColorAttachment multiViewAtt(d.tex); // using array elements 0 and 1 multiViewAtt.setLayer(0); - multiViewAtt.setMultiViewCount(2); + multiViewAtt.setMultiViewCount(2); // the view count must be set both on the render target and the pipeline QRhiTextureRenderTargetDescription rtDesc(multiViewAtt); d.rt = m_r->newTextureRenderTarget(rtDesc); d.releasePool << d.rt; @@ -155,7 +155,7 @@ void Window::customInit() { QRhiShaderStage::Vertex, getShader(QLatin1String(":/multiview.vert.qsb")) }, { QRhiShaderStage::Fragment, getShader(QLatin1String(":/multiview.frag.qsb")) } }); - + d.triPs->setMultiViewCount(2); // the view count must be set both on the render target and the pipeline inputLayout.setBindings({ { 5 * sizeof(float) } }); diff --git a/tests/manual/rhi/multiview/multiview.frag.qsb b/tests/manual/rhi/multiview/multiview.frag.qsb index a6dd95c720b..ca177058f29 100644 Binary files a/tests/manual/rhi/multiview/multiview.frag.qsb and b/tests/manual/rhi/multiview/multiview.frag.qsb differ diff --git a/tests/manual/rhi/multiview/multiview.vert.qsb b/tests/manual/rhi/multiview/multiview.vert.qsb index 7eaa5ec4b51..34494606c42 100644 Binary files a/tests/manual/rhi/multiview/multiview.vert.qsb and b/tests/manual/rhi/multiview/multiview.vert.qsb differ diff --git a/tests/manual/rhi/multiview/texture.frag.qsb b/tests/manual/rhi/multiview/texture.frag.qsb index 2193dc7306a..1a6be88df7d 100644 Binary files a/tests/manual/rhi/multiview/texture.frag.qsb and b/tests/manual/rhi/multiview/texture.frag.qsb differ diff --git a/tests/manual/rhi/multiview/texture.vert.qsb b/tests/manual/rhi/multiview/texture.vert.qsb index 3af083e6a6f..78fc8b10729 100644 Binary files a/tests/manual/rhi/multiview/texture.vert.qsb and b/tests/manual/rhi/multiview/texture.vert.qsb differ