gpu particle testing

This commit is contained in:
jacob 2026-01-06 02:15:45 -06:00
parent 0e269f0e83
commit b8e08c72ad
21 changed files with 806 additions and 453 deletions

Binary file not shown.

Binary file not shown.

View File

@ -486,9 +486,11 @@
typedef u32 b32; typedef u32 b32;
#elif IsLanguageG #elif IsLanguageG
typedef int i32; typedef int i32;
typedef int64_t i64;
typedef uint u32; typedef uint u32;
typedef uint64_t u64;
typedef float f32; typedef float f32;
typedef uint b32; typedef bool b32; // bool has 32-bit size & alignment in HLSL
#endif #endif
//- Min / max constants //- Min / max constants
@ -609,6 +611,7 @@
#define CompLit(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) } #define CompLit(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) }
#define StringFromPointers(p0, p1) ((String) { (u8 *)(p1) - (u8 *)(p0), (u8 *)p0 }) #define StringFromPointers(p0, p1) ((String) { (u8 *)(p1) - (u8 *)(p0), (u8 *)p0 })
#define StringFromStruct(ptr) ((String) { sizeof(*(ptr)), (u8 *)(ptr) }) #define StringFromStruct(ptr) ((String) { sizeof(*(ptr)), (u8 *)(ptr) })
#define StringFromStructs(ptr, count) ((String) { sizeof(*(ptr)) * (count), (u8 *)(ptr) })
#define StringFromArena(arena) (STRING((arena)->pos, ArenaFirst(arena, u8))) #define StringFromArena(arena) (STRING((arena)->pos, ArenaFirst(arena, u8)))
#define StringFromFixedArray(a) \ #define StringFromFixedArray(a) \
@ -742,23 +745,23 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Basic mixing/hashing //~ 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 #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: // FNV-1a parameters for different hash sizes:
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
Inline u64 HashFnv64(u64 seed, String s) Inline u64 HashFnv64(u64 seed, String s)

View File

