aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/scenegraph/d3d12
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-05-08 20:18:38 +0200
committerLaszlo Agocs <laszlo.agocs@theqtcompany.com>2016-05-08 20:03:39 +0000
commit92da3afdb69c52b955f3828ec10574031f094c7d (patch)
tree8183d033621f3660d701e95be73d99dcfb1a51fb /src/plugins/scenegraph/d3d12
parent76c7f7b10f309b05d83114669f7c9aa116bcbd9a (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.cpp125
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h2
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h15
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp14
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/shaders.pri9
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/tdr.hlsl11
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 = &param;
+
+ 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;
+}