render quad sprites in full resolution

This commit is contained in:
jacob 2026-02-01 13:04:34 -06:00
parent 2dd15d1078
commit c26488870a
4 changed files with 171 additions and 128 deletions

View File

@ -1915,7 +1915,7 @@ void V_TickForever(WaveLaneCtx *lane)
Affine wep_uv_to_world_af = ScaleAffine(wep_pix_to_world_af, DimsFromRng2(wep.tex_rect)); Affine wep_uv_to_world_af = ScaleAffine(wep_pix_to_world_af, DimsFromRng2(wep.tex_rect));
V_Quad *quad = PushStruct(frame->quads_arena, V_Quad); V_Quad *quad = PushStruct(frame->quads_arena, V_Quad);
quad->quad_uv_to_shade_af = MulAffine(frame->af.world_to_shade, wep_uv_to_world_af); quad->quad_uv_to_screen_af = MulAffine(frame->af.world_to_screen, wep_uv_to_world_af);
quad->tex = wep.tex; quad->tex = wep.tex;
quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims); quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims);
} }
@ -1926,7 +1926,7 @@ void V_TickForever(WaveLaneCtx *lane)
Affine body_uv_to_world_af = ScaleAffine(body_pix_to_world_af, DimsFromRng2(body.tex_rect)); Affine body_uv_to_world_af = ScaleAffine(body_pix_to_world_af, DimsFromRng2(body.tex_rect));
V_Quad *quad = PushStruct(frame->quads_arena, V_Quad); V_Quad *quad = PushStruct(frame->quads_arena, V_Quad);
quad->quad_uv_to_shade_af = MulAffine(frame->af.world_to_shade, body_uv_to_world_af); quad->quad_uv_to_screen_af = MulAffine(frame->af.world_to_screen, body_uv_to_world_af);
quad->tex = body.tex; quad->tex = body.tex;
quad->tex_slice_uv = DivRng2Vec2(body.tex_rect, body.tex_dims); quad->tex_slice_uv = DivRng2Vec2(body.tex_rect, body.tex_dims);
} }
@ -4277,6 +4277,16 @@ void V_TickForever(WaveLaneCtx *lane)
Rng3 screen_viewport = RNG3(VEC3(0, 0, 0), VEC3(frame->screen_dims.x, frame->screen_dims.y, 1)); Rng3 screen_viewport = RNG3(VEC3(0, 0, 0), VEC3(frame->screen_dims.x, frame->screen_dims.y, 1));
Rng2 screen_scissor = RNG2(VEC2(screen_viewport.p0.x, screen_viewport.p0.y), VEC2(screen_viewport.p1.x, screen_viewport.p1.y)); Rng2 screen_scissor = RNG2(VEC2(screen_viewport.p0.x, screen_viewport.p0.y), VEC2(screen_viewport.p1.x, screen_viewport.p1.y));
// Albedo texture
G_ResourceHandle albedo_target = G_PushTexture2D(
frame->gpu_arena, frame->cl,
G_Format_R16G16B16A16_Float,
frame->screen_dims,
G_Layout_DirectQueue_RenderTargetWrite,
.flags = G_ResourceFlag_AllowRenderTarget
);
G_Texture2DRef albedo_target_ro = G_PushTexture2DRef(frame->gpu_arena, albedo_target);
// Shade texture // Shade texture
G_ResourceHandle shade_target = G_PushTexture2D( G_ResourceHandle shade_target = G_PushTexture2D(
frame->gpu_arena, frame->cl, frame->gpu_arena, frame->cl,
@ -4290,16 +4300,6 @@ void V_TickForever(WaveLaneCtx *lane)
Rng3 shade_viewport = RNG3(VEC3(0, 0, 0), VEC3(frame->shade_dims.x, frame->shade_dims.y, 1)); Rng3 shade_viewport = RNG3(VEC3(0, 0, 0), VEC3(frame->shade_dims.x, frame->shade_dims.y, 1));
Rng2 shade_scissor = RNG2(VEC2(shade_viewport.p0.x, shade_viewport.p0.y), VEC2(shade_viewport.p1.x, shade_viewport.p1.y)); Rng2 shade_scissor = RNG2(VEC2(shade_viewport.p0.x, shade_viewport.p0.y), VEC2(shade_viewport.p1.x, shade_viewport.p1.y));
// Albedo texture
G_ResourceHandle albedo_target = G_PushTexture2D(
frame->gpu_arena, frame->cl,
G_Format_R16G16B16A16_Float,
frame->shade_dims,
G_Layout_DirectQueue_RenderTargetWrite,
.flags = G_ResourceFlag_AllowRenderTarget
);
G_Texture2DRef albedo_target_ro = G_PushTexture2DRef(frame->gpu_arena, albedo_target);
// Quad buffers // Quad buffers
G_ResourceHandle quads_buff = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromArena(frame->quads_arena)); G_ResourceHandle quads_buff = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromArena(frame->quads_arena));
G_StructuredBufferRef quads_ref = G_PushStructuredBufferRef(frame->gpu_arena, quads_buff, V_Quad); G_StructuredBufferRef quads_ref = G_PushStructuredBufferRef(frame->gpu_arena, quads_buff, V_Quad);
@ -4356,9 +4356,9 @@ void V_TickForever(WaveLaneCtx *lane)
params.shade_dims = frame->shade_dims; params.shade_dims = frame->shade_dims;
params.shade_ro = shade_target_ro; params.shade_ro = shade_target_ro;
params.shade_rw = shade_target_rw; params.shade_rw = shade_target_rw;
params.albedo_ro = albedo_target_ro;
params.screen_dims = frame->screen_dims; params.screen_dims = frame->screen_dims;
params.albedo_ro = albedo_target_ro;
params.tiles = gpu_tiles_ref; params.tiles = gpu_tiles_ref;
params.quads = quads_ref; params.quads = quads_ref;
@ -4463,12 +4463,9 @@ void V_TickForever(WaveLaneCtx *lane)
V_QuadVS, V_QuadPS, V_QuadVS, V_QuadPS,
G_CountBuffer(quads_buff, V_Quad), G_QuadIndices(), G_CountBuffer(quads_buff, V_Quad), G_QuadIndices(),
1, &G_Rt(albedo_target, G_BlendMode_CompositeStraightAlpha), 1, &G_Rt(albedo_target, G_BlendMode_CompositeStraightAlpha),
shade_viewport, shade_scissor, screen_viewport, screen_scissor,
G_RasterMode_TriangleList G_RasterMode_TriangleList
); );
// Transition albedo to readonly
G_DumbMemoryLayoutSync(frame->cl, albedo_target, G_Layout_DirectQueue_ShaderRead);
} }
////////////////////////////// //////////////////////////////
@ -4488,6 +4485,14 @@ void V_TickForever(WaveLaneCtx *lane)
G_DumbGlobalMemorySync(frame->cl); G_DumbGlobalMemorySync(frame->cl);
} }
//////////////////////////////
//- Transition G-buffers
{
// Make albedo readonly
G_DumbMemoryLayoutSync(frame->cl, albedo_target, G_Layout_DirectQueue_ShaderRead);
}
////////////////////////////// //////////////////////////////
//- Shading pass //- Shading pass

