From cc3918abbc45ccb2716c279a603d1aaee96b974e Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 12 Aug 2019 14:08:50 +0200 Subject: RHI/Windows: Fix launching of MSVC binaries on Windows 7 Dynamically resolve CreateDXGIFactory2() which is not present on the platform. Task-number: QTBUG-76845 Change-Id: I4d15d72633544a8c11d2b78c8736cd20a2afdff1 Reviewed-by: Laszlo Agocs --- src/gui/rhi/qrhid3d11.cpp | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) (limited to 'src/gui/rhi/qrhid3d11.cpp') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 0c5df600a0..c0b13f1cc8 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -157,6 +157,36 @@ static inline Int aligned(Int v, Int byteAlign) return (v + byteAlign - 1) & ~(byteAlign - 1); } +static IDXGIFactory1 *createDXGIFactory2() +{ + IDXGIFactory1 *result = nullptr; + if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) { + using PtrCreateDXGIFactory2 = HRESULT (WINAPI *)(UINT, REFIID, void **); + QSystemLibrary dxgilib(QStringLiteral("dxgi")); + if (auto createDXGIFactory2 = (PtrCreateDXGIFactory2)dxgilib.resolve("CreateDXGIFactory2")) { + const HRESULT hr = createDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast(&result)); + if (FAILED(hr)) { + qWarning("CreateDXGIFactory2() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr))); + result = nullptr; + } + } else { + qWarning("Unable to resolve CreateDXGIFactory2()"); + } + } + return result; +} + +static IDXGIFactory1 *createDXGIFactory1() +{ + IDXGIFactory1 *result = nullptr; + const HRESULT hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast(&result)); + if (FAILED(hr)) { + qWarning("CreateDXGIFactory1() failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr))); + result = nullptr; + } + return result; +} + bool QRhiD3D11::create(QRhi::Flags flags) { Q_UNUSED(flags); @@ -165,19 +195,14 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (debugLayer) devFlags |= D3D11_CREATE_DEVICE_DEBUG; - HRESULT hr; -#if !defined(Q_CC_MINGW) - hasDxgi2 = QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7; - if (hasDxgi2) - hr = CreateDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast(&dxgiFactory)); + dxgiFactory = createDXGIFactory2(); + if (dxgiFactory != nullptr) + hasDxgi2 = true; else -#endif - hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast(&dxgiFactory)); + dxgiFactory = createDXGIFactory1(); - if (FAILED(hr)) { - qWarning("Failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr))); + if (dxgiFactory == nullptr) return false; - } if (!importedDevice) { IDXGIAdapter1 *adapterToUse = nullptr; -- cgit v1.2.3 From 1ae39cc72b7840b383aabbb5fd89fcfa3483d68c Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 12 Aug 2019 16:15:40 +0200 Subject: rhi: d3d11: Rework swapchain effect handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use FLIP_DISCARD swapchains only on Win10, stick with DISCARD otherwise. This may fix the swapchain creation problems on Windows 7. Add a QT_D3D_NO_FLIP env.var. to make it possible to disable using FLIP_DISCARD even on Win10. This is there for troubleshooting purposes. Finally, fix the backbuffer handling. What we originally ported from the D3D12 backend of Qt Quick is not quite how DXGI used to work with D3D11 and earlier. GetBuffer() can only be used to query index 0, and that's the backbuffer, the rest is managed internally. Follow this model. As an added bonus, disable Alt+Enter. Change-Id: Ie5c7a1e813864e7f873d55bc72cb22fc09213a05 Reviewed-by: Friedemann Kleint Reviewed-by: André de la Rocha Reviewed-by: Christian Strømme --- src/gui/rhi/qrhid3d11.cpp | 113 +++++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 41 deletions(-) (limited to 'src/gui/rhi/qrhid3d11.cpp') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index c0b13f1cc8..ddcc5179d2 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -196,14 +196,22 @@ bool QRhiD3D11::create(QRhi::Flags flags) devFlags |= D3D11_CREATE_DEVICE_DEBUG; dxgiFactory = createDXGIFactory2(); - if (dxgiFactory != nullptr) + if (dxgiFactory != nullptr) { hasDxgi2 = true; - else + supportsFlipDiscardSwapchain = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 + && !qEnvironmentVariableIntValue("QT_D3D_NO_FLIP"); + } else { dxgiFactory = createDXGIFactory1(); + hasDxgi2 = false; + supportsFlipDiscardSwapchain = false; + } if (dxgiFactory == nullptr) return false; + qCDebug(QRHI_LOG_INFO, "DXGI 1.2 = %s, FLIP_DISCARD swapchain supported = %s", + hasDxgi2 ? "true" : "false", supportsFlipDiscardSwapchain ? "true" : "false"); + if (!importedDevice) { IDXGIAdapter1 *adapterToUse = nullptr; IDXGIAdapter1 *adapter; @@ -916,7 +924,7 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF swapChainD->cb.resetState(); swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ? - swapChainD->msaaRtv[currentFrameSlot] : swapChainD->rtv[currentFrameSlot]; + swapChainD->msaaRtv[currentFrameSlot] : swapChainD->backBufferRtv; swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr; QRHI_PROF_F(beginSwapChainFrame(swapChain)); @@ -945,7 +953,7 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame executeCommandBuffer(&swapChainD->cb); if (swapChainD->sampleDesc.Count > 1) { - context->ResolveSubresource(swapChainD->tex[currentFrameSlot], 0, + context->ResolveSubresource(swapChainD->backBufferTex, 0, swapChainD->msaaTex[currentFrameSlot], 0, swapChainD->colorFormat); } @@ -1329,14 +1337,14 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate // has to be supported. Insert a resolve. QD3D11CommandBuffer::Command rcmd; rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes; - rcmd.args.resolveSubRes.dst = swapChainD->tex[swapChainD->currentFrameSlot]; + rcmd.args.resolveSubRes.dst = swapChainD->backBufferTex; rcmd.args.resolveSubRes.dstSubRes = 0; rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot]; rcmd.args.resolveSubRes.srcSubRes = 0; rcmd.args.resolveSubRes.format = swapChainD->colorFormat; cbD->commands.append(rcmd); } - src = swapChainD->tex[swapChainD->currentFrameSlot]; + src = swapChainD->backBufferTex; dxgiFormat = swapChainD->colorFormat; pixelSize = swapChainD->pixelSize; format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr); @@ -3528,9 +3536,9 @@ QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi) rt(rhi), cb(rhi) { + backBufferTex = nullptr; + backBufferRtv = nullptr; for (int i = 0; i < BUFFER_COUNT; ++i) { - tex[i] = nullptr; - rtv[i] = nullptr; msaaTex[i] = nullptr; msaaRtv[i] = nullptr; timestampActive[i] = false; @@ -3547,15 +3555,15 @@ QD3D11SwapChain::~QD3D11SwapChain() void QD3D11SwapChain::releaseBuffers() { + if (backBufferRtv) { + backBufferRtv->Release(); + backBufferRtv = nullptr; + } + if (backBufferTex) { + backBufferTex->Release(); + backBufferTex = nullptr; + } for (int i = 0; i < BUFFER_COUNT; ++i) { - if (rtv[i]) { - rtv[i]->Release(); - rtv[i] = nullptr; - } - if (tex[i]) { - tex[i]->Release(); - tex[i] = nullptr; - } if (msaaRtv[i]) { msaaRtv[i]->Release(); msaaRtv[i] = nullptr; @@ -3681,18 +3689,19 @@ bool QD3D11SwapChain::buildOrResize() const UINT swapChainFlags = 0; QRHI_RES_RHI(QRhiD3D11); + const bool useFlipDiscard = rhiD->hasDxgi2 && rhiD->supportsFlipDiscardSwapchain; if (!swapChain) { HWND hwnd = reinterpret_cast(window->winId()); sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); - // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the - // old DISCARD with back buffer count == 1). This makes no difference for - // the rest of the stuff except that automatic MSAA is unsupported and - // needs to be implemented via a custom multisample render target and an - // explicit resolve. - HRESULT hr; - if (rhiD->hasDxgi2) { + if (useFlipDiscard) { + // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the + // old DISCARD with back buffer count == 1). This makes no difference for + // the rest of the stuff except that automatic MSAA is unsupported and + // needs to be implemented via a custom multisample render target and an + // explicit resolve. + DXGI_SWAP_CHAIN_DESC1 desc; memset(&desc, 0, sizeof(desc)); desc.Width = pixelSize.width(); @@ -3715,7 +3724,10 @@ bool QD3D11SwapChain::buildOrResize() if (SUCCEEDED(hr)) swapChain = sc1; } else { - // Windows 7 + // Windows 7 for instance. Use DISCARD mode. Regardless, keep on + // using our manual resolve for symmetry with the FLIP_DISCARD code + // path when MSAA is requested. + DXGI_SWAP_CHAIN_DESC desc; memset(&desc, 0, sizeof(desc)); desc.BufferDesc.Width = pixelSize.width(); @@ -3725,10 +3737,10 @@ bool QD3D11SwapChain::buildOrResize() desc.BufferDesc.Format = colorFormat; desc.SampleDesc.Count = 1; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.BufferCount = BUFFER_COUNT; + desc.BufferCount = 1; desc.OutputWindow = hwnd; desc.Windowed = true; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; desc.Flags = swapChainFlags; hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain); @@ -3737,30 +3749,49 @@ bool QD3D11SwapChain::buildOrResize() qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr))); return false; } + rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER); } else { releaseBuffers(); - HRESULT hr = swapChain->ResizeBuffers(2, pixelSize.width(), pixelSize.height(), colorFormat, swapChainFlags); + const UINT count = useFlipDiscard ? BUFFER_COUNT : 1; + HRESULT hr = swapChain->ResizeBuffers(count, pixelSize.width(), pixelSize.height(), + colorFormat, swapChainFlags); if (FAILED(hr)) { qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr))); return false; } } + // This looks odd (for FLIP_DISCARD, esp. compared with backends for Vulkan + // & co.) but the backbuffer is always at index 0, with magic underneath. + // Some explanation from + // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements + // + // "In Direct3D 11, applications could call GetBuffer( 0, … ) only once. + // Every call to Present implicitly changed the resource identity of the + // returned interface. Direct3D 12 no longer supports that implicit + // resource identity change, due to the CPU overhead required and the + // flexible resource descriptor design. As a result, the application must + // manually call GetBuffer for every each buffer created with the + // swapchain." + + // So just query index 0 once (per resize) and be done with it. + HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast(&backBufferTex)); + if (FAILED(hr)) { + qWarning("Failed to query swapchain backbuffer: %s", qPrintable(comErrorMessage(hr))); + return false; + } + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + memset(&rtvDesc, 0, sizeof(rtvDesc)); + rtvDesc.Format = srgbAdjustedFormat; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv); + if (FAILED(hr)) { + qWarning("Failed to create rtv for swapchain backbuffer: %s", qPrintable(comErrorMessage(hr))); + return false; + } + + // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer. for (int i = 0; i < BUFFER_COUNT; ++i) { - HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast(&tex[i])); - if (FAILED(hr)) { - qWarning("Failed to query swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr))); - return false; - } - D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; - memset(&rtvDesc, 0, sizeof(rtvDesc)); - rtvDesc.Format = srgbAdjustedFormat; - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - hr = rhiD->dev->CreateRenderTargetView(tex[i], &rtvDesc, &rtv[i]); - if (FAILED(hr)) { - qWarning("Failed to create rtv for swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr))); - return false; - } if (sampleDesc.Count > 1) { if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i])) return false; -- cgit v1.2.3 From af686f5aef68a3a2d1c04593ef1ca296e29405cd Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 26 Aug 2019 11:36:56 +0200 Subject: Mark all rhi docs as internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is private API for the time being. Without this the QRhi* classes show up in the class list in the docs. Change-Id: I662abb9cc8eaae13ffe9266bd6313faa8e138353 Reviewed-by: Christian Strømme --- src/gui/rhi/qrhid3d11.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/gui/rhi/qrhid3d11.cpp') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index ddcc5179d2..9b411c5021 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -58,7 +58,8 @@ QT_BEGIN_NAMESPACE /*! \class QRhiD3D11InitParams - \inmodule QtRhi + \internal + \inmodule QtGui \brief Direct3D 11 specific initialization parameters. A D3D11-based QRhi needs no special parameters for initialization. If @@ -97,7 +98,8 @@ QT_BEGIN_NAMESPACE /*! \class QRhiD3D11NativeHandles - \inmodule QtRhi + \internal + \inmodule QtGui \brief Holds the D3D device and device context used by the QRhi. \note The class uses \c{void *} as the type since including the COM-based @@ -107,7 +109,8 @@ QT_BEGIN_NAMESPACE /*! \class QRhiD3D11TextureNativeHandles - \inmodule QtRhi + \internal + \inmodule QtGui \brief Holds the D3D texture object that is backing a QRhiTexture instance. \note The class uses \c{void *} as the type since including the COM-based -- cgit v1.2.3 From 4e97c4061e0ac7d885e62cfaf9ab7eb1918906f1 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 26 Aug 2019 16:10:31 +0200 Subject: rhi: Attempt to fix up the logic around beginExternal() Mainly for Vulkan where it lacked the recording of the still queued commands. Uncovered by Qt Quick examples that integrate custom Vulkan rendering. This still has an issue that needs to be tackled separately. (we probably will switch to using a dedicated secondary command buffer with RENDER_PASS_CONTINUE_BIT for the external commands, and then just have a vkCmdExecuteCommands in our own queue instead of recording everything in beginExternal). The possibility of losing glMemoryBarrier() calls due to begin/endExternal() with the OpenGL backend is fixed too. The logic here mirrors Vulkan to some extent except that we do not have a concept of (and so the trouble with) renderpass instances. Clean up around the implementations of finish() as well. Attempting to share code via a "flushCommandBuffer" function is admirable but is not worth it since some semantics are different. (finish() cannot be called within a begin/endPass, unlike begin/endExternal). Change-Id: I5137db598d6a40d484e53678f5c919abf750d9ed Reviewed-by: Andy Nichols --- src/gui/rhi/qrhid3d11.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'src/gui/rhi/qrhid3d11.cpp') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 9b411c5021..e978536dd7 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -873,8 +873,10 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb) void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb) { - Q_UNUSED(cb); - flushCommandBuffer(); + QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); + // no timestampSwapChain, in order to avoid timestamp mess + executeCommandBuffer(cbD); + cbD->resetCommands(); } void QRhiD3D11::endExternal(QRhiCommandBuffer *cb) @@ -1135,27 +1137,25 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format) QRhi::FrameOpResult QRhiD3D11::finish() { - if (inFrame) - flushCommandBuffer(); + if (inFrame) { + if (ofr.active) { + Q_ASSERT(!contextState.currentSwapChain); + Q_ASSERT(ofr.cbWrapper.recordingPass == QD3D11CommandBuffer::NoPass); + executeCommandBuffer(&ofr.cbWrapper); + ofr.cbWrapper.resetCommands(); + } else { + Q_ASSERT(contextState.currentSwapChain); + Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass); + executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess + contextState.currentSwapChain->cb.resetCommands(); + } + } finishActiveReadbacks(); return QRhi::FrameOpSuccess; } -void QRhiD3D11::flushCommandBuffer() -{ - if (ofr.active) { - Q_ASSERT(!contextState.currentSwapChain); - executeCommandBuffer(&ofr.cbWrapper); - ofr.cbWrapper.resetCommands(); - } else { - Q_ASSERT(contextState.currentSwapChain); - executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess - contextState.currentSwapChain->cb.resetCommands(); - } -} - void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc) { -- cgit v1.2.3 From 2602209f545ea3873f0dc880c258669a508d283a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 27 Aug 2019 12:49:38 +0200 Subject: rhi: vulkan: Introduce secondary command buffer usage As an option. Must opt in via setting ExternalContentsInPass in the flags for beginFrame(). It is somewhat unfortunate to require declaring this up front, but forcing using secondary command buffers always, even though beginExternal() may not be used in many applications, would be an overkill. Change-Id: I8d52bcab40c96f89f140c4c7877b6c459925e3c7 Reviewed-by: Andy Nichols --- src/gui/rhi/qrhid3d11.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/gui/rhi/qrhid3d11.cpp') diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index e978536dd7..3e136cdb80 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -991,8 +991,9 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame return QRhi::FrameOpSuccess; } -QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb) +QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) { + Q_UNUSED(flags); ofr.active = true; ofr.cbWrapper.resetState(); @@ -1001,8 +1002,9 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb) return QRhi::FrameOpSuccess; } -QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame() +QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags) { + Q_UNUSED(flags); ofr.active = false; executeCommandBuffer(&ofr.cbWrapper); -- cgit v1.2.3