summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-12-31 14:39:53 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2022-01-03 14:35:13 +0100
commit5ae238f90fb31ac216f83c0290737bbbec8ff565 (patch)
treee2211c4ddafce12b8ff5b527ca6708346a8320e2 /src/gui
parent8e2f101a6bf6c59e707b2747d6df76e8e221e319 (diff)
rhi: Add the basics for HDR swapchains
...backed by support in the backends for D3D11 and Vulkan. Expose only what works in practice: scRGB with RGBA16F and HDR10 with RGB10A2 (or A2BGRA10 etc.). For general use, e.g. to render 2D/3D content, scRGB (i.e. extended sRGB linear) should be chosen because that is a linear space. On Windows with a HDR10 display (and an NVIDIA card) both of these are known to work, as long as Use HDR is enabled in the Settings for the display on which the window is created. When requesting a HDR format and it is not supported, we will fall back to the default SDR RGBA8/BGRA8 format. However, the behavior seems to be a bit weird with Vulkan and NVIDIA at least when there is a HDR display but Use HDR is set to Off: this seems to enable HDR mode for the lifetime of the window (with the usual set of black screens while switching over the entire display). Not sure why the driver does this. With D3D/DXGI, with fewer abstractions in the way, we can check upfront properly, so that will nicely fall back to the defaults regardless of why HDR is not available. Support can also be checked in advance via QRhiSwapChain::isFormatSupported() as long as the QWindow is available. (though with Vulkan, as said above, this also seems to ignore the Use HDR setting of Windows) Complications, such as moving a window from one screen to another, are currently not known how they behave. To be seen how this is handled by the Windows compositor. (from 1903 and up it is said to be able to automatically downconvert scRGB to SDR so perhaps moving from a HDR to a non-HDR screen would work - remains to be seen if this needs something more involved) When it comes to other platforms and potential future support: - based on its docs Android 8+ may support scRGB with Vulkan as-is on a suitable device/display, as long as the application declares android:colorMode="wideColorGamut" in the manifest. - for Metal the layer can be made EDR enabled and then e.g. MTLPixelFormatRGBA16Float/kCGColorSpaceExtendedLinearSRGB should work. However, this won't be added unless we can test it. - Linux is unknown. If one needs access to display specific values such as the min/max luminance when implementing tonemapping or a transfer function, that needs platform/API specific approaches, and sadly the kind of data exposed seems to vary, potentially making it difficult to maintain a single cross-platform logic. With D3D one can get the DXGI_OUTPUT_DESC1 from the IDXGIOutput6. This has the min/max luminances in nits and a bunch of other things. For convenience the output object is now exposed from the swapchain's nativeHandles() whenever the D3D backend is used at run time. For Metal one would presumably access maximumExtendedDynamicRangeColorComponentValue and co. in the NSScreen. Elsewhere one needs to rely on platform/winsys specific approaches, if there are any. Remains to be seen longer term if/how this needs/can be better supported. Change-Id: I2e61a0e062282d4bfdfba39655941c0f9a795112 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/rhi/qrhi.cpp62
-rw-r--r--src/gui/rhi/qrhi_p.h13
-rw-r--r--src/gui/rhi/qrhid3d11.cpp175
-rw-r--r--src/gui/rhi/qrhid3d11_p.h5
-rw-r--r--src/gui/rhi/qrhid3d11_p_p.h9
-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.cpp90
-rw-r--r--src/gui/rhi/qrhivulkan_p_p.h3
13 files changed, 328 insertions, 47 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 70525689e9..8d928185bc 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().
@@ -4319,6 +4320,27 @@ QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
*/
/*!
+ \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
*/
QRhiSwapChain::QRhiSwapChain(QRhiImplementation *rhi)
@@ -4396,6 +4418,22 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
*/
/*!
+ \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()
\return a command buffer on which rendering commands can be recorded. Only
@@ -4433,6 +4471,18 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
*/
/*!
+ \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
\inmodule QtGui
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
index 5344046841..7a3bfff0c7 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 a8fd9a686f..fadd900890 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -46,7 +46,6 @@
#include <d3dcompiler.h>
#include <comdef.h>
-#include <dxgi1_3.h>
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<void **>(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<HWND>(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<IDXGIFactory2 *>(rhiD->dxgiFactory);
IDXGISwapChain1 *sc1;
- hr = static_cast<IDXGIFactory2 *>(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<void **>(&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 ed26e9af16..f4237d911d 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 70c7ad969b..a8ded4a2a9 100644
--- a/src/gui/rhi/qrhid3d11_p_p.h
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -57,7 +57,7 @@
#include <QWindow>
#include <d3d11_1.h>
-#include <dxgi1_3.h>
+#include <dxgi1_6.h>
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 67d08d3042..90ade77dcc 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 c0f3883002..5dcafe2887 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 626eac433b..58e9efc07f 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 f24912a640..245d31b087 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 6e85e02cc2..1ad772b546 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 217ec8ef39..cb85ec857a 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 e23900069c..4fac2cb173 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<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
+ vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
+ inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
+ vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
+ 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<VkSurfaceFormatKHR, 8> 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<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
- rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
- rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
- rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
- rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
- 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<VkSurfaceFormatKHR> 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 da69a0d70d..9fda252653 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;