diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 30ea2a5b7e..e875eac414 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -346,11 +346,6 @@ bool QRhiD3D11::create(QRhi::Flags flags) if (FAILED(context->QueryInterface(__uuidof(ID3DUserDefinedAnnotation), reinterpret_cast(&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; @@ -376,7 +371,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(); @@ -1258,7 +1262,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 +1284,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 +1313,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 +1338,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,11 +1352,15 @@ 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)) { @@ -1375,12 +1401,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 +1439,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; @@ -1550,7 +1614,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 +1989,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); @@ -2643,7 +2694,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 +2707,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; @@ -4694,14 +4749,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]); @@ -4713,7 +4767,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)) { @@ -4724,20 +4778,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; @@ -4746,26 +4799,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) { @@ -4774,8 +4822,8 @@ 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; } @@ -5252,7 +5300,7 @@ bool QD3D11SwapChain::createOrResize() } 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 } diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h index b5684e05e2..82d33a3914 100644 --- a/src/gui/rhi/qrhid3d11_p.h +++ b/src/gui/rhi/qrhid3d11_p.h @@ -356,6 +356,8 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer struct Command { enum Cmd { + BeginFrame, + EndFrame, ResetShaderResources, SetRenderTarget, Clear, @@ -385,6 +387,15 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer // QRhi*/QD3D11* references should be kept at minimum (so no // QRhiTexture/Buffer/etc. pointers). union Args { + struct { + ID3D11Query *tsQuery; + ID3D11Query *tsDisjointQuery; + QD3D11RenderTargetData *swapchainData; + } beginFrame; + struct { + ID3D11Query *tsQuery; + ID3D11Query *tsDisjointQuery; + } endFrame; struct { QRhiRenderTarget *rt; } setRenderTarget; @@ -556,17 +567,15 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer } }; -static const int QD3D11_SWAPCHAIN_BUFFER_COUNT = 2; - -struct QD3D11Timestamps +struct QD3D11SwapChainTimestamps { - static const int MAX_TIMESTAMP_PAIRS = QD3D11_SWAPCHAIN_BUFFER_COUNT; - bool active[MAX_TIMESTAMP_PAIRS] = {}; - ID3D11Query *disjointQuery[MAX_TIMESTAMP_PAIRS] = {}; - ID3D11Query *query[MAX_TIMESTAMP_PAIRS * 2] = {}; - int pairCount = 0; + static const int TIMESTAMP_PAIRS = 2; - bool prepare(int pairCount, QRhiD3D11 *rhiD); + bool active[TIMESTAMP_PAIRS] = {}; + ID3D11Query *disjointQuery[TIMESTAMP_PAIRS] = {}; + ID3D11Query *query[TIMESTAMP_PAIRS * 2] = {}; + + bool prepare(QRhiD3D11 *rhiD); void destroy(); bool tryQueryTimestamps(int idx, ID3D11DeviceContext *context, double *elapsedSec); }; @@ -604,7 +613,7 @@ struct QD3D11SwapChain : public QRhiSwapChain ID3D11Texture2D *backBufferTex; ID3D11RenderTargetView *backBufferRtv; ID3D11RenderTargetView *backBufferRtvRight = nullptr; - static const int BUFFER_COUNT = QD3D11_SWAPCHAIN_BUFFER_COUNT; + static const int BUFFER_COUNT = 2; ID3D11Texture2D *msaaTex[BUFFER_COUNT]; ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT]; DXGI_SAMPLE_DESC sampleDesc; @@ -614,7 +623,8 @@ struct QD3D11SwapChain : public QRhiSwapChain UINT swapInterval = 1; IDCompositionTarget *dcompTarget = nullptr; IDCompositionVisual *dcompVisual = nullptr; - QD3D11Timestamps timestamps; + QD3D11SwapChainTimestamps timestamps; + int currentTimestampPairIndex = 0; }; class QRhiD3D11 : public QRhiImplementation @@ -739,7 +749,7 @@ public: const uint *dynOfsPairs, int dynOfsPairCount, bool offsetOnlyChange); void resetShaderResources(); - void executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain = nullptr); + void executeCommandBuffer(QD3D11CommandBuffer *cbD); DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount) const; void finishActiveReadbacks(); void reportLiveObjects(ID3D11Device *device); @@ -781,8 +791,8 @@ public: OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { } bool active = false; QD3D11CommandBuffer cbWrapper; - QD3D11Timestamps timestamps; - int timestampIdx = 0; + ID3D11Query *tsQueries[2] = {}; + ID3D11Query *tsDisjointQuery = nullptr; } ofr; struct TextureReadback {