diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 70525689e92..8d928185bc6 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -4285,12 +4285,13 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const with premultiplied alpha. In that case the behavior with this flag set is expected to be equivalent to SurfaceHasPreMulAlpha. - \value sRGB Requests to pick an sRGB format for the swapchain and/or its - render target views, where applicable. Note that this implies that sRGB - framebuffer update and blending will get enabled for all content targeting - this swapchain, and opting out is not possible. For OpenGL, set - \l{QSurfaceFormat::sRGBColorSpace}{sRGBColorSpace} on the QSurfaceFormat of - the QWindow in addition. + \value sRGB Requests to pick an sRGB format for the swapchain's color + buffers and/or render target views, where applicable. Note that this + implies that sRGB framebuffer update and blending will get enabled for all + content targeting this swapchain, and opting out is not possible. For + OpenGL, set \l{QSurfaceFormat::sRGBColorSpace}{sRGBColorSpace} on the + QSurfaceFormat of the QWindow in addition. Applicable only when the + swapchain format is set to QRhiSwapChain::SDR. \value UsedAsTransferSource Indicates the swapchain will be used as the source of a readback in QRhiResourceUpdateBatch::readBackTexture(). @@ -4318,6 +4319,27 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const QRhi::FramesInFlight and typically 2). */ +/*! + \enum QRhiSwapChain::Format + Decribes the swapchain format. The default format is SDR. + + \value SDR 8-bit RGBA or BGRA, depending on the backend and platform. With + OpenGL ES in particular, it could happen that the platform provides less + than 8 bits (e.g. due to EGL and the QSurfaceFormat choosing a 565 or 444 + format - this is outside the control of QRhi). Standard dynamic range. May + be combined with setting the QRhiSwapChain::sRGB flag. + + \value HDRExtendedSrgbLinear 16-bit float RGBA, high dynamic range, + extended linear sRGB (scRGB) color space. This involves Rec. 709 primaries + (same as SDR/sRGB) and linear colors. Conversion to the display's native + color space (such as, HDR10) is performed by the windowing system. On + Windows this is the canonical color space of the system compositor, and is + the recommended format for HDR swapchains in general. + + \value HDR10 10-bit unsigned int RGB or BGR with 2 bit alpha, high dynamic + range, HDR10 (Rec. 2020) color space with an ST2084 PQ transfer function. + */ + /*! \internal */ @@ -4395,6 +4417,22 @@ QRhiResource::Type QRhiSwapChain::resourceType() const \sa currentPixelSize() */ +/*! + \fn bool QRhiSwapChain::isFormatSuported(Format f) + + \return true if the given swapchain format is supported. SDR is always + supported. + + \note Can be called independently of createOrResize(), but window() must + already be set. Calling without the window set may lead to unexpected + results depending on the backend and platform (most likely false for any + HDR format), because HDR format support is usually tied to the output + (screen) to which the swapchain's associated window belongs at any given + time. If the result is true for a HDR format, then creating the swapchain + with that format is expected to succeed as long as the window is not moved + to another screen in the meantime. + */ + /*! \fn QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer() @@ -4432,6 +4470,18 @@ QRhiResource::Type QRhiSwapChain::resourceType() const Regardless of the return value, calling destroy() is always safe. */ +/*! + \return a pointer to a backend-specific QRhiNativeHandles subclass, such as + QRhiD3D11SwapChainNativeHandles. The returned value is \nullptr when + exposing the underlying native resources is not supported by the backend. + + \sa QRhiD3D11SwapChainNativeHandles + */ +const QRhiNativeHandles *QRhiSwapChain::nativeHandles() +{ + return nullptr; +} + /*! \class QRhiComputePipeline \internal diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 5344046841a..7a3bfff0c74 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1327,6 +1327,12 @@ public: }; Q_DECLARE_FLAGS(Flags, Flag) + enum Format { + SDR, + HDRExtendedSrgbLinear, + HDR10 + }; + QRhiResource::Type resourceType() const override; QWindow *window() const { return m_window; } @@ -1335,6 +1341,9 @@ public: Flags flags() const { return m_flags; } void setFlags(Flags f) { m_flags = f; } + Format format() const { return m_format; } + void setFormat(Format f) { m_format = f; } + QRhiRenderBuffer *depthStencil() const { return m_depthStencil; } void setDepthStencil(QRhiRenderBuffer *ds) { m_depthStencil = ds; } @@ -1349,13 +1358,17 @@ public: virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0; virtual QRhiRenderTarget *currentFrameRenderTarget() = 0; virtual QSize surfacePixelSize() = 0; + virtual bool isFormatSupported(Format f) = 0; virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0; virtual bool createOrResize() = 0; + virtual const QRhiNativeHandles *nativeHandles(); + protected: QRhiSwapChain(QRhiImplementation *rhi); QWindow *m_window = nullptr; Flags m_flags; + Format m_format = SDR; QRhiRenderBuffer *m_depthStencil = nullptr; int m_sampleCount = 1; QRhiRenderPassDescriptor *m_renderPassDesc = nullptr; diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index a8fd9a686fb..fadd9008900 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -46,7 +46,6 @@ #include #include -#include QT_BEGIN_NAMESPACE @@ -116,6 +115,18 @@ QT_BEGIN_NAMESPACE \c{ID3D11Device *} and \c{ID3D11DeviceContext *}. */ +/*! + \class QRhiD3D11SwapChainNativeHandles + \internal + \inmodule QtGui + \brief Exposes D3D/DXGI specific data for a swapchain + + dxgiOutput6 is the IDXGIOutput6* for the swapchain's current output, if + supported, null otherwise. The current output is determined based on the + position of the swapchain's associated window at the time of calling + QRhiSwapChain::createOrResize(). + */ + // help mingw with its ancient sdk headers #ifndef DXGI_ADAPTER_FLAG_SOFTWARE #define DXGI_ADAPTER_FLAG_SOFTWARE 2 @@ -246,7 +257,7 @@ bool QRhiD3D11::create(QRhi::Flags flags) } } - IDXGIAdapter1 *adapterToUse = nullptr; + activeAdapter = nullptr; for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { DXGI_ADAPTER_DESC1 desc; adapter->GetDesc1(&desc); @@ -257,8 +268,8 @@ bool QRhiD3D11::create(QRhi::Flags flags) desc.VendorId, desc.DeviceId, desc.Flags); - if (!adapterToUse && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) { - adapterToUse = adapter; + if (!activeAdapter && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) { + activeAdapter = adapter; adapterLuid = desc.AdapterLuid; driverInfoStruct.deviceName = name.toUtf8(); driverInfoStruct.deviceId = desc.DeviceId; @@ -268,7 +279,7 @@ bool QRhiD3D11::create(QRhi::Flags flags) adapter->Release(); } } - if (!adapterToUse) { + if (!activeAdapter) { qWarning("No adapter"); return false; } @@ -283,7 +294,7 @@ bool QRhiD3D11::create(QRhi::Flags flags) } ID3D11DeviceContext *ctx = nullptr; - HRESULT hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags, + HRESULT hr = D3D11CreateDevice(activeAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags, requestFeatureLevels ? requestedFeatureLevels.constData() : nullptr, requestFeatureLevels ? requestedFeatureLevels.count() : 0, D3D11_SDK_VERSION, @@ -293,13 +304,12 @@ bool QRhiD3D11::create(QRhi::Flags flags) qCDebug(QRHI_LOG_INFO, "Debug layer was requested but is not available. " "Attempting to create D3D11 device without it."); devFlags &= ~D3D11_CREATE_DEVICE_DEBUG; - hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags, + hr = D3D11CreateDevice(activeAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags, requestFeatureLevels ? requestedFeatureLevels.constData() : nullptr, requestFeatureLevels ? requestedFeatureLevels.count() : 0, D3D11_SDK_VERSION, &dev, &featureLevel, &ctx); } - adapterToUse->Release(); if (FAILED(hr)) { qWarning("Failed to create D3D11 device and context: %s", qPrintable(comErrorMessage(hr))); return false; @@ -379,6 +389,11 @@ void QRhiD3D11::destroy() } } + if (activeAdapter) { + activeAdapter->Release(); + activeAdapter = nullptr; + } + if (dxgiFactory) { dxgiFactory->Release(); dxgiFactory = nullptr; @@ -4401,6 +4416,11 @@ void QD3D11SwapChain::destroy() swapChain->Release(); swapChain = nullptr; + if (output6) { + output6->Release(); + output6 = nullptr; + } + QRHI_RES_RHI(QRhiD3D11); if (rhiD) { QRHI_PROF; @@ -4425,6 +4445,62 @@ QSize QD3D11SwapChain::surfacePixelSize() return m_window->size() * m_window->devicePixelRatio(); } +static bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result) +{ + bool ok = false; + QRect wr = w->geometry(); + wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio()); + const QPoint center = wr.center(); + IDXGIOutput *currentOutput = nullptr; + IDXGIOutput *output = nullptr; + for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) { + DXGI_OUTPUT_DESC desc; + output->GetDesc(&desc); + const RECT r = desc.DesktopCoordinates; + const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); + if (dr.contains(center)) { + currentOutput = output; + break; + } else { + output->Release(); + } + } + if (currentOutput) { + ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(result))); + currentOutput->Release(); + } + return ok; +} + +static bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result) +{ + bool ok = false; + IDXGIOutput6 *out6 = nullptr; + if (output6ForWindow(w, adapter, &out6)) { + ok = SUCCEEDED(out6->GetDesc1(result)); + out6->Release(); + } + return ok; +} + +bool QD3D11SwapChain::isFormatSupported(Format f) +{ + if (f == SDR) + return true; + + if (!m_window) { + qWarning("Attempted to call isFormatSupported() without a window set"); + return false; + } + + QRHI_RES_RHI(QRhiD3D11); + DXGI_OUTPUT_DESC1 desc1; + if (outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) + return desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + + return false; +} + QRhiRenderPassDescriptor *QD3D11SwapChain::newCompatibleRenderPassDescriptor() { return new QD3D11RenderPassDescriptor(m_rhi); @@ -4466,6 +4542,9 @@ bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI return true; } +static const DXGI_FORMAT DEFAULT_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; +static const DXGI_FORMAT DEFAULT_SRGB_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + bool QD3D11SwapChain::createOrResize() { // Can be called multiple times due to window resizes - that is not the @@ -4485,17 +4564,15 @@ bool QD3D11SwapChain::createOrResize() if (pixelSize.isEmpty()) return false; - colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM; - const DXGI_FORMAT srgbAdjustedFormat = m_flags.testFlag(sRGB) ? - DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM; - - const UINT swapChainFlags = 0; - QRHI_RES_RHI(QRhiD3D11); bool useFlipDiscard = rhiD->hasDxgi2 && rhiD->supportsFlipDiscardSwapchain; + + const UINT swapChainFlags = 0; if (!swapChain) { HWND hwnd = reinterpret_cast(window->winId()); sampleDesc = rhiD->effectiveSampleCount(m_sampleCount); + colorFormat = DEFAULT_FORMAT; + srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT; // Take a shortcut for alpha: our QWindow is OpenGLSurface so whatever // the platform plugin does to enable transparency for OpenGL window @@ -4511,6 +4588,38 @@ bool QD3D11SwapChain::createOrResize() HRESULT hr; if (useFlipDiscard) { + DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR + if (output6) { + output6->Release(); + output6 = nullptr; + } + if (output6ForWindow(m_window, rhiD->activeAdapter, &output6) && m_format != SDR) { + // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range + output6->GetDesc1(&hdrOutputDesc); + if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { + switch (m_format) { + case HDRExtendedSrgbLinear: + colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; + hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + srgbAdjustedColorFormat = colorFormat; + break; + case HDR10: + colorFormat = DXGI_FORMAT_R10G10B10A2_UNORM; + hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + srgbAdjustedColorFormat = colorFormat; + break; + default: + break; + } + } else { + // This happens also when Use HDR is set to Off in the Windows + // Display settings. Show a helpful warning, but continue with the + // default non-HDR format. + qWarning("The output associated with the window is not HDR capable " + "(or Use HDR is Off in the Display Settings), ignoring HDR format request"); + } + } + // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the // old DISCARD with back buffer count == 1). This makes no difference for // the rest of the stuff except that automatic MSAA is unsupported and @@ -4532,11 +4641,33 @@ bool QD3D11SwapChain::createOrResize() // path for now when alpha is requested. desc.Flags = swapChainFlags; + IDXGIFactory2 *fac = static_cast(rhiD->dxgiFactory); IDXGISwapChain1 *sc1; - hr = static_cast(rhiD->dxgiFactory)->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, - nullptr, nullptr, &sc1); - if (SUCCEEDED(hr)) + hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1); + + // If failed and we tried a HDR format, then try with SDR. This + // matches other backends, such as Vulkan where if the format is + // not supported, the default one is used instead. + if (FAILED(hr) && m_format != SDR) { + colorFormat = DEFAULT_FORMAT; + desc.Format = DEFAULT_FORMAT; + hr = fac->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc, nullptr, nullptr, &sc1); + } + + if (SUCCEEDED(hr)) { swapChain = sc1; + if (m_format != SDR) { + IDXGISwapChain3 *sc3 = nullptr; + if (SUCCEEDED(sc1->QueryInterface(IID_IDXGISwapChain3, reinterpret_cast(&sc3)))) { + hr = sc3->SetColorSpace1(hdrColorSpace); + if (FAILED(hr)) + qWarning("Failed to set color space on swapchain: %s", qPrintable(comErrorMessage(hr))); + sc3->Release(); + } else { + qWarning("IDXGISwapChain3 not available, HDR swapchain will not work as expected"); + } + } + } } else { // Windows 7 for instance. Use DISCARD mode. Regardless, keep on // using our manual resolve for symmetry with the FLIP_DISCARD code @@ -4600,7 +4731,7 @@ bool QD3D11SwapChain::createOrResize() } D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; memset(&rtvDesc, 0, sizeof(rtvDesc)); - rtvDesc.Format = srgbAdjustedFormat; + rtvDesc.Format = srgbAdjustedColorFormat; rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; hr = rhiD->dev->CreateRenderTargetView(backBufferTex, &rtvDesc, &backBufferRtv); if (FAILED(hr)) { @@ -4611,7 +4742,7 @@ bool QD3D11SwapChain::createOrResize() // Try to reduce stalls by having a dedicated MSAA texture per swapchain buffer. for (int i = 0; i < BUFFER_COUNT; ++i) { if (sampleDesc.Count > 1) { - if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i])) + if (!newColorBuffer(pixelSize, srgbAdjustedColorFormat, sampleDesc, &msaaTex[i], &msaaRtv[i])) return false; } } @@ -4681,6 +4812,12 @@ bool QD3D11SwapChain::createOrResize() return true; } +const QRhiNativeHandles *QD3D11SwapChain::nativeHandles() +{ + nativeHandlesStruct.dxgiOutput6 = output6; + return &nativeHandlesStruct; +} + void QRhiD3D11::DeviceCurse::initResources() { framesLeft = framesToActivate; diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h index ed26e9af16e..f4237d911d4 100644 --- a/src/gui/rhi/qrhid3d11_p.h +++ b/src/gui/rhi/qrhid3d11_p.h @@ -76,6 +76,11 @@ struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles qint32 adapterLuidHigh = 0; }; +struct Q_GUI_EXPORT QRhiD3D11SwapChainNativeHandles : public QRhiNativeHandles +{ + void *dxgiOutput6 = nullptr; +}; + QT_END_NAMESPACE #endif diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 70c7ad969bf..a8ded4a2a96 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -57,7 +57,7 @@ #include #include -#include +#include QT_BEGIN_NAMESPACE @@ -559,9 +559,11 @@ struct QD3D11SwapChain : public QRhiSwapChain QRhiRenderTarget *currentFrameRenderTarget() override; QSize surfacePixelSize() override; + bool isFormatSupported(Format f) override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; + const QRhiNativeHandles *nativeHandles() override; void releaseBuffers(); bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc, @@ -572,6 +574,7 @@ struct QD3D11SwapChain : public QRhiSwapChain QD3D11ReferenceRenderTarget rt; QD3D11CommandBuffer cb; DXGI_FORMAT colorFormat; + DXGI_FORMAT srgbAdjustedColorFormat; IDXGISwapChain *swapChain = nullptr; static const int BUFFER_COUNT = 2; ID3D11Texture2D *backBufferTex; @@ -586,6 +589,9 @@ struct QD3D11SwapChain : public QRhiSwapChain ID3D11Query *timestampDisjointQuery[BUFFER_COUNT]; ID3D11Query *timestampQuery[BUFFER_COUNT * 2]; UINT swapInterval = 1; + IDXGIOutput6 *output6 = nullptr; + DXGI_OUTPUT_DESC1 hdrOutputDesc; + QRhiD3D11SwapChainNativeHandles nativeHandlesStruct; }; class QRhiD3D11 : public QRhiImplementation @@ -722,6 +728,7 @@ public: D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL(0); LUID adapterLuid = {}; ID3DUserDefinedAnnotation *annotations = nullptr; + IDXGIAdapter1 *activeAdapter = nullptr; IDXGIFactory1 *dxgiFactory = nullptr; bool hasDxgi2 = false; bool supportsFlipDiscardSwapchain = false; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 67d08d3042b..90ade77dccc 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -5470,6 +5470,11 @@ QSize QGles2SwapChain::surfacePixelSize() return m_window->size() * m_window->devicePixelRatio(); } +bool QGles2SwapChain::isFormatSupported(Format f) +{ + return f == SDR; +} + QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor() { return new QGles2RenderPassDescriptor(m_rhi); diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index c0f38830024..5dcafe28877 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -722,6 +722,7 @@ struct QGles2SwapChain : public QRhiSwapChain QRhiRenderTarget *currentFrameRenderTarget() override; QSize surfacePixelSize() override; + bool isFormatSupported(Format f) override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 626eac433bc..58e9efc07f5 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -4007,6 +4007,11 @@ QSize QMetalSwapChain::surfacePixelSize() return QSizeF::fromCGSize(layerSize).toSize(); } +bool QMetalSwapChain::isFormatSupported(Format f) +{ + return f == SDR; +} + QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor() { chooseFormats(); // ensure colorFormat and similar are filled out diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index f24912a6403..245d31b087b 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -323,6 +323,7 @@ struct QMetalSwapChain : public QRhiSwapChain QRhiCommandBuffer *currentFrameCommandBuffer() override; QRhiRenderTarget *currentFrameRenderTarget() override; QSize surfacePixelSize() override; + bool isFormatSupported(Format f) override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 6e85e02cc29..1ad772b5468 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -1025,6 +1025,11 @@ QSize QNullSwapChain::surfacePixelSize() return QSize(1280, 720); } +bool QNullSwapChain::isFormatSupported(Format f) +{ + return f == SDR; +} + QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor() { return new QNullRenderPassDescriptor(m_rhi); diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index 217ec8ef390..cb85ec857a7 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -195,6 +195,7 @@ struct QNullSwapChain : public QRhiSwapChain QRhiRenderTarget *currentFrameRenderTarget() override; QSize surfacePixelSize() override; + bool isFormatSupported(Format f) override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index e23900069cb..4fac2cb1735 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -640,6 +640,20 @@ bool QRhiVulkan::create(QRhi::Flags flags) qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); } + vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast( + inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); + vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast( + inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR")); + vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast( + inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR")); + if (!vkGetPhysicalDeviceSurfaceCapabilitiesKHR + || !vkGetPhysicalDeviceSurfaceFormatsKHR + || !vkGetPhysicalDeviceSurfacePresentModesKHR) + { + qWarning("Physical device surface queries not available"); + return false; + } + df = inst->deviceFunctions(dev); VkCommandPoolCreateInfo poolInfo; @@ -7196,6 +7210,49 @@ QSize QVkSwapChain::surfacePixelSize() return QSize(int(bufferSize.width), int(bufferSize.height)); } +static inline bool hdrFormatMatchesVkSurfaceFormat(QRhiSwapChain::Format f, const VkSurfaceFormatKHR &s) +{ + switch (f) { + case QRhiSwapChain::HDRExtendedSrgbLinear: + return s.format == VK_FORMAT_R16G16B16A16_SFLOAT + && s.colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT; + case QRhiSwapChain::HDR10: + return (s.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 || s.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32) + && s.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT; + default: + break; + } + return false; +} + +bool QVkSwapChain::isFormatSupported(Format f) +{ + if (f == SDR) + return true; + + if (!m_window) { + qWarning("Attempted to call isFormatSupported() without a window set"); + return false; + } + + // we may be called before create so query the surface + VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window); + + QRHI_RES_RHI(QRhiVulkan); + uint32_t formatCount = 0; + rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, nullptr); + QVarLengthArray formats(formatCount); + if (formatCount) { + rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surf, &formatCount, formats.data()); + for (uint32_t i = 0; i < formatCount; ++i) { + if (hdrFormatMatchesVkSurfaceFormat(f, formats[i])) + return true; + } + } + + return false; +} + QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor() { // not yet built so cannot rely on data computed in createOrResize() @@ -7262,34 +7319,27 @@ bool QVkSwapChain::ensureSurface() } } - if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR) { - rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast( - rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); - rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast( - rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR")); - rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast( - rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR")); - if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR - || !rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR - || !rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR) - { - qWarning("Physical device surface queries not available"); - return false; - } - } - quint32 formatCount = 0; rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr); QList formats(formatCount); if (formatCount) rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, formats.data()); + // See if there is a better match than the default BGRA8 format. (but if + // not, we will stick to the default) const bool srgbRequested = m_flags.testFlag(sRGB); for (int i = 0; i < int(formatCount); ++i) { - if (formats[i].format != VK_FORMAT_UNDEFINED && srgbRequested == isSrgbFormat(formats[i].format)) { - colorFormat = formats[i].format; - colorSpace = formats[i].colorSpace; - break; + if (formats[i].format != VK_FORMAT_UNDEFINED) { + bool ok = false; + if (m_format == SDR) + ok = srgbRequested == isSrgbFormat(formats[i].format); + else + ok = hdrFormatMatchesVkSurfaceFormat(m_format, formats[i]); + if (ok) { + colorFormat = formats[i].format; + colorSpace = formats[i].colorSpace; + break; + } } } diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index da69a0d70d0..9fda2526532 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -603,6 +603,7 @@ struct QVkSwapChain : public QRhiSwapChain QRhiRenderTarget *currentFrameRenderTarget() override; QSize surfacePixelSize() override; + bool isFormatSupported(Format f) override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; @@ -878,7 +879,7 @@ public: PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; PFN_vkQueuePresentKHR vkQueuePresentKHR; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;