use premultiplied alpha for ui composition

This commit is contained in:
jacob 2026-01-02 18:15:30 -06:00
parent 8aa44b5deb
commit ba8b1667db
14 changed files with 256 additions and 140 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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__ \
})

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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
);

View File

@ -33,9 +33,7 @@ Struct(V_WidgetTheme)
f32 rounding;
f32 window_bd_sz;
f32 window_padding;
f32 text_padding_x;
f32 text_padding_y;

View File

@ -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(&params));
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
);

View File

@ -6,6 +6,8 @@ G_DeclConstant(b32, UI_ShaderConst_DebugDraw, 1);
Struct(UI_DParams)
{
f32 aa;
Vec2I32 target_size;
G_Texture2DRef target_ro;

View File

@ -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<Vec4> tex = G_Dereference<Vec4>(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;
}

View File

@ -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);
};