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
|
||||
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.
|
||||
*/
|
||||
|
||||
@ -5045,6 +5045,47 @@ void QRhi::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.
|
||||
|
||||
|
@ -1422,6 +1422,8 @@ public:
|
||||
|
||||
void releaseCachedResources();
|
||||
|
||||
bool isDeviceLost() const;
|
||||
|
||||
protected:
|
||||
QRhi();
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -284,6 +284,7 @@ public:
|
||||
void sendVMemStatsToProfiler() override;
|
||||
void makeThreadLocalNativeContextCurrent() override;
|
||||
void releaseCachedResources() override;
|
||||
bool isDeviceLost() const override;
|
||||
|
||||
QRhiNullNativeHandles nativeHandlesStruct;
|
||||
QRhiSwapChain *currentSwapChain = nullptr;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user