diff options
author | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2016-05-08 20:18:38 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2016-05-08 20:03:39 +0000 |
commit | 92da3afdb69c52b955f3828ec10574031f094c7d (patch) | |
tree | 8183d033621f3660d701e95be73d99dcfb1a51fb /src/plugins/scenegraph/d3d12 | |
parent | 76c7f7b10f309b05d83114669f7c9aa116bcbd9a (diff) |
D3D12: Add a way to test device loss and fix discovered issues
Set QT_D3D_DEVICE_LOSS_TEST to a number N > 0 to trigger a device reset N
times in 5 second intervals.
Cinematic is able to survive now which is excellent news.
Also tested a real driver update which worked as well, Cinematic kept
running all the time (unlike Creator).
Change-Id: Ib59c7dece6b90321e6bd7e01c0687c51d5b61333
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/plugins/scenegraph/d3d12')
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp | 125 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h | 2 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h | 15 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp | 14 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/shaders/shaders.pri | 9 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/shaders/tdr.hlsl | 11 |
6 files changed, 169 insertions, 7 deletions
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 7e0518896e..4580e58edd 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -45,6 +45,14 @@ #include <qmath.h> #include <QtCore/private/qsimd_p.h> +// Comment out to disable DeviceLossTester functionality in order to reduce +// code size and improve startup perf a tiny bit. +#define DEVLOSS_TEST + +#ifdef DEVLOSS_TEST +#include "cs_tdr.hlslh" +#endif + QT_BEGIN_NAMESPACE // NOTE: Avoid categorized logging. It is slow. @@ -520,6 +528,11 @@ QImage QSGD3D12Engine::executeAndWaitReadbackRenderTarget(uint id) return d->executeAndWaitReadbackRenderTarget(id); } +void QSGD3D12Engine::simulateDeviceLoss() +{ + d->simulateDeviceLoss(); +} + QSGRendererInterface::GraphicsAPI QSGD3D12Engine::graphicsAPI() const { return Direct3D12; @@ -626,6 +639,7 @@ void QSGD3D12EnginePrivate::releaseResources() return; mipmapper.releaseResources(); + devLossTest.releaseResources(); frameCommandList = nullptr; copyCommandList = nullptr; @@ -805,6 +819,9 @@ void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int if (!mipmapper.initialize(this)) return; + if (!devLossTest.initialize(this)) + return; + currentRenderTarget = 0; initialized = true; @@ -1147,6 +1164,12 @@ void QSGD3D12EnginePrivate::updateBuffer(Buffer *buf) buf->cpuDataRef.dirty.clear(); } +void QSGD3D12EnginePrivate::ensureDevice() +{ + if (!initialized && window) + initialize(window, windowSize, windowDpr, windowSamples); +} + void QSGD3D12EnginePrivate::beginFrame() { if (inFrame && !activeLayers) @@ -1167,9 +1190,11 @@ void QSGD3D12EnginePrivate::beginFrame() inFrame = true; - // The device may have been lost. This is the point to attempt to start again from scratch. - if (!initialized && window) - initialize(window, windowSize, windowDpr, windowSamples); + // The device may have been lost. This is the point to attempt to start + // again from scratch. Except when it is not. Operations that can happen + // out of frame (e.g. textures, render targets) may trigger reinit earlier + // than beginFrame. + ensureDevice(); // Wait for a buffer to be available for Present, if the waitable event is in use. if (waitableSwapChainMaxLatency) @@ -2039,7 +2064,7 @@ uint QSGD3D12EnginePrivate::genBuffer() void QSGD3D12EnginePrivate::releaseBuffer(uint id) { - if (!id) + if (!id || !initialized) return; const int idx = id - 1; @@ -2199,6 +2224,8 @@ static inline QImage::Format imageFormatForTexture(DXGI_FORMAT format) void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Format format, QSGD3D12Engine::TextureCreateFlags createFlags) { + ensureDevice(); + Q_ASSERT(id); const int idx = id - 1; Q_ASSERT(idx < textures.count() && textures[idx].entryInUse()); @@ -2471,7 +2498,7 @@ void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &i void QSGD3D12EnginePrivate::releaseTexture(uint id) { - if (!id) + if (!id || !initialized) return; const int idx = id - 1; @@ -2708,6 +2735,8 @@ uint QSGD3D12EnginePrivate::genRenderTarget() void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const QVector4D &clearColor, uint samples) { + ensureDevice(); + Q_ASSERT(id); const int idx = id - 1; Q_ASSERT(idx < renderTargets.count() && renderTargets[idx].entryInUse()); @@ -2760,7 +2789,7 @@ void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const void QSGD3D12EnginePrivate::releaseRenderTarget(uint id) { - if (!id) + if (!id || !initialized) return; const int idx = id - 1; @@ -2944,6 +2973,90 @@ QImage QSGD3D12EnginePrivate::executeAndWaitReadbackRenderTarget(uint id) return img; } +void QSGD3D12EnginePrivate::simulateDeviceLoss() +{ + qWarning("QSGD3D12Engine: Triggering device loss via TDR"); + devLossTest.killDevice(); +} + +bool QSGD3D12EnginePrivate::DeviceLossTester::initialize(QSGD3D12EnginePrivate *enginePriv) +{ + engine = enginePriv; + +#ifdef DEVLOSS_TEST + D3D12_DESCRIPTOR_RANGE descRange[2]; + descRange[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + descRange[0].NumDescriptors = 1; + descRange[0].BaseShaderRegister = 0; + descRange[0].RegisterSpace = 0; + descRange[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + descRange[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + descRange[1].NumDescriptors = 1; + descRange[1].BaseShaderRegister = 0; + descRange[1].RegisterSpace = 0; + descRange[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER param; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.DescriptorTable.NumDescriptorRanges = 2; + param.DescriptorTable.pDescriptorRanges = descRange; + + D3D12_ROOT_SIGNATURE_DESC desc = {}; + desc.NumParameters = 1; + desc.pParameters = ¶m; + + ComPtr<ID3DBlob> signature; + ComPtr<ID3DBlob> error; + if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) { + QByteArray msg(static_cast<const char *>(error->GetBufferPointer()), error->GetBufferSize()); + qWarning("Failed to serialize compute root signature: %s", qPrintable(msg)); + return false; + } + if (FAILED(engine->device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), + IID_PPV_ARGS(&computeRootSignature)))) { + qWarning("Failed to create compute root signature"); + return false; + } + + D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {}; + psoDesc.pRootSignature = computeRootSignature.Get(); + psoDesc.CS.pShaderBytecode = g_timeout; + psoDesc.CS.BytecodeLength = sizeof(g_timeout); + + if (FAILED(engine->device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&computeState)))) { + qWarning("Failed to create compute pipeline state"); + return false; + } +#endif + + return true; +} + +void QSGD3D12EnginePrivate::DeviceLossTester::releaseResources() +{ + computeState = nullptr; + computeRootSignature = nullptr; +} + +void QSGD3D12EnginePrivate::DeviceLossTester::killDevice() +{ +#ifdef DEVLOSS_TEST + ID3D12CommandAllocator *ca = engine->frameCommandAllocator[engine->frameIndex % engine->frameInFlightCount].Get(); + ID3D12GraphicsCommandList *cl = engine->frameCommandList.Get(); + cl->Reset(ca, computeState.Get()); + + cl->SetComputeRootSignature(computeRootSignature.Get()); + cl->Dispatch(256, 1, 1); + + cl->Close(); + ID3D12CommandList *commandLists[] = { cl }; + engine->commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists); + + engine->waitGPU(); +#endif +} + void *QSGD3D12EnginePrivate::getResource(QSGRendererInterface::Resource resource) const { switch (resource) { diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h index f117400c25..d4c0d0b9e3 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h @@ -351,6 +351,8 @@ public: QImage executeAndWaitReadbackRenderTarget(uint id = 0); + void simulateDeviceLoss(); + // QSGRendererInterface GraphicsAPI graphicsAPI() const override; void *getResource(Resource resource) const override; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h index 34e5d2da7c..d8a0166dc5 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h @@ -182,12 +182,15 @@ public: QImage executeAndWaitReadbackRenderTarget(uint id); + void simulateDeviceLoss(); + void *getResource(QSGRendererInterface::Resource resource) const; // the device is intentionally hidden here. all resources have to go // through the engine and, unlike with GL, cannot just be created in random // places due to the need for proper tracking, managing and releasing. private: + void ensureDevice(); void setupDefaultRenderTargets(); void deviceLost() override; @@ -417,6 +420,18 @@ private: }; QVector<Buffer> buffers; + + struct DeviceLossTester { + bool initialize(QSGD3D12EnginePrivate *enginePriv); + void releaseResources(); + void killDevice(); + + QSGD3D12EnginePrivate *engine; + ComPtr<ID3D12PipelineState> computeState; + ComPtr<ID3D12RootSignature> computeRootSignature; + }; + + DeviceLossTester devLossTest; }; inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 9798f0ca66..44c2744978 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -656,6 +656,20 @@ void QSGD3D12RenderThread::syncAndRender() int(threadTimer.elapsed() - renderTime / 1000000)); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame); + + static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS"); + if (devLossTest > 0) { + static QElapsedTimer kt; + static bool timerRunning = false; + if (!timerRunning) { + kt.start(); + timerRunning = true; + } else if (kt.elapsed() > 5000) { + --devLossTest; + kt.restart(); + engine->simulateDeviceLoss(); + } + } } template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window) diff --git a/src/plugins/scenegraph/d3d12/shaders/shaders.pri b/src/plugins/scenegraph/d3d12/shaders/shaders.pri index 296e119cb2..41bac334de 100644 --- a/src/plugins/scenegraph/d3d12/shaders/shaders.pri +++ b/src/plugins/scenegraph/d3d12/shaders/shaders.pri @@ -98,6 +98,12 @@ shadereffectdefault_pshader.header = ps_shadereffectdefault.hlslh shadereffectdefault_pshader.entry = PS_DefaultShaderEffect shadereffectdefault_pshader.type = ps_5_0 +tdr_CS = $$PWD/tdr.hlsl +tdr_cshader.input = tdr_CS +tdr_cshader.header = cs_tdr.hlslh +tdr_cshader.entry = timeout +tdr_cshader.type = cs_5_0 + HLSL_SHADERS = \ vertexcolor_vshader vertexcolor_pshader \ stencilclip_vshader stencilclip_pshader \ @@ -107,6 +113,7 @@ HLSL_SHADERS = \ mipmapgen_cshader \ textmask_vshader textmask_pshader24 textmask_pshader32 textmask_pshader8 \ styledtext_vshader styledtext_pshader outlinedtext_vshader outlinedtext_pshader \ - shadereffectdefault_vshader shadereffectdefault_pshader + shadereffectdefault_vshader shadereffectdefault_pshader \ + tdr_cshader load(hlsl_bytecode_header) diff --git a/src/plugins/scenegraph/d3d12/shaders/tdr.hlsl b/src/plugins/scenegraph/d3d12/shaders/tdr.hlsl new file mode 100644 index 0000000000..f32d4fbace --- /dev/null +++ b/src/plugins/scenegraph/d3d12/shaders/tdr.hlsl @@ -0,0 +1,11 @@ +// http://gamedev.stackexchange.com/questions/108141/how-can-i-test-dxgi-error-device-removed-error-handling + +RWBuffer<uint> uav; +cbuffer ConstantBuffer { uint zero; } + +[numthreads(256, 1, 1)] +void timeout(uint3 id: SV_DispatchThreadID) +{ + while (zero == 0) + uav[id.x] = zero; +} |