rhi: d3d11: Add the device lost testing machinery
The device can be lost when physically removing the graphics adapter, disabling the driver (Device Manager), upgrading/uninstalling the graphics driver, and when it is reset due to an error. Some of these can (and should) be tested manually, but the last one has a convenient, programmatic way of triggering: by triggering the timeout detection and recovery (TDR) of WDDM. A compute shader with an infinite loop should trigger this after 2 seconds by default. All tests in tests/manual/rhi can now be started with a --curse <count> argument where <count> specifies the number of frames to render before breaking the device. Qt Quick will get an environment variable with similar semantics in a separate patch. Change-Id: I4b6f8d977a15b5b89d686b3973965df6435810ae Reviewed-by: Christian Strømme <christian.stromme@qt.io>bb10
parent
08ad96404b
commit
f567129bb5
|
|
@ -0,0 +1,209 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: http://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Gui module
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL3$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see http://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at http://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or later as published by the Free
|
||||
** Software Foundation and appearing in the file LICENSE.GPL included in
|
||||
** the packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 2.0 requirements will be
|
||||
** met: http://www.gnu.org/licenses/gpl-2.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qglobal.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <qt_windows.h>
|
||||
|
||||
#if 0
|
||||
//
|
||||
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
|
||||
//
|
||||
//
|
||||
// Buffer Definitions:
|
||||
//
|
||||
// cbuffer ConstantBuffer
|
||||
// {
|
||||
//
|
||||
// uint zero; // Offset: 0 Size: 4
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Resource Bindings:
|
||||
//
|
||||
// Name Type Format Dim HLSL Bind Count
|
||||
// ------------------------------ ---------- ------- ----------- -------------- ------
|
||||
// uav UAV uint buf u0 1
|
||||
// ConstantBuffer cbuffer NA NA cb0 1
|
||||
//
|
||||
//
|
||||
//
|
||||
// Input signature:
|
||||
//
|
||||
// Name Index Mask Register SysValue Format Used
|
||||
// -------------------- ----- ------ -------- -------- ------- ------
|
||||
// no Input
|
||||
//
|
||||
// Output signature:
|
||||
//
|
||||
// Name Index Mask Register SysValue Format Used
|
||||
// -------------------- ----- ------ -------- -------- ------- ------
|
||||
// no Output
|
||||
cs_5_0
|
||||
dcl_globalFlags refactoringAllowed
|
||||
dcl_constantbuffer CB0[1], immediateIndexed
|
||||
dcl_uav_typed_buffer (uint,uint,uint,uint) u0
|
||||
dcl_input vThreadID.x
|
||||
dcl_thread_group 256, 1, 1
|
||||
loop
|
||||
breakc_nz cb0[0].x
|
||||
store_uav_typed u0.xyzw, vThreadID.xxxx, cb0[0].xxxx
|
||||
endloop
|
||||
ret
|
||||
// Approximately 5 instruction slots used
|
||||
#endif
|
||||
|
||||
const BYTE g_killDeviceByTimingOut[] =
|
||||
{
|
||||
68, 88, 66, 67, 217, 62,
|
||||
220, 38, 136, 51, 86, 245,
|
||||
161, 96, 18, 35, 141, 17,
|
||||
26, 13, 1, 0, 0, 0,
|
||||
164, 2, 0, 0, 5, 0,
|
||||
0, 0, 52, 0, 0, 0,
|
||||
100, 1, 0, 0, 116, 1,
|
||||
0, 0, 132, 1, 0, 0,
|
||||
8, 2, 0, 0, 82, 68,
|
||||
69, 70, 40, 1, 0, 0,
|
||||
1, 0, 0, 0, 144, 0,
|
||||
0, 0, 2, 0, 0, 0,
|
||||
60, 0, 0, 0, 0, 5,
|
||||
83, 67, 0, 1, 0, 0,
|
||||
0, 1, 0, 0, 82, 68,
|
||||
49, 49, 60, 0, 0, 0,
|
||||
24, 0, 0, 0, 32, 0,
|
||||
0, 0, 40, 0, 0, 0,
|
||||
36, 0, 0, 0, 12, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
124, 0, 0, 0, 4, 0,
|
||||
0, 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 255, 255,
|
||||
255, 255, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0,
|
||||
0, 0, 128, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 117, 97,
|
||||
118, 0, 67, 111, 110, 115,
|
||||
116, 97, 110, 116, 66, 117,
|
||||
102, 102, 101, 114, 0, 171,
|
||||
128, 0, 0, 0, 1, 0,
|
||||
0, 0, 168, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
208, 0, 0, 0, 0, 0,
|
||||
0, 0, 4, 0, 0, 0,
|
||||
2, 0, 0, 0, 220, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
255, 255, 255, 255, 0, 0,
|
||||
0, 0, 255, 255, 255, 255,
|
||||
0, 0, 0, 0, 122, 101,
|
||||
114, 111, 0, 100, 119, 111,
|
||||
114, 100, 0, 171, 0, 0,
|
||||
19, 0, 1, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
213, 0, 0, 0, 77, 105,
|
||||
99, 114, 111, 115, 111, 102,
|
||||
116, 32, 40, 82, 41, 32,
|
||||
72, 76, 83, 76, 32, 83,
|
||||
104, 97, 100, 101, 114, 32,
|
||||
67, 111, 109, 112, 105, 108,
|
||||
101, 114, 32, 49, 48, 46,
|
||||
49, 0, 73, 83, 71, 78,
|
||||
8, 0, 0, 0, 0, 0,
|
||||
0, 0, 8, 0, 0, 0,
|
||||
79, 83, 71, 78, 8, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
8, 0, 0, 0, 83, 72,
|
||||
69, 88, 124, 0, 0, 0,
|
||||
80, 0, 5, 0, 31, 0,
|
||||
0, 0, 106, 8, 0, 1,
|
||||
89, 0, 0, 4, 70, 142,
|
||||
32, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 156, 8,
|
||||
0, 4, 0, 224, 17, 0,
|
||||
0, 0, 0, 0, 68, 68,
|
||||
0, 0, 95, 0, 0, 2,
|
||||
18, 0, 2, 0, 155, 0,
|
||||
0, 4, 0, 1, 0, 0,
|
||||
1, 0, 0, 0, 1, 0,
|
||||
0, 0, 48, 0, 0, 1,
|
||||
3, 0, 4, 4, 10, 128,
|
||||
32, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 164, 0,
|
||||
0, 7, 242, 224, 17, 0,
|
||||
0, 0, 0, 0, 6, 0,
|
||||
2, 0, 6, 128, 32, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 22, 0, 0, 1,
|
||||
62, 0, 0, 1, 83, 84,
|
||||
65, 84, 148, 0, 0, 0,
|
||||
5, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 2, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0
|
||||
};
|
||||
|
||||
#endif // Q_OS_WIN
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "qrhid3d11_p_p.h"
|
||||
#include "qshader_p.h"
|
||||
#include "cs_tdr.h"
|
||||
#include <QWindow>
|
||||
#include <QOperatingSystemVersion>
|
||||
#include <qmath.h>
|
||||
|
|
@ -119,9 +120,14 @@ QT_BEGIN_NAMESPACE
|
|||
*/
|
||||
|
||||
QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice)
|
||||
: ofr(this)
|
||||
: ofr(this),
|
||||
deviceCurse(this)
|
||||
{
|
||||
debugLayer = params->enableDebugLayer;
|
||||
|
||||
deviceCurse.framesToActivate = params->framesUntilKillingDeviceViaTdr;
|
||||
deviceCurse.permanent = params->repeatDeviceKill;
|
||||
|
||||
importedDevice = importDevice != nullptr;
|
||||
if (importedDevice) {
|
||||
dev = reinterpret_cast<ID3D11Device *>(importDevice->dev);
|
||||
|
|
@ -264,6 +270,9 @@ bool QRhiD3D11::create(QRhi::Flags flags)
|
|||
nativeHandlesStruct.dev = dev;
|
||||
nativeHandlesStruct.context = context;
|
||||
|
||||
if (deviceCurse.framesToActivate > 0)
|
||||
deviceCurse.initResources();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -281,6 +290,8 @@ void QRhiD3D11::destroy()
|
|||
|
||||
clearShaderCache();
|
||||
|
||||
deviceCurse.releaseResources();
|
||||
|
||||
if (annotations) {
|
||||
annotations->Release();
|
||||
annotations = nullptr;
|
||||
|
|
@ -1003,6 +1014,20 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
|
|||
|
||||
swapChainD->frameCount += 1;
|
||||
contextState.currentSwapChain = nullptr;
|
||||
|
||||
if (deviceCurse.framesToActivate > 0) {
|
||||
deviceCurse.framesLeft -= 1;
|
||||
if (deviceCurse.framesLeft == 0) {
|
||||
deviceCurse.framesLeft = deviceCurse.framesToActivate;
|
||||
if (!deviceCurse.permanent)
|
||||
deviceCurse.framesToActivate = -1;
|
||||
|
||||
deviceCurse.activate();
|
||||
} else if (deviceCurse.framesLeft % 100 == 0) {
|
||||
qDebug("Impending doom: %d frames left", deviceCurse.framesLeft);
|
||||
}
|
||||
}
|
||||
|
||||
return QRhi::FrameOpSuccess;
|
||||
}
|
||||
|
||||
|
|
@ -3914,4 +3939,34 @@ bool QD3D11SwapChain::buildOrResize()
|
|||
return true;
|
||||
}
|
||||
|
||||
void QRhiD3D11::DeviceCurse::initResources()
|
||||
{
|
||||
framesLeft = framesToActivate;
|
||||
|
||||
HRESULT hr = q->dev->CreateComputeShader(g_killDeviceByTimingOut, sizeof(g_killDeviceByTimingOut), nullptr, &cs);
|
||||
if (FAILED(hr)) {
|
||||
qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void QRhiD3D11::DeviceCurse::releaseResources()
|
||||
{
|
||||
if (cs) {
|
||||
cs->Release();
|
||||
cs = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void QRhiD3D11::DeviceCurse::activate()
|
||||
{
|
||||
if (!cs)
|
||||
return;
|
||||
|
||||
qDebug("Activating Curse. Goodbye Cruel World.");
|
||||
|
||||
q->context->CSSetShader(cs, nullptr, 0);
|
||||
q->context->Dispatch(256, 1, 1);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ QT_BEGIN_NAMESPACE
|
|||
struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams
|
||||
{
|
||||
bool enableDebugLayer = false;
|
||||
|
||||
int framesUntilKillingDeviceViaTdr = -1;
|
||||
bool repeatDeviceKill = false;
|
||||
};
|
||||
|
||||
struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles
|
||||
|
|
|
|||
|
|
@ -694,6 +694,19 @@ public:
|
|||
QByteArray bytecode;
|
||||
};
|
||||
QHash<QRhiShaderStage, Shader> m_shaderCache;
|
||||
|
||||
struct DeviceCurse {
|
||||
DeviceCurse(QRhiD3D11 *impl) : q(impl) { }
|
||||
QRhiD3D11 *q;
|
||||
int framesToActivate = -1;
|
||||
bool permanent = false;
|
||||
int framesLeft = 0;
|
||||
ID3D11ComputeShader *cs = nullptr;
|
||||
|
||||
void initResources();
|
||||
void releaseResources();
|
||||
void activate();
|
||||
} deviceCurse;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(QRhiD3D11::ActiveReadback, Q_MOVABLE_TYPE);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
RWBuffer<uint> uav;
|
||||
cbuffer ConstantBuffer { uint zero; }
|
||||
|
||||
[numthreads(256, 1, 1)]
|
||||
void killDeviceByTimingOut(uint3 id: SV_DispatchThreadID)
|
||||
{
|
||||
while (zero == 0)
|
||||
uav[id.x] = zero;
|
||||
}
|
||||
|
|
@ -126,6 +126,7 @@ int sampleCount = 1;
|
|||
QRhiSwapChain::Flags scFlags = 0;
|
||||
QRhi::BeginFrameFlags beginFrameFlags = 0;
|
||||
QRhi::EndFrameFlags endFrameFlags = 0;
|
||||
int framesUntilTdr = -1;
|
||||
|
||||
class Window : public QWindow
|
||||
{
|
||||
|
|
@ -278,6 +279,10 @@ void Window::init()
|
|||
if (graphicsApi == D3D11) {
|
||||
QRhiD3D11InitParams params;
|
||||
params.enableDebugLayer = true;
|
||||
if (framesUntilTdr > 0) {
|
||||
params.framesUntilKillingDeviceViaTdr = framesUntilTdr;
|
||||
params.repeatDeviceKill = true;
|
||||
}
|
||||
m_r = QRhi::create(QRhi::D3D11, ¶ms, rhiFlags);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -461,8 +466,13 @@ int main(int argc, char **argv)
|
|||
// Testing cleanup both with QWindow::close() (hitting X or Alt-F4) and
|
||||
// QCoreApplication::quit() (e.g. what a menu widget would do) is important.
|
||||
// Use this parameter for the latter.
|
||||
QCommandLineOption sdOption({ "s", "self-destruct" }, QLatin1String("Self destruct after 5 seconds"));
|
||||
QCommandLineOption sdOption({ "s", "self-destruct" }, QLatin1String("Self-destruct after 5 seconds."));
|
||||
cmdLineParser.addOption(sdOption);
|
||||
// Attempt testing device lost situations on D3D at least.
|
||||
QCommandLineOption tdrOption(QLatin1String("curse"), QLatin1String("Curse the graphics device. "
|
||||
"(generate a device reset every <count> frames when on D3D11)"),
|
||||
QLatin1String("count"));
|
||||
cmdLineParser.addOption(tdrOption);
|
||||
|
||||
cmdLineParser.process(app);
|
||||
if (cmdLineParser.isSet(nullOption))
|
||||
|
|
@ -521,6 +531,9 @@ int main(int argc, char **argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (cmdLineParser.isSet(tdrOption))
|
||||
framesUntilTdr = cmdLineParser.value(tdrOption).toInt();
|
||||
|
||||
// Create and show the window.
|
||||
Window w;
|
||||
#if QT_CONFIG(vulkan)
|
||||
|
|
|
|||
Loading…
Reference in New Issue