Too bright (sRGB) for some reason? EXT_sRGB_write_control not available on D3D11

474 views
Skip to first unread message

Mick Pearson

unread,
Aug 21, 2021, 7:53:04 PM8/21/21
to angleproject
I have one app that seems to be locked into "linear to sRGB" present model. I have another that doesn't do this. I can't understand why. Maybe just the mere fact of using glBindFrameBuffer, I don't know. For OPENGL glDisable(FRAMEBUFFER_SRGB_EXT) fixes this app. D3D11 doesn't have this "extension". It's extremely frustrating when ANGLE backends support different extensions that aren't exotic and should be easy to implement.

I'm not even sure how the sRGB present system works. I assume it pretend the back-buffer is sRGB bytes but really it's allocated as a floating-point buffer. Because if not you can't store linear data in bytes in any kind of acceptable quality. OPENGL only reports and 8-bit per component back buffer, whereas D3D11 wants to default to 10-bit back-buffer. I don't know if linear could be stored in 10-bits acceptable, I assume it's more like 16-bit floating point. But either way how is an app that doesn't write linear support this? Why would ANGLE be forcing this mode in the first place other than the ES spec says this is implementation dependent?

Does this mean I have to add linear conversion to all my shaders because ANGLE prefers this mode and doesn't implement this extension? If so why does my other app not do this? It can't be both.

I do use GL_SRGB8_ALPHA8 buffers for better filtering but setting those to GL_RGB8 doesn't change this behavior. There seems to sometimes be confusion that an app that uses this format also wants linear present, but that doesn't seem to be the case here.

I post this here because I'm not sure it's a cut and dry bug fit for the bug tracker in my current understanding. But adding sRGB->linear code in the shaders just to have it undone by the present step seems like a hack if there ever was one. TBH this is about issue #30 or #50 since I started with ANGLE. About 5 to 10 issues have been pretty strong showstoppers. I hope ANGLE is on track to become more well-rounded in the future. It's hard to see what its priorities are if not these kinds of things.

Mick Pearson

unread,
Aug 21, 2021, 7:59:36 PM8/21/21
to angleproject
P.S. I forgot to mention, I also had this problem with Microsoft's D3D9on12 project. I was reminded the other day my GPU doesn't advertise the D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION capability bit that's required for D3DPRESENT_LINEAR_CONTENT to work in D3D9 (it fails on my system) so I wonder if potentially it could be the case that this super bright effect comes from this not being implemented by the hardware. The D3D9on12 project tried my app and didn't see it bright, and thought they'd already fixed it probably, but I wonder now if this could be related, and that they didn't see it because their GPU was different/implements this capability.

Mark Callow

unread,
Aug 22, 2021, 4:04:01 AM8/22/21
to ho...@swordofmoonlight.net, angleproject
I can’t speak to ANGLE or D3D but I can tell you what OpenGL and OpenGL ES do. As ANGLE is an OpenGL ES implementation it should be doing much the same.

The only difference between an sRGB framebuffer and a linear framebuffer is that for the former the hardware encodes the fragment shader outputs to sRGB. Period. All of the implementations I have looked at pass “linear" frame buffers to the screen basically unchanged - the specs don’t mandate any behavior - and since the screen is sRGB that is what the buffer really is. Some implementations may have external (to OpenGL {,ES}) mechanisms for encoding the framebuffer to sRGB on the way to the screen, such as gamma “correction”. In my own apps I always choose sRGB framebuffers which are available everywhere except WebGL. WebGL's “linear” framebuffer is passed more or less directly to the sRGB screen so the only way to get correct color in WebGL is to have the fragment shader encode its outputs to sRGB. Efforts to improve this are under discussion at  https://github.com/WICG/canvas-color-space.

The FRAMEBUFFER_SRGB_EXT enable/disable is only available in OpenGL. The implementers of OpenGL ES felt the extra hardware complexity would outweigh the benefits it would bring so you have to create the default frame buffer as sRGB or attach an sRGB texture to an FBO to get sRGB rendering. You cannot change on the fly.

