Skip to content

renderer: implement native sRGB support #1687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

illwieckz
Copy link
Member

@illwieckz illwieckz commented Jun 25, 2025

Implement native sRGB support.

Obsoletes #1543:

It includes the commit from #1686:

This is based on an even older branch from 2019 as I initially tested native sRGB support before attempting to implement it in GLSL.

I rebased on current master and merged improvements from the srgb-naive branch as it implemented more corner cases than this one.

It still suffers from the problems I faced in 2019: 2D colors are wrong. I need help on that part.


Status:

color source conversion implemented when is converted how is converted
color map ✅️ sampling metadata
diffuse map ✅️ sampling metadata
glow map ✅️ sampling metadata
specular map ✅️ sampling metadata
skybox ✅️ sampling metadata
light map ✅️ sampling metadata
light grid ✅️ loading conversion
map tris vertex color ✅️ loading conversion
map patch vertex color ✅️ loading conversion
expression_t ✅️ uploading conversion
CGEN_CONST ✅️ uploading conversion
CGEN_WAVEFORM ✅️ uploading conversion
CGEN_ENTITY ✅️ uploading conversion
CGEN_ONE_MINUS_ENTITY ✅️ uploading conversion
polyVert_t::modulate ✅️ uploading conversion

@illwieckz illwieckz added A-Renderer T-Feature-Request Proposed new feature labels Jun 25, 2025
@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch 2 times, most recently from c914db4 to ce0a6e3 Compare June 25, 2025 07:46
@illwieckz
Copy link
Member Author

illwieckz commented Jun 25, 2025

Hmmm… I don't know where to write the line glEnable( GL_FRAMEBUFFER_SRGB );, should it be called at the beginning of every frame?

@VReaperV
Copy link
Contributor

Hmmm… I don't know where to write the line glEnable( GL_FRAMEBUFFER_SRGB );, should it be called at the beginning of every frame?.

You only really need to call it once, it's global state and will affect all sRGB framebuffers.

@illwieckz
Copy link
Member Author

Which framebuffer should be sRGB there?

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch from ce0a6e3 to 387cac8 Compare June 30, 2025 17:41
@illwieckz
Copy link
Member Author

As a matter of fact, I have zero confidence this implementation is correct, unlike the GLSL alternative implementation.
I expect this to be wrong because I lack some understanding of how sRGB stuff are done the OpenGL way.

Said otherway, I know how to implement the whole computation myself in GLSL, but I don't know how to configure OpenGL to let it do the computation by itself without me writing the explicit computation.

The GLSL implementation is a math algorithm, the OpenGL implementation is a puzzle.

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch from 387cac8 to feee7ff Compare July 6, 2025 00:45
@illwieckz
Copy link
Member Author

@VReaperV what is expected if I, for example, set upload an image with GL_SRGB format instead of GL_RGB but do glDisable( GL_FRAMEBUFFER_SRGB )?

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch 2 times, most recently from 86e853b to b2f3100 Compare July 7, 2025 06:19
@illwieckz
Copy link
Member Author

what is expected if I, for example, set upload an image with GL_SRGB format instead of GL_RGB but do glDisable( GL_FRAMEBUFFER_SRGB )?

The question is now likely irrelevant.

@illwieckz
Copy link
Member Author

illwieckz commented Jul 7, 2025

It now works with both maps built with and without the -sRGB q3map2 compiler option.

But you have to disable the tone mapper when running maps compiled with -sRGB, otherwise that looks very surreal.

I don't know anything about the tone mapper so I don't know if something is wrong.

Right now the camera post-process GLSL shader doesn't run in linear space because colorgrades were done for the non-linear space.

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch 2 times, most recently from 8702bec to b2a9fba Compare July 7, 2025 06:43
@illwieckz
Copy link
Member Author

The 2D rendering is done like before, as @slipher suggested:

We should probably stick with naive blending for the 2D rendering since that's what HTML/CSS do.
-- Unvanquished/Unvanquished#3390 (comment)

