gfx-rs / wgpu

A cross-platform, safe, pure-Rust graphics API.
https://wgpu.rs
Apache License 2.0
12.14k stars 887 forks source link

Vulkan color space is different when rendering to swap chain (sRGB) #4842

Open valaphee opened 9 months ago

valaphee commented 9 months ago

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?): Screenshot 2023-12-14 163957 Vulkan (AMD RX 6900 XT, Win 11, No HDR): Screenshot 2023-12-14 164014 Vulkan (Nvidia RTX 3070, Win 10, No HDR): Unbenannt

wgpu-info.json

Platform

valaphee commented 9 months ago

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.

teoxoy commented 9 months ago

@cwfitzgerald any ideas why this is happening?

cwfitzgerald commented 9 months ago

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.

valaphee commented 9 months ago

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?

MarijnS95 commented 9 months ago

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:

MarijnS95 commented 9 months ago

On Linux Vulkan works perfectly fine.

What compositor do you use on Linux that has HDR support?

valaphee commented 8 months ago

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.

Elabajaba commented 8 months ago

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?)

valaphee commented 8 months ago

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.

valaphee commented 7 months ago

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.

valaphee commented 5 months ago

~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~

valaphee commented 4 months ago
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