From f30ed79e9180d29c2d580ae2e86f80bd8a21e40e Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 7 Jan 2026 00:36:58 -0600 Subject: [PATCH] separate decals into stains & cells --- src/pp/pp_vis/pp_vis.lay | 2 +- src/pp/pp_vis/pp_vis_core.c | 78 ++++++++++++++---------- src/pp/pp_vis/pp_vis_core.h | 2 +- src/pp/pp_vis/pp_vis_gpu.g | 101 ++++++++++++++++++-------------- src/pp/pp_vis/pp_vis_gpu.gh | 2 +- src/pp/pp_vis/pp_vis_shared.cgh | 20 ++++--- 6 files changed, 117 insertions(+), 88 deletions(-) diff --git a/src/pp/pp_vis/pp_vis.lay b/src/pp/pp_vis/pp_vis.lay index 9d1e3335..e5a88a3e 100644 --- a/src/pp/pp_vis/pp_vis.lay +++ b/src/pp/pp_vis/pp_vis.lay @@ -15,7 +15,7 @@ @EmbedDir V_Resources pp_vis_res -@ComputeShader V_ClearDecalsCS +@ComputeShader V_ClearCellsCS @ComputeShader V_ClearParticlesCS @ComputeShader V_BackdropCS @VertexShader V_DQuadVS diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 468b891c..0df2ac3b 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -309,7 +309,7 @@ void V_TickForever(WaveLaneCtx *lane) world->tiles = PushStructs(world_arena, u8, S_TilesCount); Vec2I32 tiles_dims = VEC2I32(S_TilesPitch, S_TilesPitch); - Vec2I32 decals_dims = VEC2I32(V_PixelsPerMeter * S_WorldPitch, V_PixelsPerMeter * S_WorldPitch); + Vec2I32 cells_dims = VEC2I32(V_CellsPerMeter * S_WorldPitch, V_CellsPerMeter * S_WorldPitch); // u32 max_particles = Kibi(128); u32 max_particles = Mebi(1); @@ -320,11 +320,13 @@ void V_TickForever(WaveLaneCtx *lane) G_ResourceHandle gpu_state = Zi; G_ResourceHandle gpu_tiles = Zi; G_ResourceHandle gpu_particles = Zi; - G_ResourceHandle gpu_decals = Zi; + G_ResourceHandle gpu_cells = Zi; + G_ResourceHandle gpu_stains = Zi; G_RWStructuredBufferRef gpu_state_ref = Zi; G_Texture2DRef gpu_tiles_ref = Zi; G_RWStructuredBufferRef gpu_particles_ref = Zi; - G_RWTexture2DRef gpu_decals_ref = Zi; + G_RWTexture2DRef gpu_cells_ref = Zi; + G_RWTexture2DRef gpu_stains_ref = Zi; { G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct); { @@ -338,7 +340,6 @@ void V_TickForever(WaveLaneCtx *lane) ); gpu_state_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_state, V_GpuState); } - // Init tile map texture { gpu_tiles = G_PushTexture2D( @@ -360,18 +361,31 @@ void V_TickForever(WaveLaneCtx *lane) ); gpu_particles_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles, V_Particle); } - // Init decal texture + // Init cells texture { - gpu_decals = G_PushTexture2D( + gpu_cells = G_PushTexture2D( gpu_perm, cl, // G_Format_R8_Uint, // G_Format_R11G11B10_Float, G_Format_R10G10B10A2_Unorm, - decals_dims, + cells_dims, G_Layout_DirectQueue_ShaderReadWrite, .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite ); - gpu_decals_ref = G_PushRWTexture2DRef(gpu_perm, gpu_decals); + gpu_cells_ref = G_PushRWTexture2DRef(gpu_perm, gpu_cells); + } + // Init stain texture + { + gpu_stains = G_PushTexture2D( + gpu_perm, cl, + // G_Format_R8_Uint, + // G_Format_R11G11B10_Float, + G_Format_R10G10B10A2_Unorm, + cells_dims, + G_Layout_DirectQueue_ShaderReadWrite, + .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite + ); + gpu_stains_ref = G_PushRWTexture2DRef(gpu_perm, gpu_stains); } } G_CommitCommandList(cl); @@ -876,14 +890,14 @@ void V_TickForever(WaveLaneCtx *lane) frame->xf.draw_to_world = InvertXform(frame->xf.world_to_draw); } - // World <-> decal - // TODO: This can be constant (so shaders don't need to read it every frame) - frame->xf.world_to_decal = XformIdentity; - frame->xf.decal_to_world = XformIdentity; + // World <-> cell + // TODO: This never changes, should be #defined (so shaders don't need to read it every frame) + frame->xf.world_to_cell = XformIdentity; + frame->xf.cell_to_world = XformIdentity; { - frame->xf.world_to_decal = ScaleXform(frame->xf.world_to_decal, VEC2(V_PixelsPerMeter, V_PixelsPerMeter)); - frame->xf.world_to_decal = TranslateXform(frame->xf.world_to_decal, VEC2((S_WorldPitch / 2.0), (S_WorldPitch / 2.0))); - frame->xf.decal_to_world = InvertXform(frame->xf.world_to_decal); + frame->xf.world_to_cell = ScaleXform(frame->xf.world_to_cell, VEC2(V_CellsPerMeter, V_CellsPerMeter)); + frame->xf.world_to_cell = TranslateXform(frame->xf.world_to_cell, VEC2((S_WorldPitch / 2.0), (S_WorldPitch / 2.0))); + frame->xf.cell_to_world = InvertXform(frame->xf.world_to_cell); } ////////////////////////////// @@ -2168,14 +2182,14 @@ void V_TickForever(WaveLaneCtx *lane) { UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); Vec2I32 tile_pos = S_TilePosFromWorldPos(frame->world_cursor); - Vec2 decal_pos = MulXformV2(frame->xf.world_to_decal, frame->world_cursor); + Vec2 cell_pos = MulXformV2(frame->xf.world_to_cell, frame->world_cursor); i32 tile_idx = S_TileIdxFromTilePos(tile_pos); { UI_BuildLabelF("Camera pos: %F", FmtFloat2(frame->camera_pos)); UI_BuildLabelF("Cursor world pos: %F", FmtFloat2(frame->world_cursor)); UI_BuildLabelF("Cursor tile pos: %F", FmtSint2(tile_pos)); UI_BuildLabelF("Cursor tile idx: %F", FmtSint(tile_idx)); - UI_BuildLabelF("Cursor decal pos: %F", FmtFloat2(decal_pos)); + UI_BuildLabelF("Cursor cell pos: %F", FmtFloat2(cell_pos)); UI_BuildLabelF("Hovered ent: %F", S_FmtKey(hovered_ent->key)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); @@ -2356,7 +2370,8 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Process vis commands - b32 should_clear_decals = !TweakBool("Persist decals", 0); + // b32 should_clear_stains = !TweakBool("Persist stains", 0); + b32 should_clear_stains = 0; b32 should_clear_particles = 0; for (V_CmdNode *cmd_node = frame->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { @@ -2495,9 +2510,9 @@ void V_TickForever(WaveLaneCtx *lane) } } break; - case V_CmdKind_clear_decals: + case V_CmdKind_clear_stains: { - should_clear_decals = 1; + should_clear_stains = 1; } break; case V_CmdKind_clear_particles: @@ -2618,7 +2633,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->emitters_count += 1; emitter = &en->emitter; } - V_ParticleFlag flags = 0; + V_ParticleFlag flags = V_ParticleFlag_StainOnPrune; emitter->flags = flags; Vec2 dir = victim_raycast.normal; @@ -2628,16 +2643,16 @@ void V_TickForever(WaveLaneCtx *lane) // emitter->count = 100; // emitter->count = 500; - emitter->speed = 20; - emitter->speed_spread = 40; + emitter->speed = 10; + emitter->speed_spread = 20; emitter->velocity_falloff = 30; emitter->velocity_falloff_spread = 60; emitter->angle = AngleFromVec2(dir); // emitter->angle_spread = Tau / 4; - // emitter->angle_spread = Tau / 20; - emitter->angle_spread = Tau / 32; + emitter->angle_spread = Tau / 20; + // emitter->angle_spread = Tau / 32; emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1)); emitter->color_spread = VEC3(0.1, 0, 0); @@ -2874,7 +2889,9 @@ void V_TickForever(WaveLaneCtx *lane) params.max_particles = max_particles; params.particles = gpu_particles_ref; - params.decals = gpu_decals_ref; + params.should_clear_stains = should_clear_stains; + params.cells = gpu_cells_ref; + params.stains = gpu_stains_ref; } G_ResourceHandle gpu_params = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromStruct(¶ms)); G_StructuredBufferRef gpu_params_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_params, V_GpuParams); @@ -2904,11 +2921,8 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Setup pass - // Clear decals - if (should_clear_decals) - { - G_Compute(frame->cl, V_ClearDecalsCS, V_ThreadGroupSizeFromTexSize(decals_dims)); - } + // Clear cells + G_Compute(frame->cl, V_ClearCellsCS, V_ThreadGroupSizeFromTexSize(cells_dims)); // Clear particles if (should_clear_particles) @@ -2935,7 +2949,7 @@ void V_TickForever(WaveLaneCtx *lane) // Simulate particles G_Compute(frame->cl, V_SimParticlesCS, V_ThreadGroupSizeFromBufferSize(max_particles)); - // Barrier since decals were written + // Barrier since stains were written G_DumbGlobalMemorySync(frame->cl); } diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 2d41d7da..65d27cb1 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -15,7 +15,7 @@ X(spawn, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \ X(spawn_dummy, Spawn Dummy, V_CmdDescFlag_None, V_HOTKEY( Button_R ), ) \ X(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \ - X(clear_decals, Clear decals, V_CmdDescFlag_None, V_HOTKEY( Button_C, .alt = 1 ), ) \ + X(clear_stains, Clear stains, V_CmdDescFlag_None, V_HOTKEY( Button_C, .alt = 1 ), ) \ X(clear_particles, Clear particles, V_CmdDescFlag_None, V_HOTKEY( Button_C ), ) \ /* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index 9b9cf2f6..499e092d 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -1,15 +1,20 @@ //////////////////////////////////////////////////////////// //~ Utility shaders -//- Clear decals -ComputeShader2D(V_ClearDecalsCS, 8, 8) +//- Clear cells +ComputeShader2D(V_ClearCellsCS, 8, 8) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; - RWTexture2D decals = G_Dereference(params.decals); - Vec2 decal_idx = SV_DispatchThreadID; - if (decal_idx.x < countof(decals).x && decal_idx.y < countof(decals).y) + RWTexture2D cells = G_Dereference(params.cells); + RWTexture2D stains = G_Dereference(params.stains); + Vec2 cells_idx = SV_DispatchThreadID; + if (cells_idx.x < countof(cells).x && cells_idx.y < countof(cells).y) { - decals[decal_idx] = 0; + cells[cells_idx] = 0; + if (params.should_clear_stains) + { + stains[cells_idx] = 0; + } } } @@ -148,30 +153,38 @@ ComputeShader2D(V_BackdropCS, 8, 8) + // TODO: Remove this - // Decals test + // Cells test { - RWTexture2D decals = G_Dereference(params.decals); - Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(world_pos, 1)); - // Vec2 decal_uv = decal_pos / countof(decals); - Vec4 decal = decals.Load(decal_pos); - if (decal.a != 0) + RWTexture2D cells = G_Dereference(params.cells); + Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(world_pos, 1))); + Vec4 cell = cells.Load(cell_pos); + if (cell.a != 0) { - result = decal; + result = cell; + } + else + { + RWTexture2D stains = G_Dereference(params.stains); + Vec4 stain = stains.Load(cell_pos); + if (stain.a != 0) + { + result = stain; + } } } // TODO: Remove this - // Decals test + // Stains test // { - // RWTexture2D decals = G_Dereference(params.decals); - // Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(world_pos, 1)); - // // Vec2 decal_uv = decal_pos / countof(decals); - // V_ParticleKind decal = decals.Load(decal_pos); + // RWTexture2D stains = G_Dereference(params.stains); + // Vec2 cell_pos = mul(params.xf.world_to_cell, Vec3(world_pos, 1)); + // V_ParticleKind stain = stains.Load(cell_pos); - // if (decal == V_ParticleKind_Test) + // if (stain == V_ParticleKind_Test) // { // // result = Color_Yellow; // // result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); @@ -269,7 +282,8 @@ ComputeShader(V_SimParticlesCS, 64) { V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; RWStructuredBuffer particles = G_Dereference(params.particles); - RWTexture2D decals = G_Dereference(params.decals); + RWTexture2D cells = G_Dereference(params.cells); + RWTexture2D stains = G_Dereference(params.stains); u32 particle_idx = SV_DispatchThreadID; if (particle_idx < params.max_particles) @@ -319,22 +333,35 @@ ComputeShader(V_SimParticlesCS, 64) { particle.pos += particle.velocity * params.dt; particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * params.dt); - Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(particle.pos, 1)); - if (decal_pos.x >= 0 && decal_pos.y >= 0 && decal_pos.x < countof(decals).x && decal_pos.y < countof(decals).y) + if (particle.exists < 0.0001) { - // FIXME: Atomic write - decals[floor(decal_pos)] = Vec4(particle.color, 1); + particle.exists = 0; + } + } + + // Commit + { + // FIXME: Atomic write + Vec2 cell_pos = floor(mul(params.xf.world_to_cell, Vec3(particle.pos, 1))); + if (cell_pos.x >= 0 && cell_pos.y >= 0 && cell_pos.x < countof(stains).x && cell_pos.y < countof(stains).y) + { + b32 should_stain = 0; + if (particle.flags & V_ParticleFlag_StainOnPrune && particle.exists == 0) + { + should_stain = 1; + } + + cells[cell_pos] = Vec4(particle.color, 1); + if (should_stain) + { + stains[cell_pos] = Vec4(particle.color, 1); + } } else { // Prune out of bounds particle particle.exists = 0; } - - if (particle.exists < 0.0001) - { - particle.exists = 0; - } } particles[particle_idx] = particle; } @@ -447,22 +474,6 @@ PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input) } } - // // TODO: Remove this - // // Decals test - // { - // RWTexture2D decals = G_Dereference(params.decals); - // Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(world_pos, 1)); - // // Vec2 decal_uv = decal_pos / countof(decals); - // V_ParticleKind decal = decals.Load(decal_pos); - - // if (decal == V_ParticleKind_Test) - // { - // result = Color_Yellow; - // // result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); - // } - // } - - V_OverlayPSOutput output; output.sv_target0 = result; return output; diff --git a/src/pp/pp_vis/pp_vis_gpu.gh b/src/pp/pp_vis/pp_vis_gpu.gh index 7cb0155b..4a0d1e24 100644 --- a/src/pp/pp_vis/pp_vis_gpu.gh +++ b/src/pp/pp_vis/pp_vis_gpu.gh @@ -44,7 +44,7 @@ Struct(V_OverlayPSOutput) //~ Shaders //- Utility shaders -ComputeShader2D(V_ClearDecalsCS, 8, 8); +ComputeShader2D(V_ClearCellsCS, 8, 8); ComputeShader(V_ClearParticlesCS, 64); //- Backdrop shader diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index f8952aa1..53072a7c 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -1,5 +1,5 @@ -#define V_PixelsPerMeter 48.0 -#define V_PixelsPerSqMeter (V_PixelsPerMeter * V_PixelsPerMeter) +#define V_CellsPerMeter 48.0 +#define V_CellsPerSqMeter (V_CellsPerMeter * V_CellsPerMeter) //////////////////////////////////////////////////////////// //~ Constant types @@ -28,9 +28,9 @@ Struct(V_Xforms) Xform world_to_draw; Xform draw_to_world; - // World <-> decal - Xform world_to_decal; - Xform decal_to_world; + // World <-> cell + Xform world_to_cell; + Xform cell_to_world; }; Struct(V_GpuState) @@ -74,7 +74,10 @@ Struct(V_GpuParams) u32 max_particles; G_RWStructuredBufferRef particles; - G_RWTexture2DRef decals; + + b32 should_clear_stains; + G_RWTexture2DRef cells; + G_RWTexture2DRef stains; }; //////////////////////////////////////////////////////////// @@ -82,7 +85,8 @@ Struct(V_GpuParams) Enum(V_ParticleFlag) { - V_ParticleFlag_None = (0), + V_ParticleFlag_None = 0, + V_ParticleFlag_StainOnPrune = (1 << 0), }; Struct(V_Emitter) @@ -112,6 +116,7 @@ Struct(V_Particle) V_ParticleFlag flags; u32 emitter_init_num; // if != 0, then initialize using emitter at index (emitter_init_num - 1) + u32 seq; u32 idx_in_emitter; Vec2 pos; @@ -137,7 +142,6 @@ Struct(V_Particle) Enum(V_DQuadFlag) { V_DQuadFlag_None = 0, - V_DQuadFlag_DrawGrid = (1 << 0), }; Struct(V_DQuad)