@ -1224,22 +1224,22 @@ u32 G_PushRef(G_ArenaHandle arena_handle, G_ResourceHandle resource_handle, G_Re
G_RefKind kind = ref_desc.kind; G_RefKind kind = ref_desc.kind;
b32 is_buffer = kind == G_RefKind_StructuredBuffer || b32 is_buffer = kind == G_RefKind_StructuredBuffer ||
kind == G_RefKind_RWStructuredBuffer || kind == G_RefKind_RWStructuredBuffer ||
kind == G_RefKind_ByteAddressBuffer || kind == G_RefKind_ByteAddressBuffer ||
kind == G_RefKind_RWByteAddressBuffer; kind == G_RefKind_RWByteAddressBuffer;
b32 is_sampler = kind == G_RefKind_SamplerState; b32 is_sampler = kind == G_RefKind_SamplerState;
b32 is_texture = !is_buffer && !is_sampler; b32 is_texture = !is_buffer && !is_sampler;
b32 is_raw = kind == G_RefKind_ByteAddressBuffer || b32 is_raw = kind == G_RefKind_ByteAddressBuffer ||
kind == G_RefKind_RWByteAddressBuffer; kind == G_RefKind_RWByteAddressBuffer;
b32 is_uav = kind == G_RefKind_RWStructuredBuffer || b32 is_uav = kind == G_RefKind_RWStructuredBuffer ||
kind == G_RefKind_RWByteAddressBuffer || kind == G_RefKind_RWByteAddressBuffer ||
kind == G_RefKind_RWTexture1D || kind == G_RefKind_RWTexture1D ||
kind == G_RefKind_RWTexture2D || kind == G_RefKind_RWTexture2D ||
kind == G_RefKind_RWTexture3D; kind == G_RefKind_RWTexture3D;
if (is_uav) if (is_uav)
{ {

View File

@ -103,11 +103,11 @@ G_ForceDeclConstant(f32, G_ShaderConst_TweakF32, 10
template<typename T> u32 countof(RWStructuredBuffer<T> buff) { u32 result; buff.GetDimensions(result); return result; } template<typename T> u32 countof(RWStructuredBuffer<T> buff) { u32 result; buff.GetDimensions(result); return result; }
u32 countof(ByteAddressBuffer 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(RWByteAddressBuffer buff) { u32 result; buff.GetDimensions(result); return result; }
u32 countof(Texture1D tex) { u32 result; tex.GetDimensions(result); return result; } template<typename T> u32 countof(Texture1D<T> tex) { u32 result; tex.GetDimensions(result); return result; }
template<typename T> u32 countof(RWTexture1D<T> tex) { u32 result; tex.GetDimensions(result); return result; } template<typename T> u32 countof(RWTexture1D<T> tex) { u32 result; tex.GetDimensions(result); return result; }
Vec2U32 countof(Texture2D tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; } template<typename T> Vec2U32 countof(Texture2D<T> tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; }
template<typename T> Vec2U32 countof(RWTexture2D<T> tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; } template<typename T> Vec2U32 countof(RWTexture2D<T> 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<typename T> Vec3U32 countof(Texture3D<T> tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; }
template<typename T> Vec3U32 countof(RWTexture3D<T> tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; } template<typename T> Vec3U32 countof(RWTexture3D<T> tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; }
#endif #endif

View File

@ -8,8 +8,9 @@
////////////////////////////// //////////////////////////////
//- Api //- Api
@IncludeC pp_sim_tiles.cgh @IncludeC pp_sim_shared.cgh
@IncludeG pp_sim_tiles.cgh @IncludeG pp_sim_shared.cgh
@IncludeC pp_sim_core.h @IncludeC pp_sim_core.h
@IncludeC pp_sim_transcode.h @IncludeC pp_sim_transcode.h
@ -20,5 +21,5 @@
@IncludeC pp_sim_core.c @IncludeC pp_sim_core.c
@IncludeC pp_sim_transcode.c @IncludeC pp_sim_transcode.c
@IncludeC pp_sim_tiles.cg @IncludeC pp_sim_shared.cg
@IncludeG pp_sim_tiles.cg @IncludeG pp_sim_shared.cg

View File

@ -1469,8 +1469,6 @@ void S_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Debug draw entities //- Debug draw entities

View File

@ -1,7 +1,7 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Tile types //~ Tile types
#define S_WorldPitch 96 #define S_WorldPitch 96.0
#define S_TilesPitch (S_WorldPitch * 2) #define S_TilesPitch (S_WorldPitch * 2)
#define S_TilesCount (S_TilesPitch * S_TilesPitch) #define S_TilesCount (S_TilesPitch * S_TilesPitch)

View File

@ -22,15 +22,18 @@
@PixelShader V_DVertPS @PixelShader V_DVertPS
@VertexShader V_OverlayVS @VertexShader V_OverlayVS
@PixelShader V_OverlayPS @PixelShader V_OverlayPS
@ComputeShader V_EmitParticlesCS
@ComputeShader V_SimParticlesCS
////////////////////////////// //////////////////////////////
//- Api //- Api
@IncludeC pp_vis_shaders.cgh @IncludeC pp_vis_shared.cgh
@IncludeG pp_vis_shared.cgh
@IncludeC pp_vis_core.h @IncludeC pp_vis_core.h
@IncludeG pp_vis_shaders.cgh @IncludeG pp_vis_gpu.gh
@IncludeG pp_vis_shaders.gh
@Bootstrap V_Bootstrap @Bootstrap V_Bootstrap
@ -39,4 +42,4 @@
@IncludeC pp_vis_core.c @IncludeC pp_vis_core.c
@IncludeG pp_vis_shaders.g @IncludeG pp_vis_gpu.g

View File

@ -307,27 +307,67 @@ void V_TickForever(WaveLaneCtx *lane)
world->ent_bins_count = Kibi(16); world->ent_bins_count = Kibi(16);
world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count); world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count);
world->tiles = PushStructs(world_arena, u8, S_TilesCount); 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_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_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); G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct);
{ {
gpu_tiles = G_PushTexture2D( // Init state buffer
gpu_perm, cl, {
G_Format_R8_Uint, gpu_state = G_PushBuffer(
tiles_dims, gpu_perm, cl,
G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite V_GpuState,
); 1,
gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles); .flags = G_ResourceFlag_AllowShaderReadWrite
// TODO: Clear tiles from GPU (instead of copy from cpu) );
G_CopyCpuToTexture( gpu_state_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_state, V_GpuState);
cl, }
gpu_tiles, VEC3I32(0, 0, 0),
world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1), // Init tile map texture
RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1)) {
); 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); G_CommitCommandList(cl);
} }
@ -342,7 +382,7 @@ void V_TickForever(WaveLaneCtx *lane)
} }
////////////////////////////// //////////////////////////////
//- State //- Init shortcuts
// Init shortcuts // Init shortcuts
u64 shortcut_bins_count = 1024; u64 shortcut_bins_count = 1024;
@ -420,6 +460,7 @@ void V_TickForever(WaveLaneCtx *lane)
frame->time_ns = TimeNs(); frame->time_ns = TimeNs();
frame->dt_ns = ClampI64(frame->time_ns - last_frame->time_ns, 1, NsFromSeconds(1.0 / 50)); frame->dt_ns = ClampI64(frame->time_ns - last_frame->time_ns, 1, NsFromSeconds(1.0 / 50));
frame->dt = SecondsFromNs(frame->dt_ns); frame->dt = SecondsFromNs(frame->dt_ns);
frame->rand = last_frame->rand;
if (S_IsKeyNil(V.player_key)) 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 // World <-> ui
frame->world_to_ui_xf = XformIdentity; frame->xf.world_to_ui = XformIdentity;
frame->ui_to_world_xf = XformIdentity; frame->xf.ui_to_world = XformIdentity;
{ {
// Determine target camera pos // Determine target camera pos
Vec2 target_camera_pos = Zi; 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); 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); 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->xf.world_to_ui = XformFromScale(VEC2(camera_scale, camera_scale));
frame->world_to_ui_xf = TranslateXform(frame->world_to_ui_xf, NegVec2(frame->camera_pos)); frame->xf.world_to_ui = TranslateXform(frame->xf.world_to_ui, NegVec2(frame->camera_pos));
frame->world_to_ui_xf = WorldTranslateXform(frame->world_to_ui_xf, MulVec2(Vec2FromVec(frame->draw_dims), 0.5)); frame->xf.world_to_ui = WorldTranslateXform(frame->xf.world_to_ui, MulVec2(Vec2FromVec(frame->draw_dims), 0.5));
frame->ui_to_world_xf = InvertXform(frame->world_to_ui_xf); frame->xf.ui_to_world = InvertXform(frame->xf.world_to_ui);
} }
} }
} }
// Draw <-> ui // Draw <-> ui
frame->draw_to_ui_xf = XformIdentity; frame->xf.draw_to_ui = XformIdentity;
frame->ui_to_draw_xf = 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 // World <-> draw
frame->world_to_draw_xf = XformIdentity; frame->xf.world_to_draw = XformIdentity;
frame->draw_to_world_xf = XformIdentity; frame->xf.draw_to_world = XformIdentity;
{ {
frame->world_to_draw_xf = MulXform(frame->world_to_ui_xf, frame->ui_to_draw_xf); frame->xf.world_to_draw = MulXform(frame->xf.world_to_ui, frame->xf.ui_to_draw);
frame->draw_to_world_xf = InvertXform(frame->world_to_draw_xf); 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 //- Update cursors / selection
frame->ui_cursor = ui_frame->cursor_pos; frame->ui_cursor = ui_frame->cursor_pos;
frame->draw_cursor = MulXformV2(frame->ui_to_draw_xf, frame->ui_cursor); frame->draw_cursor = MulXformV2(frame->xf.ui_to_draw, frame->ui_cursor);
frame->world_cursor = MulXformV2(frame->ui_to_world_xf, frame->ui_cursor); frame->world_cursor = MulXformV2(frame->xf.ui_to_world, frame->ui_cursor);
frame->world_selection_start = frame->world_cursor; frame->world_selection_start = frame->world_cursor;
if (frame->is_editing) 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.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->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.p0 = MulXformV2(frame->xf.world_to_ui, frame->world_selection.p0);
frame->ui_selection.p1 = MulXformV2(frame->world_to_ui_xf, frame->world_selection.p1); 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.p0 = MulXformV2(frame->xf.world_to_draw, frame->world_selection.p0);
frame->draw_selection.p1 = MulXformV2(frame->world_to_draw_xf, frame->world_selection.p1); frame->draw_selection.p1 = MulXformV2(frame->xf.world_to_draw, frame->world_selection.p1);
////////////////////////////// //////////////////////////////
//- Place tiles //- Place tiles
@ -2111,12 +2162,14 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
Vec2I32 tile_pos = S_TilePosFromWorldPos(frame->world_cursor); 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); i32 tile_idx = S_TileIdxFromTilePos(tile_pos);
{ {
UI_BuildLabelF("Camera pos: %F", FmtFloat2(frame->camera_pos)); UI_BuildLabelF("Camera pos: %F", FmtFloat2(frame->camera_pos));
UI_BuildLabelF("Cursor world pos: %F", FmtFloat2(frame->world_cursor)); UI_BuildLabelF("Cursor world pos: %F", FmtFloat2(frame->world_cursor));
UI_BuildLabelF("Cursor tile pos: %F", FmtSint2(tile_pos)); UI_BuildLabelF("Cursor tile pos: %F", FmtSint2(tile_pos));
UI_BuildLabelF("Cursor tile idx: %F", FmtSint(tile_idx)); 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_BuildLabelF("Hovered ent: %F", S_FmtKey(hovered_ent->key));
} }
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
@ -2498,11 +2551,10 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Draw entities //- Draw entities
// Build shape buffers
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
{ {
Xform ent_to_world_xf = ent->xf; 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); S_Shape draw_shape = S_MulXformShape(ent_to_draw_xf, ent->local_shape);
f32 opacity = 0.5; f32 opacity = 0.5;
@ -2561,7 +2613,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
case S_DebugDrawKind_Point: 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( S_Shape ui_shape = S_ShapeFromDesc(
.count = 1, .count = 1,
.points = { ui_p }, .points = { ui_p },
@ -2572,27 +2624,68 @@ void V_TickForever(WaveLaneCtx *lane)
case S_DebugDrawKind_Line: case S_DebugDrawKind_Line:
{ {
Vec2 ui_p0 = MulXformV2(frame->world_to_ui_xf, desc->line.p0); Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, desc->line.p0);
Vec2 ui_p1 = MulXformV2(frame->world_to_ui_xf, desc->line.p1); Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, desc->line.p1);
V_DrawLine(ui_p0, ui_p1, color); V_DrawLine(ui_p0, ui_p1, color);
} break; } break;
case S_DebugDrawKind_Rect: case S_DebugDrawKind_Rect:
{ {
Rng2 ui_rect = Zi; Rng2 ui_rect = Zi;
ui_rect.p0 = MulXformV2(frame->world_to_ui_xf, desc->rect.p0); ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, desc->rect.p0);
ui_rect.p1 = MulXformV2(frame->world_to_ui_xf, desc->rect.p1); ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, desc->rect.p1);
V_DrawRect(ui_rect, color, V_DrawFlag_Line); V_DrawRect(ui_rect, color, V_DrawFlag_Line);
} break; } break;
case S_DebugDrawKind_Shape: 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); V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
} break; } 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 //- 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_StructuredBufferRef dverts_ro = G_PushStructuredBufferRef(frame->gpu_arena, dverts_buff, V_DVert);
G_IndexBufferDesc dvert_idxs_ib = G_IdxBuff32(dvert_idxs_buff); 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 // Params
V_DParams params = Zi; V_GpuParams params = Zi;
{ {
params.dt = frame->dt;
params.target_size = frame->draw_dims; params.target_size = frame->draw_dims;
params.target_ro = draw_target_ro; params.target_ro = draw_target_ro;
params.target_rw = draw_target_rw; params.target_rw = draw_target_rw;
params.xf = frame->xf;
params.world_to_draw_xf = frame->world_to_draw_xf; params.seed = RandU64FromState(&frame->rand);
params.draw_to_world_xf = frame->draw_to_world_xf; params.noise = G_BasicNoiseTexture();
params.selection_mode = frame->selection_mode; params.selection_mode = frame->selection_mode;
params.equipped_tile = frame->equipped_tile; params.equipped_tile = frame->equipped_tile;
@ -2642,12 +2740,21 @@ void V_TickForever(WaveLaneCtx *lane)
params.tiles = gpu_tiles_ref; params.tiles = gpu_tiles_ref;
params.shape_verts = dverts_ro; 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(&params)); G_ResourceHandle gpu_params = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(&params));
G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, V_DParams); G_StructuredBufferRef gpu_params_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_params, V_GpuParams);
// Constants // 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 // Sync
G_DumbGlobalMemorySync(frame->cl); 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 //- Finalize draw target

