move gamma correction to UI post processing

This commit is contained in:
jacob 2025-10-23 04:19:56 -05:00
parent eec8b754de
commit aec35bca37
8 changed files with 89 additions and 89 deletions

View File

@ -53,7 +53,7 @@ JobDef(F_Load, sig, _)
GPU_ResourceDesc desc = ZI;
desc.kind = GPU_ResourceKind_Texture2D;
desc.flags = GPU_ResourceFlag_None;
desc.texture.format = GPU_Format_R8G8B8A8_Unorm;
desc.texture.format = GPU_Format_R8G8B8A8_Unorm_Srgb;
desc.texture.size = VEC3I32(decoded.image_width, decoded.image_height, 1);
texture = GPU_AcquireResource(desc);
@ -269,57 +269,4 @@ F_Run F_RunFromString(Arena *arena, F_Font *font, String str)
}
return result;
#if 0
F_Run result = ZI;
result.rects = PushDry(arena, F_RunRect);
f32 left = 0;
f32 right = 0;
f32 width = 0;
f32 top = 0;
f32 bottom = 0;
f32 baseline_cursor = 0;
f32 baseline_depth = 0;
for (CodepointIter it = InitCodepointIter(str); NextCodepoint(&it);)
{
u32 codepoint = it.codepoint;
u16 index = font->lookup[codepoint];
F_Glyph glyph = font->glyphs[index];
F_RunRect *rect = PushStruct(arena, F_RunRect);
++result.count;
rect->atlas_p0 = glyph.atlas_p0;
rect->atlas_p1 = glyph.atlas_p1;
Vec2I32 size = SubVec2I32(glyph.atlas_p1, glyph.atlas_p0);
f32 offset_x = glyph.baseline_offset.x;
f32 offset_y = glyph.baseline_offset.y;
rect->pos.x = baseline_cursor + offset_x;
/* NOTE: Actual Y position gets calculated in second pass after baseline depth is determined */
rect->pos.y = offset_y;
baseline_depth = MaxF32(baseline_depth, -offset_y);
left = MinF32(left, rect->pos.x);
top = MinF32(top, offset_y);
right = MaxF32(right, rect->pos.x + size.x);
bottom = MaxF32(bottom, rect->pos.y + size.y);
baseline_cursor += glyph.advance;
}
/* Adjust rect Y positions so they're relative to top of run */
for (u32 i = 0; i < result.count; ++i)
{
F_RunRect *rect = &result.rects[i];
rect->pos.y += baseline_depth;
}
result.size = VEC2(right - left, bottom - top);
result.baseline_depth = baseline_depth;
return result;
#endif
}

View File

