From aec35bca37c297a96b6fa5a2ff99ed4544fe7fcf Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 23 Oct 2025 04:19:56 -0500 Subject: [PATCH] move gamma correction to UI post processing --- src/font/font.c | 55 +--------------------------------------------- src/pp/pp.c | 10 ++++----- src/pp/pp_draw.gpu | 35 +++++++++++++++-------------- src/pp/pp_draw.h | 3 ++- src/ui/ui.c | 34 ++++++++++++++++++---------- src/ui/ui.lay | 1 + src/ui/ui_draw.gpu | 22 +++++++++++++++++++ src/ui/ui_draw.h | 18 +++++++++++++++ 8 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/font/font.c b/src/font/font.c index 5a28f52c..aea78658 100644 --- a/src/font/font.c +++ b/src/font/font.c @@ -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 } diff --git a/src/pp/pp.c b/src/pp/pp.c index 311bdb69..50db4eca 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -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, diff --git a/src/pp/pp_draw.gpu b/src/pp/pp_draw.gpu index 6d7b77fe..a9ccec80 100644 --- a/src/pp/pp_draw.gpu +++ b/src/pp/pp_draw.gpu @@ -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 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 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; } diff --git a/src/pp/pp_draw.h b/src/pp/pp_draw.h index 1a922f94..0a051273 100644 --- a/src/pp/pp_draw.h +++ b/src/pp/pp_draw.h @@ -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 */ diff --git a/src/ui/ui.c b/src/ui/ui.c index 48ec7d7e..5538b31e 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -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); diff --git a/src/ui/ui.lay b/src/ui/ui.lay index 5494a069..b05699f2 100644 --- a/src/ui/ui.lay +++ b/src/ui/ui.lay @@ -18,6 +18,7 @@ //- Shaders @VertexShader UI_RectVS @PixelShader UI_RectPS +@ComputeShader UI_PostCS //- Embeds @EmbedDir UI_Resources ui_res diff --git a/src/ui/ui_draw.gpu b/src/ui/ui_draw.gpu index 945063a3..973fbd38 100644 --- a/src/ui/ui_draw.gpu +++ b/src/ui/ui_draw.gpu @@ -1,4 +1,5 @@ ConstantBuffer UI_rect_sig : register (b0); +ConstantBuffer 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 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 tex = UniformResourceFromRid(sig.tex); + Vec4 pixel = tex[id]; + + /* Apply gamma correction */ + pixel = pow(abs(pixel), 1/sig.gamma); + + tex[id] = pixel; + } +} diff --git a/src/ui/ui_draw.h b/src/ui/ui_draw.h index 3d5ea9f6..517b12de 100644 --- a/src/ui/ui_draw.h +++ b/src/ui/ui_draw.h @@ -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);