diff --git a/src/rendervulkan.cpp b/src/rendervulkan.cpp index 9ac4c7092e..fb409a62cc 100644 --- a/src/rendervulkan.cpp +++ b/src/rendervulkan.cpp @@ -464,8 +464,12 @@ bool CVulkanDevice::createDevice() // We need to refactor some Vulkan stuff to do that though. if ( hasDrmProps ) { + VkPhysicalDeviceDriverProperties driverProps = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, + }; VkPhysicalDeviceDrmPropertiesEXT drmProps = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT, + .pNext = &driverProps, }; VkPhysicalDeviceProperties2 props2 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, @@ -473,6 +477,8 @@ bool CVulkanDevice::createDevice() }; vk.GetPhysicalDeviceProperties2( physDev(), &props2 ); + m_bIsNvidiaProprietaryDriver = driverProps.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY; + if ( !GetBackend()->UsesVulkanSwapchain() && !drmProps.hasPrimary ) { vk_log.errorf( "physical device has no primary node" ); return false; @@ -3638,6 +3644,7 @@ gamescope::Rc vulkan_acquire_screenshot_texture(uint32_t width, screenshotImageFlags.bMappable = true; screenshotImageFlags.bTransferDst = true; screenshotImageFlags.bStorage = true; + screenshotImageFlags.bSampled = true; // required for RGB-to-NV12 shader to sample this texture if (exportable || drmFormat == DRM_FORMAT_NV12) { screenshotImageFlags.bExportable = true; screenshotImageFlags.bLinear = true; // TODO: support multi-planar DMA-BUF export via PipeWire diff --git a/src/rendervulkan.hpp b/src/rendervulkan.hpp index 0833fc46ff..737eef10f0 100644 --- a/src/rendervulkan.hpp +++ b/src/rendervulkan.hpp @@ -810,6 +810,7 @@ class CVulkanDevice inline bool hasDrmPrimaryDevId() {return m_bHasDrmPrimaryDevId;} inline dev_t primaryDevId() {return m_drmPrimaryDevId;} inline bool supportsFp16() {return m_bSupportsFp16;} + inline bool isNvidiaProprietaryDriver() {return m_bIsNvidiaProprietaryDriver;} inline std::pair uploadBufferData(uint32_t size) { @@ -873,6 +874,7 @@ class CVulkanDevice bool m_bSupportsFp16 = false; bool m_bHasDrmPrimaryDevId = false; bool m_bSupportsModifiers = false; + bool m_bIsNvidiaProprietaryDriver = false; bool m_bInitialized = false; diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 006f6a521b..e5a4c3903e 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -2388,8 +2388,15 @@ static void paint_pipewire() if ( pFocus->overrideWindow && !pFocus->focusWindow->isSteamStreamingClient ) paint_window( pFocus->overrideWindow, pFocus->focusWindow, &frameInfo, nullptr, PaintWindowFlag::NoFilter, 1.0f, pFocus->overrideWindow ); + // On the NVIDIA proprietary driver, sampling VK_FORMAT_A2R10G10B10_UNORM_PACK32 + // (DRM_FORMAT_XRGB2101010) storage images returns R and B swapped, causing colour + // inversion in the RGB-to-NV12 conversion shader. Use DRM_FORMAT_XBGR2101010 + // (VK_FORMAT_A2B10G10R10_UNORM_PACK32) instead so components arrive in the correct + // order. This does not affect non-proprietary NVIDIA drivers or other vendors. + const uint32_t uRGBCaptureFormat = g_device.isNvidiaProprietaryDriver() + ? DRM_FORMAT_XBGR2101010 : DRM_FORMAT_XRGB2101010; gamescope::Rc pRGBTexture = s_pPipewireBuffer->texture->isYcbcr() - ? vulkan_acquire_screenshot_texture( uWidth, uHeight, false, DRM_FORMAT_XRGB2101010 ) + ? vulkan_acquire_screenshot_texture( uWidth, uHeight, false, uRGBCaptureFormat ) : gamescope::Rc{ s_pPipewireBuffer->texture }; gamescope::Rc pYUVTexture = s_pPipewireBuffer->texture->isYcbcr() ? s_pPipewireBuffer->texture : nullptr;