@ -68,7 +68,7 @@ void PushGameUiStyle(void)
// UI_Push(FontSize, 24);
UI_Push(FontSize, 12);
// UI_Push(TextPadding, SzEm);
UI_Push(TextPadding, 3);
// UI_Push(Border, 1);
// UI_Push(BorderColor, Rgba32F(1, 0, 1, 0.4));
}
@ -2028,9 +2028,6 @@ void UpdateUser(P_Window *window)
UI_Push(Height, UI_TextSize(0));
UI_Push(BackgroundColor, Rgba32F(0.3, 0.6, 0.3, 0.5));
UI_BuildLabelF("ytest string");
// UI_BuildSeparator();
UI_BuildLabelF("blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved));
// UI_BuildSeparator();
@ -2432,7 +2429,7 @@ void UpdateUser(P_Window *window)
GPU_ProfN(cl, Lit("Shade pass"));
Vec3I32 noise_size = GPU_GetTextureSize3D(GPU_GetCommonNoise());
u32 shade_flags = ShadeFlag_None;
u32 shade_flags = ShadeFlag_None | ShadeFlag_ToneMap;
if (effects_disabled)
{
shade_flags |= ShadeFlag_DisableEffects;
@ -2442,6 +2439,7 @@ void UpdateUser(P_Window *window)
sig.flags = shade_flags;
sig.tex_width = g->render_size.x;
sig.tex_height = g->render_size.y;
sig.exposure = 2.0;
sig.frame_seed = VEC4U32((u32)(RandU64FromState(&g->frame_rand) & 0xFFFFFFFF),
(u32)(RandU64FromState(&g->frame_rand) & 0xFFFFFFFF),
(u32)(RandU64FromState(&g->frame_rand) & 0xFFFFFFFF),
@ -2486,7 +2484,7 @@ void UpdateUser(P_Window *window)
sig.sampler = GPU_SamplerStateRidFromResource(GPU_GetCommonPointSampler());
sig.src = GPU_Texture2DRidFromResource(g->shade_read);
sig.projection = blit_vp_matrix;
sig.flags = UiBlitFlag_ToneMap | UiBlitFlag_GammaCorrect;
// sig.flags = UiBlitFlag_ToneMap | UiBlitFlag_GammaCorrect;
sig.exposure = 2.0;
sig.gamma = (f32)2.2;
GPU_Rasterize(cl,

View File

@ -188,6 +188,8 @@ void CSDef(FloodCS, Semantic(Vec3U32, sv_dispatchthreadid))
#define LightMarches 16
#define LightEdgeFalloff 100
//- Lighting
f32 RandAngle(Vec2U32 pos, u32 ray_index)
{
ConstantBuffer<ShadeSig> sig = shade_sig;
@ -253,13 +255,21 @@ Vec3 ColorFromPos(Vec2U32 pos)
return result;
}
//- Tone mappign
/* ACES approximation by Krzysztof Narkowicz
* https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ */
Vec3 ToneMap(Vec3 v)
{
return saturate((v * (2.51f * v + 0.03f)) / (v * (2.43f * v + 0.59f) + 0.14f));
}
//- Compute shader
[numthreads(8, 8, 1)]
void CSDef(ShadeCS, Semantic(Vec3U32, sv_dispatchthreadid))
{
ConstantBuffer<ShadeSig> sig = shade_sig;
Vec2U32 id = sv_dispatchthreadid.xy;
if (id.x < sig.tex_width && id.y < sig.tex_height)
{
@ -277,6 +287,14 @@ void CSDef(ShadeCS, Semantic(Vec3U32, sv_dispatchthreadid))
color.rgb *= ColorFromPos(id);
}
/* Apply tone mapping */
if (sig.flags & ShadeFlag_ToneMap)
{
/* TODO: Dynamic exposure based on average scene luminance */
color.rgb *= sig.exposure;
color.rgb = ToneMap(color.rgb);
}
/* Apply temporal accumulation */
f32 hysterisis = 0;
// hysterisis = 0.2;
@ -302,15 +320,6 @@ Struct(UiBlitPS_Output)
Semantic(Vec4, sv_target);
};
//- ACES
/* ACES approximation by Krzysztof Narkowicz
* https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ */
Vec3 ToneMap(Vec3 v)
{
return saturate((v * (2.51f * v + 0.03f)) / (v * (2.43f * v + 0.59f) + 0.14f));
}
//- Vertex shader
UiBlitPS_Input VSDef(UiBlitVS, Semantic(u32, sv_vertexid))
@ -344,12 +353,6 @@ UiBlitPS_Output PSDef(UiBlitPS, UiBlitPS_Input input)
color.rgb = ToneMap(color.rgb);
}
/* Apply gamma correction */
if (sig.flags & UiBlitFlag_GammaCorrect)
{
color = pow(abs(color), 1/sig.gamma);
}
result.sv_target = color;
return result;
}

View File

@ -75,6 +75,7 @@ AssertRootConst(FloodSig, 8);
#define ShadeFlag_None (0 << 0)
#define ShadeFlag_DisableEffects (1 << 0)
#define ShadeFlag_ToneMap (1 << 1)
Struct(ShadeSig)
{
@ -82,9 +83,9 @@ Struct(ShadeSig)
Vec4U32 frame_seed; /* 04 consts */
/* ----------------------------------------------------- */
u32 flags; /* 01 consts */
u32 _pad0; /* 01 consts (padding) */
u32 tex_width; /* 01 consts */
u32 tex_height; /* 01 consts */
f32 exposure; /* 01 consts */
/* ----------------------------------------------------- */
Vec2 camera_offset; /* 02 consts */
u32 frame_index; /* 01 consts */

View File

@ -380,7 +380,7 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
text_size = box->font->ascent + box->font->descent;
}
}
box->solved_dims[axis] = text_size + box->text_padding;
box->solved_dims[axis] = text_size + box->text_padding * 2;
}
}
}
@ -526,15 +526,9 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
f32 ascent = box->font->ascent;
f32 descent = box->font->descent;
f32 padding = box->text_padding;
f32 half_padding = padding * 0.5f;
Vec2 baseline = box->p0;
baseline = AddVec2(baseline, VEC2(half_padding, half_padding + ascent));
// offset.x += run.width / 2;
// offset.y += run.baseline_depth;
// offset.x += half_padding;
// offset.y += half_padding;
baseline = AddVec2(baseline, VEC2(padding, padding + ascent));
for (u64 i = 0; i < run.count; ++i)
{
@ -546,7 +540,6 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
{
UI_RectInstance *rect = PushStruct(g->draw_rects_arena, UI_RectInstance);
rect->flags |= UI_RectFlag_DrawTexture;
rect->p0 = AddVec2(baseline, rr.baseline_start_offset);
rect->p1 = AddVec2(rect->p0, glyph_size);
rect->tex_uv0 = MulVec2Vec2(atlas_p0, inv_font_image_size);
@ -572,7 +565,7 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
{
GPU_ResourceDesc desc = ZI;
desc.kind = GPU_ResourceKind_Texture2D;
desc.flags = GPU_ResourceFlag_Renderable;
desc.flags = GPU_ResourceFlag_Renderable | GPU_ResourceFlag_Writable;
desc.texture.format = GPU_Format_R8G8B8A8_Unorm;
desc.texture.size = VEC3I32(render_viewport.size.x, render_viewport.size.y, 1);
g->render_target = GPU_AcquireResource(desc);
@ -596,8 +589,8 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
//- Rect pass
if (draw_rects_count > 0)
{
__profn("Rect pass");
GPU_ProfN(cl, Lit("Rect pass"));
__profn("UI rect pass");
GPU_ProfN(cl, Lit("UI rect pass"));
GPU_Viewport viewport = GPU_ViewportFromRect(render_viewport);
GPU_Scissor scissor = GPU_ScissorFromRect(render_viewport);
@ -616,6 +609,23 @@ GPU_Resource *UI_EndBuild(Rect render_viewport)
GPU_GetCommonQuadIndices(),
GPU_RasterizeMode_TriangleList);
}
//- Prep post pass
{
GPU_TransitionToWritable(cl, g->render_target);
}
//- Post pass
{
__profn("UI post");
GPU_ProfN(cl, Lit("UI post"));
Vec2I32 viewport_size = RoundVec2ToVec2I32(render_viewport.size);
UI_PostSig sig = ZI;
sig.tex_size = viewport_size;
sig.tex = GPU_RWTexture2DRidFromResource(g->render_target);
sig.gamma = 2.2f;
GPU_Compute(cl, &sig, UI_PostCS, (viewport_size.x + 7) / 8, (viewport_size.y + 7) / 8, 1);
}
}
g->render_fence_target = GPU_EndCommandList(cl);

View File

@ -18,6 +18,7 @@
//- Shaders
@VertexShader UI_RectVS
@PixelShader UI_RectPS
@ComputeShader UI_PostCS
//- Embeds
@EmbedDir UI_Resources ui_res

View File

@ -1,4 +1,5 @@
ConstantBuffer<UI_RectSig> UI_rect_sig : register (b0);
ConstantBuffer<UI_PostSig> UI_post_sig : register (b0);
////////////////////////////////////////////////////////////
//~ Rect
@ -85,3 +86,24 @@ UI_RectPS_Output PSDef(UI_RectPS, UI_RectPS_Input input)
output.sv_target0 = result;
return output;
}
////////////////////////////////////////////////////////////
//~ Post
[numthreads(8, 8, 1)]
void CSDef(UI_PostCS, Semantic(Vec3U32, sv_dispatchthreadid))
{
ConstantBuffer<UI_PostSig> sig = UI_post_sig;
Vec2U32 id = sv_dispatchthreadid.xy;
if (id.x < sig.tex_size.x && id.y < sig.tex_size.y)
{
SamplerState sampler = UniformSamplerFromRid(sig.sampler);
RWTexture2D<Vec4> tex = UniformResourceFromRid(sig.tex);
Vec4 pixel = tex[id];
/* Apply gamma correction */
pixel = pow(abs(pixel), 1/sig.gamma);
tex[id] = pixel;
}
}

View File

@ -29,3 +29,21 @@ Struct(UI_RectInstance)
Vec2 tex_uv1;
Texture2DRid tex;
};
////////////////////////////////////////////////////////////
//~ Post types
Struct(UI_PostSig)
{
/* ----------------------------------------------------- */
Vec2I32 tex_size; /* 02 consts */
RWTexture2DRid tex; /* 01 consts */
f32 gamma; /* 01 consts */
/* ----------------------------------------------------- */
SamplerStateRid sampler; /* 01 consts */
u32 _pad0; /* 01 consts (padding) */
u32 _pad1; /* 01 consts (padding) */
u32 _pad2; /* 01 consts (padding) */
/* ----------------------------------------------------- */
};
AssertRootConst(UI_PostSig, 8);