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>
This commit is contained in:
parent
d39d1a9e67
commit
0616e14de0
@ -455,7 +455,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
|||||||
|
|
||||||
\value FrameOpDeviceLost The graphics device was lost. This can be
|
\value FrameOpDeviceLost The graphics device was lost. This can be
|
||||||
recoverable by attempting to repeat the operation (such as, beginFrame())
|
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.
|
resources.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -5045,6 +5045,47 @@ void QRhi::releaseCachedResources()
|
|||||||
d->releaseCachedResources();
|
d->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.
|
\return a new graphics pipeline resource.
|
||||||
|
|
||||||
|
@ -1422,6 +1422,8 @@ public:
|
|||||||
|
|
||||||
void releaseCachedResources();
|
void releaseCachedResources();
|
||||||
|
|
||||||
|
bool isDeviceLost() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QRhi();
|
QRhi();
|
||||||
|
|
||||||
|
@ -158,6 +158,7 @@ public:
|
|||||||
virtual void sendVMemStatsToProfiler() = 0;
|
virtual void sendVMemStatsToProfiler() = 0;
|
||||||
virtual void makeThreadLocalNativeContextCurrent() = 0;
|
virtual void makeThreadLocalNativeContextCurrent() = 0;
|
||||||
virtual void releaseCachedResources() = 0;
|
virtual void releaseCachedResources() = 0;
|
||||||
|
virtual bool isDeviceLost() const = 0;
|
||||||
|
|
||||||
bool isCompressedFormat(QRhiTexture::Format format) const;
|
bool isCompressedFormat(QRhiTexture::Format format) const;
|
||||||
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
|
void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
|
||||||
|
@ -267,6 +267,8 @@ bool QRhiD3D11::create(QRhi::Flags flags)
|
|||||||
if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast<void **>(&annotations))))
|
if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast<void **>(&annotations))))
|
||||||
annotations = nullptr;
|
annotations = nullptr;
|
||||||
|
|
||||||
|
deviceLost = false;
|
||||||
|
|
||||||
nativeHandlesStruct.dev = dev;
|
nativeHandlesStruct.dev = dev;
|
||||||
nativeHandlesStruct.context = context;
|
nativeHandlesStruct.context = context;
|
||||||
|
|
||||||
@ -487,6 +489,11 @@ void QRhiD3D11::releaseCachedResources()
|
|||||||
clearShaderCache();
|
clearShaderCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QRhiD3D11::isDeviceLost() const
|
||||||
|
{
|
||||||
|
return deviceLost;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
||||||
int sampleCount, QRhiRenderBuffer::Flags flags)
|
int sampleCount, QRhiRenderBuffer::Flags flags)
|
||||||
{
|
{
|
||||||
@ -1003,8 +1010,14 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
|
|||||||
if (!flags.testFlag(QRhi::SkipPresent)) {
|
if (!flags.testFlag(QRhi::SkipPresent)) {
|
||||||
const UINT presentFlags = 0;
|
const UINT presentFlags = 0;
|
||||||
HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
|
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)));
|
qWarning("Failed to present: %s", qPrintable(comErrorMessage(hr)));
|
||||||
|
return QRhi::FrameOpError;
|
||||||
|
}
|
||||||
|
|
||||||
// move on to the next buffer
|
// move on to the next buffer
|
||||||
swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT;
|
swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT;
|
||||||
@ -3850,7 +3863,11 @@ bool QD3D11SwapChain::buildOrResize()
|
|||||||
const UINT count = useFlipDiscard ? BUFFER_COUNT : 1;
|
const UINT count = useFlipDiscard ? BUFFER_COUNT : 1;
|
||||||
HRESULT hr = swapChain->ResizeBuffers(count, UINT(pixelSize.width()), UINT(pixelSize.height()),
|
HRESULT hr = swapChain->ResizeBuffers(count, UINT(pixelSize.width()), UINT(pixelSize.height()),
|
||||||
colorFormat, swapChainFlags);
|
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)));
|
qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -633,6 +633,7 @@ public:
|
|||||||
void sendVMemStatsToProfiler() override;
|
void sendVMemStatsToProfiler() override;
|
||||||
void makeThreadLocalNativeContextCurrent() override;
|
void makeThreadLocalNativeContextCurrent() override;
|
||||||
void releaseCachedResources() override;
|
void releaseCachedResources() override;
|
||||||
|
bool isDeviceLost() const override;
|
||||||
|
|
||||||
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
|
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
|
||||||
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
|
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
|
||||||
@ -658,6 +659,7 @@ public:
|
|||||||
IDXGIFactory1 *dxgiFactory = nullptr;
|
IDXGIFactory1 *dxgiFactory = nullptr;
|
||||||
bool hasDxgi2 = false;
|
bool hasDxgi2 = false;
|
||||||
bool supportsFlipDiscardSwapchain = false;
|
bool supportsFlipDiscardSwapchain = false;
|
||||||
|
bool deviceLost = false;
|
||||||
QRhiD3D11NativeHandles nativeHandlesStruct;
|
QRhiD3D11NativeHandles nativeHandlesStruct;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -772,6 +772,11 @@ void QRhiGles2::releaseCachedResources()
|
|||||||
m_shaderCache.clear();
|
m_shaderCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QRhiGles2::isDeviceLost() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
||||||
int sampleCount, QRhiRenderBuffer::Flags flags)
|
int sampleCount, QRhiRenderBuffer::Flags flags)
|
||||||
{
|
{
|
||||||
|
@ -666,6 +666,7 @@ public:
|
|||||||
void sendVMemStatsToProfiler() override;
|
void sendVMemStatsToProfiler() override;
|
||||||
void makeThreadLocalNativeContextCurrent() override;
|
void makeThreadLocalNativeContextCurrent() override;
|
||||||
void releaseCachedResources() override;
|
void releaseCachedResources() override;
|
||||||
|
bool isDeviceLost() const override;
|
||||||
|
|
||||||
bool ensureContext(QSurface *surface = nullptr) const;
|
bool ensureContext(QSurface *surface = nullptr) const;
|
||||||
void executeDeferredReleases();
|
void executeDeferredReleases();
|
||||||
|
@ -596,6 +596,11 @@ void QRhiMetal::releaseCachedResources()
|
|||||||
d->shaderCache.clear();
|
d->shaderCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QRhiMetal::isDeviceLost() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
||||||
int sampleCount, QRhiRenderBuffer::Flags flags)
|
int sampleCount, QRhiRenderBuffer::Flags flags)
|
||||||
{
|
{
|
||||||
|
@ -418,6 +418,7 @@ public:
|
|||||||
void sendVMemStatsToProfiler() override;
|
void sendVMemStatsToProfiler() override;
|
||||||
void makeThreadLocalNativeContextCurrent() override;
|
void makeThreadLocalNativeContextCurrent() override;
|
||||||
void releaseCachedResources() override;
|
void releaseCachedResources() override;
|
||||||
|
bool isDeviceLost() const override;
|
||||||
|
|
||||||
void executeDeferredReleases(bool forced = false);
|
void executeDeferredReleases(bool forced = false);
|
||||||
void finishActiveReadbacks(bool forced = false);
|
void finishActiveReadbacks(bool forced = false);
|
||||||
|
@ -179,6 +179,11 @@ void QRhiNull::releaseCachedResources()
|
|||||||
// nothing to do here
|
// nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QRhiNull::isDeviceLost() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
||||||
int sampleCount, QRhiRenderBuffer::Flags flags)
|
int sampleCount, QRhiRenderBuffer::Flags flags)
|
||||||
{
|
{
|
||||||
|
@ -284,6 +284,7 @@ public:
|
|||||||
void sendVMemStatsToProfiler() override;
|
void sendVMemStatsToProfiler() override;
|
||||||
void makeThreadLocalNativeContextCurrent() override;
|
void makeThreadLocalNativeContextCurrent() override;
|
||||||
void releaseCachedResources() override;
|
void releaseCachedResources() override;
|
||||||
|
bool isDeviceLost() const override;
|
||||||
|
|
||||||
QRhiNullNativeHandles nativeHandlesStruct;
|
QRhiNullNativeHandles nativeHandlesStruct;
|
||||||
QRhiSwapChain *currentSwapChain = nullptr;
|
QRhiSwapChain *currentSwapChain = nullptr;
|
||||||
|
@ -3750,6 +3750,11 @@ void QRhiVulkan::releaseCachedResources()
|
|||||||
// nothing to do here
|
// nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QRhiVulkan::isDeviceLost() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
|
||||||
int sampleCount, QRhiRenderBuffer::Flags flags)
|
int sampleCount, QRhiRenderBuffer::Flags flags)
|
||||||
{
|
{
|
||||||
|
@ -713,6 +713,7 @@ public:
|
|||||||
void sendVMemStatsToProfiler() override;
|
void sendVMemStatsToProfiler() override;
|
||||||
void makeThreadLocalNativeContextCurrent() override;
|
void makeThreadLocalNativeContextCurrent() override;
|
||||||
void releaseCachedResources() override;
|
void releaseCachedResources() override;
|
||||||
|
bool isDeviceLost() const override;
|
||||||
|
|
||||||
VkResult createDescriptorPool(VkDescriptorPool *pool);
|
VkResult createDescriptorPool(VkDescriptorPool *pool);
|
||||||
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex);
|
bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex);
|
||||||
|
Loading…
Reference in New Issue
Block a user