Actually it makes many things far easier, especially it doesn't require to know how the map should be rendered to render the UI, and it means the UI isn't affected by what's done there.

@slipher
Copy link
Member

slipher commented Jul 8, 2025

Wait, why would we even want GL_FRAMEBUFFER_SRGB? I don't think it can do anything useful for us. Apparently it is only useful for converting from colors linear to sRGB at a final stage of rendering, as described here. In principle, this is something we need to do at the point of the cameraEffects shader. But our current color grade and tonemap operations are designed for sRGB inputs. So it would work a lot more smoothly if we convert the colors from from linear space ourselves in cameraEffects before feeding them into color grades and tonemapping.

I wonder why GL_FRAMEBUFFER_SRGB would have any effect in the current version of the code though. Supposedly you have to create the framebuffer with an SRGB format for it to have an effect but the code does not appear to do that.

@illwieckz
Copy link
Member Author

illwieckz commented Jul 8, 2025

What I understood (but maybe I'm wrong), is that once GL_FRAMEBUFFER_SRGB is enabled, any subsequent framebuffer operation will assume any input and output is in sRGB and will convert them before and after processing (and also converting framebuffers back and forth when blending). I'm not saying this is truth, but I read in various places something that seemed to mean that, and it looks like doing things that way makes it work (I also read things that meant totally different things, but that seemed wrong).

Maybe we also need to set sRGB on framebuffers, maybe that's required for blending. I don't know, everyone is saying something different on the Internet.

@illwieckz
Copy link
Member Author

Also, something I've read (but I may be wrong) is that sRGB variants only exist for 8-bit formats, and some of our framebuffers are 16-bit… I don't know what to do with that information.

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch 2 times, most recently from 5383610 to 4e28980 Compare July 8, 2025 04:24
@slipher
Copy link
Member

slipher commented Jul 8, 2025

I found various people complaining of buggy handling of the framebuffer SRGB stuff. Like Nvidia applies it even when the framebuffer was not created as sRGB, or that Intel doesn't do it at all... another reason not to use it.

Some more random considerations:

  • Vertex colors of models are another thing that theoretically probably needs to be converted. Not sure if we actually have any models that use those.
  • Vertex colors of (3D) polygons too? OTOH we could just say that is the responsibility of the caller to use a linear space.
  • I guess the 2D vs. 3D shader registration flag stuff will become important now (since you only want sRGB-to-linear conversion when reading the latter) and probably uncover some bugs.

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch from 4e28980 to acceb04 Compare July 8, 2025 05:09
@illwieckz
Copy link
Member Author

Hmm, comparing a Xonotic map like afterslime on Dæmon and Darkplaces reveals there is still something wrong.

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch 2 times, most recently from 8c6565a to 772a065 Compare July 13, 2025 06:03
@illwieckz
Copy link
Member Author

So unless I'm wrong it looks like CGEN_ENTITY and CGEN_ONE_MINUS_ENTITY may not be implemented to begin with, I only see the worldspawn having a shaderRGBA color, and it is set to white.

@illwieckz
Copy link
Member Author

illwieckz commented Jul 13, 2025

I implemented the conversion of polyVert_t::modulate.

@illwieckz
Copy link
Member Author

So unless I'm wrong it looks like CGEN_ENTITY and CGEN_ONE_MINUS_ENTITY may not be implemented to begin with, I only see the worldspawn having a shaderRGBA color, and it is set to white.

Ah I missed that answer:

For example CG_DrawSphere and CG_RenderParticle.

It is implemented in the cgame…

@illwieckz
Copy link
Member Author

illwieckz commented Jul 13, 2025

OK so e.shaderRGBA is now properly taken care of, so CGEN_ENTITY and CGEN_ONE_MINUS_ENTITY are now doing the conversions.

I added a table in the first post to check the status.

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch 4 times, most recently from bf930dd to 22ffeb5 Compare July 13, 2025 18:34
@@ -256,6 +281,22 @@ class BasicColor
data_[ 3 ] = v;
}

