From ba8b1667db80b5afab90e5c2b2b1b30087429a75 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 2 Jan 2026 18:15:30 -0600 Subject: [PATCH] use premultiplied alpha for ui composition --- src/base/base_math.c | 57 ++++++++++----- src/base/base_math.h | 17 ++++- src/base/base_shader.gh | 12 ++- src/base/base_tweak.h | 2 +- src/gpu/gpu_core.h | 21 +++++- src/gpu/gpu_dx12/gpu_dx12_core.c | 121 +++++++++++++++++++++++-------- src/gpu/gpu_dx12/gpu_dx12_core.h | 5 +- src/meta/meta.c | 13 +++- src/pp/pp_vis/pp_vis_core.c | 31 ++++---- src/pp/pp_vis/pp_vis_core.h | 2 - src/ui/ui_core.c | 8 +- src/ui/ui_shaders.cgh | 2 + src/ui/ui_shaders.g | 98 ++++++++++++------------- src/ui/ui_shaders.gh | 7 +- 14 files changed, 256 insertions(+), 140 deletions(-) diff --git a/src/base/base_math.c b/src/base/base_math.c index 5a244c54..fd29a925 100644 --- a/src/base/base_math.c +++ b/src/base/base_math.c @@ -260,25 +260,30 @@ Vec4 SrgbFromLinear(Vec4 lin) return result; } -u32 LinearU32FromSrgb(Vec4 srgb) +Vec4 PremulFromLinear(Vec4 lin) +{ + Vec4 result = Zi; + result.x = lin.x * lin.w; + result.y = lin.y * lin.w; + result.z = lin.z * lin.w; + result.w = lin.w; + return result; +} + +Vec4 PremulFromSrgb(Vec4 srgb) { Vec4 lin = LinearFromSrgb(srgb); - u32 result = U32FromVec4(lin); - return result; + Vec4 premul = PremulFromLinear(lin); + return premul; } Vec4 LerpSrgb(Vec4 v0, Vec4 v1, f32 t) { - Vec4 v0_l = LinearFromSrgb(v0); - Vec4 v1_l = LinearFromSrgb(v1); - Vec4 blend_l = Zi; - { - blend_l.x = LerpF32(v0_l.x, v1_l.x, t); - blend_l.y = LerpF32(v0_l.y, v1_l.y, t); - blend_l.z = LerpF32(v0_l.z, v1_l.z, t); - blend_l.w = LerpF32(v0_l.w, v1_l.w, t); - } - Vec4 result = SrgbFromLinear(blend_l); + Vec4 result = Zi; + Vec4 v0_lin = LinearFromSrgb(v0); + Vec4 v1_lin = LinearFromSrgb(v1); + Vec4 lerp_lin = LerpVec4(v0_lin, v1_lin, t); + result = SrgbFromLinear(lerp_lin); return result; } @@ -494,21 +499,21 @@ Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p) //- Lerp // Interpolate position vectors -Vec2 LerpVec2(Vec2 val0, Vec2 val1, f32 t) +Vec2 LerpVec2(Vec2 v0, Vec2 v1, f32 t) { - return VEC2(LerpF32(val0.x, val1.x, t), LerpF32(val0.y, val1.y, t)); + return VEC2(LerpF32(v0.x, v1.x, t), LerpF32(v0.y, v1.y, t)); } -Vec2 LerpVec2Vec2(Vec2 val0, Vec2 val1, Vec2 t) +Vec2 LerpVec2Vec2(Vec2 v0, Vec2 v1, Vec2 t) { - return VEC2(LerpF32(val0.x, val1.x, t.x), LerpF32(val0.y, val1.y, t.y)); + return VEC2(LerpF32(v0.x, v1.x, t.x), LerpF32(v0.y, v1.y, t.y)); } // Interpolate direction vectors (spherical lerp) -Vec2 SlerpVec2(Vec2 val0, Vec2 val1, f32 t) +Vec2 SlerpVec2(Vec2 v0, Vec2 v1, f32 t) { - f32 rot = LerpAngleF32(AngleFromVec2(val0), AngleFromVec2(val1), t); - f32 len = LerpF32(Vec2Len(val0), Vec2Len(val1), t); + f32 rot = LerpAngleF32(AngleFromVec2(v0), AngleFromVec2(v1), t); + f32 len = LerpF32(Vec2Len(v0), Vec2Len(v1), t); return MulVec2(Vec2FromAngle(rot), len); } @@ -560,6 +565,18 @@ Vec4 MulVec4Vec4(Vec4 a, Vec4 b) return VEC4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); } +//- Lerp + +Vec4 LerpVec4(Vec4 v0, Vec4 v1, f32 t) +{ + Vec4 result = Zi; + result.x = LerpF32(v0.x, v1.x, t); + result.y = LerpF32(v0.y, v1.y, t); + result.z = LerpF32(v0.z, v1.z, t); + result.w = LerpF32(v0.w, v1.w, t); + return result; +} + //- Conversion Vec4 Vec4FromU32(u32 v) diff --git a/src/base/base_math.h b/src/base/base_math.h index 598a7860..fd4fa05d 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -297,9 +297,14 @@ i64 LerpU64(u64 val0, u64 val1, f64 t); f32 SrgbFromLinearF32(f32 lin); f32 LinearFromSrgbF32(f32 srgb); + Vec4 LinearFromSrgb(Vec4 srgb); Vec4 SrgbFromLinear(Vec4 lin); -u32 LinearU32FromSrgb(Vec4 srgb); + +Vec4 PremulFromLinear(Vec4 lin); +Vec4 PremulFromSrgb(Vec4 srgb); + +// Vec4 LerpLinear(Vec4 v0, Vec4 v1, f32 t); Vec4 LerpSrgb(Vec4 v0, Vec4 v1, f32 t); //////////////////////////////////////////////////////////// @@ -356,9 +361,9 @@ f32 AngleFromVec2Points(Vec2 pt1, Vec2 pt2); Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p); //- Lerp -Vec2 LerpVec2(Vec2 val0, Vec2 val1, f32 t); -Vec2 LerpVec2Vec2(Vec2 val0, Vec2 val1, Vec2 t); -Vec2 SlerpVec2(Vec2 val0, Vec2 val1, f32 t); +Vec2 LerpVec2(Vec2 v0, Vec2 v1, f32 t); +Vec2 LerpVec2Vec2(Vec2 v0, Vec2 v1, Vec2 t); +Vec2 SlerpVec2(Vec2 v0, Vec2 v1, f32 t); //////////////////////////////////////////////////////////// //~ Vec2I32 @@ -375,6 +380,10 @@ Vec2I32 SubVec2I32(Vec2I32 a, Vec2I32 b); Vec4 MulVec4(Vec4 v, f32 s); Vec4 MulVec4Vec4(Vec4 a, Vec4 b); +//- Lerp + +Vec4 LerpVec4(Vec4 v0, Vec4 v1, f32 t); + //- Conversion Vec4 Vec4FromU32(u32 v); u32 U32FromVec4(Vec4 v); diff --git a/src/base/base_shader.gh b/src/base/base_shader.gh index 046f3df1..90453762 100644 --- a/src/base/base_shader.gh +++ b/src/base/base_shader.gh @@ -86,7 +86,7 @@ u32 countof(T arr[N]) Vec4 Vec4FromU32(u32 v) { - Vec4 result; + Vec4 result = 0; result.x = ((v >> 0) & 0xFF) / 255.0; result.y = ((v >> 8) & 0xFF) / 255.0; result.z = ((v >> 16) & 0xFF) / 255.0; @@ -96,7 +96,7 @@ Vec4 Vec4FromU32(u32 v) u32 U32FromVec4(Vec4 v) { - u32 result; + u32 result = 0; result |= (((u32)(v.x * 255.0)) & 0xFF) << 0; result |= (((u32)(v.y * 255.0)) & 0xFF) << 8; result |= (((u32)(v.z * 255.0)) & 0xFF) << 16; @@ -128,6 +128,14 @@ Vec4 LinearFromSrgb(Vec4 srgb) return result; } +Vec4 Premul(Vec4 v) +{ + Vec4 result; + result.rgb = v.rgb * v.a; + result.a = v.a; + return result; +} + //////////////////////////////////////////////////////////// //~ Vertex ID helpers diff --git a/src/base/base_tweak.h b/src/base/base_tweak.h index f4415a7d..56fbca69 100644 --- a/src/base/base_tweak.h +++ b/src/base/base_tweak.h @@ -107,6 +107,6 @@ f64 TweakFloat_(String name, f64 initial, TweakFloatDesc desc); .category = Lit("Debug"), \ .min = (_min), \ .max = (_max), \ - .precision = 2, \ + .precision = 3, \ __VA_ARGS__ \ }) diff --git a/src/gpu/gpu_core.h b/src/gpu/gpu_core.h index f35032bb..0411155e 100644 --- a/src/gpu/gpu_core.h +++ b/src/gpu/gpu_core.h @@ -479,6 +479,13 @@ Enum(G_RasterMode) G_RasterMode_WireTriangleStrip, }; +Enum(G_BlendMode) +{ + G_BlendMode_Opaque, + G_BlendMode_CompositeStraightAlpha, + G_BlendMode_CompositePremultipliedAlpha, +}; + Struct(G_IndexBufferDesc) { G_ResourceHandle resource; @@ -486,6 +493,12 @@ Struct(G_IndexBufferDesc) u32 index_count; }; +Struct(G_RenderTargetDesc) +{ + G_ResourceHandle resource; + G_BlendMode blend; +}; + //////////////////////////////////////////////////////////// //~ Statistic types @@ -585,6 +598,10 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena, G_CommandListHandle cl, G_R #define G_IdxBuff16(_res) ((G_IndexBufferDesc) { .resource = (_res), .index_size = 2, .index_count = (G_CountBuffer((_res), i16)) }) #define G_IdxBuff32(_res) ((G_IndexBufferDesc) { .resource = (_res), .index_size = 4, .index_count = (G_CountBuffer((_res), i32)) }) +//- Render target helpers + +#define G_Rt(_res, _blend_mode) ((G_RenderTargetDesc) { .resource = (_res), .blend = (_blend_mode) }) + //- Count u64 G_CountBufferBytes(G_ResourceHandle buffer); @@ -765,9 +782,9 @@ void G_Rasterize( G_CommandListHandle cl, VertexShader vs, PixelShader ps, u32 instances_count, G_IndexBufferDesc index_buffer, - u32 render_targets_count, G_ResourceHandle *render_targets, + u32 render_targets_count, G_RenderTargetDesc *render_targets, Rng3 viewport, Rng2 scissor, - G_RasterMode mode + G_RasterMode raster_mode ); //- Clear diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index d618a56f..0ef5076f 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -496,6 +496,30 @@ G_D12_Pipeline *G_D12_PipelineFromDesc(G_D12_PipelineDesc desc) ID3D12PipelineState *pso = 0; if (ok && (!IsResourceNil(desc.vs.resource) || !IsResourceNil(desc.ps.resource))) { + i32 rts_count = 0; + b32 has_multiple_blend_modes = 0; + { + G_BlendMode last_blend_mode = 0; + for (i32 rt_idx = 0; rt_idx < countof(desc.render_target_formats); ++rt_idx) + { + G_BlendMode blend_mode = desc.render_target_blend_modes[rt_idx]; + DXGI_FORMAT format = G_D12_DxgiFormatFromGpuFormat(desc.render_target_formats[rt_idx]); + if (format == DXGI_FORMAT_UNKNOWN) + { + break; + } + else + { + if (rt_idx > 0 && blend_mode != last_blend_mode) + { + has_multiple_blend_modes = 1; + } + last_blend_mode = blend_mode; + rts_count += 1; + } + } + } + D3D12_RASTERIZER_DESC raster_desc = Zi; { if (desc.is_wireframe) @@ -520,16 +544,51 @@ G_D12_Pipeline *G_D12_PipelineFromDesc(G_D12_PipelineDesc desc) D3D12_BLEND_DESC blend_desc = Zi; { + blend_desc.IndependentBlendEnable = has_multiple_blend_modes; blend_desc.AlphaToCoverageEnable = 0; - blend_desc.IndependentBlendEnable = 0; - blend_desc.RenderTarget[0].BlendEnable = 1; - blend_desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; - blend_desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; - blend_desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; - blend_desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; - blend_desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; - blend_desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; - blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + for (i32 rt_idx = 0; rt_idx < rts_count; ++rt_idx) + { + G_BlendMode blend_mode = desc.render_target_blend_modes[rt_idx]; + D3D12_RENDER_TARGET_BLEND_DESC *rt = &blend_desc.RenderTarget[rt_idx]; + switch (blend_mode) + { + default: + { + rt->BlendEnable = 0; + rt->RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + } break; + + case G_BlendMode_CompositeStraightAlpha: + { + rt->BlendEnable = 1; + + rt->SrcBlend = D3D12_BLEND_SRC_ALPHA; + rt->BlendOp = D3D12_BLEND_OP_ADD; + rt->DestBlend = D3D12_BLEND_INV_SRC_ALPHA; + + rt->SrcBlendAlpha = D3D12_BLEND_ONE; + rt->BlendOpAlpha = D3D12_BLEND_OP_ADD; + rt->DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; + + rt->RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + } break; + + case G_BlendMode_CompositePremultipliedAlpha: + { + rt->BlendEnable = 1; + + rt->SrcBlend = D3D12_BLEND_ONE; + rt->BlendOp = D3D12_BLEND_OP_ADD; + rt->DestBlend = D3D12_BLEND_INV_SRC_ALPHA; + + rt->SrcBlendAlpha = D3D12_BLEND_ONE; + rt->BlendOpAlpha = D3D12_BLEND_OP_ADD; + rt->DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; + + rt->RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + } break; + } + } } D3D12_DEPTH_STENCIL_DESC ds_desc = Zi; @@ -554,18 +613,11 @@ G_D12_Pipeline *G_D12_PipelineFromDesc(G_D12_PipelineDesc desc) pso_desc.SampleMask = UINT_MAX; pso_desc.SampleDesc.Count = 1; pso_desc.SampleDesc.Quality = 0; - for (i32 i = 0; i < (i32)countof(desc.render_target_formats); ++i) + pso_desc.NumRenderTargets = rts_count; + for (i32 rt_idx = 0; rt_idx < rts_count; ++rt_idx) { - StaticAssert(countof(pso_desc.RTVFormats) <= countof(desc.render_target_formats)); - DXGI_FORMAT format = G_D12_DxgiFormatFromGpuFormat(desc.render_target_formats[i]); - if (format != DXGI_FORMAT_UNKNOWN) - { - pso_desc.RTVFormats[pso_desc.NumRenderTargets++] = format; - } - else - { - break; - } + DXGI_FORMAT format = G_D12_DxgiFormatFromGpuFormat(desc.render_target_formats[rt_idx]); + pso_desc.RTVFormats[rt_idx] = format; } } hr = ID3D12Device_CreateGraphicsPipelineState(G_D12.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pso); @@ -1727,7 +1779,7 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle) } { b32 tweak_b32 = TweakBool("GPU tweak-bool", 1); - f32 tweak_f32 = TweakFloat("GPU tweak-float", 1, 0, 1, .precision = 3); + f32 tweak_f32 = TweakFloat("GPU tweak-float", 1, 0, 1); slotted_constants[G_ShaderConst_TweakB32] = tweak_b32; slotted_constants[G_ShaderConst_TweakF32] = *(u32 *)&tweak_f32; } @@ -2029,6 +2081,7 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle) case G_D12_CmdKind_Compute: { + // Fetch pipeline G_D12_Pipeline *pipeline = 0; { G_D12_PipelineDesc pipeline_desc = Zi; @@ -2084,6 +2137,7 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle) case G_D12_CmdKind_Rasterize: { + // Fetch pipeline G_D12_Pipeline *pipeline = 0; { G_D12_PipelineDesc pipeline_desc = Zi; @@ -2091,7 +2145,7 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle) pipeline_desc.ps = cmd->rasterize.ps; { pipeline_desc.topology_type = D3D12_PRIMITIVE_TOPOLOGY_TYPE_UNDEFINED; - switch (cmd->rasterize.mode) + switch (cmd->rasterize.raster_mode) { default: Assert(0); break; case G_RasterMode_PointList: pipeline_desc.topology_type = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; break; @@ -2103,16 +2157,18 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle) case G_RasterMode_WireTriangleStrip: pipeline_desc.topology_type = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; break; } } - if (cmd->rasterize.mode == G_RasterMode_WireTriangleList || cmd->rasterize.mode == G_RasterMode_WireTriangleStrip) + if (cmd->rasterize.raster_mode == G_RasterMode_WireTriangleList || cmd->rasterize.raster_mode == G_RasterMode_WireTriangleStrip) { pipeline_desc.is_wireframe = 1; } - for (u32 i = 0; i < countof(cmd->rasterize.render_targets); ++i) + for (u32 i = 0; i < countof(cmd->rasterize.render_target_descs); ++i) { - G_D12_Resource *rt = cmd->rasterize.render_targets[i]; + G_RenderTargetDesc desc = cmd->rasterize.render_target_descs[i]; + G_D12_Resource *rt = G_D12_ResourceFromHandle(desc.resource); if (rt) { pipeline_desc.render_target_formats[i] = rt->texture_format; + pipeline_desc.render_target_blend_modes[i] = desc.blend; } else { @@ -2226,7 +2282,7 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle) // Set topology { D3D_PRIMITIVE_TOPOLOGY topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - switch (cmd->rasterize.mode) + switch (cmd->rasterize.raster_mode) { default: Assert(0); break; case G_RasterMode_PointList: topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST; break; @@ -2254,9 +2310,10 @@ i64 G_CommitCommandList(G_CommandListHandle cl_handle) { b32 om_dirty = 0; u32 rtvs_count = 0; - for (u32 i = 0; i < countof(cmd->rasterize.render_targets); ++i) + for (u32 i = 0; i < countof(cmd->rasterize.render_target_descs); ++i) { - G_D12_Resource *rt = cmd->rasterize.render_targets[i]; + G_RenderTargetDesc desc = cmd->rasterize.render_target_descs[i]; + G_D12_Resource *rt = G_D12_ResourceFromHandle(desc.resource); if (rt) { if (bound_render_target_uids[i] != rt->uid) @@ -2627,9 +2684,9 @@ void G_Rasterize( G_CommandListHandle cl_handle, VertexShader vs, PixelShader ps, u32 instances_count, G_IndexBufferDesc index_buffer, - u32 render_targets_count, G_ResourceHandle *render_targets, + u32 render_targets_count, G_RenderTargetDesc *render_targets, Rng3 viewport, Rng2 scissor, - G_RasterMode mode + G_RasterMode raster_mode ) { G_D12_CmdList *cl = G_D12_CmdListFromHandle(cl_handle); @@ -2641,11 +2698,11 @@ void G_Rasterize( cmd->rasterize.index_buffer_desc = index_buffer; for (u32 i = 0; i < MinU32(render_targets_count, G_MaxRenderTargets); ++i) { - cmd->rasterize.render_targets[i] = G_D12_ResourceFromHandle(render_targets[i]); + cmd->rasterize.render_target_descs[i] = render_targets[i]; } cmd->rasterize.viewport = viewport; cmd->rasterize.scissor = scissor; - cmd->rasterize.mode = mode; + cmd->rasterize.raster_mode = raster_mode; } //- Clear diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.h b/src/gpu/gpu_dx12/gpu_dx12_core.h index fbb158e3..0fd94459 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.h +++ b/src/gpu/gpu_dx12/gpu_dx12_core.h @@ -36,6 +36,7 @@ Struct(G_D12_PipelineDesc) b32 is_wireframe; D3D12_PRIMITIVE_TOPOLOGY_TYPE topology_type; G_Format render_target_formats[G_MaxRenderTargets]; + G_BlendMode render_target_blend_modes[G_MaxRenderTargets]; }; Struct(G_D12_Pipeline) @@ -345,10 +346,10 @@ Struct(G_D12_Cmd) PixelShader ps; u32 instances_count; G_IndexBufferDesc index_buffer_desc; - G_D12_Resource *render_targets[G_MaxRenderTargets]; + G_RenderTargetDesc render_target_descs[G_MaxRenderTargets]; Rng3 viewport; Rng2 scissor; - G_RasterMode mode; + G_RasterMode raster_mode; } rasterize; struct diff --git a/src/meta/meta.c b/src/meta/meta.c index 6c285e66..9a98cbfa 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -421,9 +421,15 @@ void BuildEntryPoint(WaveLaneCtx *lane) //- Dxc { - // PushStringToList(perm, &cp.flags_dxc, Lit("-Od")); PushStringToList(perm, &cp.flags_dxc, Lit("-O3")); PushStringToList(perm, &cp.flags_dxc, Lit("-Zi -Qembed_debug")); + + // Enable warnings + PushStringToList(perm, &cp.warnings_dxc, Lit("-Wall")); + PushStringToList(perm, &cp.warnings_dxc, Lit("-WX")); + + // Disable warnings + PushStringToList(perm, &cp.warnings_dxc, Lit("-Wno-unused-variable")); } } @@ -868,13 +874,14 @@ void BuildEntryPoint(WaveLaneCtx *lane) : Lit("vs_6_6"); String compile_cmd = StringF( perm, - "dxc.exe -T %F -E %F -Fo %F %F %F %F", + "dxc.exe -T %F -E %F -Fo %F %F %F %F %F", FmtString(target), FmtString(e->name), FmtString(out_file), FmtString(gpu_out_file), FmtString(StringFromList(perm, cp.defs, Lit(" "))), - FmtString(StringFromList(perm, cp.flags_dxc, Lit(" "))) + FmtString(StringFromList(perm, cp.flags_dxc, Lit(" "))), + FmtString(StringFromList(perm, cp.warnings_dxc, Lit(" "))) ); OS_CommandResult cmd_result = OS_RunCommand(perm, compile_cmd); diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index d75eabef..55c67b4a 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -98,9 +98,7 @@ V_WidgetTheme V_GetWidgetTheme(void) theme.text_padding_x = 5; theme.text_padding_y = 5; - - theme.window_bd_sz = 1; - theme.window_padding = theme.window_bd_sz - 1; + theme.window_padding = 1; //- Colors theme.col.window_bg = Rgb32(0xff1a1d1e); @@ -1321,7 +1319,7 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 drag_offset = SubVec2(ui_frame->drag_cursor_pos, palette_reps.drag.screen_anchor); palette->pos = SubVec2(frame->ui_cursor, drag_offset); } - window_border_color = LerpSrgb(window_border_color, Rgb32(0x0078a6), titlebar_reps.draw.hot); + window_border_color = LerpSrgb(window_border_color, theme.col.button_active, titlebar_reps.draw.hot); f32 scale = LerpF32(0.85, 1, palette->show); UI_Push(Tint, VEC4(1, 1, 1, palette->show)); @@ -1329,7 +1327,8 @@ void V_TickForever(WaveLaneCtx *lane) UI_Push(BackgroundColor, window_background_color); UI_Push(BorderColor, window_border_color); - UI_Push(BorderSize, theme.window_bd_sz); + // UI_Push(BorderSize, theme.window_bd_sz); + UI_Push(BorderSize, 1); UI_Push(Rounding, UI_RGROW(0.095 * theme.rounding)); UI_Push(Width, UI_FNT(40, 0)); UI_Push(Height, UI_SHRINK(0, 0)); @@ -1358,7 +1357,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_BuildRow(); // Title box - UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2); + UI_SetNext(FontSize, UI_Top(FontSize) * theme.h3); UI_SetNext(ChildAlignment, UI_Region_Center); UI_SetNext(Width, UI_SHRINK(0, 1)); UI_SetNext(Text, Lit("Debug Palette")); @@ -1376,7 +1375,7 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Build palette items list - f32 window_padding = theme.window_bd_sz; + f32 window_padding = theme.window_padding; UI_SetNext(Tint, 0); UI_SetNext(Rounding, 0); UI_PushCP(UI_BuildRow()); @@ -1475,8 +1474,10 @@ void V_TickForever(WaveLaneCtx *lane) } } - Vec4 item_color = theme.col.window_bg; - Vec4 item_border_color = item_color; + // Vec4 item_color = theme.col.window_bg; + Vec4 item_color = Zi; + // Vec4 item_color = theme.col.hint; + Vec4 item_border_color = Zi; if (item->flags & PaletteItemFlag_IsCmd) { item_color = LerpSrgb(item_color, theme.col.button_hot, item_rep.hot); @@ -1491,12 +1492,12 @@ void V_TickForever(WaveLaneCtx *lane) f32 item_size_px = UI_FNT(1.5, 1).v; f32 tweak_size_px = UI_FNT(1.25, 1).v; - UI_SetNext(BorderColor, 0); - UI_SetNext(Rounding, UI_RPIX(0)); + UI_SetNext(Tint, 0); UI_PushCP(UI_BuildRow()); { - UI_SetNext(BorderColor, item_border_color); UI_SetNext(BackgroundColor, item_color); + UI_SetNext(BorderColor, item_border_color); + UI_SetNext(BorderSize, 1); UI_SetNext(Rounding, UI_RPIX(5)); UI_SetNext(Width, UI_GROW(1, 0)); UI_SetNext(Height, UI_PIX(item_size_px, 1)); @@ -1701,7 +1702,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_SetNext(BackgroundColor, slider_progress_color); // UI_SetNext(Rounding, UI_RGROW(theme.rounding)); UI_SetNext(Rounding, 0); - UI_SetNext(BorderColor, slider_border_color); + UI_SetNext(BorderColor, 0); UI_SetNext(BorderSize, 1); UI_SetNext(Width, UI_PIX(marker_pos + half_marker_dims.x, 0)); UI_SetNext(Height, UI_PIX(tweak_size_px * 0.75, 1)); @@ -2309,7 +2310,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->cl, V_DVertVS, V_DVertPS, 1, dvert_idxs_ib, - 1, &draw_target, + 1, &G_Rt(draw_target, G_BlendMode_CompositeStraightAlpha), viewport, scissor, G_RasterMode_TriangleList ); @@ -2323,7 +2324,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->cl, V_OverlayVS, V_OverlayPS, 1, G_QuadIndices(), - 1, &draw_target, + 1, &G_Rt(draw_target, G_BlendMode_CompositeStraightAlpha), viewport, scissor, G_RasterMode_TriangleList ); diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 5779d686..418d3987 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -33,9 +33,7 @@ Struct(V_WidgetTheme) f32 rounding; - f32 window_bd_sz; f32 window_padding; - f32 text_padding_x; f32 text_padding_y; diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 0f1dbafc..fd69fc67 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -1499,6 +1499,7 @@ void UI_EndFrame(UI_Frame *frame) } break; } + // Clamp rounding based on parent rounding if (parent && !AllBits(box->desc.flags, UI_BoxFlag_Floating | UI_BoxFlag_NoFloatingClamp)) { Vec2 vtl = SubVec2(VEC2(parent->screen_rect.p0.x, parent->screen_rect.p0.y), VEC2(box->screen_rect.p0.x, box->screen_rect.p0.y)); @@ -1711,6 +1712,7 @@ void UI_EndFrame(UI_Frame *frame) params.rects = rects_ro; params.sampler = G_BasicSampler(); params.cursor_pos = frame->cursor_pos; + params.aa = TweakFloat("UI anti-aliasing", 1, 0, 1); } G_ResourceHandle params_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(¶ms)); G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, UI_DParams); @@ -1742,7 +1744,7 @@ void UI_EndFrame(UI_Frame *frame) frame->cl, UI_DRectVS, UI_DRectPS, rects_count, G_QuadIndices(), - 1, &draw_target, + 1, &G_Rt(draw_target, G_BlendMode_CompositePremultipliedAlpha), draw_viewport, draw_scissor, G_RasterMode_TriangleList ); @@ -1755,7 +1757,7 @@ void UI_EndFrame(UI_Frame *frame) frame->cl, UI_DRectVS, UI_DRectPS, rects_count, G_QuadIndices(), - 1, &draw_target, + 1, &G_Rt(draw_target, G_BlendMode_CompositePremultipliedAlpha), draw_viewport, draw_scissor, G_RasterMode_WireTriangleList ); @@ -1772,7 +1774,7 @@ void UI_EndFrame(UI_Frame *frame) frame->cl, UI_BlitVS, UI_BlitPS, 1, G_QuadIndices(), - 1, &backbuffer, + 1, &G_Rt(backbuffer, G_BlendMode_Opaque), monitor_viewport, monitor_scissor, G_RasterMode_TriangleList ); diff --git a/src/ui/ui_shaders.cgh b/src/ui/ui_shaders.cgh index 432a61fe..8c995a8c 100644 --- a/src/ui/ui_shaders.cgh +++ b/src/ui/ui_shaders.cgh @@ -6,6 +6,8 @@ G_DeclConstant(b32, UI_ShaderConst_DebugDraw, 1); Struct(UI_DParams) { + f32 aa; + Vec2I32 target_size; G_Texture2DRef target_ro; diff --git a/src/ui/ui_shaders.g b/src/ui/ui_shaders.g index 797ecaf3..45c0cb88 100644 --- a/src/ui/ui_shaders.g +++ b/src/ui/ui_shaders.g @@ -15,14 +15,18 @@ VertexShader(UI_DRectVS, UI_DRectPSInput) Vec2 target_pos = lerp(rect.bounds.p0, rect.bounds.p1, rect_uv); UI_DRectPSInput result; - result.sv_position = Vec4(NdcFromPos(target_pos, Vec2(params.target_size).xy), 0, 1); - result.background_lin = rect.background_lin; - result.border_lin = rect.border_lin; - result.tint_lin = rect.tint_lin; - result.rect_idx = SV_InstanceID; - result.rect_uv = rect_uv; - result.tex_uv = tex_uv; + { + result.sv_position = Vec4(NdcFromPos(target_pos, Vec2(params.target_size).xy), 0, 1); + result.rect_idx = SV_InstanceID; + result.base_background_premul = Premul(rect.background_lin); + result.base_border_premul = Premul(rect.border_lin); + result.tint_premul = Premul(rect.tint_lin); + result.debug_premul = Premul(rect.debug_lin); + + result.rect_uv = rect_uv; + result.tex_uv = tex_uv; + } return result; } @@ -42,7 +46,7 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input) Vec2 p0 = rect.bounds.p0; Vec2 p1 = rect.bounds.p1; - // Compute rect sdf (negative means pixel is inside of rect) + //- Compute rect dist (negative means pixel is inside of rect) f32 rect_dist = min(min(p.x - p0.x, p1.x - p.x), min(p.y - p0.y, p1.y - p.y)); { f32 tl_radius = rect.tl_rounding; @@ -59,62 +63,54 @@ PixelShader(UI_DRectPS, UI_DRectPSOutput, UI_DRectPSInput input) if (p.x < bl.x && p.y > bl.y) { rect_dist = min(rect_dist, bl_radius - length(bl - p)); } } rect_dist = -rect_dist; + f32 rect_dist_fwidth = fwidth(rect_dist); - // Compute border sdf (negative means pixel is inside of border) - f32 border_size = 0; - f32 border_dist = 0; - Vec4 border_color = 0; - { - if (rect.border_size > 0) - { - border_size = rect.border_size; - border_color = input.border_lin; - } - else - { - border_size = 0; - border_color = input.background_lin; - } - border_dist = abs(rect_dist); - if (rect_dist <= 0) - { - border_dist -= border_size; - } - } + bool is_inside = rect_dist < 0; - Vec4 final_color = 0; + //- Compute background color + Vec4 background_premul = 0; + if (is_inside) { - // Background color if (G_IsRefNil(rect.tex)) { - final_color = input.background_lin; + background_premul = input.base_background_premul; } else { Texture2D tex = G_Dereference(rect.tex); - final_color = tex.Sample(sampler, input.tex_uv); - } - final_color *= rect_dist <= 0; - - // Border color - { - f32 half_border_dist_fwidth = fwidth(border_dist) * 0.5; - f32 border_alpha = smoothstep(half_border_dist_fwidth, -half_border_dist_fwidth, border_dist); - final_color = lerp(final_color, border_color, border_alpha); - } - - // Tint - final_color *= input.tint_lin; - - // Debug color - if (UI_ShaderConst_DebugDraw) - { - final_color = rect.debug_lin; + background_premul = tex.Sample(sampler, input.tex_uv); + background_premul.rgb *= background_premul.a; } } + //- Compute borer color + Vec4 border_premul = input.base_border_premul; + + //- Compute border dist + f32 border_dist = 0; + border_dist = abs(rect_dist); + if (is_inside) + { + border_dist -= rect.border_size; + } + + //- Compute anti-aliased border-over-background color + Vec4 composite_premul = 0; + { + f32 smoothness = rect_dist_fwidth * params.aa * 0.5; + f32 border_coverage = smoothstep(smoothness, -smoothness, border_dist); + composite_premul = lerp(background_premul, border_premul, border_coverage); + } + + //- Finalize + Vec4 result = composite_premul * input.tint_premul; + if (UI_ShaderConst_DebugDraw) + { + result = input.debug_premul; + } + UI_DRectPSOutput output; - output.sv_target0 = final_color; + output.sv_target0 = result; return output; } diff --git a/src/ui/ui_shaders.gh b/src/ui/ui_shaders.gh index 817a0111..df77f2cd 100644 --- a/src/ui/ui_shaders.gh +++ b/src/ui/ui_shaders.gh @@ -5,9 +5,10 @@ Struct(UI_DRectPSInput) { Semantic(Vec4, sv_position); Semantic(nointerpolation u32, rect_idx); - Semantic(Vec4, background_lin); - Semantic(Vec4, border_lin); - Semantic(Vec4, tint_lin); + Semantic(Vec4, base_background_premul); + Semantic(Vec4, base_border_premul); + Semantic(Vec4, tint_premul); + Semantic(Vec4, debug_premul); Semantic(Vec2, rect_uv); Semantic(Vec2, tex_uv); };