Mesen-S icon indicating copy to clipboard operation
Mesen-S copied to clipboard

Should Use SRGB backbuffer and textures for more accurate interpolation

Open Dwedit opened this issue 4 years ago • 3 comments

Mesen-S is currently using DXGI_FORMAT_B8G8R8A8_UNORM as its format for Textures and Backbuffer, and it should be changed to DXGI_FORMAT_B8G8R8A8_UNORM_SRGB.

The common SRGB color space is not a linear color space. It is a pre-compressed color space where one half of White 255,255,255 is actually 187,187,187, and not 128,128,128. Compare a checkerboard of Black and White pixels, and you will see that 187,187,187 matches the intensity better. Article about that here: https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/ Another article: http://www.ericbrasseur.org/gamma.html

Direct3D 11 has built in support for SRGB correction. If you declare your Texture as SRGB, then it automatically performs an SRGB to Linear RGB correction when sampling from the texture. So reading your 187,187,187 pixel will result in 0.5, 0.5, 0.5 values being used in the math. And if you create the render target or backbuffer as SRGB, it performs a Linear RGB to SRGB correction back when writing to the destination. So writing 0.5, 0.5, 0.5 will write the 187,187,187 pixel. This applies even in the simple case where you just draw one quad with Linear filtering. This has no effect on the 8-bit RGB values you are putting into the texture. They are the same numbers as before.

As a side effect, thin white lines on a black background will look much brighter after being scaled this way than the old way (misinterpreting SRGB as a linear color space), so it takes a little time to get used to it if you have been doing things the wrong way for so long.

Dwedit avatar Oct 27 '19 20:10 Dwedit

Well, I learned something today, thanks for the info!

I might not have understood every single detail of this, but from what I'm observing, in this specific case it only has an impact on scaling when bilinear interpolation is turned on? Or am I missing something?

The old non-sRGB format definitely makes the black outlines on sprites and the like stand out a lot more than in the sRGB format when upscaling with bilinear interpolation.

Seems like the SDL2 backend for Linux has the non-sRGB behavior, too, but looks like I might be out of luck for that one since you're apparently meant to set SDL_GL_FRAMEBUFFER_SRGB_CAPABLE before creating the window, but SDL2 isn't creating the window, Mono is, so it doesn't look like I have any way of properly enabling it.

Also, I imagine all of this would apply to Mesen just as much as Mesen-S, too?

SourMesen avatar Oct 27 '19 23:10 SourMesen

It would also affect what values the pixel shaders would see for the colors sampled from the textures, so shaders may make different decisions.

On OpenGL, you the extensions EXT_texture_sRGB for reading from the texture, and EXT_framebuffer_sRGB for writing to the buffer, and this stuff is handled within SDL.

I don't have all the code in front of me to check the Linux SDL version, but I do know that you can use the ILSpy .NET decompiler to read the code inside .NET .DLL files.

Dwedit avatar Oct 28 '19 00:10 Dwedit

Updated the code a couple of days ago to use the SRGB formats on Windows. On Linux, the problem is that Mono creates the window (a winforms control in this case) used for rendering, and then SDL attaches to it and enables OpenGL rendering for it. From what I'm reading, it looks like SDL is expecting the SRGB flag to be set before calling SDL_CreateWindow, but I'm calling SDL_CreateWindowFrom, which seems like it ignored the flags from the little bit of testing I did.

SDL in general hasn't been too kind to the whole Mono+SDL combination in the past, either, so I wouldn't be too surprised if this is part of the problem, too. I'll have to test again and see if I did something wrong.

SourMesen avatar Nov 02 '19 02:11 SourMesen