summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2019-09-10 14:09:41 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-09-12 10:40:16 +0000
commit0616e14de0bf867bc2730b2b07005fbcbb234bb4 (patch)
treee7b0b86d78289279216c36aebee5a7b3bf1a49c3 /src
parentd39d1a9e67affb3fd44fdd9ec71a50282a6fed1f (diff)
rhi: Better handling of device loss
Starting with D3D11. The other backends will follow later. Change-Id: I4f165c9f1743df0fb00bdce1e898917575bf5f6e Reviewed-by: Christian Strømme <christian.stromme@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/gui/rhi/qrhi.cpp43
-rw-r--r--src/gui/rhi/qrhi_p.h2
-rw-r--r--src/gui/rhi/qrhi_p_p.h1
-rw-r--r--src/gui/rhi/qrhid3d11.cpp21
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h2
-rw-r--r--src/gui/rhi/qrhigles2.cpp5
-rw-r--r--src/gui/rhi/qrhigles2_p_p.h1
-rw-r--r--src/gui/rhi/qrhimetal.mm5
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h1
-rw-r--r--src/gui/rhi/qrhinull.cpp5
-rw-r--r--src/gui/rhi/qrhinull_p_p.h1
-rw-r--r--src/gui/rhi/qrhivulkan.cpp5
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h1
13 files changed, 90 insertions, 3 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 5ad433cf23..fbdc90bd1b 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -455,7 +455,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\value FrameOpDeviceLost The graphics device was lost. This can be
recoverable by attempting to repeat the operation (such as, beginFrame())
- and releasing and reinitializing all objects backed by native graphics
+ after releasing and reinitializing all objects backed by native graphics
resources.
*/
@@ -5046,6 +5046,47 @@ void QRhi::releaseCachedResources()
}
/*!
+ \return true if the graphics device was lost.
+
+ The loss of the device is typically detected in beginFrame(), endFrame() or
+ QRhiSwapChain::buildOrResize(), depending on the backend and the underlying
+ native APIs. The most common is endFrame() because that is where presenting
+ happens. With some backends QRhiSwapChain::buildOrResize() can also fail
+ due to a device loss. Therefore this function is provided as a generic way
+ to check if a device loss was detected by a previous operation.
+
+ When the device is lost, no further operations should be done via the QRhi.
+ Rather, all QRhi resources should be released, followed by destroying the
+ QRhi. A new QRhi can then be attempted to be created. If successful, all
+ graphics resources must be reinitialized. If not, try again later,
+ repeatedly.
+
+ While simple applications may decide to not care about device loss,
+ on the commonly used desktop platforms a device loss can happen
+ due to a variety of reasons, including physically disconnecting the
+ graphics adapter, disabling the device or driver, uninstalling or upgrading
+ the graphics driver, or due to errors that lead to a graphics device reset.
+ Some of these can happen under perfectly normal circumstances as well, for
+ example the upgrade of the graphics driver to a newer version is a common
+ task that can happen at any time while a Qt application is running. Users
+ may very well expect applications to be able to survive this, even when the
+ application is actively using an API like OpenGL or Direct3D.
+
+ Qt's own frameworks built on top of QRhi, such as, Qt Quick, can be
+ expected to handle and take appropriate measures when a device loss occurs.
+ If the data for graphics resources, such as textures and buffers, are still
+ available on the CPU side, such an event may not be noticeable on the
+ application level at all since graphics resources can seamlessly be
+ reinitialized then. However, applications and libraries working directly
+ with QRhi are expected to be prepared to check and handle device loss
+ situations themselves.
+ */
+bool QRhi::isDeviceLost() const
+{
+ return d->isDeviceLost();
+}
+
+/*!
\return a new graphics pipeline resource.
\sa QRhiResource::release()
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index 928d1f8fa7..51e70af18e 100644
--- a/src/gui/rhi/qrhi_p.h
+++ b/src/gui/rhi/qrhi_p.h
@@ -1422,6 +1422,8 @@ public:
void releaseCachedResources();
+ bool isDeviceLost() const;
+
protected:
QRhi();
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
index b69757ae6d..7c0b000c33 100644
--- a/src/gui/rhi/qrhi_p_p.h
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -158,6 +158,7 @@ public:
virtual void sendVMemStatsToProfiler() = 0;
virtual void makeThreadLocalNativeContextCurrent() = 0;
virtual void releaseCachedResources() = 0;
+ virtual bool isDeviceLost() const = 0;
bool isCompressedFormat(QRhiTexture::Format format) const;
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 119234035a..2350691dc0 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -267,6 +267,8 @@ bool QRhiD3D11::create(QRhi::Flags flags)
if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast<void **>(&annotations))))
annotations = nullptr;
+ deviceLost = false;
+
nativeHandlesStruct.dev = dev;
nativeHandlesStruct.context = context;
@@ -487,6 +489,11 @@ void QRhiD3D11::releaseCachedResources()
clearShaderCache();
}
+bool QRhiD3D11::isDeviceLost() const
+{
+ return deviceLost;
+}
+
QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
@@ -1003,8 +1010,14 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
if (!flags.testFlag(QRhi::SkipPresent)) {
const UINT presentFlags = 0;
HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
- if (FAILED(hr))
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ qWarning("Device loss detected in Present()");
+ deviceLost = true;
+ return QRhi::FrameOpDeviceLost;
+ } else if (FAILED(hr)) {
qWarning("Failed to present: %s", qPrintable(comErrorMessage(hr)));
+ return QRhi::FrameOpError;
+ }
// move on to the next buffer
swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT;
@@ -3850,7 +3863,11 @@ bool QD3D11SwapChain::buildOrResize()
const UINT count = useFlipDiscard ? BUFFER_COUNT : 1;
HRESULT hr = swapChain->ResizeBuffers(count, UINT(pixelSize.width()), UINT(pixelSize.height()),
colorFormat, swapChainFlags);
- if (FAILED(hr)) {
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ qWarning("Device loss detected in ResizeBuffers()");
+ rhiD->deviceLost = true;
+ return false;
+ } else if (FAILED(hr)) {
qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
return false;
}
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
index cc4e095d10..da7fe84b1a 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -633,6 +633,7 @@ public:
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
+ bool isDeviceLost() const override;
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
@@ -658,6 +659,7 @@ public:
IDXGIFactory1 *dxgiFactory = nullptr;
bool hasDxgi2 = false;
bool supportsFlipDiscardSwapchain = false;
+ bool deviceLost = false;
QRhiD3D11NativeHandles nativeHandlesStruct;
struct {
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 4d91ac45bd..d408490b34 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -772,6 +772,11 @@ void QRhiGles2::releaseCachedResources()
m_shaderCache.clear();
}
+bool QRhiGles2::isDeviceLost() const
+{
+ return false;
+}
+
QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
index 877eb88d27..3664e7162b 100644
--- a/src/gui/rhi/qrhigles2_p_p.h
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -666,6 +666,7 @@ public:
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
+ bool isDeviceLost() const override;
bool ensureContext(QSurface *surface = nullptr) const;
void executeDeferredReleases();
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 3bf95ad676..770786db98 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -596,6 +596,11 @@ void QRhiMetal::releaseCachedResources()
d->shaderCache.clear();
}
+bool QRhiMetal::isDeviceLost() const
+{
+ return false;
+}
+
QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index 01b0bf4f56..633e9b8de4 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -418,6 +418,7 @@ public:
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
+ bool isDeviceLost() const override;
void executeDeferredReleases(bool forced = false);
void finishActiveReadbacks(bool forced = false);
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
index 29a3968bfc..b58d9f5c56 100644
--- a/src/gui/rhi/qrhinull.cpp
+++ b/src/gui/rhi/qrhinull.cpp
@@ -179,6 +179,11 @@ void QRhiNull::releaseCachedResources()
// nothing to do here
}
+bool QRhiNull::isDeviceLost() const
+{
+ return false;
+}
+
QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
index b43f830d5e..d6dbdbdc75 100644
--- a/src/gui/rhi/qrhinull_p_p.h
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -284,6 +284,7 @@ public:
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
+ bool isDeviceLost() const override;
QRhiNullNativeHandles nativeHandlesStruct;
QRhiSwapChain *currentSwapChain = nullptr;
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index c4f298dafb..5dd4b12329 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -3750,6 +3750,11 @@ void QRhiVulkan::releaseCachedResources()
// nothing to do here
}
+bool QRhiVulkan::isDeviceLost() const
+{
+ return false;
+}
+
QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
int sampleCount, QRhiRenderBuffer::Flags flags)
{
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
index 23cc80b814..0f16c54779 100644
--- a/src/gui/rhi/qrhivulkan_p_p.h
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -713,6 +713,7 @@ public:
void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
+ bool isDeviceLost() const override;
VkResult createDescriptorPool(VkDescriptorPool *pool);
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex);