wgpu
wgpu copied to clipboard
Vulkan color space is different when rendering to swap chain (sRGB)
Description When comparing the cube and water example for different back-ends on window (screenshots even of Vulkan seems to be fine which is weird). The colors are noticeable different.
The colors on Vulkan are darker compared to the screenshots and GL, DX12.
Repro steps Run the cube or water example with WGPU_BACKEND=dx12 and with WGPU_BACKEND=vulkan
Expected vs observed behavior Darker colors compared to the baseline and other back-ends window output.
Extra materials
DX12/OpenGL (AMD RX 6900 XT, Win 10/11, No HDR), Vulkan/OpenGL (AMD RX 6900 XT, Linux, No HDR) (expected?):
Vulkan (AMD RX 6900 XT, Win 11, No HDR):
Vulkan (Nvidia RTX 3070, Win 10, No HDR):
Platform
Might be related to https://github.com/gfx-rs/wgpu/issues/1646 and https://github.com/gfx-rs/wgpu/issues/3449, but probably not. As the format seems correct and it also looks right in RenderDoc, but Vulkan seems to do some gamma correction on its own.
@cwfitzgerald any ideas why this is happening?
This looks like a tale of different primaries. When outputting SDR, what the output of compositing isn't sRGB. It's your monitor's color space.
What looks like it's happening is that DX12 is outputting sRGB color (by converting the sRGB into the monitor's color space), whereas vulkan is just yeeting the colors into the monitor and hoping for the best.
In short, I'm not sure there's much we can do about this.
Updated the images, I guess the color channel issue is not related to the linear/non-linear color space issue, but interestingly at least the color space is correct on Nvidia. (But I guess its most likely really a vendor issue)
On Linux Vulkan works perfectly fine.
What's the reason why DX12 is not the default on Windows?
What looks like it's happening is that DX12 is outputting sRGB color (by converting the sRGB into the monitor's color space), whereas vulkan is just yeeting the colors into the monitor and hoping for the best.
Theoretically both Vulkan and Dx12 should set/communicate to DWM what the color space of the output swapchain is, and DWM will appropriately convert it:
https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
Then it's up to the SDR->HDR settings in Windows to map the brightness, which is expected to behave the same.
(In our renderer, which does not utilize WGPU, and we disable automatic sRGB conversion by using _UNORM
textures, the color output in Vulkan and Dx12 is the same, on a 6800 XT)
Maybe useful to make sure that:
- Both backends use the same
UNORM
texture format; - Vulkan selects the
SRGB_NONLINEAR
colorspace; - Explicitly set the Dx12 color space to
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
.
On Linux Vulkan works perfectly fine.
What compositor do you use on Linux that has HDR support?
X11, and Wayland, on two different machines, one with an AMD iGPU and the other one had a Nvidia dGPU and an Intel iGPU, and both worked fine with both compositors, no HDR enabled (nor supported).
The surface descriptor is also quite similar for both platforms.
Windows 11 (23H2) AMD 6800xt (23.11.2 drivers) no hdr I get identical colours with both vulkan and dx12. (also the background colour on the cube example is blue for me by default, not green?)
Forget to mention, the default is majority blue, but I changed it to pure green to compare the color values of the screenshots.
Tested with latest commit, but that's odd, I'm using the latest 23.12.1 drivers, HDR is disabled, also tried different display options. (color depth 8/10 bpc, limited/full rgb)
Would be interesting if someone with an RX 6900XT could test.
Did some further testing, because I was wondering why wgpu doesn't use HDR by default, and found that the first supported format is used by default.
And when using Rgba16Float with Vulkan the image looks like the expected one, same with DX12, but with OpenGL the color space is wrong.
~Updated the issue because on Linux there is a similar problem involving OpenGL (non-es) where the ES variant results in the same image as Vulkan, but OpenGL (non-es) looks the same as Vulkan on Windows 11~
pub fn map_vk_surface_formats(sf: vk::SurfaceFormatKHR) -> Option<wgt::TextureFormat> {
use ash::vk::Format as F;
use wgt::TextureFormat as Tf;
// List we care about pulled from https://vulkan.gpuinfo.org/listsurfaceformats.php
Some(match sf.color_space {
vk::ColorSpaceKHR::SRGB_NONLINEAR => match sf.format {
F::B8G8R8A8_UNORM => Tf::Bgra8Unorm,
F::B8G8R8A8_SRGB => Tf::Bgra8UnormSrgb,
F::R8G8B8A8_SNORM => Tf::Rgba8Snorm,
F::R8G8B8A8_UNORM => Tf::Rgba8Unorm,
F::R8G8B8A8_SRGB => Tf::Rgba8UnormSrgb,
_ => return None,
},
vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT => match sf.format {
F::R16G16B16A16_SFLOAT => Tf::Rgba16Float,
F::R16G16B16A16_SNORM => Tf::Rgba16Snorm,
F::R16G16B16A16_UNORM => Tf::Rgba16Unorm,
F::A2B10G10R10_UNORM_PACK32 => Tf::Rgb10a2Unorm,
_ => return None,
},
_ => return None,
})
}
this is also not ideal, as A2B10G10R10_UNORM_PACK32 can be both, nonlinear and linear, and I guess this is also true for all other formats.
But this check is hardcoded
let color_space = if config.format == wgt::TextureFormat::Rgba16Float {
// Enable wide color gamut mode
// Vulkan swapchain for Android only supports DISPLAY_P3_NONLINEAR_EXT and EXTENDED_SRGB_LINEAR_EXT
vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT
} else {
vk::ColorSpaceKHR::SRGB_NONLINEAR
};
and doesn't match up with above