View File

@ -89,15 +89,14 @@ VertexShader(V_QuadVS, V_QuadPSInput)
V_Quad quad = quads[SV_InstanceID]; V_Quad quad = quads[SV_InstanceID];
Vec2 rect_uv = RectUvFromIdx(SV_VertexID); Vec2 rect_uv = RectUvFromIdx(SV_VertexID);
Vec2 shade_pos = mul(quad.quad_uv_to_shade_af, Vec3(rect_uv, 1)); Vec2 screen_pos = mul(quad.quad_uv_to_screen_af, Vec3(rect_uv, 1));
Vec2 tex_pos_uv = lerp(quad.tex_slice_uv.p0, quad.tex_slice_uv.p1, rect_uv); Vec2 samp_uv = lerp(quad.tex_slice_uv.p0, quad.tex_slice_uv.p1, rect_uv);
// Vec2 shade_pos = lerp(quad.p0, quad.p1, rect_uv);
V_QuadPSInput result; V_QuadPSInput result;
result.sv_position = Vec4(NdcFromPos(shade_pos, params.shade_dims).xy, 0, 1); result.sv_position = Vec4(NdcFromPos(screen_pos, params.screen_dims).xy, 0, 1);
result.quad_idx = SV_InstanceID; result.quad_idx = SV_InstanceID;
result.tex_pos_uv = tex_pos_uv; result.samp_uv = samp_uv;
return result; return result;
} }
@ -113,7 +112,7 @@ PixelShader(V_QuadPS, V_QuadPSOutput, V_QuadPSInput input)
V_Quad quad = quads[input.quad_idx]; V_Quad quad = quads[input.quad_idx];
Texture2D<Vec4> tex = G_Dereference<Vec4>(quad.tex); Texture2D<Vec4> tex = G_Dereference<Vec4>(quad.tex);
Vec4 albedo = tex.Sample(clamp_sampler, input.tex_pos_uv); Vec4 albedo = tex.Sample(clamp_sampler, input.samp_uv);
// Vec4 albedo = Color_Cyan; // Vec4 albedo = Color_Cyan;
@ -443,38 +442,38 @@ ComputeShader(V_SimParticlesCS, 64)
} }
// Commit // Commit
// FIXME: Atomic writes
{ {
// FIXME: Atomic write b32 should_stain = is_in_world_bounds && (
if (is_in_world_bounds) AnyBit(particle.flags, V_ParticleFlag_StainTrail) ||
{ (AnyBit(particle.flags, V_ParticleFlag_StainOnPrune) && particle.exists == 0)
b32 should_stain = 0; );
if ((particle.flags & V_ParticleFlag_StainTrail) || ((particle.flags & V_ParticleFlag_StainOnPrune) && particle.exists == 0)) b32 should_draw = is_in_world_bounds;
{
should_stain = 1;
}
Vec4 color = particle.color;
color.a *= old_exists;
cells[cell_pos] = color;
if (should_stain)
{
f32 old_dryness = drynesses[cell_pos];
Vec4 old_stain = stains[cell_pos];
// old_stain = V_DryColor(old_stain, drynesses[cell_pos] * 0.5);
// old_stain = V_DryColor(old_stain, old_dryness);
Vec4 new_stain = 0;
new_stain.rgb = (color.rgb * color.a) + (old_stain.rgb * (1.0 - color.a));
new_stain.a = color.a + (old_stain.a * (1.0 - color.a));
// new_stain = V_DryColor(new_stain, old_dryness * 0.1);
// new_stain = V_DryColor(new_stain, old_dryness * 0.5);
stains[cell_pos] = new_stain; Vec4 color = particle.color;
drynesses[cell_pos] = 0; color.a *= old_exists;
}
} // Stain
else if (should_stain)
{ {
// Prune out of bounds particle f32 old_dryness = drynesses[cell_pos];
particle.exists = 0; Vec4 old_stain = stains[cell_pos];
// old_stain = V_DryColor(old_stain, drynesses[cell_pos] * 0.5);
// old_stain = V_DryColor(old_stain, old_dryness);
Vec4 new_stain = 0;
new_stain.rgb = (color.rgb * color.a) + (old_stain.rgb * (1.0 - color.a));
new_stain.a = color.a + (old_stain.a * (1.0 - color.a));
// new_stain = V_DryColor(new_stain, old_dryness * 0.1);
// new_stain = V_DryColor(new_stain, old_dryness * 0.5);
stains[cell_pos] = new_stain;
drynesses[cell_pos] = 0;
}
// Draw
if (should_draw)
{
cells[cell_pos] = color;
} }
} }
particles[particle_idx] = particle; particles[particle_idx] = particle;
@ -489,7 +488,7 @@ ComputeShader2D(V_ShadeCS, 8, 8)
{ {
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0]; V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
RWTexture2D<Vec4> shade_tex = G_Dereference<Vec4>(params.shade_rw); RWTexture2D<Vec4> shade_tex = G_Dereference<Vec4>(params.shade_rw);
Texture2D<Vec4> albedo_tex = G_Dereference<Vec4>(params.albedo_ro); // Texture2D<Vec4> albedo_tex = G_Dereference<Vec4>(params.albedo_ro);
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(params.tiles); Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(params.tiles);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains); RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses); RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses);
@ -509,9 +508,89 @@ ComputeShader2D(V_ShadeCS, 8, 8)
//- Compute albedo //- Compute albedo
Vec4 albedo = 0; Vec4 albedo = 0;
//////////////////////////////
//- Compute result
Vec4 result = albedo;
//////////////////////////////
//- Write result
if (all(shade_pos < countof(shade_tex)))
{ {
shade_tex[shade_pos] = result;
}
}
////////////////////////////////////////////////////////////
//~ Composite
//////////////////////////////
//- Vertex shader
VertexShader(V_CompositeVS, V_CompositePSInput)
{
Vec2 uv = RectUvFromIdx(SV_VertexID);
V_CompositePSInput result;
result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1);
return result;
}
//////////////////////////////
//- Pixel shader
PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
{
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
Texture2D<Vec4> shade_tex = G_Dereference<Vec4>(params.shade_ro);
Texture2D<Vec4> albedo_tex = G_Dereference<Vec4>(params.albedo_ro);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
RWTexture2D<Vec4> cells = G_Dereference<Vec4>(params.cells);
RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses);
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(params.tiles);
SamplerState clamp_sampler = G_Dereference(params.pt_clamp_sampler);
Vec2 screen_pos = input.sv_position.xy;
Vec2 world_pos = mul(params.af.screen_to_world, Vec3(screen_pos, 1));
Vec2 tile_pos = mul(params.af.world_to_tile, Vec3(world_pos, 1));
Vec2 cell_pos = floor(mul(params.af.world_to_cell, Vec3(world_pos, 1)));
Vec2 shade_pos = mul(params.af.screen_to_shade, Vec3(screen_pos.xy, 1));
f32 half_thickness = 1;
Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5;
Vec2 world_bounds_screen_p0 = mul(params.af.world_to_screen, Vec3(-half_world_dims.xy, 1));
Vec2 world_bounds_screen_p1 = mul(params.af.world_to_screen, Vec3(half_world_dims.xy, 1));
b32 is_in_world_bounds = (
screen_pos.x > (world_bounds_screen_p0.x - half_thickness) &&
screen_pos.y > (world_bounds_screen_p0.y - half_thickness) &&
screen_pos.x < (world_bounds_screen_p1.x + half_thickness) &&
screen_pos.y < (world_bounds_screen_p1.y + half_thickness)
);
P_TileKind tile = tiles.Load(Vec3(tile_pos, 0));
P_TileKind equipped_tile = params.equipped_tile;
//////////////////////////////
//- Shade color
// Vec4 shade_color = 0;
// if (all(shade_pos >= Vec2(0, 0)) && all(shade_pos < countof(shade_tex)))
// {
// Vec2 shade_uv = shade_pos / countof(shade_tex);
// shade_color = shade_tex.SampleLevel(clamp_sampler, shade_uv, 0);
// }
//////////////////////////////
//- Albedo color
Vec4 albedo_color = 0;
{
//////////////////////////////
//- Tile color //- Tile color
// TODO: Remove this // TODO: Remove this
b32 tile_is_wall = 0; b32 tile_is_wall = 0;
Vec4 tile_color = Vec4(0.025, 0.025, 0.025, 1); Vec4 tile_color = Vec4(0.025, 0.025, 0.025, 1);
if (is_in_world_bounds) if (is_in_world_bounds)
@ -581,10 +660,9 @@ ComputeShader2D(V_ShadeCS, 8, 8)
} }
} }
//- Albedo tex color //////////////////////////////
Vec4 albedo_tex_color = albedo_tex.Load(Vec3(shade_pos, 0));
//- Stain color //- Stain color
Vec4 stain_color = 0; Vec4 stain_color = 0;
{ {
f32 dryness = drynesses.Load(cell_pos); f32 dryness = drynesses.Load(cell_pos);
@ -592,84 +670,38 @@ ComputeShader2D(V_ShadeCS, 8, 8)
stain_color.rgb *= 1.0 - (0.75 * tile_is_wall); // Darken wall stains stain_color.rgb *= 1.0 - (0.75 * tile_is_wall); // Darken wall stains
} }
//- Composite albedo //////////////////////////////
albedo = BlendPremul(!tile_is_wall * tile_color, albedo); // Blend floor tile //- Albedo tex
albedo = BlendPremul(!tile_is_wall * stain_color, albedo); // Blend floor stain
albedo = BlendPremul(albedo_tex_color, albedo); Vec4 albedo_tex_color = albedo_tex.Load(Vec3(screen_pos, 0));
albedo = BlendPremul(tile_is_wall * tile_color, albedo); // Blend wall tile
albedo = BlendPremul(tile_is_wall * stain_color, albedo); // Blend wall stain //////////////////////////////
//- Compose albedo
albedo_color = BlendPremul(!tile_is_wall * tile_color, albedo_color); // Blend floor tile
albedo_color = BlendPremul(!tile_is_wall * stain_color, albedo_color); // Blend floor stain
albedo_color = BlendPremul(albedo_tex_color, albedo_color);
albedo_color = BlendPremul(tile_is_wall * tile_color, albedo_color); // Blend wall tile
albedo_color = BlendPremul(tile_is_wall * stain_color, albedo_color); // Blend wall stain
} }
////////////////////////////// //////////////////////////////
//- Compute result //- Particle color
Vec4 result = albedo; // TODO: Remove this
Vec4 particle_color = 0;
if (is_in_world_bounds)
{
particle_color = cells.Load(cell_pos);
}
////////////////////////////// //////////////////////////////
//- Write result
if (all(shade_pos < countof(shade_tex)))
{
shade_tex[shade_pos] = result;
}
}
////////////////////////////////////////////////////////////
//~ Composite
//////////////////////////////
//- Vertex shader
VertexShader(V_CompositeVS, V_CompositePSInput)
{
Vec2 uv = RectUvFromIdx(SV_VertexID);
V_CompositePSInput result;
result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1);
return result;
}
//////////////////////////////
//- Pixel shader
PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
{
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
Texture2D<Vec4> shade_tex = G_Dereference<Vec4>(params.shade_ro);
SamplerState clamp_sampler = G_Dereference(params.pt_clamp_sampler);
Vec2 screen_pos = input.sv_position.xy;
Vec2 world_pos = mul(params.af.screen_to_world, Vec3(screen_pos, 1));
Vec2 tile_pos = mul(params.af.world_to_tile, Vec3(world_pos, 1));
P_TileKind equipped_tile = params.equipped_tile;
f32 half_thickness = 1;
Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5;
Vec2 world_bounds_screen_p0 = mul(params.af.world_to_screen, Vec3(-half_world_dims.xy, 1));
Vec2 world_bounds_screen_p1 = mul(params.af.world_to_screen, Vec3(half_world_dims.xy, 1));
b32 is_in_world_bounds = (
screen_pos.x > (world_bounds_screen_p0.x - half_thickness) &&
screen_pos.y > (world_bounds_screen_p0.y - half_thickness) &&
screen_pos.x < (world_bounds_screen_p1.x + half_thickness) &&
screen_pos.y < (world_bounds_screen_p1.y + half_thickness)
);
Vec2 shade_pos = mul(params.af.screen_to_shade, Vec3(screen_pos.xy, 1));
//- Shaded color
Vec4 shade_color = 0;
if (all(shade_pos >= Vec2(0, 0)) && all(shade_pos < countof(shade_tex)))
{
Vec2 shade_uv = shade_pos / countof(shade_tex);
shade_color = shade_tex.SampleLevel(clamp_sampler, shade_uv, 0);
}
//- Tile selection overlay //- Tile selection overlay
Vec4 selection_color = 0; Vec4 selection_color = 0;
if (params.has_mouse_focus && params.selection_mode == V_SelectionMode_Tile) if (params.has_mouse_focus && params.selection_mode == V_SelectionMode_Tile)
{ {
Vec4 border_color = LinearFromSrgb(Vec4(1, 1, 1, 1)); Vec4 border_color = LinearFromSrgb(Vec4(1, 1, 1, 1));
// Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.4, 0.4, 0.25)); // Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.4, 0.4, 0.25));
Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.8, 0.4, 0.6)); Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.8, 0.4, 0.6));
@ -713,7 +745,9 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
selection_color.rgb *= selection_color.a; selection_color.rgb *= selection_color.a;
} }
//////////////////////////////
//- Grid overlay //- Grid overlay
Vec4 overlay_color = 0; Vec4 overlay_color = 0;
if (is_in_world_bounds) if (is_in_world_bounds)
{ {
@ -771,9 +805,13 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input)
overlay_color.rgb *= overlay_color.a; overlay_color.rgb *= overlay_color.a;
} }
//- Composite //////////////////////////////
//- Blend
Vec4 result = Vec4(0, 0, 0, 1); Vec4 result = Vec4(0, 0, 0, 1);
result = BlendPremul(shade_color, result); // result = BlendPremul(shade_color, result);
result = BlendPremul(albedo_color, result);
result = BlendPremul(particle_color, result);
result = BlendPremul(selection_color, result); result = BlendPremul(selection_color, result);
result = BlendPremul(overlay_color, result); result = BlendPremul(overlay_color, result);

View File

@ -5,7 +5,7 @@ Struct(V_QuadPSInput)
{ {
Semantic(Vec4, sv_position); Semantic(Vec4, sv_position);
Semantic(nointerpolation u32, quad_idx); Semantic(nointerpolation u32, quad_idx);
Semantic(Vec2, tex_pos_uv); Semantic(Vec2, samp_uv);
}; };
Struct(V_QuadPSOutput) Struct(V_QuadPSOutput)

View File

@ -183,7 +183,7 @@ Enum(V_QuadFlag)
Struct(V_Quad) Struct(V_Quad)
{ {
V_QuadFlag flags; V_QuadFlag flags;
Affine quad_uv_to_shade_af; Affine quad_uv_to_screen_af;
G_Texture2DRef tex; G_Texture2DRef tex;
Rng2 tex_slice_uv; Rng2 tex_slice_uv;
}; };