diff options
Diffstat (limited to 'src/quick/scenegraph/qsgrhisupport.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgrhisupport.cpp | 209 |
1 files changed, 155 insertions, 54 deletions
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp index f18d6a7d00..45c183a5f8 100644 --- a/src/quick/scenegraph/qsgrhisupport.cpp +++ b/src/quick/scenegraph/qsgrhisupport.cpp @@ -50,19 +50,22 @@ void QSGRhiSupport::applySettings() if (m_requested.valid) { // explicit rhi backend request from C++ (e.g. via QQuickWindow) switch (m_requested.api) { - case QSGRendererInterface::OpenGLRhi: + case QSGRendererInterface::OpenGL: m_rhiBackend = QRhi::OpenGLES2; break; - case QSGRendererInterface::Direct3D11Rhi: + case QSGRendererInterface::Direct3D11: m_rhiBackend = QRhi::D3D11; break; - case QSGRendererInterface::VulkanRhi: + case QSGRendererInterface::Direct3D12: + m_rhiBackend = QRhi::D3D12; + break; + case QSGRendererInterface::Vulkan: m_rhiBackend = QRhi::Vulkan; break; - case QSGRendererInterface::MetalRhi: + case QSGRendererInterface::Metal: m_rhiBackend = QRhi::Metal; break; - case QSGRendererInterface::NullRhi: + case QSGRendererInterface::Null: m_rhiBackend = QRhi::Null; break; default: @@ -79,6 +82,8 @@ void QSGRhiSupport::applySettings() m_rhiBackend = QRhi::OpenGLES2; } else if (rhiBackend == QByteArrayLiteral("d3d11") || rhiBackend == QByteArrayLiteral("d3d")) { m_rhiBackend = QRhi::D3D11; + } else if (rhiBackend == QByteArrayLiteral("d3d12")) { + m_rhiBackend = QRhi::D3D12; } else if (rhiBackend == QByteArrayLiteral("vulkan")) { m_rhiBackend = QRhi::Vulkan; } else if (rhiBackend == QByteArrayLiteral("metal")) { @@ -92,7 +97,7 @@ void QSGRhiSupport::applySettings() } #if defined(Q_OS_WIN) m_rhiBackend = QRhi::D3D11; -#elif defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#elif QT_CONFIG(metal) m_rhiBackend = QRhi::Metal; #elif QT_CONFIG(opengl) m_rhiBackend = QRhi::OpenGLES2; @@ -111,26 +116,11 @@ void QSGRhiSupport::applySettings() // (QQuickWindow) may depend on the graphics API as well (surfaceType // f.ex.), and all that is based on what we report from here. So further // adjustments are not possible (or, at minimum, not safe and portable). - - m_killDeviceFrameCount = qEnvironmentVariableIntValue("QSG_RHI_SIMULATE_DEVICE_LOSS"); - if (m_killDeviceFrameCount > 0 && m_rhiBackend == QRhi::D3D11) - qDebug("Graphics device will be reset every %d frames", m_killDeviceFrameCount); - - QByteArray hdrRequest = qgetenv("QSG_RHI_HDR"); - if (!hdrRequest.isEmpty()) { - hdrRequest = hdrRequest.toLower(); - if (hdrRequest == QByteArrayLiteral("scrgb") || hdrRequest == QByteArrayLiteral("extendedsrgblinear")) - m_swapChainFormat = QRhiSwapChain::HDRExtendedSrgbLinear; - else if (hdrRequest == QByteArrayLiteral("hdr10")) - m_swapChainFormat = QRhiSwapChain::HDR10; - else - qWarning("Unknown HDR mode '%s'", hdrRequest.constData()); - } } void QSGRhiSupport::adjustToPlatformQuirks() { -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#if QT_CONFIG(metal) // A macOS VM may not have Metal support at all. We have to decide at this // point, it will be too late afterwards, and the only way is to see if // MTLCreateSystemDefaultDevice succeeds. @@ -225,12 +215,24 @@ void QSGRhiSupport::checkEnvQSgInfo() #define GL_RGB10_A2 0x8059 #endif -QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format) +#ifndef GL_SRGB_ALPHA +#define GL_SRGB_ALPHA 0x8C42 +#endif + +#ifndef GL_SRGB8_ALPHA8 +#define GL_SRGB8_ALPHA8 0x8C43 +#endif + +QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format, QRhiTexture::Flags *flags) { + bool sRGB = false; auto rhiFormat = QRhiTexture::UnknownFormat; switch (format) { - case GL_RGBA: + case GL_SRGB_ALPHA: + case GL_SRGB8_ALPHA8: + sRGB = true; Q_FALLTHROUGH(); + case GL_RGBA: case GL_RGBA8: case 0: rhiFormat = QRhiTexture::RGBA8; @@ -292,6 +294,8 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromGL(uint format) qWarning("GL format %d is not supported", format); break; } + if (sRGB) + (*flags) |=(QRhiTexture::sRGB); return rhiFormat; } #endif @@ -508,7 +512,7 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromVulkan(uint format, QRh #endif #ifdef Q_OS_WIN -QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromD3D11(uint format, QRhiTexture::Flags *flags) +QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromDXGI(uint format, QRhiTexture::Flags *flags) { auto rhiFormat = QRhiTexture::UnknownFormat; bool sRGB = false; @@ -608,7 +612,7 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormatFromD3D11(uint format, QRhi } #endif -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#if QT_CONFIG(metal) namespace QSGRhiSupportMac { QRhiTexture::Format toRhiTextureFormatFromMetal(uint format, QRhiTexture::Flags *flags); } @@ -655,15 +659,17 @@ QSGRendererInterface::GraphicsApi QSGRhiSupport::graphicsApi() const { switch (m_rhiBackend) { case QRhi::Null: - return QSGRendererInterface::NullRhi; + return QSGRendererInterface::Null; case QRhi::Vulkan: - return QSGRendererInterface::VulkanRhi; + return QSGRendererInterface::Vulkan; case QRhi::OpenGLES2: - return QSGRendererInterface::OpenGLRhi; + return QSGRendererInterface::OpenGL; case QRhi::D3D11: - return QSGRendererInterface::Direct3D11Rhi; + return QSGRendererInterface::Direct3D11; + case QRhi::D3D12: + return QSGRendererInterface::Direct3D12; case QRhi::Metal: - return QSGRendererInterface::MetalRhi; + return QSGRendererInterface::Metal; default: return QSGRendererInterface::Unknown; } @@ -677,6 +683,7 @@ QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const case QRhi::OpenGLES2: return QSurface::OpenGLSurface; case QRhi::D3D11: + case QRhi::D3D12: return QSurface::Direct3DSurface; case QRhi::Metal: return QSurface::MetalSurface; @@ -714,6 +721,10 @@ static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, return &maybeVkRpNat->renderPass; else return nullptr; + case QSGRendererInterface::GraphicsQueueFamilyIndexResource: + return &vknat->gfxQueueFamilyIdx; + case QSGRendererInterface::GraphicsQueueIndexResource: + return &vknat->gfxQueueIdx; default: return nullptr; } @@ -746,9 +757,22 @@ static const void *qsgrhi_d3d11_rifResource(QSGRendererInterface::Resource res, return nullptr; } } + +static const void *qsgrhi_d3d12_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat) +{ + const QRhiD3D12NativeHandles *d3dnat = static_cast<const QRhiD3D12NativeHandles *>(nat); + switch (res) { + case QSGRendererInterface::DeviceResource: + return d3dnat->dev; + case QSGRendererInterface::CommandQueueResource: + return d3dnat->commandQueue; + default: + return nullptr; + } +} #endif -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#if QT_CONFIG(metal) static const void *qsgrhi_mtl_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat, const QRhiNativeHandles *cbNat) { @@ -795,7 +819,7 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res, case QSGRendererInterface::RhiRedirectCommandBuffer: return QQuickWindowPrivate::get(w)->redirect.commandBuffer; case QSGRendererInterface::RhiRedirectRenderTarget: - return QQuickWindowPrivate::get(w)->redirect.rt.renderTarget; + return QQuickWindowPrivate::get(w)->redirect.rt.rt.renderTarget; default: break; } @@ -822,8 +846,10 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res, #ifdef Q_OS_WIN case QRhi::D3D11: return qsgrhi_d3d11_rifResource(res, nat); + case QRhi::D3D12: + return qsgrhi_d3d12_rifResource(res, nat); #endif -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#if QT_CONFIG(metal) case QRhi::Metal: { QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); @@ -1026,8 +1052,18 @@ void QSGRhiSupport::finalizePipelineCache(QRhi *rhi, const QQuickGraphicsConfigu // If empty, do nothing. This is exactly what will happen if the rhi was // created without QRhi::EnablePipelineCacheDataSave set. - if (buf.isEmpty()) + if (buf.isEmpty()) { + if (isAutomatic) { + // Attempt to remove the file. If it does not exist or this fails, + // that's fine. The goal is just to prevent warnings from + // setPipelineCacheData in future runs, e.g. if the Qt or driver + // version does not match _and_ we do not generate any data at run + // time, then not writing the file out also means the warning would + // appear again and again on every run. Prevent that. + QDir().remove(pipelineCacheSave); + } return; + } QLockFile lock(pipelineCacheLockFileName(pipelineCacheSave)); if (!lock.lock()) { @@ -1068,7 +1104,7 @@ void QSGRhiSupport::finalizePipelineCache(QRhi *rhi, const QQuickGraphicsConfigu } // must be called on the render thread -QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QSurface *offscreenSurface) +QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QSurface *offscreenSurface, bool forcePreferSwRenderer) { QRhi *rhi = nullptr; QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); @@ -1083,7 +1119,8 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS const bool debugLayer = wd->graphicsConfig.isDebugLayerEnabled(); const bool debugMarkers = wd->graphicsConfig.isDebugMarkersEnabled(); - const bool preferSoftware = wd->graphicsConfig.prefersSoftwareDevice(); + const bool timestamps = wd->graphicsConfig.timestampsEnabled(); + const bool preferSoftware = wd->graphicsConfig.prefersSoftwareDevice() || forcePreferSwRenderer; const bool pipelineCacheSave = !wd->graphicsConfig.pipelineCacheSaveFile().isEmpty() || (wd->graphicsConfig.isAutomaticPipelineCacheEnabled() && !isAutomaticPipelineCacheSaveSkippedForWindow(window->flags())); @@ -1093,13 +1130,18 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS "Creating QRhi with backend %s for window %p (wflags 0x%X)\n" " Graphics API debug/validation layers: %d\n" " Debug markers: %d\n" - " Prefer software device: %d\n" + " Timestamps: %d\n" + " Prefer software device: %d%s\n" " Shader/pipeline cache collection: %d", - qPrintable(backendName), window, int(window->flags()), debugLayer, debugMarkers, preferSoftware, pipelineCacheSave); + qPrintable(backendName), window, int(window->flags()), debugLayer, + debugMarkers, timestamps, preferSoftware, forcePreferSwRenderer ? " [FORCED]" : "", pipelineCacheSave); QRhi::Flags flags; + flags |= QRhi::SuppressSmokeTestWarnings; if (debugMarkers) flags |= QRhi::EnableDebugMarkers; + if (timestamps) + flags |= QRhi::EnableTimestamps; if (preferSoftware) flags |= QRhi::PreferSoftwareRenderer; if (pipelineCacheSave) @@ -1169,10 +1211,6 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS if (backend == QRhi::D3D11) { QRhiD3D11InitParams rhiParams; rhiParams.enableDebugLayer = debugLayer; - if (m_killDeviceFrameCount > 0) { - rhiParams.framesUntilKillingDeviceViaTdr = m_killDeviceFrameCount; - rhiParams.repeatDeviceKill = true; - } if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) { QRhiD3D11NativeHandles importDev; importDev.dev = customDevD->u.deviceAndContext.device; @@ -1190,7 +1228,32 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS rhi = QRhi::create(backend, &rhiParams, flags, &importDev); } else { rhi = QRhi::create(backend, &rhiParams, flags); - if (!rhi && !flags.testFlag(QRhi::PreferSoftwareRenderer)) { + if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer)) { + qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; " + "attempting to get a software rasterizer backed device instead"); + flags |= QRhi::PreferSoftwareRenderer; + rhi = QRhi::create(backend, &rhiParams, flags); + } + } + } else if (backend == QRhi::D3D12) { + QRhiD3D12InitParams rhiParams; + rhiParams.enableDebugLayer = debugLayer; + if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) { + QRhiD3D12NativeHandles importDev; + importDev.dev = customDevD->u.deviceAndContext.device; + qCDebug(QSG_LOG_INFO, "Using existing native D3D12 device %p", importDev.dev); + rhi = QRhi::create(backend, &rhiParams, flags, &importDev); + } else if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Adapter) { + QRhiD3D12NativeHandles importDev; + importDev.adapterLuidLow = customDevD->u.adapter.luidLow; + importDev.adapterLuidHigh = customDevD->u.adapter.luidHigh; + importDev.minimumFeatureLevel = customDevD->u.adapter.featureLevel; + qCDebug(QSG_LOG_INFO, "Using D3D12 adapter LUID %u, %d and minimum feature level %d", + importDev.adapterLuidLow, importDev.adapterLuidHigh, importDev.minimumFeatureLevel); + rhi = QRhi::create(backend, &rhiParams, flags, &importDev); + } else { + rhi = QRhi::create(backend, &rhiParams, flags); + if (!rhi && attemptReinitWithSwRastUponFail() && !flags.testFlag(QRhi::PreferSoftwareRenderer)) { qCDebug(QSG_LOG_INFO, "Failed to create a D3D device with default settings; " "attempting to get a software rasterizer backed device instead"); flags |= QRhi::PreferSoftwareRenderer; @@ -1199,7 +1262,7 @@ QSGRhiSupport::RhiCreateResult QSGRhiSupport::createRhi(QQuickWindow *window, QS } } #endif -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#if QT_CONFIG(metal) if (backend == QRhi::Metal) { QRhiMetalInitParams rhiParams; if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndCommandQueue) { @@ -1460,10 +1523,28 @@ QImage QSGRhiSupport::grabOffscreenForProtectedContent(QQuickWindow *window) } #endif -void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet) +void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet, QQuickWindow *window) { + Q_ASSERT(scWithWindowSet->window() == window); + + QRhiSwapChain::Format swapChainFormat = QRhiSwapChain::SDR; + + QByteArray hdrRequest = qgetenv("QSG_RHI_HDR"); + if (hdrRequest.isEmpty()) + hdrRequest = window->property("_qt_sg_hdr_format").toByteArray(); + + if (!hdrRequest.isEmpty()) { + hdrRequest = hdrRequest.toLower(); + if (hdrRequest == QByteArrayLiteral("scrgb") || hdrRequest == QByteArrayLiteral("extendedsrgblinear")) + swapChainFormat = QRhiSwapChain::HDRExtendedSrgbLinear; + else if (hdrRequest == QByteArrayLiteral("hdr10")) + swapChainFormat = QRhiSwapChain::HDR10; + else if (hdrRequest == QByteArrayLiteral("p3")) + swapChainFormat = QRhiSwapChain::HDRExtendedDisplayP3Linear; + } + const char *fmtStr = "unknown"; - switch (m_swapChainFormat) { + switch (swapChainFormat) { case QRhiSwapChain::SDR: fmtStr = "SDR"; break; @@ -1473,12 +1554,15 @@ void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet) case QRhiSwapChain::HDR10: fmtStr = "HDR10"; break; + case QRhiSwapChain::HDRExtendedDisplayP3Linear: + fmtStr = "Extended Linear Display P3"; + break; default: break; } - if (!scWithWindowSet->isFormatSupported(m_swapChainFormat)) { - if (m_swapChainFormat != QRhiSwapChain::SDR) { + if (!scWithWindowSet->isFormatSupported(swapChainFormat)) { + if (swapChainFormat != QRhiSwapChain::SDR) { qCDebug(QSG_LOG_INFO, "Requested a %s swapchain but it is reported to be unsupported with the current display(s). " "In multi-screen configurations make sure the window is located on a HDR-enabled screen. " "Request ignored, using SDR swapchain.", fmtStr); @@ -1486,9 +1570,9 @@ void QSGRhiSupport::applySwapChainFormat(QRhiSwapChain *scWithWindowSet) return; } - scWithWindowSet->setFormat(m_swapChainFormat); + scWithWindowSet->setFormat(swapChainFormat); - if (m_swapChainFormat != QRhiSwapChain::SDR) { + if (swapChainFormat != QRhiSwapChain::SDR) { qCDebug(QSG_LOG_INFO, "Creating %s swapchain", fmtStr); qCDebug(QSG_LOG_INFO) << "HDR output info:" << scWithWindowSet->hdrInfo(); } @@ -1504,19 +1588,36 @@ QRhiTexture::Format QSGRhiSupport::toRhiTextureFormat(uint nativeFormat, QRhiTex #if QT_CONFIG(opengl) case QRhi::OpenGLES2: Q_UNUSED(flags); - return toRhiTextureFormatFromGL(nativeFormat); + return toRhiTextureFormatFromGL(nativeFormat, flags); #endif #ifdef Q_OS_WIN case QRhi::D3D11: - return toRhiTextureFormatFromD3D11(nativeFormat, flags); + case QRhi::D3D12: + return toRhiTextureFormatFromDXGI(nativeFormat, flags); #endif -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) +#if QT_CONFIG(metal) case QRhi::Metal: return toRhiTextureFormatFromMetal(nativeFormat, flags); #endif default: return QRhiTexture::UnknownFormat; } + Q_UNUSED(nativeFormat) + Q_UNUSED(flags) +} + +bool QSGRhiSupport::attemptReinitWithSwRastUponFail() const +{ + const QRhi::Implementation backend = rhiBackend(); + + // On Windows it makes sense to retry using a software adapter whenever + // device creation or swapchain creation fails, as WARP is usually available + // (built in to the OS) and is good quality. This helps a lot in particular + // when running in a VM that cripples proper 3D graphics. + if (backend == QRhi::D3D11 || backend == QRhi::D3D12) + return true; + + return false; } QT_END_NAMESPACE |