View File

@ -213,6 +213,7 @@ Struct(V_Frame)
i64 time_ns; i64 time_ns;
i64 dt_ns; i64 dt_ns;
f64 dt; f64 dt;
RandState rand;
Button held_buttons[Button_COUNT]; Button held_buttons[Button_COUNT];
V_Palette palette; V_Palette palette;
@ -245,17 +246,8 @@ Struct(V_Frame)
Vec2 camera_pos; Vec2 camera_pos;
f32 camera_zoom; f32 camera_zoom;
// World <-> ui // Xforms;
Xform world_to_ui_xf; V_Xforms 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;
// Cursors // Cursors
Vec2 ui_cursor; Vec2 ui_cursor;

428
src/pp/pp_vis/pp_vis_gpu.g Normal file
View File

@ -0,0 +1,428 @@
////////////////////////////////////////////////////////////
//~ Backdrop shader
ComputeShader2D(V_BackdropCS, 8, 8)
{
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
Texture2D<S_TileKind> tiles = G_Dereference<S_TileKind>(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_GpuParams>(V_ShaderConst_Params)[0];
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
RWTexture2D<Vec4> target = G_Dereference<Vec4>(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_GpuParams>(V_ShaderConst_Params)[0];
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(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_GpuParams>(V_ShaderConst_Params)[0];
RWStructuredBuffer<V_GpuState> state_buff = G_Dereference<V_GpuState>(V_ShaderConst_State);
StructuredBuffer<V_Emitter> emitters = G_Dereference<V_Emitter>(params.emitters);
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(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_GpuParams>(V_ShaderConst_Params)[0];
// RWStructuredBuffer<V_GpuState> state_buff = G_Dereference<V_GpuState>(V_ShaderConst_State);
// StructuredBuffer<V_Emitter> emitters = G_Dereference<V_Emitter>(params.emitters);
// RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
// Texture3D<u32> noise3d = G_Dereference<u32>(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_GpuParams>(V_ShaderConst_Params)[0];
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
RWTexture2D<V_ParticleKind> decals = G_Dereference<V_ParticleKind>(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<V_Emitter>(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_GpuParams>(V_ShaderConst_Params)[0];
StructuredBuffer<V_DVert> verts = G_Dereference<V_DVert>(params.shape_verts);
RWTexture2D<Vec4> target = G_Dereference<Vec4>(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_GpuParams>(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<V_ParticleKind> decals = G_Dereference<V_ParticleKind>(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;
}

View File

@ -17,7 +17,6 @@ Struct(V_DQuadPSOutput)
Struct(V_DVertPSInput) Struct(V_DVertPSInput)
{ {
Semantic(Vec4, sv_position); Semantic(Vec4, sv_position);
Semantic(Vec4, color_lin); Semantic(Vec4, color_lin);
}; };
@ -44,17 +43,21 @@ Struct(V_OverlayPSOutput)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Shaders //~ Shaders
//- Backdrop //- Backdrop shader
ComputeShader2D(V_BackdropCS, 8, 8); ComputeShader2D(V_BackdropCS, 8, 8);
//- Quad //- Quad shader
VertexShader(V_DQuadVS, V_DQuadPSInput); VertexShader(V_DQuadVS, V_DQuadPSInput);
PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input); 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); VertexShader(V_DVertVS, V_DVertPSInput);
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input); PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input);
//- Overlay //- Overlay shader
VertexShader(V_OverlayVS, V_OverlayPSInput); VertexShader(V_OverlayVS, V_OverlayPSInput);
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input); PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input);

View File

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

View File

@ -1,276 +0,0 @@
////////////////////////////////////////////////////////////
//~ Backdrop shader
ComputeShader2D(V_BackdropCS, 8, 8)
{
V_DParams params = G_Dereference<V_DParams>(V_ShaderConst_Params)[0];
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
Texture2D<uint> tiles = G_Dereference<uint>(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_DParams>(V_ShaderConst_Params)[0];
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
RWTexture2D<Vec4> target = G_Dereference<Vec4>(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_DParams>(V_ShaderConst_Params)[0];
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(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_DParams>(V_ShaderConst_Params)[0];
StructuredBuffer<V_DVert> verts = G_Dereference<V_DVert>(params.shape_verts);
RWTexture2D<Vec4> target = G_Dereference<Vec4>(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_DParams>(V_ShaderConst_Params)[0];
Texture2D<uint> tiles = G_Dereference<uint>(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;
}

View File

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

View File

@ -20,13 +20,14 @@
////////////////////////////// //////////////////////////////
//- Api //- Api
@IncludeC ui_shared.cgh
@IncludeG ui_shared.cgh
@IncludeC ui_icon.h @IncludeC ui_icon.h
@IncludeC ui_core.h @IncludeC ui_core.h
@IncludeC ui_extras.h @IncludeC ui_extras.h
@IncludeC ui_shaders.cgh
@IncludeG ui_shaders.cgh @IncludeG ui_gpu.gh
@IncludeG ui_shaders.gh
@Bootstrap UI_Bootstrap @Bootstrap UI_Bootstrap
@ -35,4 +36,5 @@
@IncludeC ui_core.c @IncludeC ui_core.c
@IncludeC ui_extras.c @IncludeC ui_extras.c
@IncludeG ui_shaders.g
@IncludeG ui_gpu.g