diff options
Diffstat (limited to 'src/gui/rhi/qrhid3d11.cpp')
-rw-r--r-- | src/gui/rhi/qrhid3d11.cpp | 555 |
1 files changed, 339 insertions, 216 deletions
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 76e0d9d420..5dd32611cd 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -6,11 +6,9 @@ #include "vs_test_p.h" #include <QWindow> #include <qmath.h> -#include <private/qsystemlibrary_p.h> #include <QtCore/qcryptographichash.h> #include <QtCore/private/qsystemerror_p.h> - -#include <d3dcompiler.h> +#include "qrhid3dhelpers_p.h" QT_BEGIN_NAMESPACE @@ -32,7 +30,7 @@ using namespace Qt::StringLiterals; \since 6.6 \brief Direct3D 11 specific initialization parameters. - \note This a RHI API with limited compatibility guarantees, see \l QRhi + \note This is a RHI API with limited compatibility guarantees, see \l QRhi for details. A D3D11-based QRhi needs no special parameters for initialization. If @@ -89,28 +87,53 @@ using namespace Qt::StringLiterals; \c{d3d11.h} headers is not acceptable here. The actual types are \c{ID3D11Device *} and \c{ID3D11DeviceContext *}. - \note This a RHI API with limited compatibility guarantees, see \l QRhi + \note This is a RHI API with limited compatibility guarantees, see \l QRhi for details. */ /*! \variable QRhiD3D11NativeHandles::dev + + Points to a + \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11device}{ID3D11Device} + or left set to \nullptr if no existing device is to be imported. + + \note When importing a device, both the device and the device context must be set to valid objects. */ /*! \variable QRhiD3D11NativeHandles::context + + Points to a \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nn-d3d11-id3d11devicecontext}{ID3D11DeviceContext} + or left set to \nullptr if no existing device context is to be imported. + + \note When importing a device, both the device and the device context must be set to valid objects. */ /*! \variable QRhiD3D11NativeHandles::featureLevel + + Specifies the feature level passed to + \l{https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice}{D3D11CreateDevice()}. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. When not set, the default rules outlined in the D3D + documentation apply. */ /*! \variable QRhiD3D11NativeHandles::adapterLuidLow + + The low part of the local identifier (LUID) of the DXGI adapter to use. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. */ /*! \variable QRhiD3D11NativeHandles::adapterLuidHigh + + The high part of the local identifier (LUID) of the DXGI adapter to use. + Relevant only when QRhi creates the device, ignored when importing a device + and device context. */ // help mingw with its ancient sdk headers @@ -194,13 +217,14 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (qEnvironmentVariableIntValue("QT_D3D_FLIP_DISCARD")) qWarning("The default swap effect is FLIP_DISCARD, QT_D3D_FLIP_DISCARD is now ignored"); - if (qEnvironmentVariableIntValue("QT_D3D_NO_FLIP")) - qWarning("Non-FLIP swapchains are no longer supported, QT_D3D_NO_FLIP is now ignored"); + // Support for flip model swapchains is required now (since we are + // targeting Windows 10+), but the option for using the old model is still + // there. (some features are not supported then, however) + useLegacySwapchainModel = qEnvironmentVariableIntValue("QT_D3D_NO_FLIP"); - qCDebug(QRHI_LOG_INFO, "FLIP_* swapchain supported = true, ALLOW_TEARING supported = %s", - supportsAllowTearing ? "true" : "false"); - - qCDebug(QRHI_LOG_INFO, "Default swap effect: FLIP_DISCARD"); + qCDebug(QRHI_LOG_INFO, "FLIP_* swapchain supported = true, ALLOW_TEARING supported = %s, use legacy (non-FLIP) model = %s", + supportsAllowTearing ? "true" : "false", + useLegacySwapchainModel ? "true" : "false"); if (!importedDeviceAndContext) { IDXGIAdapter1 *adapter; @@ -249,9 +273,7 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) { activeAdapter = adapter; adapterLuid = desc.AdapterLuid; - driverInfoStruct.deviceName = name.toUtf8(); - driverInfoStruct.deviceId = desc.DeviceId; - driverInfoStruct.vendorId = desc.VendorId; + QRhiD3D::fillDriverInfo(&driverInfoStruct, desc); qCDebug(QRHI_LOG_INFO, " using this adapter"); } else { adapter->Release(); @@ -294,21 +316,24 @@ bool QRhiD3D11::create(QRhi::Flags flags) return false; } + const bool supports11_1 = SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void **>(&context))); + ctx->Release(); + if (!supports11_1) { + qWarning("ID3D11DeviceContext1 not supported"); + return false; + } + // Test if creating a Shader Model 5.0 vertex shader works; we want to // fail already in create() if that's not the case. ID3D11VertexShader *testShader = nullptr; if (SUCCEEDED(dev->CreateVertexShader(g_testVertexShader, sizeof(g_testVertexShader), nullptr, &testShader))) { testShader->Release(); } else { - qWarning("D3D11 smoke test failed (failed to create vertex shader)"); - ctx->Release(); - return false; - } - - const bool supports11_1 = SUCCEEDED(ctx->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void **>(&context))); - ctx->Release(); - if (!supports11_1) { - qWarning("ID3D11DeviceContext1 not supported"); + static const char *msg = "D3D11 smoke test: Failed to create vertex shader"; + if (flags.testFlag(QRhi::SuppressSmokeTestWarnings)) + qCDebug(QRHI_LOG_INFO, "%s", msg); + else + qWarning("%s", msg); return false; } @@ -318,11 +343,19 @@ bool QRhiD3D11::create(QRhi::Flags flags) // still not support this D3D_FEATURE_LEVEL_11_1 feature. (e.g. // because it only does 11_0) if (!features.ConstantBufferOffsetting) { - qWarning("Constant buffer offsetting is not supported by the driver"); + static const char *msg = "D3D11 smoke test: Constant buffer offsetting is not supported by the driver"; + if (flags.testFlag(QRhi::SuppressSmokeTestWarnings)) + qCDebug(QRHI_LOG_INFO, "%s", msg); + else + qWarning("%s", msg); return false; } } else { - qWarning("Failed to query D3D11_FEATURE_D3D11_OPTIONS"); + static const char *msg = "D3D11 smoke test: Failed to query D3D11_FEATURE_D3D11_OPTIONS"; + if (flags.testFlag(QRhi::SuppressSmokeTestWarnings)) + qCDebug(QRHI_LOG_INFO, "%s", msg); + else + qWarning("%s", msg); return false; } } else { @@ -332,12 +365,14 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (SUCCEEDED(dev->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void **>(&dxgiDev)))) { IDXGIAdapter *adapter = nullptr; if (SUCCEEDED(dxgiDev->GetAdapter(&adapter))) { - DXGI_ADAPTER_DESC desc; - adapter->GetDesc(&desc); - adapterLuid = desc.AdapterLuid; - driverInfoStruct.deviceName = QString::fromUtf16(reinterpret_cast<char16_t *>(desc.Description)).toUtf8(); - driverInfoStruct.deviceId = desc.DeviceId; - driverInfoStruct.vendorId = desc.VendorId; + IDXGIAdapter1 *adapter1 = nullptr; + if (SUCCEEDED(adapter->QueryInterface(__uuidof(IDXGIAdapter1), reinterpret_cast<void **>(&adapter1)))) { + DXGI_ADAPTER_DESC1 desc; + adapter1->GetDesc1(&desc); + adapterLuid = desc.AdapterLuid; + QRhiD3D::fillDriverInfo(&driverInfoStruct, desc); + adapter1->Release(); + } adapter->Release(); } dxgiDev->Release(); @@ -348,11 +383,6 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (FAILED(context->QueryInterface(__uuidof(ID3DUserDefinedAnnotation), reinterpret_cast<void **>(&annotations)))) annotations = nullptr; - if (flags.testFlag(QRhi::EnableTimestamps)) { - ofr.timestamps.prepare(2, this); - // timestamp queries are optional so we can go on even if they failed - } - deviceLost = false; nativeHandlesStruct.dev = dev; @@ -378,7 +408,16 @@ void QRhiD3D11::destroy() clearShaderCache(); - ofr.timestamps.destroy(); + if (ofr.tsDisjointQuery) { + ofr.tsDisjointQuery->Release(); + ofr.tsDisjointQuery = nullptr; + } + for (int i = 0; i < 2; ++i) { + if (ofr.tsQueries[i]) { + ofr.tsQueries[i]->Release(); + ofr.tsQueries[i] = nullptr; + } + } if (annotations) { annotations->Release(); @@ -427,19 +466,13 @@ QList<int> QRhiD3D11::supportedSampleCounts() const return { 1, 2, 4, 8 }; } -DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleCount(int sampleCount) const +DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleDesc(int sampleCount) const { DXGI_SAMPLE_DESC desc; desc.Count = 1; desc.Quality = 0; - // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1. - int s = qBound(1, sampleCount, 64); - - if (!supportedSampleCounts().contains(s)) { - qWarning("Attempted to set unsupported sample count %d", sampleCount); - return desc; - } + const int s = effectiveSampleCount(sampleCount); desc.Count = UINT(s); if (s > 1) @@ -588,6 +621,12 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return true; case QRhi::ThreeDimensionalTextureMipmaps: return true; + case QRhi::MultiView: + return false; + case QRhi::TextureViewFormat: + return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing + case QRhi::ResolveDepthStencil: + return false; default: Q_UNREACHABLE(); return false; @@ -1258,7 +1297,6 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb) void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb) { QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb); - // no timestampSwapChain, in order to avoid timestamp mess executeCommandBuffer(cbD); cbD->resetCommands(); } @@ -1281,6 +1319,19 @@ double QRhiD3D11::lastCompletedGpuTime(QRhiCommandBuffer *cb) return cbD->lastGpuTime; } +static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt) +{ + switch (rt->resourceType()) { + case QRhiResource::SwapChainRenderTarget: + return &QRHI_RES(QD3D11SwapChainRenderTarget, rt)->d; + case QRhiResource::TextureRenderTarget: + return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d; + default: + Q_UNREACHABLE(); + return nullptr; + } +} + QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) { Q_UNUSED(flags); @@ -1297,12 +1348,22 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF finishActiveReadbacks(); - if (swapChainD->timestamps.active[currentFrameSlot]) { + if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) { double elapsedSec = 0; - if (swapChainD->timestamps.tryQueryTimestamps(currentFrameSlot, context, &elapsedSec)) + if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, context, &elapsedSec)) swapChainD->cb.lastGpuTime = elapsedSec; } + ID3D11Query *tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2]; + ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex]; + const bool recordTimestamps = tsStart && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]; + + QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::BeginFrame; + cmd.args.beginFrame.tsQuery = recordTimestamps ? tsStart : nullptr; + cmd.args.beginFrame.tsDisjointQuery = recordTimestamps ? tsDisjoint : nullptr; + cmd.args.beginFrame.swapchainData = rtData(&swapChainD->rt); + return QRhi::FrameOpSuccess; } @@ -1312,17 +1373,13 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame Q_ASSERT(contextState.currentSwapChain = swapChainD); const int currentFrameSlot = swapChainD->currentFrameSlot; - ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[currentFrameSlot]; - const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot; - ID3D11Query *tsStart = swapChainD->timestamps.query[tsIdx]; - ID3D11Query *tsEnd = swapChainD->timestamps.query[tsIdx + 1]; - const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !swapChainD->timestamps.active[currentFrameSlot]; + QD3D11CommandBuffer::Command &cmd(swapChainD->cb.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::EndFrame; + cmd.args.endFrame.tsQuery = nullptr; // done later manually, see below + cmd.args.endFrame.tsDisjointQuery = nullptr; // send all commands to the context - if (recordTimestamps) - executeCommandBuffer(&swapChainD->cb, swapChainD); - else - executeCommandBuffer(&swapChainD->cb); + executeCommandBuffer(&swapChainD->cb); if (swapChainD->sampleDesc.Count > 1) { context->ResolveSubresource(swapChainD->backBufferTex, 0, @@ -1330,17 +1387,25 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame swapChainD->colorFormat); } - // this is here because we want to include the time spent on the resolve as well + // this is here because we want to include the time spent on the ResolveSubresource as well + ID3D11Query *tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1]; + ID3D11Query *tsDisjoint = swapChainD->timestamps.disjointQuery[swapChainD->currentTimestampPairIndex]; + const bool recordTimestamps = tsEnd && tsDisjoint && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]; if (recordTimestamps) { context->End(tsEnd); context->End(tsDisjoint); - swapChainD->timestamps.active[currentFrameSlot] = true; + swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true; + swapChainD->currentTimestampPairIndex = (swapChainD->currentTimestampPairIndex + 1) % QD3D11SwapChainTimestamps::TIMESTAMP_PAIRS; } if (!flags.testFlag(QRhi::SkipPresent)) { UINT presentFlags = 0; if (swapChainD->swapInterval == 0 && (swapChainD->swapChainFlags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)) presentFlags |= DXGI_PRESENT_ALLOW_TEARING; + if (!swapChainD->swapChain) { + qWarning("Failed to present: IDXGISwapChain is unavailable"); + return QRhi::FrameOpError; + } HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { qWarning("Device loss detected in Present()"); @@ -1375,12 +1440,36 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi: ofr.cbWrapper.resetState(); *cb = &ofr.cbWrapper; - if (ofr.timestamps.active[ofr.timestampIdx]) { - double elapsedSec = 0; - if (ofr.timestamps.tryQueryTimestamps(ofr.timestampIdx, context, &elapsedSec)) - ofr.cbWrapper.lastGpuTime = elapsedSec; + if (rhiFlags.testFlag(QRhi::EnableTimestamps)) { + D3D11_QUERY_DESC queryDesc = {}; + if (!ofr.tsDisjointQuery) { + queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; + HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsDisjointQuery); + if (FAILED(hr)) { + qWarning("Failed to create timestamp disjoint query: %s", + qPrintable(QSystemError::windowsComString(hr))); + return QRhi::FrameOpError; + } + } + queryDesc.Query = D3D11_QUERY_TIMESTAMP; + for (int i = 0; i < 2; ++i) { + if (!ofr.tsQueries[i]) { + HRESULT hr = dev->CreateQuery(&queryDesc, &ofr.tsQueries[i]); + if (FAILED(hr)) { + qWarning("Failed to create timestamp query: %s", + qPrintable(QSystemError::windowsComString(hr))); + return QRhi::FrameOpError; + } + } + } } + QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::BeginFrame; + cmd.args.beginFrame.tsQuery = ofr.tsQueries[0] ? ofr.tsQueries[0] : nullptr; + cmd.args.beginFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr; + cmd.args.beginFrame.swapchainData = nullptr; + return QRhi::FrameOpSuccess; } @@ -1389,25 +1478,39 @@ QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags) Q_UNUSED(flags); ofr.active = false; - ID3D11Query *tsDisjoint = ofr.timestamps.disjointQuery[ofr.timestampIdx]; - ID3D11Query *tsStart = ofr.timestamps.query[ofr.timestampIdx * 2]; - ID3D11Query *tsEnd = ofr.timestamps.query[ofr.timestampIdx * 2 + 1]; - const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !ofr.timestamps.active[ofr.timestampIdx]; - if (recordTimestamps) { - context->Begin(tsDisjoint); - context->End(tsStart); // record timestamp; no Begin() for D3D11_QUERY_TIMESTAMP - } + QD3D11CommandBuffer::Command &cmd(ofr.cbWrapper.commands.get()); + cmd.cmd = QD3D11CommandBuffer::Command::EndFrame; + cmd.args.endFrame.tsQuery = ofr.tsQueries[1] ? ofr.tsQueries[1] : nullptr; + cmd.args.endFrame.tsDisjointQuery = ofr.tsDisjointQuery ? ofr.tsDisjointQuery : nullptr; executeCommandBuffer(&ofr.cbWrapper); context->Flush(); finishActiveReadbacks(); - if (recordTimestamps) { - context->End(tsEnd); - context->End(tsDisjoint); - ofr.timestamps.active[ofr.timestampIdx] = true; - ofr.timestampIdx = (ofr.timestampIdx + 1) % 2; + if (ofr.tsQueries[0]) { + quint64 timestamps[2]; + D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj; + HRESULT hr; + bool ok = true; + do { + hr = context->GetData(ofr.tsDisjointQuery, &dj, sizeof(dj), 0); + } while (hr == S_FALSE); + ok &= hr == S_OK; + do { + hr = context->GetData(ofr.tsQueries[1], ×tamps[1], sizeof(quint64), 0); + } while (hr == S_FALSE); + ok &= hr == S_OK; + do { + hr = context->GetData(ofr.tsQueries[0], ×tamps[0], sizeof(quint64), 0); + } while (hr == S_FALSE); + ok &= hr == S_OK; + if (ok) { + if (!dj.Disjoint && dj.Frequency) { + const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f; + ofr.cbWrapper.lastGpuTime = elapsedMs / 1000.0; + } + } } return QRhi::FrameOpSuccess; @@ -1447,11 +1550,13 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex case QRhiTexture::D16: return DXGI_FORMAT_R16_TYPELESS; case QRhiTexture::D24: - return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + return DXGI_FORMAT_R24G8_TYPELESS; case QRhiTexture::D24S8: - return DXGI_FORMAT_D24_UNORM_S8_UINT; + return DXGI_FORMAT_R24G8_TYPELESS; case QRhiTexture::D32F: return DXGI_FORMAT_R32_TYPELESS; + case QRhiTexture::D32FS8: + return DXGI_FORMAT_R32G8X24_TYPELESS; case QRhiTexture::BC1: return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM; @@ -1532,6 +1637,7 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format) case QRhiTexture::Format::D24: case QRhiTexture::Format::D24S8: case QRhiTexture::Format::D32F: + case QRhiTexture::Format::D32FS8: return true; default: @@ -1550,7 +1656,7 @@ QRhi::FrameOpResult QRhiD3D11::finish() } 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 + executeCommandBuffer(&contextState.currentSwapChain->cb); contextState.currentSwapChain->cb.resetCommands(); } } @@ -1925,19 +2031,6 @@ void QRhiD3D11::finishActiveReadbacks() f(); } -static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt) -{ - switch (rt->resourceType()) { - case QRhiResource::SwapChainRenderTarget: - return &QRHI_RES(QD3D11SwapChainRenderTarget, rt)->d; - case QRhiResource::TextureRenderTarget: - return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d; - default: - Q_UNREACHABLE(); - return nullptr; - } -} - void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) { Q_ASSERT(QRHI_RES(QD3D11CommandBuffer, cb)->recordingPass == QD3D11CommandBuffer::NoPass); @@ -2056,6 +2149,8 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, UINT(colorAtt.layer()), 1); cmd.args.resolveSubRes.format = dstTexD->dxgiFormat; } + if (rtTex->m_desc.depthResolveTexture()) + qWarning("Resolving multisample depth-stencil buffers is not supported with D3D"); } cbD->recordingPass = QD3D11CommandBuffer::NoPass; @@ -2643,7 +2738,7 @@ void QRhiD3D11::resetShaderResources() currentShaderMask &= ~StageU##MaskBit; \ } -void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain) +void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD) { quint32 stencilRef = 0; float blendConstants[] = { 1, 1, 1, 1 }; @@ -2656,26 +2751,30 @@ void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain * }; int currentShaderMask = 0xFF; - if (timestampSwapChain) { - const int currentFrameSlot = timestampSwapChain->currentFrameSlot; - ID3D11Query *tsDisjoint = timestampSwapChain->timestamps.disjointQuery[currentFrameSlot]; - const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot; - ID3D11Query *tsStart = timestampSwapChain->timestamps.query[tsIdx]; - if (tsDisjoint && tsStart && !timestampSwapChain->timestamps.active[currentFrameSlot]) { - // The timestamps seem to include vsync time with Present(1), except - // when running on a non-primary gpu. This is not ideal. So try working - // it around by issuing a semi-fake OMSetRenderTargets early and - // writing the first timestamp only afterwards. - context->Begin(tsDisjoint); - QD3D11RenderTargetData *rtD = rtData(×tampSwapChain->rt); - context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); - context->End(tsStart); // just record a timestamp, no Begin needed - } - } - for (auto it = cbD->commands.cbegin(), end = cbD->commands.cend(); it != end; ++it) { const QD3D11CommandBuffer::Command &cmd(*it); switch (cmd.cmd) { + case QD3D11CommandBuffer::Command::BeginFrame: + if (cmd.args.beginFrame.tsDisjointQuery) + context->Begin(cmd.args.beginFrame.tsDisjointQuery); + if (cmd.args.beginFrame.tsQuery) { + if (cmd.args.beginFrame.swapchainData) { + // The timestamps seem to include vsync time with Present(1), except + // when running on a non-primary gpu. This is not ideal. So try working + // it around by issuing a semi-fake OMSetRenderTargets early and + // writing the first timestamp only afterwards. + QD3D11RenderTargetData *rtD = cmd.args.beginFrame.swapchainData; + context->OMSetRenderTargets(UINT(rtD->colorAttCount), rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv); + } + context->End(cmd.args.beginFrame.tsQuery); // no Begin() for D3D11_QUERY_TIMESTAMP + } + break; + case QD3D11CommandBuffer::Command::EndFrame: + if (cmd.args.endFrame.tsQuery) + context->End(cmd.args.endFrame.tsQuery); + if (cmd.args.endFrame.tsDisjointQuery) + context->End(cmd.args.endFrame.tsDisjointQuery); + break; case QD3D11CommandBuffer::Command::ResetShaderResources: resetShaderResources(); break; @@ -3029,7 +3128,7 @@ bool QD3D11RenderBuffer::create() return false; QRHI_RES_RHI(QRhiD3D11); - sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); + sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount); D3D11_TEXTURE2D_DESC desc = {}; desc.Width = UINT(m_pixelSize.width()); @@ -3159,6 +3258,8 @@ static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format) return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; case QRhiTexture::Format::D32F: return DXGI_FORMAT_R32_FLOAT; + case QRhiTexture::Format::D32FS8: + return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; default: Q_UNREACHABLE(); return DXGI_FORMAT_R32_FLOAT; @@ -3171,11 +3272,13 @@ static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format) case QRhiTexture::Format::D16: return DXGI_FORMAT_D16_UNORM; case QRhiTexture::Format::D24: - return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + return DXGI_FORMAT_D24_UNORM_S8_UINT; case QRhiTexture::Format::D24S8: return DXGI_FORMAT_D24_UNORM_S8_UINT; case QRhiTexture::Format::D32F: return DXGI_FORMAT_D32_FLOAT; + case QRhiTexture::Format::D32FS8: + return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; default: Q_UNREACHABLE(); return DXGI_FORMAT_D32_FLOAT; @@ -3200,7 +3303,7 @@ bool QD3D11Texture::prepareCreate(QSize *adjustedSize) QRHI_RES_RHI(QRhiD3D11); dxgiFormat = toD3DTextureFormat(m_format, m_flags); mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1); - sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); + sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount); if (sampleDesc.Count > 1) { if (isCube) { qWarning("Cubemap texture cannot be multisample"); @@ -3834,6 +3937,27 @@ bool QD3D11TextureRenderTarget::create() dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format()); dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; + if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) { + if (depthTexD->sampleDesc.Count > 1) { + dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) { + dsvDesc.Texture2DMSArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart()); + dsvDesc.Texture2DMSArray.ArraySize = UINT(depthTexD->arrayRangeLength()); + } else { + dsvDesc.Texture2DMSArray.FirstArraySlice = 0; + dsvDesc.Texture2DMSArray.ArraySize = UINT(qMax(0, depthTexD->arraySize())); + } + } else { + dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + if (depthTexD->arrayRangeStart() >= 0 && depthTexD->arrayRangeLength() >= 0) { + dsvDesc.Texture2DArray.FirstArraySlice = UINT(depthTexD->arrayRangeStart()); + dsvDesc.Texture2DArray.ArraySize = UINT(depthTexD->arrayRangeLength()); + } else { + dsvDesc.Texture2DArray.FirstArraySlice = 0; + dsvDesc.Texture2DArray.ArraySize = UINT(qMax(0, depthTexD->arraySize())); + } + } + } HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv); if (FAILED(hr)) { qWarning("Failed to create dsv: %s", @@ -4128,6 +4252,22 @@ static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format return DXGI_FORMAT_R16G16_FLOAT; case QRhiVertexInputAttribute::Half: return DXGI_FORMAT_R16_FLOAT; + case QRhiVertexInputAttribute::UShort4: + // Note: D3D does not support UShort3. Pass through UShort3 as UShort4. + case QRhiVertexInputAttribute::UShort3: + return DXGI_FORMAT_R16G16B16A16_UINT; + case QRhiVertexInputAttribute::UShort2: + return DXGI_FORMAT_R16G16_UINT; + case QRhiVertexInputAttribute::UShort: + return DXGI_FORMAT_R16_UINT; + case QRhiVertexInputAttribute::SShort4: + // Note: D3D does not support SShort3. Pass through SShort3 as SShort4. + case QRhiVertexInputAttribute::SShort3: + return DXGI_FORMAT_R16G16B16A16_SINT; + case QRhiVertexInputAttribute::SShort2: + return DXGI_FORMAT_R16G16_SINT; + case QRhiVertexInputAttribute::SShort: + return DXGI_FORMAT_R16_SINT; default: Q_UNREACHABLE(); return DXGI_FORMAT_R32G32B32A32_FLOAT; @@ -4240,18 +4380,6 @@ static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op) } } -static pD3DCompile resolveD3DCompile() -{ - for (const wchar_t *libraryName : {L"D3DCompiler_47", L"D3DCompiler_43"}) { - QSystemLibrary library(libraryName); - if (library.load()) { - if (auto symbol = library.resolve("D3DCompile")) - return reinterpret_cast<pD3DCompile>(symbol); - } - } - return nullptr; -} - static inline QByteArray sourceHash(const QByteArray &source) { // taken from the GL backend, use the same mechanism to get a key @@ -4317,7 +4445,7 @@ QByteArray QRhiD3D11::compileHlslShaderSource(const QShader &shader, QShader::Va return cacheIt.value(); } - static const pD3DCompile d3dCompile = resolveD3DCompile(); + static const pD3DCompile d3dCompile = QRhiD3D::resolveD3DCompile(); if (d3dCompile == nullptr) { qWarning("Unable to resolve function D3DCompile()"); return QByteArray(); @@ -4367,7 +4495,7 @@ bool QD3D11GraphicsPipeline::create() rastDesc.SlopeScaledDepthBias = m_slopeScaledDepthBias; rastDesc.DepthClipEnable = true; rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor); - rastDesc.MultisampleEnable = rhiD->effectiveSampleCount(m_sampleCount).Count > 1; + rastDesc.MultisampleEnable = rhiD->effectiveSampleDesc(m_sampleCount).Count > 1; HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState); if (FAILED(hr)) { qWarning("Failed to create rasterizer state: %s", @@ -4685,14 +4813,13 @@ void QD3D11CommandBuffer::destroy() // nothing to do here } -bool QD3D11Timestamps::prepare(int pairCount, QRhiD3D11 *rhiD) +bool QD3D11SwapChainTimestamps::prepare(QRhiD3D11 *rhiD) { // Creates the query objects if not yet done, but otherwise calling this // function is expected to be a no-op. - Q_ASSERT(pairCount <= MAX_TIMESTAMP_PAIRS); D3D11_QUERY_DESC queryDesc = {}; - for (int i = 0; i < pairCount; ++i) { + for (int i = 0; i < TIMESTAMP_PAIRS; ++i) { if (!disjointQuery[i]) { queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &disjointQuery[i]); @@ -4704,7 +4831,7 @@ bool QD3D11Timestamps::prepare(int pairCount, QRhiD3D11 *rhiD) } queryDesc.Query = D3D11_QUERY_TIMESTAMP; for (int j = 0; j < 2; ++j) { - const int idx = pairCount * i + j; + const int idx = 2 * i + j; if (!query[idx]) { HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &query[idx]); if (FAILED(hr)) { @@ -4715,20 +4842,19 @@ bool QD3D11Timestamps::prepare(int pairCount, QRhiD3D11 *rhiD) } } } - this->pairCount = pairCount; return true; } -void QD3D11Timestamps::destroy() +void QD3D11SwapChainTimestamps::destroy() { - for (int i = 0; i < MAX_TIMESTAMP_PAIRS; ++i) { + for (int i = 0; i < TIMESTAMP_PAIRS; ++i) { active[i] = false; if (disjointQuery[i]) { disjointQuery[i]->Release(); disjointQuery[i] = nullptr; } for (int j = 0; j < 2; ++j) { - const int idx = MAX_TIMESTAMP_PAIRS * i + j; + const int idx = TIMESTAMP_PAIRS * i + j; if (query[idx]) { query[idx]->Release(); query[idx] = nullptr; @@ -4737,26 +4863,21 @@ void QD3D11Timestamps::destroy() } } -bool QD3D11Timestamps::tryQueryTimestamps(int idx, ID3D11DeviceContext *context, double *elapsedSec) +bool QD3D11SwapChainTimestamps::tryQueryTimestamps(int pairIndex, ID3D11DeviceContext *context, double *elapsedSec) { bool result = false; - if (!active[idx]) + if (!active[pairIndex]) return result; - ID3D11Query *tsDisjoint = disjointQuery[idx]; - const int tsIdx = pairCount * idx; - ID3D11Query *tsStart = query[tsIdx]; - ID3D11Query *tsEnd = query[tsIdx + 1]; + ID3D11Query *tsDisjoint = disjointQuery[pairIndex]; + ID3D11Query *tsStart = query[pairIndex * 2]; + ID3D11Query *tsEnd = query[pairIndex * 2 + 1]; quint64 timestamps[2]; D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj; bool ok = true; ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; ok &= context->GetData(tsEnd, ×tamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; - // this above is often not ready, not even in frame_where_recorded+2, - // not clear why. so make the whole thing async and do not touch the - // queries until they are finally all available in frame this+2 or - // this+4 or ... ok &= context->GetData(tsStart, ×tamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK; if (ok) { @@ -4765,16 +4886,14 @@ bool QD3D11Timestamps::tryQueryTimestamps(int idx, ID3D11DeviceContext *context, *elapsedSec = elapsedMs / 1000.0; result = true; } - active[idx] = false; - } // else leave active set, will retry in a subsequent beginFrame or similar + active[pairIndex] = false; + } // else leave active set, will retry in a subsequent beginFrame return result; } QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi) - : QRhiSwapChain(rhi), - rt(rhi, this), - cb(rhi) + : QRhiSwapChain(rhi), rt(rhi, this), rtRight(rhi, this), cb(rhi) { backBufferTex = nullptr; backBufferRtv = nullptr; @@ -4795,6 +4914,10 @@ void QD3D11SwapChain::releaseBuffers() backBufferRtv->Release(); backBufferRtv = nullptr; } + if (backBufferRtvRight) { + backBufferRtvRight->Release(); + backBufferRtvRight = nullptr; + } if (backBufferTex) { backBufferTex->Release(); backBufferTex = nullptr; @@ -4834,8 +4957,12 @@ void QD3D11SwapChain::destroy() } QRHI_RES_RHI(QRhiD3D11); - if (rhiD) + if (rhiD) { rhiD->unregisterResource(this); + // See Deferred Destruction Issues with Flip Presentation Swap Chains in + // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-flush + rhiD->context->Flush(); + } } QRhiCommandBuffer *QD3D11SwapChain::currentFrameCommandBuffer() @@ -4848,48 +4975,15 @@ QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget() return &rt; } -QSize QD3D11SwapChain::surfacePixelSize() +QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer) { - Q_ASSERT(m_window); - return m_window->size() * m_window->devicePixelRatio(); + return targetBuffer == StereoTargetBuffer::LeftBuffer? &rt: &rtRight; } -static bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result) -{ - bool ok = false; - QRect wr = w->geometry(); - wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio()); - const QPoint center = wr.center(); - IDXGIOutput *currentOutput = nullptr; - IDXGIOutput *output = nullptr; - for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) { - DXGI_OUTPUT_DESC desc; - output->GetDesc(&desc); - const RECT r = desc.DesktopCoordinates; - const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); - if (dr.contains(center)) { - currentOutput = output; - break; - } else { - output->Release(); - } - } - if (currentOutput) { - ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast<void **>(result))); - currentOutput->Release(); - } - return ok; -} - -static bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result) +QSize QD3D11SwapChain::surfacePixelSize() { - bool ok = false; - IDXGIOutput6 *out6 = nullptr; - if (output6ForWindow(w, adapter, &out6)) { - ok = SUCCEEDED(out6->GetDesc1(result)); - out6->Release(); - } - return ok; + Q_ASSERT(m_window); + return m_window->size() * m_window->devicePixelRatio(); } bool QD3D11SwapChain::isFormatSupported(Format f) @@ -4904,8 +4998,10 @@ bool QD3D11SwapChain::isFormatSupported(Format f) QRHI_RES_RHI(QRhiD3D11); DXGI_OUTPUT_DESC1 desc1; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) - return desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) { + if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) + return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10; + } return false; } @@ -4913,14 +5009,16 @@ bool QD3D11SwapChain::isFormatSupported(Format f) QRhiSwapChainHdrInfo QD3D11SwapChain::hdrInfo() { QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo(); - if (m_format != QRhiSwapChain::SDR && m_window) { + // Must use m_window, not window, given this may be called before createOrResize(). + if (m_window) { QRHI_RES_RHI(QRhiD3D11); DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { - info.isHardCodedDefaults = false; + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits; info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance; info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance; + info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits + info.sdrWhiteLevel = QRhiD3D::sdrWhiteLevelInNits(hdrOutputDesc); } } return info; @@ -4970,26 +5068,19 @@ bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI return true; } -static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; -static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - bool QRhiD3D11::ensureDirectCompositionDevice() { if (dcompDevice) return true; qCDebug(QRHI_LOG_INFO, "Creating Direct Composition device (needed for semi-transparent windows)"); - - HRESULT hr = DCompositionCreateDevice(nullptr, __uuidof(IDCompositionDevice), reinterpret_cast<void **>(&dcompDevice)); - if (FAILED(hr)) { - qWarning("Failed to Direct Composition device: %s", - qPrintable(QSystemError::windowsComString(hr))); - return false; - } - - return true; + dcompDevice = QRhiD3D::createDirectCompositionDevice(); + return dcompDevice ? true : false; } +static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; +static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + bool QD3D11SwapChain::createOrResize() { // Can be called multiple times due to window resizes - that is not the @@ -4997,6 +5088,7 @@ bool QD3D11SwapChain::createOrResize() // resize the buffers then. const bool needsRegistration = !window || window != m_window; + const bool stereo = m_window->format().stereo(); // except if the window actually changes if (window && window != m_window) @@ -5015,9 +5107,9 @@ bool QD3D11SwapChain::createOrResize() QRHI_RES_RHI(QRhiD3D11); if (m_flags.testFlag(SurfaceHasPreMulAlpha) || m_flags.testFlag(SurfaceHasNonPreMulAlpha)) { - if (rhiD->ensureDirectCompositionDevice()) { + if (!rhiD->useLegacySwapchainModel && rhiD->ensureDirectCompositionDevice()) { if (!dcompTarget) { - hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, true, &dcompTarget); + hr = rhiD->dcompDevice->CreateTargetForHwnd(hwnd, false, &dcompTarget); if (FAILED(hr)) { qWarning("Failed to create Direct Compsition target for the window: %s", qPrintable(QSystemError::windowsComString(hr))); @@ -5048,13 +5140,13 @@ bool QD3D11SwapChain::createOrResize() swapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; if (!swapChain) { - sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); + sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount); colorFormat = DEFAULT_FORMAT; srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT; DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { + if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { switch (m_format) { @@ -5094,8 +5186,9 @@ bool QD3D11SwapChain::createOrResize() desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount = BUFFER_COUNT; desc.Flags = swapChainFlags; - desc.Scaling = DXGI_SCALING_NONE; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.Scaling = rhiD->useLegacySwapchainModel ? DXGI_SCALING_STRETCH : DXGI_SCALING_NONE; + desc.SwapEffect = rhiD->useLegacySwapchainModel ? DXGI_SWAP_EFFECT_DISCARD : DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.Stereo = stereo; if (dcompVisual) { // With DirectComposition setting AlphaMode to STRAIGHT fails the @@ -5155,14 +5248,19 @@ bool QD3D11SwapChain::createOrResize() qWarning("Failed to set content for Direct Composition visual: %s", qPrintable(QSystemError::windowsComString(hr))); } + } else { + // disable Alt+Enter; not relevant when using DirectComposition + rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES); } } if (FAILED(hr)) { - qWarning("Failed to create D3D11 swapchain: %s", - qPrintable(QSystemError::windowsComString(hr))); + qWarning("Failed to create D3D11 swapchain: %s" + " (Width=%u Height=%u Format=%u SampleCount=%u BufferCount=%u Scaling=%u SwapEffect=%u Stereo=%u)", + qPrintable(QSystemError::windowsComString(hr)), + desc.Width, desc.Height, UINT(desc.Format), desc.SampleDesc.Count, + desc.BufferCount, UINT(desc.Scaling), UINT(desc.SwapEffect), UINT(desc.Stereo)); return false; } - rhiD->dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES); } else { releaseBuffers(); // flip model -> buffer count is the real buffer count, not 1 like with the legacy modes @@ -5209,6 +5307,19 @@ bool QD3D11SwapChain::createOrResize() return false; } + if (stereo) { + // Create a second render target view for the right eye + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.FirstArraySlice = 1; + rtvDesc.Texture2DArray.ArraySize = 1; + hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtvRight); + if (FAILED(hr)) { + qWarning("Failed to create rtv for swapchain backbuffer (right eye): %s", + qPrintable(QSystemError::windowsComString(hr))); + return false; + } + } + // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer. for (int i = 0; i < BUFFER_COUNT; ++i) { if (sampleDesc.Count > 1) { @@ -5247,8 +5358,20 @@ bool QD3D11SwapChain::createOrResize() rtD->d.colorAttCount = 1; rtD->d.dsAttCount = m_depthStencil ? 1 : 0; + if (stereo) { + rtD = QRHI_RES(QD3D11SwapChainRenderTarget, &rtRight); + rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc); + rtD->d.pixelSize = pixelSize; + rtD->d.dpr = float(window->devicePixelRatio()); + rtD->d.sampleCount = int(sampleDesc.Count); + rtD->d.colorAttCount = 1; + rtD->d.dsAttCount = m_depthStencil ? 1 : 0; + rtD->d.rtv[0] = backBufferRtvRight; + rtD->d.dsv = ds ? ds->dsv : nullptr; + } + if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps)) { - timestamps.prepare(BUFFER_COUNT, rhiD); + timestamps.prepare(rhiD); // timestamp queries are optional so we can go on even if they failed } |