You are right about the quality issues with 8-bit linear buffers. If an 8-bit linear buffer is correctly transformed for the display, you will likely see terrible banding. 10-bits is an improvement but you need 14-bits to remove all discernible banding between levels. When image data is encoded to sRGB, 8-bits is sufficient. See Poynton’s Rehabilitation of gamma. Highly recommended reading if you aren’t already familiar with the nitty-gritty of linear vs. sRGB and transfer functions in general.

Regards

    -Mark

signature.asc

Mark Callow

unread,
Aug 22, 2021, 9:52:02 AM8/22/21
to khr...@callow.im, ho...@swordofmoonlight.net, angleproject


On Aug 22, 2021, at 5:03 PM, Mark Callow <khr...@callow.im> wrote:

The only difference between an sRGB framebuffer and a linear framebuffer is that for the former the hardware encodes the fragment shader outputs to sRGB. Period.

There is one more difference. Oops! When blending with the content of an sRGB framebuffer, framebuffer pixels are decoded to linear before blending with the fragment shader output. The result is then encoded back to sRGB and written to the framebuffer. If, like in WebGL, the implementation has no sRGB framebuffer support and the framebuffer is connected as is to an sRGB display, it is impossible to do correct blending unless the implementation has a framebuffer fetch extension so you can do blending in the fragment shader.

Regards

    -Mark

signature.asc

Mick Pearson

unread,
Aug 23, 2021, 9:08:35 PM8/23/21
to angleproject
For the record (if it's not clear in my OP) by sRGB I mean GL_RGBA8 (or GL_SRGB_APHA8) and it seems like you (Mark) are saying ES only works in this mode (8 bits per component, maybe 10) although my reading of it (somewhere) is it's implementation dependent and you have to use glGetFramebufferParameter to find out what to write out of your shaders (like I guess to support mobile devices that work with linear buffers.)

What's confusing (and I don't know the mechanism) is I have two apps, both of which I want to work in the old before sRGB/linear mode (i.e. no linear transforms at all, just like in the 90s) except one app (due to OpenGL calls after creating the context) produces a bright screen (related to sRGB transforms since it can be turned off the OPENGL backend) and one app does not. So why is ANLGE is making this call? And why does the D3D11 backend have no control over it, that's the problem I'm having. On ANGLE using the EGL surface encoding attributes makes no difference in my experiments, even though I've not traced it into ANGLE's code, it just seems like they're not implemented. 

Mark Callow

unread,
Aug 24, 2021, 12:06:09 AM8/24/21
to ho...@swordofmoonlight.net, angleproject

On Aug 24, 2021, at 10:08 AM, Mick Pearson <ho...@swordofmoonlight.net> wrote:

For the record (if it's not clear in my OP) by sRGB I mean GL_RGBA8 (or GL_SRGB_APHA8) and it seems like you (Mark) are saying ES only works in this mode (8 bits per component, maybe 10) although my reading of it (somewhere) is it's implementation dependent and you have to use glGetFramebufferParameter to find out what to write out of your shaders (like I guess to support mobile devices that work with linear buffers.)

GL_SRGB_ALPHA8 has been required to be sRGB-renderable in both OpenGL and OpenGL ES since OpenGL {,ES} 3.0. If you are not using that format then you will have to use the GL_COLOR_ENCODING param to glGetFramebufferAttachmentParameteriv to determine if it is sRGB renderable. 


What's confusing (and I don't know the mechanism) is I have two apps, both of which I want to work in the old before sRGB/linear mode (i.e. no linear transforms at all, just like in the 90s) except one app (due to OpenGL calls after creating the context) produces a bright screen (related to sRGB transforms since it can be turned off the OPENGL backend) and one app does not. So why is ANLGE is making this call? And why does the D3D11 backend have no control over it, that's the problem I'm having. On ANGLE using the EGL surface encoding attributes makes no difference in my experiments, even though I've not traced it into ANGLE's code, it just seems like they're not implemented. 


Do you know what OpenGL calls?

If you are are attaching a GL_SRGB8_ALPHA8 texture to your framebuffer, or creating a default framebuffer in that format, then, per the ES spec, you will (absent implementation bugs) get sRGB rendering. Are both your apps using GL_RGB8_ALPHA8?