CONSTEXPR_FUNCTION_RELAXED component_type ConvertFromSRGB( component_type v, bool accurate = true ) NOEXCEPT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be static

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I just need to a static keyword before the return type?

Do I need to do the same on the next ConvertFromSRGB() returning BasicColor?

Is the addition of static there to help the compiler to optimize out the accurate selector and drop the alternate code?

Also what to do with CONSTEXPR_FUNCTION_RELAXED or CONSTEXPR_FUNCTION, which one to use? I have no idea what is the difference, I just copy-pasted that from previous lines.

Copy link
Member Author

@illwieckz illwieckz Jul 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I need to do the same on the next ConvertFromSRGB() returning BasicColor?

I tested this one, this one I cannot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static because you don't need a Color object to use the function. static means there is no this argument.

Also what to do with CONSTEXPR_FUNCTION_RELAXED or CONSTEXPR_FUNCTION, which one to use? I have no idea what is the difference, I just copy-pasted that from previous lines.

Just use constexpr, those are workarounds for the compilers of 10 years ago. Also you can add const since for functions constexpr does not imply const.

@@ -728,7 +734,12 @@ static void Tess_SurfacePolychain( srfPoly_t *p )
normals[i], qtangents);

VectorCopy(p->verts[i].xyz, tess.verts[tess.numVertexes + i].xyz);
tess.verts[tess.numVertexes + i].color = Color::Adapt(p->verts[i].modulate);

Color::Color color = Color::Adapt( p->verts[ i ].modulate );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Color::Color is a float color while the source and destination are bytes, so you could avoid a round trip by using Color32Bit. (The API ought to be changed so it is not so easy to accidentally do that.) Also it is pointless to clamp with byte colors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also done in two similar places in tr_backend.cpp.

Tess_AddQuadStamp( backEnd.currentEntity->e.origin, left, up,
backEnd.currentEntity->e.shaderRGBA );
Color::Color color = backEnd.currentEntity->e.shaderRGBA;
color.Clamp();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useless clamping of byte colors

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch from 32f7d0b to 1c181ef Compare July 14, 2025 06:28
Revamp `R_LoadEntities()`:

- Use `std::string` as much as possible.
- Parse `_q3map2_cmdline` using Cmd::Args, so options are read in expected order.
- Let `deluxeMapping` override `_q3map2_cmdline`.
- Check for deluxe mapping being explicitely disabled.
- Check for the deluxe map being in model space.
- Make `vertexremapshader` do something when vertex lighting is enabled.
@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch 8 times, most recently from 47bb6dd to 27a80b0 Compare July 14, 2025 08:43
@illwieckz
Copy link
Member Author

What the heck is that (macOS CI)?

In file included from /Users/runner/work/1/s/src/common/Common.h:48:
/Users/runner/work/1/s/src/common/Color.h:291:29: error: 'constexpr' non-static member function
 will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior [-Werror,-Wconstexpr-not-const]
        const constexpr BasicColor ConvertFromSRGB( bool accurate = true ) NOEXCEPT
                                   ^
                                                                           const
/Users/runner/work/1/s/src/common/Color.h:286:9: error: variable declaration in a constexpr function
 is a C++14 extension [-Werror,-Wc++14-extensions]
                float f = float( v ) / float( component_max );
                      ^
/Users/runner/work/1/s/src/common/Color.h:287:3: error: use of this statement in a constexpr function
 is a C++14 extension [-Werror,-Wc++14-extensions]
                f = convertFromSRGB( f, accurate );
                ^

@illwieckz illwieckz force-pushed the illwieckz/srgb-native branch from da16c59 to 45834ab Compare July 15, 2025 08:30
@illwieckz
Copy link
Member Author

illwieckz commented Jul 15, 2025

I added on the testing server the maps citadel gloom2 utcs veddak wrecktify rebuilt for this branch.

@illwieckz
Copy link
Member Author

Some gameplay textures are loaded as linear when they should be sRGB, like the flames.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants