From b8e08c72ad0ec189b14c6e8d1232da8d34c64058 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 6 Jan 2026 02:15:45 -0600 Subject: [PATCH] gpu particle testing --- ppswap/pp_sim.swp.swp | Bin 36904 -> 36912 bytes ppswap/pp_vis.swp.swp | Bin 134 -> 126 bytes src/base/base.cgh | 37 +- src/gpu/gpu_dx12/gpu_dx12_core.c | 16 +- src/gpu/gpu_shader_core.cgh | 6 +- src/pp/pp_sim/pp_sim.lay | 9 +- src/pp/pp_sim/pp_sim_core.c | 2 - .../{pp_sim_tiles.cg => pp_sim_shared.cg} | 0 .../{pp_sim_tiles.cgh => pp_sim_shared.cgh} | 2 +- src/pp/pp_vis/pp_vis.lay | 11 +- src/pp/pp_vis/pp_vis_core.c | 222 ++++++--- src/pp/pp_vis/pp_vis_core.h | 14 +- src/pp/pp_vis/pp_vis_gpu.g | 428 ++++++++++++++++++ .../{pp_vis_shaders.gh => pp_vis_gpu.gh} | 13 +- src/pp/pp_vis/pp_vis_shaders.cgh | 67 --- src/pp/pp_vis/pp_vis_shaders.g | 276 ----------- src/pp/pp_vis/pp_vis_shared.cgh | 146 ++++++ src/ui/ui.lay | 10 +- src/ui/{ui_shaders.g => ui_gpu.g} | 0 src/ui/{ui_shaders.gh => ui_gpu.gh} | 0 src/ui/{ui_shaders.cgh => ui_shared.cgh} | 0 21 files changed, 806 insertions(+), 453 deletions(-) rename src/pp/pp_sim/{pp_sim_tiles.cg => pp_sim_shared.cg} (100%) rename src/pp/pp_sim/{pp_sim_tiles.cgh => pp_sim_shared.cgh} (95%) create mode 100644 src/pp/pp_vis/pp_vis_gpu.g rename src/pp/pp_vis/{pp_vis_shaders.gh => pp_vis_gpu.gh} (86%) delete mode 100644 src/pp/pp_vis/pp_vis_shaders.cgh delete mode 100644 src/pp/pp_vis/pp_vis_shaders.g create mode 100644 src/pp/pp_vis/pp_vis_shared.cgh rename src/ui/{ui_shaders.g => ui_gpu.g} (100%) rename src/ui/{ui_shaders.gh => ui_gpu.gh} (100%) rename src/ui/{ui_shaders.cgh => ui_shared.cgh} (100%) diff --git a/ppswap/pp_sim.swp.swp b/ppswap/pp_sim.swp.swp index 9d1caa5ddc2eac6923ecc9e68509b3c5c2c62823..29844c322cf7ce6fbb6836fcc70a0ed9486e5b73 100644 GIT binary patch delta 57 zcmZ3nfN8@5CaF0J`xh|)!ML~pE6}VfQ0SR1S&}!LoLpos, ArenaFirst(arena, u8))) #define StringFromFixedArray(a) \ @@ -742,23 +745,23 @@ //////////////////////////////////////////////////////////// //~ Basic mixing/hashing +// Based on Jon Maiga's "mx3" +// https://jonkagstrom.com/mx3/mx3_rev2.html +u64 MixU64(u64 seed) +{ + seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; + seed = (seed ^ (seed >> 29)) * 0xbea225f9eb34556d; + seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; + seed = (seed ^ (seed >> 29)); + return seed; +} + +u64 MixU64s(u64 seed_a, u64 seed_b) +{ + return MixU64((seed_a * 3) + seed_b); +} + #if IsLanguageC - // Based on Jon Maiga's "mx3" - // https://jonkagstrom.com/mx3/mx3_rev2.html - u64 MixU64(u64 seed) - { - seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; - seed = (seed ^ (seed >> 29)) * 0xbea225f9eb34556d; - seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d; - seed = (seed ^ (seed >> 29)); - return seed; - } - - u64 MixU64s(u64 seed_a, u64 seed_b) - { - return MixU64((seed_a * 3) + seed_b); - } - // FNV-1a parameters for different hash sizes: // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters Inline u64 HashFnv64(u64 seed, String s) diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index bc205ac2..4df53a1c 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -1224,22 +1224,22 @@ u32 G_PushRef(G_ArenaHandle arena_handle, G_ResourceHandle resource_handle, G_Re G_RefKind kind = ref_desc.kind; b32 is_buffer = kind == G_RefKind_StructuredBuffer || - kind == G_RefKind_RWStructuredBuffer || - kind == G_RefKind_ByteAddressBuffer || - kind == G_RefKind_RWByteAddressBuffer; + kind == G_RefKind_RWStructuredBuffer || + kind == G_RefKind_ByteAddressBuffer || + kind == G_RefKind_RWByteAddressBuffer; b32 is_sampler = kind == G_RefKind_SamplerState; b32 is_texture = !is_buffer && !is_sampler; b32 is_raw = kind == G_RefKind_ByteAddressBuffer || - kind == G_RefKind_RWByteAddressBuffer; + kind == G_RefKind_RWByteAddressBuffer; b32 is_uav = kind == G_RefKind_RWStructuredBuffer || - kind == G_RefKind_RWByteAddressBuffer || - kind == G_RefKind_RWTexture1D || - kind == G_RefKind_RWTexture2D || - kind == G_RefKind_RWTexture3D; + kind == G_RefKind_RWByteAddressBuffer || + kind == G_RefKind_RWTexture1D || + kind == G_RefKind_RWTexture2D || + kind == G_RefKind_RWTexture3D; if (is_uav) { diff --git a/src/gpu/gpu_shader_core.cgh b/src/gpu/gpu_shader_core.cgh index d7141116..9bab012b 100644 --- a/src/gpu/gpu_shader_core.cgh +++ b/src/gpu/gpu_shader_core.cgh @@ -103,11 +103,11 @@ G_ForceDeclConstant(f32, G_ShaderConst_TweakF32, 10 template u32 countof(RWStructuredBuffer buff) { u32 result; buff.GetDimensions(result); return result; } u32 countof(ByteAddressBuffer buff) { u32 result; buff.GetDimensions(result); return result; } u32 countof(RWByteAddressBuffer buff) { u32 result; buff.GetDimensions(result); return result; } - u32 countof(Texture1D tex) { u32 result; tex.GetDimensions(result); return result; } + template u32 countof(Texture1D tex) { u32 result; tex.GetDimensions(result); return result; } template u32 countof(RWTexture1D tex) { u32 result; tex.GetDimensions(result); return result; } - Vec2U32 countof(Texture2D tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; } + template Vec2U32 countof(Texture2D tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; } template Vec2U32 countof(RWTexture2D tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; } - Vec3U32 countof(Texture3D tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; } + template Vec3U32 countof(Texture3D tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; } template Vec3U32 countof(RWTexture3D tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; } #endif diff --git a/src/pp/pp_sim/pp_sim.lay b/src/pp/pp_sim/pp_sim.lay index b25945a7..1f407f23 100644 --- a/src/pp/pp_sim/pp_sim.lay +++ b/src/pp/pp_sim/pp_sim.lay @@ -8,8 +8,9 @@ ////////////////////////////// //- Api -@IncludeC pp_sim_tiles.cgh -@IncludeG pp_sim_tiles.cgh +@IncludeC pp_sim_shared.cgh +@IncludeG pp_sim_shared.cgh + @IncludeC pp_sim_core.h @IncludeC pp_sim_transcode.h @@ -20,5 +21,5 @@ @IncludeC pp_sim_core.c @IncludeC pp_sim_transcode.c -@IncludeC pp_sim_tiles.cg -@IncludeG pp_sim_tiles.cg +@IncludeC pp_sim_shared.cg +@IncludeG pp_sim_shared.cg diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index 92441099..b5e54db9 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -1469,8 +1469,6 @@ void S_TickForever(WaveLaneCtx *lane) - - ////////////////////////////// //- Debug draw entities diff --git a/src/pp/pp_sim/pp_sim_tiles.cg b/src/pp/pp_sim/pp_sim_shared.cg similarity index 100% rename from src/pp/pp_sim/pp_sim_tiles.cg rename to src/pp/pp_sim/pp_sim_shared.cg diff --git a/src/pp/pp_sim/pp_sim_tiles.cgh b/src/pp/pp_sim/pp_sim_shared.cgh similarity index 95% rename from src/pp/pp_sim/pp_sim_tiles.cgh rename to src/pp/pp_sim/pp_sim_shared.cgh index 51184f05..7522c49d 100644 --- a/src/pp/pp_sim/pp_sim_tiles.cgh +++ b/src/pp/pp_sim/pp_sim_shared.cgh @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////// //~ Tile types -#define S_WorldPitch 96 +#define S_WorldPitch 96.0 #define S_TilesPitch (S_WorldPitch * 2) #define S_TilesCount (S_TilesPitch * S_TilesPitch) diff --git a/src/pp/pp_vis/pp_vis.lay b/src/pp/pp_vis/pp_vis.lay index a273e263..3ac65331 100644 --- a/src/pp/pp_vis/pp_vis.lay +++ b/src/pp/pp_vis/pp_vis.lay @@ -22,15 +22,18 @@ @PixelShader V_DVertPS @VertexShader V_OverlayVS @PixelShader V_OverlayPS +@ComputeShader V_EmitParticlesCS +@ComputeShader V_SimParticlesCS ////////////////////////////// //- Api -@IncludeC pp_vis_shaders.cgh +@IncludeC pp_vis_shared.cgh +@IncludeG pp_vis_shared.cgh + @IncludeC pp_vis_core.h -@IncludeG pp_vis_shaders.cgh -@IncludeG pp_vis_shaders.gh +@IncludeG pp_vis_gpu.gh @Bootstrap V_Bootstrap @@ -39,4 +42,4 @@ @IncludeC pp_vis_core.c -@IncludeG pp_vis_shaders.g +@IncludeG pp_vis_gpu.g diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 000ccbc1..74b11673 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -307,27 +307,67 @@ void V_TickForever(WaveLaneCtx *lane) world->ent_bins_count = Kibi(16); world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count); world->tiles = PushStructs(world_arena, u8, S_TilesCount); - Vec2I32 tiles_dims = VEC2I32(S_TilesPitch, S_TilesPitch); + Vec2I32 tiles_dims = VEC2I32(S_TilesPitch, S_TilesPitch); + Vec2I32 decals_dims = VEC2I32(V_PixelsPerMeter * S_WorldPitch, V_PixelsPerMeter * S_WorldPitch); + + u32 max_particles = Kibi(128); + + // Init gpu state + G_ResourceHandle gpu_state = Zi; G_ResourceHandle gpu_tiles = Zi; + G_ResourceHandle gpu_particles = Zi; + G_ResourceHandle gpu_decals = 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; { + // FIXME: Clear initial resource memory G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct); { - gpu_tiles = G_PushTexture2D( - gpu_perm, cl, - G_Format_R8_Uint, - tiles_dims, - G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite - ); - gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles); - // TODO: Clear tiles from GPU (instead of copy from cpu) - G_CopyCpuToTexture( - cl, - gpu_tiles, VEC3I32(0, 0, 0), - world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), - RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1)) - ); + // Init state buffer + { + gpu_state = G_PushBuffer( + gpu_perm, cl, + V_GpuState, + 1, + .flags = G_ResourceFlag_AllowShaderReadWrite + ); + gpu_state_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_state, V_GpuState); + } + + // Init tile map texture + { + gpu_tiles = G_PushTexture2D( + gpu_perm, cl, + G_Format_R8_Uint, + tiles_dims, + G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite + ); + gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles); + } + // Init particle buffer + { + gpu_particles = G_PushBuffer( + gpu_perm, cl, + V_Particle, + max_particles, + .flags = G_ResourceFlag_AllowShaderReadWrite + ); + gpu_particles_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles, V_Particle); + } + // Init decal texture + { + gpu_decals = G_PushTexture2D( + gpu_perm, cl, + G_Format_R8_Uint, + decals_dims, + G_Layout_DirectQueue_ShaderReadWrite, + .flags = G_ResourceFlag_AllowShaderReadWrite + ); + gpu_decals_ref = G_PushRWTexture2DRef(gpu_perm, gpu_decals); + } } G_CommitCommandList(cl); } @@ -342,7 +382,7 @@ void V_TickForever(WaveLaneCtx *lane) } ////////////////////////////// - //- State + //- Init shortcuts // Init shortcuts u64 shortcut_bins_count = 1024; @@ -420,6 +460,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->time_ns = TimeNs(); frame->dt_ns = ClampI64(frame->time_ns - last_frame->time_ns, 1, NsFromSeconds(1.0 / 50)); frame->dt = SecondsFromNs(frame->dt_ns); + frame->rand = last_frame->rand; if (S_IsKeyNil(V.player_key)) { @@ -727,11 +768,11 @@ void V_TickForever(WaveLaneCtx *lane) } ////////////////////////////// - //- Initialize world <-> draw <-> ui transforms + //- Compute frame xforms // World <-> ui - frame->world_to_ui_xf = XformIdentity; - frame->ui_to_world_xf = XformIdentity; + frame->xf.world_to_ui = XformIdentity; + frame->xf.ui_to_world = XformIdentity; { // Determine target camera pos Vec2 target_camera_pos = Zi; @@ -812,35 +853,45 @@ void V_TickForever(WaveLaneCtx *lane) frame->camera_zoom = LerpF32(last_frame->camera_zoom, target_camera_zoom, frame->camera_lerp_rate); { f32 camera_scale = (f32)frame->draw_dims.x / (meters_per_draw_width * frame->camera_zoom); - frame->world_to_ui_xf = XformFromScale(VEC2(camera_scale, camera_scale)); - frame->world_to_ui_xf = TranslateXform(frame->world_to_ui_xf, NegVec2(frame->camera_pos)); - frame->world_to_ui_xf = WorldTranslateXform(frame->world_to_ui_xf, MulVec2(Vec2FromVec(frame->draw_dims), 0.5)); - frame->ui_to_world_xf = InvertXform(frame->world_to_ui_xf); + frame->xf.world_to_ui = XformFromScale(VEC2(camera_scale, camera_scale)); + frame->xf.world_to_ui = TranslateXform(frame->xf.world_to_ui, NegVec2(frame->camera_pos)); + frame->xf.world_to_ui = WorldTranslateXform(frame->xf.world_to_ui, MulVec2(Vec2FromVec(frame->draw_dims), 0.5)); + frame->xf.ui_to_world = InvertXform(frame->xf.world_to_ui); } } } // Draw <-> ui - frame->draw_to_ui_xf = XformIdentity; - frame->ui_to_draw_xf = XformIdentity; + frame->xf.draw_to_ui = XformIdentity; + frame->xf.ui_to_draw = XformIdentity; { - frame->ui_to_draw_xf = InvertXform(frame->draw_to_ui_xf); + frame->xf.ui_to_draw = InvertXform(frame->xf.draw_to_ui); } // World <-> draw - frame->world_to_draw_xf = XformIdentity; - frame->draw_to_world_xf = XformIdentity; + frame->xf.world_to_draw = XformIdentity; + frame->xf.draw_to_world = XformIdentity; { - frame->world_to_draw_xf = MulXform(frame->world_to_ui_xf, frame->ui_to_draw_xf); - frame->draw_to_world_xf = InvertXform(frame->world_to_draw_xf); + frame->xf.world_to_draw = MulXform(frame->xf.world_to_ui, frame->xf.ui_to_draw); + 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; + { + 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); } ////////////////////////////// //- Update cursors / selection frame->ui_cursor = ui_frame->cursor_pos; - frame->draw_cursor = MulXformV2(frame->ui_to_draw_xf, frame->ui_cursor); - frame->world_cursor = MulXformV2(frame->ui_to_world_xf, frame->ui_cursor); + frame->draw_cursor = MulXformV2(frame->xf.ui_to_draw, frame->ui_cursor); + frame->world_cursor = MulXformV2(frame->xf.ui_to_world, frame->ui_cursor); frame->world_selection_start = frame->world_cursor; if (frame->is_editing) @@ -870,11 +921,11 @@ void V_TickForever(WaveLaneCtx *lane) frame->world_selection.p1.x = MaxF32(frame->world_cursor.x, frame->world_selection_start.x); frame->world_selection.p1.y = MaxF32(frame->world_cursor.y, frame->world_selection_start.y); - frame->ui_selection.p0 = MulXformV2(frame->world_to_ui_xf, frame->world_selection.p0); - frame->ui_selection.p1 = MulXformV2(frame->world_to_ui_xf, frame->world_selection.p1); + frame->ui_selection.p0 = MulXformV2(frame->xf.world_to_ui, frame->world_selection.p0); + frame->ui_selection.p1 = MulXformV2(frame->xf.world_to_ui, frame->world_selection.p1); - frame->draw_selection.p0 = MulXformV2(frame->world_to_draw_xf, frame->world_selection.p0); - frame->draw_selection.p1 = MulXformV2(frame->world_to_draw_xf, frame->world_selection.p1); + frame->draw_selection.p0 = MulXformV2(frame->xf.world_to_draw, frame->world_selection.p0); + frame->draw_selection.p1 = MulXformV2(frame->xf.world_to_draw, frame->world_selection.p1); ////////////////////////////// //- Place tiles @@ -2111,12 +2162,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); 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("Hovered ent: %F", S_FmtKey(hovered_ent->key)); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); @@ -2498,11 +2551,10 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Draw entities - // Build shape buffers for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) { Xform ent_to_world_xf = ent->xf; - Xform ent_to_draw_xf = MulXform(frame->world_to_draw_xf, ent_to_world_xf); + Xform ent_to_draw_xf = MulXform(frame->xf.world_to_draw, ent_to_world_xf); S_Shape draw_shape = S_MulXformShape(ent_to_draw_xf, ent->local_shape); f32 opacity = 0.5; @@ -2561,7 +2613,7 @@ void V_TickForever(WaveLaneCtx *lane) { case S_DebugDrawKind_Point: { - Vec2 ui_p = MulXformV2(frame->world_to_ui_xf, desc->point.p); + Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, desc->point.p); S_Shape ui_shape = S_ShapeFromDesc( .count = 1, .points = { ui_p }, @@ -2572,27 +2624,68 @@ void V_TickForever(WaveLaneCtx *lane) case S_DebugDrawKind_Line: { - Vec2 ui_p0 = MulXformV2(frame->world_to_ui_xf, desc->line.p0); - Vec2 ui_p1 = MulXformV2(frame->world_to_ui_xf, desc->line.p1); + Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, desc->line.p0); + Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, desc->line.p1); V_DrawLine(ui_p0, ui_p1, color); } break; case S_DebugDrawKind_Rect: { Rng2 ui_rect = Zi; - ui_rect.p0 = MulXformV2(frame->world_to_ui_xf, desc->rect.p0); - ui_rect.p1 = MulXformV2(frame->world_to_ui_xf, desc->rect.p1); + ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, desc->rect.p0); + ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, desc->rect.p1); V_DrawRect(ui_rect, color, V_DrawFlag_Line); } break; case S_DebugDrawKind_Shape: { - S_Shape ui_shape = S_MulXformShape(frame->world_to_ui_xf, desc->shape); + S_Shape ui_shape = S_MulXformShape(frame->xf.world_to_ui, desc->shape); V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line); } break; } } + ////////////////////////////// + //- Build emitters + + i64 emitters_count = 0; + V_EmitterNode *first_emitter_node = 0; + V_EmitterNode *last_emitter_node = 0; + + // Spawn test emitters + { + V_Emitter *emitter = 0; + { + V_EmitterNode *en = PushStruct(frame->arena, V_EmitterNode); + SllQueuePush(first_emitter_node, last_emitter_node, en); + emitters_count += 1; + emitter = &en->emitter; + } + emitter->particle_kind = V_ParticleKind_Test; + + Vec2 dir = VEC2(0, 1); + emitter->pos = frame->world_cursor; + emitter->angle = AngleFromVec2(dir); + + emitter->count = 1000; + emitter->speed = 4; + + emitter->seed = RandU64FromState(&frame->rand); + emitter->speed_spread = 1; + emitter->angle_spread = Tau; + } + + // Flatten emitters list + V_Emitter *flattened_emitters = PushStructsNoZero(frame->arena, V_Emitter, emitters_count); + { + i64 emitter_idx = 0; + for (V_EmitterNode *en = first_emitter_node; en; en = en->next) + { + flattened_emitters[emitter_idx] = en->emitter; + ++emitter_idx; + } + } + ////////////////////////////// //- Push data to GPU @@ -2615,15 +2708,20 @@ void V_TickForever(WaveLaneCtx *lane) G_StructuredBufferRef dverts_ro = G_PushStructuredBufferRef(frame->gpu_arena, dverts_buff, V_DVert); G_IndexBufferDesc dvert_idxs_ib = G_IdxBuff32(dvert_idxs_buff); + // Emitters + G_ResourceHandle gpu_emitters = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStructs(flattened_emitters, emitters_count)); + G_StructuredBufferRef gpu_emitters_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_emitters, V_Emitter); + // Params - V_DParams params = Zi; + V_GpuParams params = Zi; { + params.dt = frame->dt; params.target_size = frame->draw_dims; params.target_ro = draw_target_ro; params.target_rw = draw_target_rw; - - params.world_to_draw_xf = frame->world_to_draw_xf; - params.draw_to_world_xf = frame->draw_to_world_xf; + params.xf = frame->xf; + params.seed = RandU64FromState(&frame->rand); + params.noise = G_BasicNoiseTexture(); params.selection_mode = frame->selection_mode; params.equipped_tile = frame->equipped_tile; @@ -2642,12 +2740,21 @@ void V_TickForever(WaveLaneCtx *lane) params.tiles = gpu_tiles_ref; params.shape_verts = dverts_ro; + + params.emitters_count = emitters_count; + params.emitters = gpu_emitters_ref; + + params.max_particles = max_particles; + params.particles = gpu_particles_ref; + + params.decals = gpu_decals_ref; } - G_ResourceHandle params_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(¶ms)); - G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, V_DParams); + G_ResourceHandle gpu_params = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(¶ms)); + G_StructuredBufferRef gpu_params_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_params, V_GpuParams); // Constants - G_SetConstant(frame->cl, V_ShaderConst_Params, params_ro); + G_SetConstant(frame->cl, V_ShaderConst_State, gpu_state_ref); + G_SetConstant(frame->cl, V_ShaderConst_Params, gpu_params_ref); // Sync G_DumbGlobalMemorySync(frame->cl); @@ -2696,6 +2803,19 @@ void V_TickForever(WaveLaneCtx *lane) ); } + ////////////////////////////// + //- Particle simulation pass + + { + // Emit particles + G_Compute(frame->cl, V_EmitParticlesCS, VEC3I32((emitters_count + 63) / 64, 1, 1)); + + G_DumbMemorySync(frame->cl, gpu_particles); + + // Simulate particles + G_Compute(frame->cl, V_SimParticlesCS, VEC3I32((max_particles + 63) / 64, 1, 1)); + } + ////////////////////////////// //- Finalize draw target diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index 8a01421f..ca24f3e4 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -213,6 +213,7 @@ Struct(V_Frame) i64 time_ns; i64 dt_ns; f64 dt; + RandState rand; Button held_buttons[Button_COUNT]; V_Palette palette; @@ -245,17 +246,8 @@ Struct(V_Frame) Vec2 camera_pos; f32 camera_zoom; - // World <-> ui - Xform world_to_ui_xf; - Xform ui_to_world_xf; - - // Draw <-> ui - Xform draw_to_ui_xf; - Xform ui_to_draw_xf; - - // World <-> draw - Xform world_to_draw_xf; - Xform draw_to_world_xf; + // Xforms; + V_Xforms xf; // Cursors Vec2 ui_cursor; diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g new file mode 100644 index 00000000..fd47c77a --- /dev/null +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -0,0 +1,428 @@ +//////////////////////////////////////////////////////////// +//~ Backdrop shader + +ComputeShader2D(V_BackdropCS, 8, 8) +{ + V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; + RWTexture2D target = G_Dereference(params.target_rw); + Texture2D tiles = G_Dereference(params.tiles); + + const Vec4 background_color_a = LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1)); + const Vec4 background_color_b = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1)); + const Vec4 grid_color = LinearFromSrgb(Vec4(0, 0, 0, 1)); + const Vec4 x_axis_color = LinearFromSrgb(Vec4(0.75, 0, 0, 1)); + const Vec4 y_axis_color = LinearFromSrgb(Vec4(0, 0.75, 0, 1)); + const Vec4 bounds_color = LinearFromSrgb(Vec4(0.75, 0.75, 0, 1)); + + Vec2 ui_pos = SV_DispatchThreadID + Vec2(0.5, 0.5); + if (ui_pos.x < params.target_size.x && ui_pos.y < params.target_size.y) + { + Vec4 result = Vec4(0.025, 0.025, 0.025, 1); + Vec2 world_pos = mul(params.xf.ui_to_world, Vec3(ui_pos, 1)); + Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos); + + f32 half_thickness = 1; + f32 half_bounds_size = S_WorldPitch * 0.5; + Vec2 bounds_screen_p0 = mul(params.xf.world_to_draw, Vec3(-half_bounds_size, -half_bounds_size, 1)); + Vec2 bounds_screen_p1 = mul(params.xf.world_to_draw, Vec3(half_bounds_size, half_bounds_size, 1)); + bool is_in_bounds = ui_pos.x > (bounds_screen_p0.x - half_thickness) && + ui_pos.y > (bounds_screen_p0.y - half_thickness) && + ui_pos.x < (bounds_screen_p1.x + half_thickness) && + ui_pos.y < (bounds_screen_p1.y + half_thickness); + if (is_in_bounds) + { + // Grid checker + { + i32 color_idx = 0; + Vec4 colors[2] = { + background_color_a, + background_color_b + }; + const f32 checker_size = 0.5; + Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2)); + if (world_pos_modded.x < checker_size) + { + color_idx = !color_idx; + } + if (world_pos_modded.y < checker_size) + { + color_idx = !color_idx; + } + if (world_pos.x < 0) + { + color_idx = !color_idx; + } + if (world_pos.y < 0) + { + color_idx = !color_idx; + } + result = colors[color_idx]; + } + // Grid outline + { + Vec2 grid_screen_p0 = mul(params.xf.world_to_draw, Vec3(floor(world_pos), 1)); + Vec2 grid_screen_p1 = mul(params.xf.world_to_draw, Vec3(ceil(world_pos), 1)); + f32 grid_dist = 100000; + grid_dist = min(grid_dist, abs(ui_pos.x - grid_screen_p0.x)); + grid_dist = min(grid_dist, abs(ui_pos.x - grid_screen_p1.x)); + grid_dist = min(grid_dist, abs(ui_pos.y - grid_screen_p0.y)); + grid_dist = min(grid_dist, abs(ui_pos.y - grid_screen_p1.y)); + if (grid_dist <= half_thickness) + { + result = grid_color; + } + } + // Tile + { + S_TileKind tile = tiles.Load(Vec3I32(tile_pos, 0)); + switch (tile) + { + default: break; + + case S_TileKind_Floor: + { + result = Color_Blue; + } break; + + case S_TileKind_Wall: + { + result = Color_Red; + } break; + } + } + // Axis + { + Vec2 zero_screen = mul(params.xf.world_to_draw, Vec3(0, 0, 1)); + f32 x_dist = abs(ui_pos.x - zero_screen.x); + f32 y_dist = abs(ui_pos.y - zero_screen.y); + if (y_dist <= half_thickness) + { + result = x_axis_color; + } + else if (x_dist <= half_thickness) + { + result = y_axis_color; + } + } + // World bounds + { + f32 bounds_dist = 100000; + bounds_dist = min(bounds_dist, abs(ui_pos.x - bounds_screen_p0.x)); + bounds_dist = min(bounds_dist, abs(ui_pos.x - bounds_screen_p1.x)); + bounds_dist = min(bounds_dist, abs(ui_pos.y - bounds_screen_p0.y)); + bounds_dist = min(bounds_dist, abs(ui_pos.y - bounds_screen_p1.y)); + if (bounds_dist <= half_thickness) + { + result = bounds_color; + } + } + } + + target[SV_DispatchThreadID] = result; + } +} + +//////////////////////////////////////////////////////////// +//~ Quad shader + +////////////////////////////// +//- Vertex shader + +VertexShader(V_DQuadVS, V_DQuadPSInput) +{ + V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; + StructuredBuffer quads = G_Dereference(params.quads); + RWTexture2D target = G_Dereference(params.target_rw); + + V_DQuad quad = quads[SV_InstanceID]; + + Vec2 rect_uv = RectUvFromVertexId(SV_VertexID); + // Vec2 tex_uv = lerp(quad.tex_uv0, quad.tex_uv1, rect_uv); + // Vec2 target_pos = lerp(quad.p0, quad.p1, rect_uv); + Vec2 target_pos = 0; + + V_DQuadPSInput result; + result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1); + result.quad_idx = SV_InstanceID; + return result; +} + +////////////////////////////// +//- Pixel shader + +PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input) +{ + V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; + StructuredBuffer quads = G_Dereference(params.quads); + V_DQuad quad = quads[input.quad_idx]; + + Vec4 final_color = 0; + + V_DQuadPSOutput output; + output.sv_target0 = final_color; + return output; +} + +//////////////////////////////////////////////////////////// +//~ Particle simulation shaders + +////////////////////////////// +//- Particle emitter shader + +// TODO: Initialize particles in per-particle-sim-thread rather than sequentially in emitter thread + +ComputeShader(V_EmitParticlesCS, 64) +{ + V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; + RWStructuredBuffer state_buff = G_Dereference(V_ShaderConst_State); + StructuredBuffer emitters = G_Dereference(params.emitters); + RWStructuredBuffer particles = G_Dereference(params.particles); + + u32 emitter_idx = SV_DispatchThreadID; + if (emitter_idx < params.emitters_count) + { + V_Emitter emitter = emitters[emitter_idx]; + + u32 first_particle_seq; + InterlockedAdd(state_buff[0].particle_seq, emitter.count, first_particle_seq); + u32 last_particle_seq = first_particle_seq + emitter.count; + + u32 particles_created_count = 0; + for (u32 particle_seq = first_particle_seq; particle_seq < last_particle_seq; ++particle_seq) + { + u32 particle_idx = particle_seq % params.max_particles; + particles[particle_idx].kind = emitter.particle_kind; + particles[particle_idx].emitter_init_num = emitter_idx + 1; + particles[particle_idx].idx_in_emitter = particles_created_count; + particles_created_count += 1; + } + } + + + + // V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; + // RWStructuredBuffer state_buff = G_Dereference(V_ShaderConst_State); + // StructuredBuffer emitters = G_Dereference(params.emitters); + // RWStructuredBuffer particles = G_Dereference(params.particles); + // Texture3D noise3d = G_Dereference(params.noise); + + // u32 emitter_idx = SV_DispatchThreadID; + // if (emitter_idx < params.emitters_count) + // { + // V_Emitter emitter = emitters[emitter_idx]; + + // u32 first_particle_seq; + // InterlockedAdd(state_buff[0].particle_seq, emitter.count, first_particle_seq); + // u32 last_particle_seq = first_particle_seq + emitter.count; + + // Vec2U32 speed_noise_coord; + // Vec2U32 angle_noise_coord; + // { + // speed_noise_coord.x = ((emitter.speed_seed >> 0) & 0xFFFF) % countof(noise3d).x; + // speed_noise_coord.y = ((emitter.speed_seed >> 16) & 0xFFFF) % countof(noise3d).y; + // angle_noise_coord.x = ((emitter.angle_seed >> 0) & 0xFFFF) % countof(noise3d).x; + // angle_noise_coord.y = ((emitter.angle_seed >> 16) & 0xFFFF) % countof(noise3d).y; + // } + + // u64 base_seed = + // for (u32 particle_seq = first_particle_seq; particle_seq < last_particle_seq; ++particle_seq) + // { + // V_Particle particle; + // { + // u32 speed_noise = noise3d.Load(Vec4U32(speed_noise_coord.xy, particle_seq % countof(noise3d).z, 0)); + // u32 angle_noise = noise3d.Load(Vec4U32(angle_noise_coord.xy, particle_seq % countof(noise3d).z, 0)); + // f32 speed_rand = speed_noise / (f32)0xFFFF; + // f32 angle_rand = angle_noise / (f32)0xFFFF; + // f32 speed = emitter.speed + ((speed_rand * 2 - 1) * emitter.speed_spread); + // f32 angle = emitter.angle + ((angle_rand * 2 - 1) * emitter.angle_spread); + + // particle.kind = emitter.particle_kind; + // particle.pos = emitter.pos; + // particle.velocity = Vec2(cos(angle), sin(angle)) * speed; + // } + // particles[particle_seq % params.max_particles] = particle; + // } + + // } +} + +////////////////////////////// +//- Particle sim shader + +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); + + u32 particle_idx = SV_DispatchThreadID; + if (particle_idx < params.max_particles) + { + V_Particle particle = particles[particle_idx]; + if (particle.kind != V_ParticleKind_None) + { + // Initialize + if (particle.emitter_init_num != 0) + { + V_Emitter emitter = G_Dereference(params.emitters)[particle.emitter_init_num - 1]; + + // TODO: Faster mix + u64 seed = MixU64(emitter.seed + particle.idx_in_emitter); + + // TODO: Blue noise + u32 speed_noise = ((u32)seed >> 0) & 0xFFFF; + u32 angle_noise = ((u32)seed >> 16) & 0xFFFF; + f32 speed_rand = speed_noise / (f32)0xFFFF; + f32 angle_rand = angle_noise / (f32)0xFFFF; + + f32 speed = emitter.speed + ((speed_rand * 2 - 1) * emitter.speed_spread); + f32 angle = emitter.angle + ((angle_rand * 2 - 1) * emitter.angle_spread); + + particle.pos = emitter.pos; + particle.velocity = Vec2(cos(angle), sin(angle)) * speed; + + particle.emitter_init_num = 0; + } + + // Simulate + { + f32 dt = params.dt; + particle.pos += particle.velocity * 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) + { + // FIXME: Atomic write + decals[floor(decal_pos)] = particle.kind; + } + } + particles[particle_idx] = particle; + } + } +} + +//////////////////////////////////////////////////////////// +//~ Shape shader + +////////////////////////////// +//- Vertex shader + +VertexShader(V_DVertVS, V_DVertPSInput) +{ + V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; + StructuredBuffer verts = G_Dereference(params.shape_verts); + RWTexture2D target = G_Dereference(params.target_rw); + + V_DVert vert = verts[SV_VertexID]; + + Vec2 target_pos = vert.pos; + + V_DVertPSInput result; + result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1); + result.color_lin = vert.color_lin; + return result; +} + +////////////////////////////// +//- Pixel shader + +PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input) +{ + V_DVertPSOutput output; + output.sv_target0 = input.color_lin; + return output; +} + +//////////////////////////////////////////////////////////// +//~ Overlay shader + +////////////////////////////// +//- Vertex shader + +VertexShader(V_OverlayVS, V_OverlayPSInput) +{ + Vec2 uv = RectUvFromVertexId(SV_VertexID); + V_OverlayPSInput result; + result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1); + return result; +} + +////////////////////////////// +//- Pixel shader + +PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input) +{ + V_GpuParams params = G_Dereference(V_ShaderConst_Params)[0]; + Vec2 ui_pos = input.sv_position.xy; + Vec4 result = 0; + + Vec2 world_pos = mul(params.xf.ui_to_world, Vec3(ui_pos, 1)); + Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos); + S_TileKind equipped_tile = params.equipped_tile; + + f32 half_thickness = 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.8, 0.4, 0.6)); + + Rng2 screen_selection = params.draw_selection; + Rng2 world_selection = params.world_selection; + + Rng2I32 tile_selection; + tile_selection.p0 = S_TilePosFromWorldPos(world_selection.p0); + tile_selection.p1 = S_TilePosFromWorldPos(world_selection.p1); + + if (params.has_mouse_focus) + { + if (params.selection_mode == V_SelectionMode_Tile) + { + f32 dist = 100000000; + dist = min(dist, ui_pos.x - screen_selection.p0.x); + dist = min(dist, ui_pos.y - screen_selection.p0.y); + dist = min(dist, screen_selection.p1.x - ui_pos.x); + dist = min(dist, screen_selection.p1.y - ui_pos.y); + dist = -dist; + + // if (dist >= -half_thickness && dist <= half_thickness) + // { + // result = border_color; + // } + // else + { + if ( + world_pos.x > -(S_WorldPitch / 2) && + world_pos.y > -(S_WorldPitch / 2) && + world_pos.x < (S_WorldPitch / 2) && + world_pos.y < (S_WorldPitch / 2) && + tile_pos.x >= tile_selection.p0.x && + tile_pos.x <= tile_selection.p1.x && + tile_pos.y >= tile_selection.p0.y && + tile_pos.y <= tile_selection.p1.y + ) + { + result = inner_color; + } + } + } + } + + // 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(floor(decal_pos)); + + if (decal == V_ParticleKind_Test) + { + result = Color_Yellow; + } + } + + + V_OverlayPSOutput output; + output.sv_target0 = result; + return output; +} diff --git a/src/pp/pp_vis/pp_vis_shaders.gh b/src/pp/pp_vis/pp_vis_gpu.gh similarity index 86% rename from src/pp/pp_vis/pp_vis_shaders.gh rename to src/pp/pp_vis/pp_vis_gpu.gh index b29865ec..dd473b41 100644 --- a/src/pp/pp_vis/pp_vis_shaders.gh +++ b/src/pp/pp_vis/pp_vis_gpu.gh @@ -17,7 +17,6 @@ Struct(V_DQuadPSOutput) Struct(V_DVertPSInput) { - Semantic(Vec4, sv_position); Semantic(Vec4, color_lin); }; @@ -44,17 +43,21 @@ Struct(V_OverlayPSOutput) //////////////////////////////////////////////////////////// //~ Shaders -//- Backdrop +//- Backdrop shader ComputeShader2D(V_BackdropCS, 8, 8); -//- Quad +//- Quad shader VertexShader(V_DQuadVS, V_DQuadPSInput); PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input); -//- Shape +//- Particle simulation shaders +ComputeShader(V_EmitParticlesCS, 64); +ComputeShader(V_SimParticlesCS, 64); + +//- Shape shader VertexShader(V_DVertVS, V_DVertPSInput); PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input); -//- Overlay +//- Overlay shader VertexShader(V_OverlayVS, V_OverlayPSInput); PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input); diff --git a/src/pp/pp_vis/pp_vis_shaders.cgh b/src/pp/pp_vis/pp_vis_shaders.cgh deleted file mode 100644 index a361c3b9..00000000 --- a/src/pp/pp_vis/pp_vis_shaders.cgh +++ /dev/null @@ -1,67 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Constant types - -G_DeclConstant(G_StructuredBufferRef, V_ShaderConst_Params, 0); - -Enum(V_SelectionMode) -{ - V_SelectionMode_None, - V_SelectionMode_Tile, -}; - -Struct(V_DParams) -{ - Vec2 target_size; - G_Texture2DRef target_ro; - G_RWTexture2DRef target_rw; - - Xform world_to_draw_xf; - Xform draw_to_world_xf; - - V_SelectionMode selection_mode; - S_TileKind equipped_tile; - - b32 has_mouse_focus; - b32 has_keyboard_focus; - Vec2 ui_cursor; - Vec2 draw_cursor; - Vec2 world_cursor; - Rng2 ui_selection; - Rng2 draw_selection; - Rng2 world_selection; - - Vec2 camera_pos; - f32 camera_zoom; - - G_Texture2DRef tiles; - G_StructuredBufferRef quads; - G_StructuredBufferRef shape_verts; -}; - -//////////////////////////////////////////////////////////// -//~ Backdrop shader types - -#define V_BackdropCSThreadSizeFromTexSize(tex_size) VEC3I32((tex_size.x + 7) / 8, (tex_size.y + 7) / 8, 1) - -//////////////////////////////////////////////////////////// -//~ Quad shader types - -Enum(V_DQuadFlag) -{ - V_DQuadFlag_None = 0, - V_DQuadFlag_DrawGrid = (1 << 0), -}; - -Struct(V_DQuad) -{ - V_DQuadFlag flags; -}; - -//////////////////////////////////////////////////////////// -//~ Shape shader types - -Struct(V_DVert) -{ - Vec2 pos; - Vec4 color_lin; -}; diff --git a/src/pp/pp_vis/pp_vis_shaders.g b/src/pp/pp_vis/pp_vis_shaders.g deleted file mode 100644 index 56e2aa48..00000000 --- a/src/pp/pp_vis/pp_vis_shaders.g +++ /dev/null @@ -1,276 +0,0 @@ -//////////////////////////////////////////////////////////// -//~ Backdrop shader - -ComputeShader2D(V_BackdropCS, 8, 8) -{ - V_DParams params = G_Dereference(V_ShaderConst_Params)[0]; - RWTexture2D target = G_Dereference(params.target_rw); - Texture2D tiles = G_Dereference(params.tiles); - - const Vec4 background_color_a = LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1)); - const Vec4 background_color_b = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1)); - const Vec4 grid_color = LinearFromSrgb(Vec4(0, 0, 0, 1)); - const Vec4 x_axis_color = LinearFromSrgb(Vec4(0.75, 0, 0, 1)); - const Vec4 y_axis_color = LinearFromSrgb(Vec4(0, 0.75, 0, 1)); - const Vec4 bounds_color = LinearFromSrgb(Vec4(0.75, 0.75, 0, 1)); - - Vec2 screen_pos = SV_DispatchThreadID + Vec2(0.5, 0.5); - if (screen_pos.x < params.target_size.x && screen_pos.y < params.target_size.y) - { - Vec4 result = Vec4(0.025, 0.025, 0.025, 1); - Vec2 world_pos = mul(params.draw_to_world_xf, Vec3(screen_pos, 1)); - Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos); - - f32 half_thickness = 1; - f32 half_bounds_size = S_WorldPitch * 0.5; - Vec2 bounds_screen_p0 = mul(params.world_to_draw_xf, Vec3(-half_bounds_size, -half_bounds_size, 1)); - Vec2 bounds_screen_p1 = mul(params.world_to_draw_xf, Vec3(half_bounds_size, half_bounds_size, 1)); - bool is_in_bounds = screen_pos.x > (bounds_screen_p0.x - half_thickness) && - screen_pos.y > (bounds_screen_p0.y - half_thickness) && - screen_pos.x < (bounds_screen_p1.x + half_thickness) && - screen_pos.y < (bounds_screen_p1.y + half_thickness); - if (is_in_bounds) - { - // Grid checker - { - i32 color_idx = 0; - Vec4 colors[2] = { - background_color_a, - background_color_b - }; - const f32 checker_size = 0.5; - Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2)); - if (world_pos_modded.x < checker_size) - { - color_idx = !color_idx; - } - if (world_pos_modded.y < checker_size) - { - color_idx = !color_idx; - } - if (world_pos.x < 0) - { - color_idx = !color_idx; - } - if (world_pos.y < 0) - { - color_idx = !color_idx; - } - result = colors[color_idx]; - } - // Grid outline - { - Vec2 grid_screen_p0 = mul(params.world_to_draw_xf, Vec3(floor(world_pos), 1)); - Vec2 grid_screen_p1 = mul(params.world_to_draw_xf, Vec3(ceil(world_pos), 1)); - f32 grid_dist = 100000; - grid_dist = min(grid_dist, abs(screen_pos.x - grid_screen_p0.x)); - grid_dist = min(grid_dist, abs(screen_pos.x - grid_screen_p1.x)); - grid_dist = min(grid_dist, abs(screen_pos.y - grid_screen_p0.y)); - grid_dist = min(grid_dist, abs(screen_pos.y - grid_screen_p1.y)); - if (grid_dist <= half_thickness) - { - result = grid_color; - } - } - // Tile - { - S_TileKind tile = (S_TileKind)tiles.Load(Vec3I32(tile_pos, 0)); - switch (tile) - { - default: break; - - case S_TileKind_Floor: - { - result = Color_Blue; - } break; - - case S_TileKind_Wall: - { - result = Color_Red; - } break; - } - } - // Axis - { - Vec2 zero_screen = mul(params.world_to_draw_xf, Vec3(0, 0, 1)); - f32 x_dist = abs(screen_pos.x - zero_screen.x); - f32 y_dist = abs(screen_pos.y - zero_screen.y); - if (y_dist <= half_thickness) - { - result = x_axis_color; - } - else if (x_dist <= half_thickness) - { - result = y_axis_color; - } - } - // World bounds - { - f32 bounds_dist = 100000; - bounds_dist = min(bounds_dist, abs(screen_pos.x - bounds_screen_p0.x)); - bounds_dist = min(bounds_dist, abs(screen_pos.x - bounds_screen_p1.x)); - bounds_dist = min(bounds_dist, abs(screen_pos.y - bounds_screen_p0.y)); - bounds_dist = min(bounds_dist, abs(screen_pos.y - bounds_screen_p1.y)); - if (bounds_dist <= half_thickness) - { - result = bounds_color; - } - } - } - - target[SV_DispatchThreadID] = result; - } -} - -//////////////////////////////////////////////////////////// -//~ Quad shader - -////////////////////////////// -//- Vertex shader - -VertexShader(V_DQuadVS, V_DQuadPSInput) -{ - V_DParams params = G_Dereference(V_ShaderConst_Params)[0]; - StructuredBuffer quads = G_Dereference(params.quads); - RWTexture2D target = G_Dereference(params.target_rw); - - V_DQuad quad = quads[SV_InstanceID]; - - Vec2 rect_uv = RectUvFromVertexId(SV_VertexID); - // Vec2 tex_uv = lerp(quad.tex_uv0, quad.tex_uv1, rect_uv); - // Vec2 target_pos = lerp(quad.p0, quad.p1, rect_uv); - Vec2 target_pos = 0; - - V_DQuadPSInput result; - result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1); - result.quad_idx = SV_InstanceID; - return result; -} - -////////////////////////////// -//- Pixel shader - -PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input) -{ - V_DParams params = G_Dereference(V_ShaderConst_Params)[0]; - StructuredBuffer quads = G_Dereference(params.quads); - V_DQuad quad = quads[input.quad_idx]; - - Vec4 final_color = 0; - - V_DQuadPSOutput output; - output.sv_target0 = final_color; - return output; -} - -//////////////////////////////////////////////////////////// -//~ Shape shader - -////////////////////////////// -//- Vertex shader - -VertexShader(V_DVertVS, V_DVertPSInput) -{ - V_DParams params = G_Dereference(V_ShaderConst_Params)[0]; - StructuredBuffer verts = G_Dereference(params.shape_verts); - RWTexture2D target = G_Dereference(params.target_rw); - - V_DVert vert = verts[SV_VertexID]; - - Vec2 target_pos = vert.pos; - - V_DVertPSInput result; - result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1); - result.color_lin = vert.color_lin; - return result; -} - -////////////////////////////// -//- Pixel shader - -PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input) -{ - V_DVertPSOutput output; - output.sv_target0 = input.color_lin; - return output; -} - -//////////////////////////////////////////////////////////// -//~ Overlay shader - -////////////////////////////// -//- Vertex shader - -VertexShader(V_OverlayVS, V_OverlayPSInput) -{ - Vec2 uv = RectUvFromVertexId(SV_VertexID); - V_OverlayPSInput result; - result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1); - return result; -} - -////////////////////////////// -//- Pixel shader - -PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input) -{ - V_DParams params = G_Dereference(V_ShaderConst_Params)[0]; - Texture2D tiles = G_Dereference(params.tiles); - Vec2 screen_pos = input.sv_position.xy; - Vec4 result = 0; - - Vec2 world_pos = mul(params.draw_to_world_xf, Vec3(screen_pos, 1)); - Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos); - S_TileKind equipped_tile = params.equipped_tile; - - f32 half_thickness = 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.8, 0.4, 0.6)); - - Rng2 screen_selection = params.draw_selection; - Rng2 world_selection = params.world_selection; - - Rng2I32 tile_selection; - tile_selection.p0 = S_TilePosFromWorldPos(world_selection.p0); - tile_selection.p1 = S_TilePosFromWorldPos(world_selection.p1); - - if (params.has_mouse_focus) - { - if (params.selection_mode == V_SelectionMode_Tile) - { - f32 dist = 100000000; - dist = min(dist, screen_pos.x - screen_selection.p0.x); - dist = min(dist, screen_pos.y - screen_selection.p0.y); - dist = min(dist, screen_selection.p1.x - screen_pos.x); - dist = min(dist, screen_selection.p1.y - screen_pos.y); - dist = -dist; - - // if (dist >= -half_thickness && dist <= half_thickness) - // { - // result = border_color; - // } - // else - { - if ( - world_pos.x > -(S_WorldPitch / 2) && - world_pos.y > -(S_WorldPitch / 2) && - world_pos.x < (S_WorldPitch / 2) && - world_pos.y < (S_WorldPitch / 2) && - tile_pos.x >= tile_selection.p0.x && - tile_pos.x <= tile_selection.p1.x && - tile_pos.y >= tile_selection.p0.y && - tile_pos.y <= tile_selection.p1.y - ) - { - result = inner_color; - } - } - } - } - - V_OverlayPSOutput output; - output.sv_target0 = result; - return output; -} diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh new file mode 100644 index 00000000..cb49ba70 --- /dev/null +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -0,0 +1,146 @@ +#define V_PixelsPerMeter 48.0 +#define V_PixelsPerSqMeter (V_PixelsPerMeter * V_PixelsPerMeter) + +//////////////////////////////////////////////////////////// +//~ Constant types + +G_DeclConstant(G_RWStructuredBufferRef, V_ShaderConst_State, 0); +G_DeclConstant(G_StructuredBufferRef, V_ShaderConst_Params, 1); + +Enum(V_SelectionMode) +{ + V_SelectionMode_None, + V_SelectionMode_Tile, +}; + +Struct(V_Xforms) +{ + // World <-> ui + Xform world_to_ui; + Xform ui_to_world; + + // Draw <-> ui + Xform draw_to_ui; + Xform ui_to_draw; + + // World <-> draw + Xform world_to_draw; + Xform draw_to_world; + + // World <-> decal + Xform world_to_decal; + Xform decal_to_world; +}; + +Struct(V_GpuState) +{ + u32 particle_seq; // Atomic monotonically increasing allocation counter sequence for particle ring buffer +}; + +Struct(V_GpuParams) +{ + f32 dt; + Vec2 target_size; + G_Texture2DRef target_ro; + G_RWTexture2DRef target_rw; + V_Xforms xf; + + u64 seed; + G_Texture3DRef noise; + + V_SelectionMode selection_mode; + S_TileKind equipped_tile; + + b32 has_mouse_focus; + b32 has_keyboard_focus; + Vec2 ui_cursor; + Vec2 draw_cursor; + Vec2 world_cursor; + Rng2 ui_selection; + Rng2 draw_selection; + Rng2 world_selection; + + Vec2 camera_pos; + f32 camera_zoom; + + G_Texture2DRef tiles; + G_StructuredBufferRef quads; + G_StructuredBufferRef shape_verts; + + u32 emitters_count; + G_StructuredBufferRef emitters; + + u32 max_particles; + G_RWStructuredBufferRef particles; + G_RWTexture2DRef decals; +}; + +//////////////////////////////////////////////////////////// +//~ Particle types + +Enum(V_ParticleKind) +{ + V_ParticleKind_None, + V_ParticleKind_Test +}; + +Struct(V_Emitter) +{ + V_ParticleKind particle_kind; + + Vec2 pos; + f32 angle; + u32 count; + f32 speed; + + u64 seed; + f32 angle_spread; + f32 speed_spread; +}; + +// TODO: Pack this efficiently +Struct(V_Particle) +{ + V_ParticleKind kind; + u32 emitter_init_num; // if != 0, then initialize using emitter at index (emitter_init_num - 1) + u32 idx_in_emitter; + + Vec2 pos; + Vec2 velocity; +}; + +#if IsLanguageC + Struct(V_EmitterNode) + { + V_EmitterNode *next; + V_Emitter emitter; + }; +#endif + +//////////////////////////////////////////////////////////// +//~ Backdrop shader types + +#define V_BackdropCSThreadSizeFromTexSize(tex_size) VEC3I32((tex_size.x + 7) / 8, (tex_size.y + 7) / 8, 1) + +//////////////////////////////////////////////////////////// +//~ Quad types + +Enum(V_DQuadFlag) +{ + V_DQuadFlag_None = 0, + V_DQuadFlag_DrawGrid = (1 << 0), +}; + +Struct(V_DQuad) +{ + V_DQuadFlag flags; +}; + +//////////////////////////////////////////////////////////// +//~ Vert types + +Struct(V_DVert) +{ + Vec2 pos; + Vec4 color_lin; +}; diff --git a/src/ui/ui.lay b/src/ui/ui.lay index 2bf8ee34..e7700152 100644 --- a/src/ui/ui.lay +++ b/src/ui/ui.lay @@ -20,13 +20,14 @@ ////////////////////////////// //- Api +@IncludeC ui_shared.cgh +@IncludeG ui_shared.cgh + @IncludeC ui_icon.h @IncludeC ui_core.h @IncludeC ui_extras.h -@IncludeC ui_shaders.cgh -@IncludeG ui_shaders.cgh -@IncludeG ui_shaders.gh +@IncludeG ui_gpu.gh @Bootstrap UI_Bootstrap @@ -35,4 +36,5 @@ @IncludeC ui_core.c @IncludeC ui_extras.c -@IncludeG ui_shaders.g + +@IncludeG ui_gpu.g diff --git a/src/ui/ui_shaders.g b/src/ui/ui_gpu.g similarity index 100% rename from src/ui/ui_shaders.g rename to src/ui/ui_gpu.g diff --git a/src/ui/ui_shaders.gh b/src/ui/ui_gpu.gh similarity index 100% rename from src/ui/ui_shaders.gh rename to src/ui/ui_gpu.gh diff --git a/src/ui/ui_shaders.cgh b/src/ui/ui_shared.cgh similarity index 100% rename from src/ui/ui_shaders.cgh rename to src/ui/ui_shared.cgh