Regards

    -Mark

signature.asc

Mick Pearson

unread,
Aug 24, 2021, 7:24:33 AM8/24/21
to angleproject
One app has framebuffer attachments that are GL_RGB8_ALPHA8, but changing them to GL_RGBA8 doesn't solve the problem. Both are 3.1 contexts.

Are you certain we're using the same terminology? There's a lot of confusion because "sRGB" is just a standard for 24bpp color[1] that previously had no hard fast criteria in terms of its color gamut, so the word "sRGB" just means legacy color model really. Whereas "linear" color means using physical units (wave lengths.) However for unrelated reasons I guess "SRGB" constants are used to tell OpenGL/D3D that they should do color blending in linear space and convert back to sRGB space. The only reason that's so is prior to sRGB there was no formula for this conversion to happen (or hardware for it) otherwise it should have done that too.

[1] Usually it's associated with 24bpp color. Technically I suppose you can represent sRGB with any figure. It's a nonlinear (not "linear") value space. Therefor I see no reason why an SRGB8_ALPHA8 buffer would convert to linear space, which is why I assume it's bright, since it's using the wrong color model than the one outputted by the shader. 

RE "Do you know what OpenGL calls?" No, this is what I mean by "(and I don't know the mechanism)". All I can determine is it's not triggered by SRGB8_ALPHA8 or the version of the app. My best guess is ANGLE is assuming linear output for all glFramebufferTexture surfaces. Although another quirk is it does have a floating-point attachment as a second (multi-render-target) attachment. If OpenGL was as flexible as D3D on the "default buffer" I could easily test out different theories. My system OpenGL driver doesn't behave this way, but it's not OpenGL ES since it's a desktop environment, so I don't know if there is a legit cause for this behavior but it's very frustrating and counterintuitive. Writing a "linear" app is not convenient, nothing in the real world uses linear units. So it doesn't seem right it would be the default behavior to do this, but I'm not familiar with ES enough to know for certain. I know when I wrote WebGL against ES I certainly didn't output linear/physical units. I have written apps that can.

Mick Pearson

unread,
Aug 24, 2021, 7:39:50 AM8/24/21
to angleproject
EDITED: Of course I meant to say " GL_SRGB8_ALPHA8" but ALSO I don't think anyone is reading this but I still to add, why linear doesn't seem right to me is 1) you have to do a hefty function to convert it, and 2) we did fine without it for decades, and 3) it looks horrible on low-poly models, which is why 3D games did fine without it for so long, since you really need very dense polygons to do lighting calculations with linear units (IRL lighting is very harsh compared to working in sRGB units.)

Mick Pearson

unread,
Aug 24, 2021, 9:57:46 AM8/24/21
to angleproject
Thank you Mark. I feel like this topic has ran too long. From what I understand the "write_control" extension would be needed to NOT do the conversion automatically, however I tested GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING and it does return GL_LINEAR with GL_RGBA8, so I think there has to be a bug here too. (It also returns GL_SRGB when using sRGB internal format.) (I really don't understand why ES would remove control over this since it takes a lot of computation to do the conversion to linear space just to have it forcibly/automatically converted back to sRGB space.)

[I think OpenGL conflates "LINEAR" with no transform and I'm not sure that's how D3D (9?) understands linear. I think sRGB is already so confusing that to make "linear" confusing in addition to it is a bad design on top of a bad design. "Linear came later to graphics" and means use physical wavelength based figures that can be pumped into gamma correction codes, so it doesn't mean "neutral" but I think that's how OpenGL uses it too because all non-sRGB happen to also be linear/neutral, but I would say it should just say this paradigm doesn't apply to those, and it also makes a bad design in tying "sRGB" to a format, which D3D avoids by making sRGB a sampler state. OpenGL always seems to make the unnatural design decision that turns out to be have actual bad consequences on top of being quirky, no foresight or something, no ability to run the simulation in the committee's head I don't know, but I shouldn't editorialize.]
Reply all
Reply to author
Forward
0 new messages