particle falloff testing

This commit is contained in:
jacob 2026-01-06 23:35:49 -06:00
parent 87a624eaeb
commit 869b415ebb
11 changed files with 460 additions and 297 deletions

View File

@ -58,7 +58,7 @@ void G_BootstrapCommon(void)
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Helpers //~ Utils
//- Arena //- Arena
@ -71,17 +71,12 @@ G_ArenaHandle G_PermArena(void)
return G_tl.gpu_perm; return G_tl.gpu_perm;
} }
//- Cpu -> Gpu upload //- Push resource from cpu
G_ResourceHandle G_PushBufferFromString_(G_ArenaHandle gpu_arena, G_CommandListHandle cl, String src, G_BufferDesc desc) G_ResourceHandle G_PushBufferFromCpuCopy_(G_ArenaHandle gpu_arena, G_CommandListHandle cl, String src, G_BufferDesc desc)
{ {
G_ResourceHandle buffer = G_PushResource(gpu_arena, cl, (G_ResourceDesc) { .kind = G_ResourceKind_Buffer, .buffer = desc }); G_ResourceHandle buffer = G_PushResource(gpu_arena, cl, (G_ResourceDesc) { .kind = G_ResourceKind_Buffer, .buffer = desc });
G_CopyCpuToBuffer(cl, buffer, 0, src.text, RNGU64(0, src.len)); G_CopyCpuToBuffer(cl, buffer, 0, src.text, RNGU64(0, src.len));
G_MemorySync(
cl, buffer,
G_Stage_Copy, G_Access_CopyWrite,
G_Stage_All, G_Access_All
);
return buffer; return buffer;
} }

View File

@ -23,17 +23,17 @@ extern ThreadLocal G_ThreadLocalCtx G_tl;
void G_BootstrapCommon(void); void G_BootstrapCommon(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Helpers //~ Utils
//- Arena //- Arena
G_ArenaHandle G_PermArena(void); G_ArenaHandle G_PermArena(void);
//- Cpu -> Gpu upload //- Push resource from cpu
G_ResourceHandle G_PushBufferFromString_(G_ArenaHandle gpu_arena, G_CommandListHandle cl, String src, G_BufferDesc desc); G_ResourceHandle G_PushBufferFromCpuCopy_(G_ArenaHandle gpu_arena, G_CommandListHandle cl, String src, G_BufferDesc desc);
#define G_PushBufferFromString(_arena, _cl, _src, ...) \ #define G_PushBufferFromCpuCopy(_arena, _cl, _src, ...) \
G_PushBufferFromString_((_arena), (_cl), (_src), (G_BufferDesc) { .size = (_src).len, __VA_ARGS__ }) G_PushBufferFromCpuCopy_((_arena), (_cl), (_src), (G_BufferDesc) { .size = (_src).len, __VA_ARGS__ })
//- Viewport / scissor //- Viewport / scissor

View File

@ -212,13 +212,13 @@ Enum(G_Stage)
G_Stage_Indirect = (1 << 8), G_Stage_Indirect = (1 << 8),
// Aggregate stages // Aggregate stages
G_Stage_AllDraw = G_Stage_IndexAssembly | G_Stage_Drawing = G_Stage_IndexAssembly |
G_Stage_VertexShading | G_Stage_VertexShading |
G_Stage_PixelShading | G_Stage_PixelShading |
G_Stage_DepthStencil | G_Stage_DepthStencil |
G_Stage_RenderTarget, G_Stage_RenderTarget,
G_Stage_AllShading = G_Stage_ComputeShading | G_Stage_Shading = G_Stage_ComputeShading |
G_Stage_VertexShading | G_Stage_VertexShading |
G_Stage_PixelShading, G_Stage_PixelShading,
@ -409,8 +409,9 @@ Enum(G_ResourceFlag)
G_ResourceFlag_AllowShaderReadWrite = (1 << 0), G_ResourceFlag_AllowShaderReadWrite = (1 << 0),
G_ResourceFlag_AllowRenderTarget = (1 << 1), G_ResourceFlag_AllowRenderTarget = (1 << 1),
G_ResourceFlag_AllowDepthStencil = (1 << 2), G_ResourceFlag_AllowDepthStencil = (1 << 2),
G_ResourceFlag_HostMemory = (1 << 3), // Resource will be mapped into the cpu's address space G_ResourceFlag_ZeroMemory = (1 << 3),
G_ResourceFlag_Uncached = (1 << 4), // Cpu writes will be combined & reads will be uncached G_ResourceFlag_HostMemory = (1 << 4), // Resource will be mapped into the cpu's address space
G_ResourceFlag_Uncached = (1 << 5), // Cpu writes will be combined & reads will be uncached
}; };
Struct(G_BufferDesc) Struct(G_BufferDesc)

View File

@ -898,6 +898,8 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
////////////////////////////// //////////////////////////////
//- Initialize heap info //- Initialize heap info
b32 can_reuse = 0;
D3D12_HEAP_FLAGS heap_flags = 0; D3D12_HEAP_FLAGS heap_flags = 0;
D3D12_HEAP_PROPERTIES heap_props = Zi; D3D12_HEAP_PROPERTIES heap_props = Zi;
b32 should_map = 0; b32 should_map = 0;
@ -913,7 +915,15 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
heap_kind = G_D12_ResourceHeapKind_CpuWriteCombined; heap_kind = G_D12_ResourceHeapKind_CpuWriteCombined;
} }
} }
if (flags & G_ResourceFlag_ZeroMemory)
{
can_reuse = 0;
}
else
{
can_reuse = 1;
heap_flags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; heap_flags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
}
// Heap props // Heap props
if (heap_kind == G_D12_ResourceHeapKind_Cpu) if (heap_kind == G_D12_ResourceHeapKind_Cpu)
{ {
@ -986,7 +996,6 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
// Pop reset resource // Pop reset resource
resource = gpu_arena->reset_resources.first; resource = gpu_arena->reset_resources.first;
b32 is_reusing = 0;
if (resource) if (resource)
{ {
DllQueueRemove(gpu_arena->reset_resources.first, gpu_arena->reset_resources.last, resource); DllQueueRemove(gpu_arena->reset_resources.first, gpu_arena->reset_resources.last, resource);
@ -1004,9 +1013,9 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
} }
} }
// TODO: Less stringent reset constraints. We could even create textures as placed resources and reset their underlying heaps. // TODO: Less stringent reuse constraints. We could even create textures as placed resources and reset their underlying heaps.
is_reusing = MatchStruct(&compare_d3d_desc, &d3d_desc); can_reuse = can_reuse && MatchStruct(&compare_d3d_desc, &d3d_desc);
if (!is_reusing) if (!can_reuse)
{ {
// Push releasable to command list // Push releasable to command list
{ {
@ -1035,10 +1044,11 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
} }
else else
{ {
can_reuse = 0;
resource = PushStruct(gpu_arena->arena, G_D12_Resource); resource = PushStruct(gpu_arena->arena, G_D12_Resource);
} }
if (!is_reusing) if (!can_reuse)
{ {
resource->d3d_desc = d3d_desc; resource->d3d_desc = d3d_desc;
} }
@ -1126,12 +1136,20 @@ G_ResourceHandle G_PushResource(G_ArenaHandle arena_handle, G_CommandListHandle
} }
////////////////////////////// //////////////////////////////
//- Transition layout if reusing //- Barrier if reusing
if (is_reusing) // TODO: These barriers are too cautious; it's rare that an arena would ever be reset mid-command list.
if (can_reuse)
{
if (is_buffer)
{
G_DumbMemorySync(cl_handle, G_D12_MakeHandle(G_ResourceHandle, resource));
}
else if (is_texture)
{ {
G_DumbMemoryLayoutSync(cl_handle, G_D12_MakeHandle(G_ResourceHandle, resource), desc.texture.initial_layout); G_DumbMemoryLayoutSync(cl_handle, G_D12_MakeHandle(G_ResourceHandle, resource), desc.texture.initial_layout);
} }
}
return G_D12_MakeHandle(G_ResourceHandle, resource); return G_D12_MakeHandle(G_ResourceHandle, resource);
} }
@ -2717,7 +2735,7 @@ G_QueueCompletions G_CompletionValuesFromQueues(G_QueueMask queue_mask)
{ {
if (queue_mask & (1 << queue_kind)) if (queue_mask & (1 << queue_kind))
{ {
completions.v[queue_kind] = G_CompletionTargetFromQueue(queue_kind); completions.v[queue_kind] = G_CompletionValueFromQueue(queue_kind);
} }
} }
return completions; return completions;
@ -3279,7 +3297,7 @@ void G_D12_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame
G_D12_Releasable *release = async->pending_releases.first; G_D12_Releasable *release = async->pending_releases.first;
if (release) if (release)
{ {
G_QueueCompletions completions = G_CompletionTargetsFromQueues(G_QueueMask_All); G_QueueCompletions completions = G_CompletionValuesFromQueues(G_QueueMask_All);
while (release) while (release)
{ {
G_D12_Releasable *next = release->next; G_D12_Releasable *next = release->next;

View File

@ -1,4 +1,5 @@
S_Ctx S = Zi; S_Ctx S = Zi;
ThreadLocal S_ThreadLocalCtx S_tl = Zi;
Readonly S_Ent S_NilEnt = { Readonly S_Ent S_NilEnt = {
.last_xf = CompXformIdentity, .last_xf = CompXformIdentity,
@ -86,10 +87,9 @@ String S_NameFromTileKind(S_TileKind kind)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Delta helpers //~ Delta helpers
Rng2I32 S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta) void S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta)
{ {
// FIXME: Bounds check tile deltas // FIXME: Bounds check tile deltas
Rng2I32 dirty_tile_rect = Zi;
if (0) if (0)
{ {
} }
@ -142,7 +142,6 @@ Rng2I32 S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta)
world->tiles[tile_idx] = src_tile; world->tiles[tile_idx] = src_tile;
} }
} }
dirty_tile_rect = range;
} }
//- Tile range //- Tile range
else if (delta->kind == S_DeltaKind_Tile) else if (delta->kind == S_DeltaKind_Tile)
@ -158,10 +157,7 @@ Rng2I32 S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta)
world->tiles[tile_idx] = (u8)tile; world->tiles[tile_idx] = (u8)tile;
} }
} }
dirty_tile_rect = range;
} }
return dirty_tile_rect;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -314,9 +310,9 @@ Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal)
return result; return result;
} }
S_CollisionData S_CollisionDataFromShapes(S_Shape shape0, S_Shape shape1, Vec2 sweep) S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1, Vec2 sweep)
{ {
S_CollisionData result = Zi; S_CollisionResult result = Zi;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
f32 tolerance = 0.00005f; // How close can non-overlapping shapes be before collision is considered f32 tolerance = 0.00005f; // How close can non-overlapping shapes be before collision is considered
@ -926,16 +922,16 @@ S_CollisionData S_CollisionDataFromShapes(S_Shape shape0, S_Shape shape1, Vec2 s
return result; return result;
} }
S_RaycastData S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir) S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir)
{ {
S_RaycastData result = Zi; S_RaycastResult result = Zi;
// HACK: Create line shape as large as the world and perform a collision test // HACK: Create line shape as large as the world and perform a collision test
Vec2 ray_shape_p0 = ray_start; Vec2 ray_shape_p0 = ray_start;
Vec2 ray_shape_p1 = AddVec2(ray_start, MulVec2(NormVec2(ray_dir), S_WorldPitch * 1.414213562)); Vec2 ray_shape_p1 = AddVec2(ray_start, MulVec2(NormVec2(ray_dir), S_WorldPitch * 1.414213562));
S_Shape ray_shape = S_ShapeFromDesc(.count = 2, .points = { ray_shape_p0, ray_shape_p1 }); S_Shape ray_shape = S_ShapeFromDesc(.count = 2, .points = { ray_shape_p0, ray_shape_p1 });
S_CollisionData cls = S_CollisionDataFromShapes(ray_shape, shape, ray_dir); S_CollisionResult cls = S_CollisionResultFromShapes(ray_shape, shape, ray_dir);
result.is_intersecting = cls.collision_points_count > 0; result.is_intersecting = cls.collision_points_count > 0;
result.normal = NegVec2(cls.collision_normal); result.normal = NegVec2(cls.collision_normal);
result.p = cls.closest_p1; result.p = cls.closest_p1;
@ -992,7 +988,7 @@ S_Ent *S_NextEnt(S_Ent *e)
void S_DebugDrawPoint(Vec2 p, Vec4 srgb) void S_DebugDrawPoint(Vec2 p, Vec4 srgb)
{ {
if (S.debug_draw_enabled) if (S_tl.is_sim_tick_thread && S.debug_draw_enabled)
{ {
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc); S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Point; desc->kind = S_DebugDrawKind_Point;
@ -1003,7 +999,7 @@ void S_DebugDrawPoint(Vec2 p, Vec4 srgb)
void S_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb) void S_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb)
{ {
if (S.debug_draw_enabled) if (S_tl.is_sim_tick_thread && S.debug_draw_enabled)
{ {
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc); S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Line; desc->kind = S_DebugDrawKind_Line;
@ -1015,7 +1011,7 @@ void S_DebugDrawLine(Vec2 p0, Vec2 p1, Vec4 srgb)
void S_DebugDrawRect(Rng2 rect, Vec4 srgb) void S_DebugDrawRect(Rng2 rect, Vec4 srgb)
{ {
if (S.debug_draw_enabled) if (S_tl.is_sim_tick_thread && S.debug_draw_enabled)
{ {
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc); S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Rect; desc->kind = S_DebugDrawKind_Rect;
@ -1026,7 +1022,7 @@ void S_DebugDrawRect(Rng2 rect, Vec4 srgb)
void S_DebugDrawShape(S_Shape shape, Vec4 srgb) void S_DebugDrawShape(S_Shape shape, Vec4 srgb)
{ {
if (S.debug_draw_enabled) if (S_tl.is_sim_tick_thread && S.debug_draw_enabled)
{ {
S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc); S_DebugDrawDesc *desc = PushStruct(S.debug_draw_descs_arena, S_DebugDrawDesc);
desc->kind = S_DebugDrawKind_Shape; desc->kind = S_DebugDrawKind_Shape;
@ -1040,6 +1036,7 @@ void S_DebugDrawShape(S_Shape shape, Vec4 srgb)
void S_TickForever(WaveLaneCtx *lane) void S_TickForever(WaveLaneCtx *lane)
{ {
S_tl.is_sim_tick_thread = 1;
Arena *perm = PermArena(); Arena *perm = PermArena();
Arena *frame_arena = AcquireArena(Gibi(64)); Arena *frame_arena = AcquireArena(Gibi(64));
@ -1348,7 +1345,7 @@ void S_TickForever(WaveLaneCtx *lane)
// Vec2 shape0_pt = S_SupportPointFromShape(shape0, shape_dir); // Vec2 shape0_pt = S_SupportPointFromShape(shape0, shape_dir);
// Vec2 shape1_pt = S_SupportPointFromShape(shape1, neg_shape_dir); // Vec2 shape1_pt = S_SupportPointFromShape(shape1, neg_shape_dir);
S_CollisionData collision_data = S_CollisionDataFromShapes(shape0, shape1, VEC2(0, 0)); S_CollisionResult collision_data = S_CollisionResultFromShapes(shape0, shape1, VEC2(0, 0));
Vec2 shape0_pt = collision_data.closest_p0; Vec2 shape0_pt = collision_data.closest_p0;
Vec2 shape1_pt = collision_data.closest_p1; Vec2 shape1_pt = collision_data.closest_p1;
@ -1410,61 +1407,120 @@ void S_TickForever(WaveLaneCtx *lane)
// TODO: Not like this // TODO: Not like this
{ // {
Struct(S_Bullet) // Struct(S_Bullet)
{ // {
Vec2 start; // Vec2 start;
Vec2 dir; // Vec2 dir;
f32 speed; // f32 speed;
}; // };
PERSIST i64 bullets_count = 1; // PERSIST i64 bullets_count = 1;
PERSIST S_Bullet *bullets = 0; // PERSIST S_Bullet *bullets = 0;
if (!bullets) // if (!bullets)
{ // {
bullets = PushStruct(PermArena(), S_Bullet); // bullets = PushStruct(PermArena(), S_Bullet);
S_Bullet *bullet = &bullets[0]; // S_Bullet *bullet = &bullets[0];
bullet->start = VEC2(1, 0); // bullet->start = VEC2(1, 0);
bullet->dir = NormVec2(VEC2(1, -1)); // bullet->dir = NormVec2(VEC2(1, -1));
bullet->speed = 1; // bullet->speed = 1;
} // }
for (i64 bullet_idx = 0; bullet_idx < bullets_count; ++bullet_idx) // for (i64 bullet_idx = 0; bullet_idx < bullets_count; ++bullet_idx)
{ // {
S_Bullet *bullet = &bullets[bullet_idx]; // S_Bullet *bullet = &bullets[bullet_idx];
// Raycast // // Raycast
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 xf = ent->xf; // Xform xf = ent->xf;
S_Shape world_shape = S_MulXformShape(xf, ent->local_shape); // S_Shape world_shape = S_MulXformShape(xf, ent->local_shape);
if (ent == S_FirstEnt(world)) // if (ent == S_FirstEnt(world))
{ // {
bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius)); // bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius));
bullet->dir = NormVec2(ent->look); // bullet->dir = NormVec2(ent->look);
} // }
else // else
{ // {
S_RaycastData raycast = S_RaycastShape(world_shape, bullet->start, bullet->dir); // S_RaycastResult raycast = S_RaycastShape(world_shape, bullet->start, bullet->dir);
Vec2 isect = raycast.p; // Vec2 isect = raycast.p;
if (raycast.is_intersecting) // if (raycast.is_intersecting)
{ // {
S_DebugDrawPoint(isect, Color_Green); // S_DebugDrawPoint(isect, Color_Green);
S_DebugDrawLine(isect, AddVec2(isect, MulVec2(raycast.normal, 0.5)), Color_White); // S_DebugDrawLine(isect, AddVec2(isect, MulVec2(raycast.normal, 0.5)), Color_White);
} // }
else // else
{ // {
S_DebugDrawPoint(isect, Color_Purple); // S_DebugDrawPoint(isect, Color_Purple);
} // }
} // }
} // }
S_DebugDrawLine(bullet->start, AddVec2(bullet->start, Vec2WithLen(bullet->dir, 0.5)), Color_Red); // S_DebugDrawLine(bullet->start, AddVec2(bullet->start, Vec2WithLen(bullet->dir, 0.5)), Color_Red);
} // }
} // }
// // Struct(S_Bullet)
// // {
// // Vec2 start;
// // Vec2 dir;
// // f32 speed;
// // };
// // PERSIST i64 bullets_count = 1;
// // PERSIST S_Bullet *bullets = 0;
// // if (!bullets)
// // {
// // bullets = PushStruct(PermArena(), S_Bullet);
// // S_Bullet *bullet = &bullets[0];
// // bullet->start = VEC2(1, 0);
// // bullet->dir = NormVec2(VEC2(1, -1));
// // bullet->speed = 1;
// // }
// // for (i64 bullet_idx = 0; bullet_idx < bullets_count; ++bullet_idx)
// // {
// // S_Bullet *bullet = &bullets[bullet_idx];
// // // Raycast
// // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
// // {
// // Xform xf = ent->xf;
// // S_Shape world_shape = S_MulXformShape(xf, ent->local_shape);
// // if (ent == S_FirstEnt(world))
// // {
// // bullet->start = AddVec2(world_shape.centroid, Vec2WithLen(ent->look, world_shape.radius));
// // bullet->dir = NormVec2(ent->look);
// // }
// // else
// // {
// // S_RaycastResult raycast = S_RaycastShape(world_shape, bullet->start, bullet->dir);
// // Vec2 isect = raycast.p;
// // if (raycast.is_intersecting)
// // {
// // S_DebugDrawPoint(isect, Color_Green);
// // S_DebugDrawLine(isect, AddVec2(isect, MulVec2(raycast.normal, 0.5)), Color_White);
// // }
// // else
// // {
// // S_DebugDrawPoint(isect, Color_Purple);
// // }
// // }
// // }
// // S_DebugDrawLine(bullet->start, AddVec2(bullet->start, Vec2WithLen(bullet->dir, 0.5)), Color_Red);
// // }
// }

View File

@ -120,7 +120,7 @@ Struct(S_ClippedLine)
Vec2 a1_clipped, b1_clipped; Vec2 a1_clipped, b1_clipped;
}; };
Struct(S_CollisionData) Struct(S_CollisionResult)
{ {
// Contact manifold // Contact manifold
i32 collision_points_count; i32 collision_points_count;
@ -132,7 +132,7 @@ Struct(S_CollisionData)
Vec2 closest_p1; Vec2 closest_p1;
}; };
Struct(S_RaycastData) Struct(S_RaycastResult)
{ {
Vec2 p; Vec2 p;
Vec2 normal; Vec2 normal;
@ -315,7 +315,13 @@ Struct(S_Ctx)
S_OutputState output_states[2]; S_OutputState output_states[2];
}; };
Struct(S_ThreadLocalCtx)
{
b32 is_sim_tick_thread;
};
extern S_Ctx S; extern S_Ctx S;
extern ThreadLocal S_ThreadLocalCtx S_tl;
extern Readonly S_Ent S_NilEnt; extern Readonly S_Ent S_NilEnt;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -346,7 +352,7 @@ String S_NameFromTileKind(S_TileKind kind);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Delta helpers //~ Delta helpers
Rng2I32 S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta); void S_UpdateWorldFromDelta(Arena *arena, S_World *world, S_Delta *delta);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Shape helpers //~ Shape helpers
@ -366,8 +372,8 @@ S_MenkowskiPoint S_MenkowskiPointFromShapes(S_Shape shape0, S_Shape shape1, Vec2
S_ClippedLine S_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal); S_ClippedLine S_ClipLineToLine(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1, Vec2 normal);
Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal); Vec2 S_ClipPointToLine(Vec2 a, Vec2 b, Vec2 p, Vec2 normal);
S_CollisionData S_CollisionDataFromShapes(S_Shape shape0, S_Shape shape1, Vec2 sweep); S_CollisionResult S_CollisionResultFromShapes(S_Shape shape0, S_Shape shape1, Vec2 sweep);
S_RaycastData S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir); S_RaycastResult S_RaycastShape(S_Shape shape, Vec2 ray_start, Vec2 ray_dir);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Lookup helpers //~ Lookup helpers

View File

@ -313,6 +313,8 @@ void V_TickForever(WaveLaneCtx *lane)
// u32 max_particles = Kibi(128); // u32 max_particles = Kibi(128);
u32 max_particles = Mebi(1); u32 max_particles = Mebi(1);
// u32 max_particles = Mebi(2);
// u32 max_particles = Mebi(16);
// Init gpu state // Init gpu state
G_ResourceHandle gpu_state = Zi; G_ResourceHandle gpu_state = Zi;
@ -324,7 +326,6 @@ void V_TickForever(WaveLaneCtx *lane)
G_RWStructuredBufferRef gpu_particles_ref = Zi; G_RWStructuredBufferRef gpu_particles_ref = Zi;
G_RWTexture2DRef gpu_decals_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);
{ {
// Init state buffer // Init state buffer
@ -333,7 +334,7 @@ void V_TickForever(WaveLaneCtx *lane)
gpu_perm, cl, gpu_perm, cl,
V_GpuState, V_GpuState,
1, 1,
.flags = G_ResourceFlag_AllowShaderReadWrite .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
); );
gpu_state_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_state, V_GpuState); gpu_state_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_state, V_GpuState);
} }
@ -344,7 +345,8 @@ void V_TickForever(WaveLaneCtx *lane)
gpu_perm, cl, gpu_perm, cl,
G_Format_R8_Uint, G_Format_R8_Uint,
tiles_dims, tiles_dims,
G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite G_Layout_DirectQueue_ShaderRead,
.flags = G_ResourceFlag_ZeroMemory
); );
gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles); gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles);
} }
@ -354,7 +356,7 @@ void V_TickForever(WaveLaneCtx *lane)
gpu_perm, cl, gpu_perm, cl,
V_Particle, V_Particle,
max_particles, max_particles,
.flags = G_ResourceFlag_AllowShaderReadWrite .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
); );
gpu_particles_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles, V_Particle); gpu_particles_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles, V_Particle);
} }
@ -362,10 +364,12 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
gpu_decals = G_PushTexture2D( gpu_decals = G_PushTexture2D(
gpu_perm, cl, gpu_perm, cl,
G_Format_R8_Uint, // G_Format_R8_Uint,
// G_Format_R11G11B10_Float,
G_Format_R10G10B10A2_Unorm,
decals_dims, decals_dims,
G_Layout_DirectQueue_ShaderReadWrite, G_Layout_DirectQueue_ShaderReadWrite,
.flags = G_ResourceFlag_AllowShaderReadWrite .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite
); );
gpu_decals_ref = G_PushRWTexture2DRef(gpu_perm, gpu_decals); gpu_decals_ref = G_PushRWTexture2DRef(gpu_perm, gpu_decals);
} }
@ -582,6 +586,7 @@ void V_TickForever(WaveLaneCtx *lane)
// FIXME: Only apply latest snapshot // FIXME: Only apply latest snapshot
b32 received_unseen_tick = 0; b32 received_unseen_tick = 0;
b32 tiles_dirty = 0;
for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next) for (S_SnapshotNode *n = sim_output->first_snapshot_node; n; n = n->next)
{ {
S_Snapshot *snapshot = &n->snapshot; S_Snapshot *snapshot = &n->snapshot;
@ -592,17 +597,11 @@ void V_TickForever(WaveLaneCtx *lane)
for (S_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next) for (S_DeltaNode *dn = snapshot->first_delta_node; dn; dn = dn->next)
{ {
S_Delta *delta = &dn->delta; S_Delta *delta = &dn->delta;
Rng2I32 dirty_tile_rect = S_UpdateWorldFromDelta(world_arena, world, delta); if (delta->kind == S_DeltaKind_RawTiles || delta->kind == S_DeltaKind_Tile)
Vec2I32 dirty_tile_dims = DimsFromRng2I32(dirty_tile_rect);
if (dirty_tile_dims.x != 0 || dirty_tile_dims.y != 0)
{ {
G_CopyCpuToTexture( tiles_dirty = 1;
frame->cl,
gpu_tiles, VEC3I32(dirty_tile_rect.p0.x, dirty_tile_rect.p0.y, 0),
world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
RNG3I32(VEC3I32(dirty_tile_rect.p0.x, dirty_tile_rect.p0.y, 0), VEC3I32(dirty_tile_rect.p1.x, dirty_tile_rect.p1.y, 1))
);
} }
S_UpdateWorldFromDelta(world_arena, world, delta);
} }
received_unseen_tick = 1; received_unseen_tick = 1;
} }
@ -961,11 +960,12 @@ void V_TickForever(WaveLaneCtx *lane)
S_Ent *player = S_EntFromKey(world, V.player_key); S_Ent *player = S_EntFromKey(world, V.player_key);
S_Ent *hovered_ent = &S_NilEnt; S_Ent *hovered_ent = &S_NilEnt;
{ {
// TODO: Real world query
S_Shape cursor_shape = S_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); S_Shape cursor_shape = S_ShapeFromDesc(.count = 1, .points = { frame->world_cursor });
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))
{ {
S_Shape ent_shape = S_MulXformShape(ent->xf, ent->local_shape); S_Shape ent_shape = S_MulXformShape(ent->xf, ent->local_shape);
b32 is_hovered = S_CollisionDataFromShapes(ent_shape, cursor_shape, VEC2(0, 0)).collision_points_count > 0; b32 is_hovered = S_CollisionResultFromShapes(ent_shape, cursor_shape, VEC2(0, 0)).collision_points_count > 0;
if (is_hovered) if (is_hovered)
{ {
hovered_ent = ent; hovered_ent = ent;
@ -2518,6 +2518,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (frame->held_buttons[Button_W]) move.y -= 1; if (frame->held_buttons[Button_W]) move.y -= 1;
if (frame->held_buttons[Button_S]) move.y += 1; if (frame->held_buttons[Button_S]) move.y += 1;
} }
f32 fire_held = frame->held_buttons[Button_M1];
Vec2 look = Zi; Vec2 look = Zi;
{ {
Vec2 center = MulXformV2(player->xf, player->local_shape.centroid); Vec2 center = MulXformV2(player->xf, player->local_shape.centroid);
@ -2535,6 +2536,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
frame->move = move; frame->move = move;
frame->look = look; frame->look = look;
frame->fire_held = fire_held;
} }
} }
@ -2544,6 +2546,7 @@ void V_TickForever(WaveLaneCtx *lane)
cmd->target = V.player_key; cmd->target = V.player_key;
cmd->move = frame->move; cmd->move = frame->move;
cmd->look = frame->look; cmd->look = frame->look;
cmd->fire_held = frame->fire_held;
} }
////////////////////////////// //////////////////////////////
@ -2562,6 +2565,108 @@ void V_TickForever(WaveLaneCtx *lane)
} }
UnlockTicketMutex(&S.input_back_tm); UnlockTicketMutex(&S.input_back_tm);
//////////////////////////////
//- Process bullets
// TODO: Not like this
{
for (S_Ent *firer = S_FirstEnt(world); firer->valid; firer = S_NextEnt(firer))
{
if (firer->fire_held)
{
Xform firer_xf = firer->xf;
S_Shape firer_world_shape = S_MulXformShape(firer_xf, firer->local_shape);
Vec2 ray_start = firer_world_shape.centroid;
Vec2 ray_dir = firer->look;
// TODO: Real raycast query
S_Ent *closest_victim = &S_NilEnt;
S_RaycastResult victim_raycast = Zi;
{
f32 closest_len_sq = Inf;
for (S_Ent *victim = S_FirstEnt(world); victim->valid; victim = S_NextEnt(victim))
{
if (victim != firer)
{
Xform victim_xf = victim->xf;
S_Shape victim_world_shape = S_MulXformShape(victim_xf, victim->local_shape);
S_RaycastResult raycast = S_RaycastShape(victim_world_shape, ray_start, ray_dir);
if (raycast.is_intersecting)
{
f32 len_sq = Vec2LenSq(SubVec2(raycast.p, ray_start));
if (len_sq < closest_len_sq)
{
closest_len_sq = len_sq;
closest_victim = victim;
victim_raycast = raycast;
}
}
}
}
}
if (closest_victim->valid)
{
V_Emitter *emitter = 0;
{
V_EmitterNode *en = PushStruct(frame->arena, V_EmitterNode);
SllQueuePush(frame->first_emitter_node, frame->last_emitter_node, en);
frame->emitters_count += 1;
emitter = &en->emitter;
}
V_ParticleFlag flags = 0;
emitter->flags = flags;
Vec2 dir = victim_raycast.normal;
emitter->pos = victim_raycast.p;
emitter->count = 50;
// emitter->count = 100;
// emitter->count = 500;
emitter->speed = 20;
emitter->speed_spread = 40;
emitter->velocity_falloff = 30;
emitter->velocity_falloff_spread = 60;
emitter->angle = AngleFromVec2(dir);
// emitter->angle_spread = Tau / 4;
// emitter->angle_spread = Tau / 20;
emitter->angle_spread = Tau / 32;
emitter->color_lin = LinearFromSrgb(VEC4(0.5, 0.1, 0.1, 1));
emitter->color_spread = VEC3(0.1, 0, 0);
// emitter->color = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
emitter->seed = RandU64FromState(&frame->rand);
// emitter->angle_spread = 1;
// emitter->angle_spread = 0.5;
// emitter->angle_spread = Tau;
// V_DrawPoint(victim_raycast.p, Color_Green);
// V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White);
}
// for (S_QueryResult query = S_FirstRaycast(wrold, ray_start, ray_dir); query.
// S_RaycastWorldResult hits = S_RaycastWorld(world, ray_start, ray_dir)
// {
// }
}
}
}
////////////////////////////// //////////////////////////////
//- Render //- Render
@ -2664,11 +2769,7 @@ void V_TickForever(WaveLaneCtx *lane)
} }
////////////////////////////// //////////////////////////////
//- Build emitters //- Spawn test emitter
i64 emitters_count = 0;
V_EmitterNode *first_emitter_node = 0;
V_EmitterNode *last_emitter_node = 0;
// Spawn test emitter // Spawn test emitter
@ -2677,39 +2778,31 @@ void V_TickForever(WaveLaneCtx *lane)
V_Emitter *emitter = 0; V_Emitter *emitter = 0;
{ {
V_EmitterNode *en = PushStruct(frame->arena, V_EmitterNode); V_EmitterNode *en = PushStruct(frame->arena, V_EmitterNode);
SllQueuePush(first_emitter_node, last_emitter_node, en); SllQueuePush(frame->first_emitter_node, frame->last_emitter_node, en);
emitters_count += 1; frame->emitters_count += 1;
emitter = &en->emitter; emitter = &en->emitter;
} }
emitter->particle_kind = V_ParticleKind_Test;
Vec2 dir = frame->look; Vec2 dir = frame->look;
emitter->pos = frame->world_cursor; emitter->pos = frame->world_cursor;
emitter->angle = AngleFromVec2(dir); emitter->angle = AngleFromVec2(dir);
emitter->count = 100; emitter->count = 25;
// emitter->count = 100;
emitter->speed = 10; emitter->speed = 10;
emitter->color_lin = LinearFromSrgb(Color_Yellow);
emitter->seed = RandU64FromState(&frame->rand); emitter->seed = RandU64FromState(&frame->rand);
emitter->speed_spread = 1; emitter->speed_spread = 1;
// emitter->angle_spread = 1; // emitter->angle_spread = 1;
// emitter->angle_spread = 0.5; // emitter->angle_spread = 0.5;
emitter->angle_spread = Tau / 4; emitter->angle_spread = Tau / 4;
} // 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 //- Begin gpu frame
// Target // Target
G_ResourceHandle draw_target = G_PushTexture2D( G_ResourceHandle draw_target = G_PushTexture2D(
@ -2725,13 +2818,26 @@ void V_TickForever(WaveLaneCtx *lane)
Rng2 scissor = RNG2(VEC2(viewport.p0.x, viewport.p0.y), VEC2(viewport.p1.x, viewport.p1.y)); Rng2 scissor = RNG2(VEC2(viewport.p0.x, viewport.p0.y), VEC2(viewport.p1.x, viewport.p1.y));
// Verts // Verts
G_ResourceHandle dverts_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromArena(frame->dverts_arena)); G_ResourceHandle dverts_buff = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromArena(frame->dverts_arena));
G_ResourceHandle dvert_idxs_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromArena(frame->dvert_idxs_arena)); G_ResourceHandle dvert_idxs_buff = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromArena(frame->dvert_idxs_arena));
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 // Emitters
G_ResourceHandle gpu_emitters = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStructs(flattened_emitters, emitters_count)); G_ResourceHandle gpu_emitters = Zi;
{
// Flatten emitters list
V_Emitter *flattened_emitters = PushStructsNoZero(frame->arena, V_Emitter, frame->emitters_count);
{
i64 emitter_idx = 0;
for (V_EmitterNode *en = frame->first_emitter_node; en; en = en->next)
{
flattened_emitters[emitter_idx] = en->emitter;
++emitter_idx;
}
}
gpu_emitters = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromStructs(flattened_emitters, frame->emitters_count));
}
G_StructuredBufferRef gpu_emitters_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_emitters, V_Emitter); G_StructuredBufferRef gpu_emitters_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_emitters, V_Emitter);
// Params // Params
@ -2762,7 +2868,7 @@ 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_count = frame->emitters_count;
params.emitters = gpu_emitters_ref; params.emitters = gpu_emitters_ref;
params.max_particles = max_particles; params.max_particles = max_particles;
@ -2770,9 +2876,23 @@ void V_TickForever(WaveLaneCtx *lane)
params.decals = gpu_decals_ref; params.decals = gpu_decals_ref;
} }
G_ResourceHandle gpu_params = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(&params)); G_ResourceHandle gpu_params = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromStruct(&params));
G_StructuredBufferRef gpu_params_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_params, V_GpuParams); G_StructuredBufferRef gpu_params_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_params, V_GpuParams);
// Upload tiles
if (tiles_dirty)
{
LogDebugF("Uploading tiles to gpu");
G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite);
G_CopyCpuToTexture(
frame->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))
);
G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead);
}
// Constants // Constants
G_SetConstant(frame->cl, V_ShaderConst_State, gpu_state_ref); G_SetConstant(frame->cl, V_ShaderConst_State, gpu_state_ref);
G_SetConstant(frame->cl, V_ShaderConst_Params, gpu_params_ref); G_SetConstant(frame->cl, V_ShaderConst_Params, gpu_params_ref);
@ -2782,37 +2902,41 @@ void V_TickForever(WaveLaneCtx *lane)
G_DumbGlobalMemorySync(frame->cl); G_DumbGlobalMemorySync(frame->cl);
////////////////////////////// //////////////////////////////
//- Utility pass //- Setup pass
// Clear decals // Clear decals
if (should_clear_decals) if (should_clear_decals)
{ {
G_Compute(frame->cl, V_ClearDecalsCS, V_ThreadGroupSizeFromTexSize(decals_dims)); G_Compute(frame->cl, V_ClearDecalsCS, V_ThreadGroupSizeFromTexSize(decals_dims));
} }
// Clear particles // Clear particles
if (should_clear_particles) if (should_clear_particles)
{ {
G_Compute(frame->cl, V_ClearParticlesCS, V_ThreadGroupSizeFromBufferSize(max_particles)); G_Compute(frame->cl, V_ClearParticlesCS, V_ThreadGroupSizeFromBufferSize(max_particles));
} }
G_DumbGlobalMemorySync(frame->cl); // Discard render target
//////////////////////////////
//- Discard pass
G_DiscardRenderTarget(frame->cl, draw_target); G_DiscardRenderTarget(frame->cl, draw_target);
// Sync
G_DumbGlobalMemorySync(frame->cl);
////////////////////////////// //////////////////////////////
//- Particle simulation pass //- Particle simulation pass
{ {
// Emit particles // Emit particles
G_Compute(frame->cl, V_EmitParticlesCS, V_ThreadGroupSizeFromBufferSize(emitters_count)); G_Compute(frame->cl, V_EmitParticlesCS, V_ThreadGroupSizeFromBufferSize(frame->emitters_count));
// Barrier particles buffer
G_DumbMemorySync(frame->cl, gpu_particles); G_DumbMemorySync(frame->cl, gpu_particles);
// Simulate particles // Simulate particles
G_Compute(frame->cl, V_SimParticlesCS, V_ThreadGroupSizeFromBufferSize(max_particles)); G_Compute(frame->cl, V_SimParticlesCS, V_ThreadGroupSizeFromBufferSize(max_particles));
// Barrier since decals were written
G_DumbGlobalMemorySync(frame->cl);
} }
////////////////////////////// //////////////////////////////

View File

@ -15,8 +15,8 @@
X(spawn, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \ X(spawn, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \
X(spawn_dummy, Spawn Dummy, V_CmdDescFlag_None, V_HOTKEY( Button_R ), ) \ X(spawn_dummy, Spawn Dummy, V_CmdDescFlag_None, V_HOTKEY( Button_R ), ) \
X(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \ X(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \
X(clear_decals, Clear decals, V_CmdDescFlag_None, V_HOTKEY( Button_C ), ) \ X(clear_decals, Clear decals, V_CmdDescFlag_None, V_HOTKEY( Button_C, .alt = 1 ), ) \
X(clear_particles, Clear particles, V_CmdDescFlag_None, V_HOTKEY( Button_C, .alt = 1 ), ) \ X(clear_particles, Clear particles, V_CmdDescFlag_None, V_HOTKEY( Button_C ), ) \
/* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -267,11 +267,17 @@ Struct(V_Frame)
// Control // Control
Vec2 move; Vec2 move;
Vec2 look; Vec2 look;
f32 fire_held;
// Sim cmds // Sim cmds
u64 sim_cmds_count; u64 sim_cmds_count;
S_CmdNode *first_sim_cmd_node; S_CmdNode *first_sim_cmd_node;
S_CmdNode *last_sim_cmd_node; S_CmdNode *last_sim_cmd_node;
// Emitters
i64 emitters_count;
V_EmitterNode *first_emitter_node;
V_EmitterNode *last_emitter_node;
}; };
Struct(V_Ctx) Struct(V_Ctx)

View File

@ -5,11 +5,11 @@
ComputeShader2D(V_ClearDecalsCS, 8, 8) ComputeShader2D(V_ClearDecalsCS, 8, 8)
{ {
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0]; V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
RWTexture2D<V_ParticleKind> decals = G_Dereference<V_ParticleKind>(params.decals); RWTexture2D<Vec4> decals = G_Dereference<Vec4>(params.decals);
Vec2 decal_idx = SV_DispatchThreadID; Vec2 decal_idx = SV_DispatchThreadID;
if (decal_idx.x < countof(decals).x && decal_idx.y < countof(decals).y) if (decal_idx.x < countof(decals).x && decal_idx.y < countof(decals).y)
{ {
decals[decal_idx] = V_ParticleKind_None; decals[decal_idx] = 0;
} }
} }
@ -21,11 +21,10 @@ ComputeShader(V_ClearParticlesCS, 64)
u32 particle_idx = SV_DispatchThreadID; u32 particle_idx = SV_DispatchThreadID;
if (particle_idx < params.max_particles) if (particle_idx < params.max_particles)
{ {
particles[particle_idx].kind = V_ParticleKind_None; particles[particle_idx].exists = 0;
} }
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Backdrop shader //~ Backdrop shader
@ -148,21 +147,38 @@ ComputeShader2D(V_BackdropCS, 8, 8)
// TODO: Remove this // TODO: Remove this
// Decals test // Decals test
{ {
RWTexture2D<V_ParticleKind> decals = G_Dereference<V_ParticleKind>(params.decals); RWTexture2D<Vec4> decals = G_Dereference<Vec4>(params.decals);
Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(world_pos, 1)); Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(world_pos, 1));
// Vec2 decal_uv = decal_pos / countof(decals); // Vec2 decal_uv = decal_pos / countof(decals);
V_ParticleKind decal = decals.Load(decal_pos); Vec4 decal = decals.Load(decal_pos);
if (decal.a != 0)
if (decal == V_ParticleKind_Test)
{ {
result = Color_Yellow; result = decal;
// result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
} }
} }
// 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(decal_pos);
// if (decal == V_ParticleKind_Test)
// {
// // result = Color_Yellow;
// // result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
// // result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1));
// }
// }
} }
target[SV_DispatchThreadID] = result; target[SV_DispatchThreadID] = result;
@ -238,7 +254,7 @@ ComputeShader(V_EmitParticlesCS, 64)
for (u32 particle_seq = first_particle_seq; particle_seq < last_particle_seq; ++particle_seq) for (u32 particle_seq = first_particle_seq; particle_seq < last_particle_seq; ++particle_seq)
{ {
u32 particle_idx = particle_seq % params.max_particles; u32 particle_idx = particle_seq % params.max_particles;
particles[particle_idx].kind = emitter.particle_kind; particles[particle_idx].exists = 1;
particles[particle_idx].emitter_init_num = emitter_idx + 1; particles[particle_idx].emitter_init_num = emitter_idx + 1;
particles[particle_idx].idx_in_emitter = particles_created_count; particles[particle_idx].idx_in_emitter = particles_created_count;
particles_created_count += 1; particles_created_count += 1;
@ -253,14 +269,13 @@ ComputeShader(V_SimParticlesCS, 64)
{ {
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0]; V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles); RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
RWTexture2D<V_ParticleKind> decals = G_Dereference<V_ParticleKind>(params.decals); RWTexture2D<Vec4> decals = G_Dereference<Vec4>(params.decals);
Texture2D<S_TileKind> tiles = G_Dereference<S_TileKind>(params.tiles);
u32 particle_idx = SV_DispatchThreadID; u32 particle_idx = SV_DispatchThreadID;
if (particle_idx < params.max_particles) if (particle_idx < params.max_particles)
{ {
V_Particle particle = particles[particle_idx]; V_Particle particle = particles[particle_idx];
if (particle.kind != V_ParticleKind_None) if (particle.exists > 0)
{ {
// Initialize // Initialize
if (particle.emitter_init_num != 0) if (particle.emitter_init_num != 0)
@ -268,128 +283,57 @@ ComputeShader(V_SimParticlesCS, 64)
V_Emitter emitter = G_Dereference<V_Emitter>(params.emitters)[particle.emitter_init_num - 1]; V_Emitter emitter = G_Dereference<V_Emitter>(params.emitters)[particle.emitter_init_num - 1];
u64 seed = MixU64(emitter.seed + particle.idx_in_emitter); u64 seed = MixU64(emitter.seed + particle.idx_in_emitter);
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 - 0.5) * emitter.speed_spread); // u32 speed_noise = (u32)((seed >> 0) & 0xFF);
f32 angle = emitter.angle + ((angle_rand - 0.5) * emitter.angle_spread); // u32 angle_noise = (u32)((seed >> 8) & 0xFF);
// u32 r_noise = (u32)((seed >> 8) & 0xFF);
// u32 g_noise = (u32)((seed >> 8) & 0xFF);
// u32 b_noise = (u32)((seed >> 8) & 0xFF);
// u32 a_noise = (u32)((seed >> 8) & 0xFF);
// f32 speed_rand = speed_noise / (f32)0xFFFF;
// f32 angle_rand = angle_noise / (f32)0xFFFF;
f32 rand_speed = (f32)((seed >> 0) & 0xFF) / (f32)0xFF - 0.5;
f32 rand_angle = (f32)((seed >> 8) & 0xFF) / (f32)0xFF - 0.5;
f32 rand_falloff = (f32)((seed >> 16) & 0xFF) / (f32)0xFF - 0.5;
f32 rand_r = (f32)((seed >> 24) & 0xFF) / (f32)0xFF - 0.5;
f32 rand_g = (f32)((seed >> 32) & 0xFF) / (f32)0xFF - 0.5;
f32 rand_b = (f32)((seed >> 48) & 0xFF) / (f32)0xFF - 0.5;
f32 speed = emitter.speed + rand_speed * emitter.speed_spread;
f32 angle = emitter.angle + rand_angle * emitter.angle_spread;
f32 velocity_falloff = emitter.velocity_falloff + rand_falloff * emitter.velocity_falloff;
Vec3 color = emitter.color_lin.rgb + Vec3(rand_r, rand_g, rand_b) * emitter.color_spread;
particle.pos = emitter.pos; particle.pos = emitter.pos;
particle.velocity = Vec2(cos(angle), sin(angle)) * speed; particle.velocity = Vec2(cos(angle), sin(angle)) * speed;
particle.velocity_falloff = velocity_falloff;
particle.color = color;
particle.emitter_init_num = 0; particle.emitter_init_num = 0;
} }
// Simulate // Simulate
{ {
f32 dt = params.dt; particle.pos += particle.velocity * params.dt;
particle.velocity = lerp(particle.velocity, 0, particle.velocity_falloff * params.dt);
Vec2 pos_a = particle.pos;
Vec2 pos_b = pos_a + particle.velocity * dt;
// TODO: Don't query tiles
Vec2I32 tile_pos_a = S_TilePosFromWorldPos(pos_a);
S_TileKind tile_a = tiles.Load(Vec3I32(tile_pos_a, 0));
b32 is_blocked_a = tile_a == S_TileKind_Wall;
Vec2 pos_a_decal = mul(params.xf.world_to_decal, Vec3(pos_a, 1));
Vec2 pos_b_decal = mul(params.xf.world_to_decal, Vec3(pos_b, 1));
Vec2 step_a = pos_a_decal;
Vec2 step_b = pos_b_decal;
Vec2 step_vab = step_b - step_a;
f32 slope = step_vab.y / step_vab.x;
i32 x_dir = (step_vab.x >= 0) - (step_vab.x < 0);
if (!is_blocked_a)
{
i32 step_x = step_a.x;
for (;;)
{
f32 step_y = step_a.y + slope * (step_x - step_a.x);
Vec2 step = Vec2(step_x, step_y);
// TODO: Don't query tiles
Vec2 world_step = mul(params.xf.decal_to_world, Vec3(step, 1));
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_step);
S_TileKind tile = tiles.Load(Vec3I32(tile_pos, 0));
b32 is_blocked = tile == S_TileKind_Wall;
if (is_blocked)
{
// particle.velocity *= -1;
particle.velocity.y *= -1;
// particle.velocity.x *= -1;
break;
}
step_x += x_dir;
if (x_dir > 0)
{
if (step_x > (step_b.x + x_dir))
{
break;
}
}
else
{
if (step_x < (step_b.x + x_dir))
{
break;
}
}
}
}
// // Line walk
// // FIXME: Real line walk, not aabb check
// b32 is_blocked = 0;
// for (u32 step_y = step_a.y; step_y <= step_b.y; ++step_y)
// {
// for (u32 step_x = step_a.x; step_x <= step_b.x; ++step_x)
// {
// // TODO: Don't query tiles
// Vec2 world_step = mul(params.xf.decal_to_world, Vec3(step_x, step_y, 1));
// Vec2I32 tile_pos = S_TilePosFromWorldPos(world_step);
// S_TileKind tile = tiles.Load(Vec3I32(tile_pos, 0));
// if (tile == S_TileKind_Wall)
// {
// if (is_blocked)
// {
// // particle.velocity *= -1;
// particle.velocity.x *= -1;
// break;
// }
// is_blocked = 1;
// }
// }
// }
// Vec2I32 tile_pos = S_TilePosFromWorldPos(pos_a);
// S_TileKind tile = tiles.Load(Vec3I32(tile_pos, 0));
particle.pos = pos_a + particle.velocity * dt;
particle.velocity = lerp(particle.velocity, 0, 4 * params.dt);
Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(particle.pos, 1)); Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(particle.pos, 1));
if (decal_pos.x >= 0 && decal_pos.y >= 0 && decal_pos.x < countof(decals).x && decal_pos.y < countof(decals).y) if (decal_pos.x >= 0 && decal_pos.y >= 0 && decal_pos.x < countof(decals).x && decal_pos.y < countof(decals).y)
{ {
// FIXME: Atomic write // FIXME: Atomic write
decals[floor(decal_pos)] = particle.kind; decals[floor(decal_pos)] = Vec4(particle.color, 1);
} }
else else
{ {
// Prune out of bounds particle // Prune out of bounds particle
particle.kind = V_ParticleKind_None; particle.exists = 0;
}
if (particle.exists < 0.0001)
{
particle.exists = 0;
} }
} }
particles[particle_idx] = particle; particles[particle_idx] = particle;

View File

@ -80,35 +80,47 @@ Struct(V_GpuParams)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Particle types //~ Particle types
Enum(V_ParticleKind) Enum(V_ParticleFlag)
{ {
V_ParticleKind_None, V_ParticleFlag_None = (0),
V_ParticleKind_Test
}; };
Struct(V_Emitter) Struct(V_Emitter)
{ {
V_ParticleKind particle_kind; V_ParticleFlag flags;
Vec2 pos; Vec2 pos;
f32 angle;
u32 count; u32 count;
f32 speed;
u64 seed; u64 seed;
f32 angle_spread;
f32 speed;
f32 speed_spread; f32 speed_spread;
f32 angle;
f32 angle_spread;
f32 velocity_falloff;
f32 velocity_falloff_spread;
Vec4 color_lin;
Vec3 color_spread;
}; };
// TODO: Pack this efficiently // TODO: Pack this efficiently
Struct(V_Particle) Struct(V_Particle)
{ {
V_ParticleKind kind; V_ParticleFlag flags;
u32 emitter_init_num; // if != 0, then initialize using emitter at index (emitter_init_num - 1) u32 emitter_init_num; // if != 0, then initialize using emitter at index (emitter_init_num - 1)
u32 idx_in_emitter; u32 idx_in_emitter;
Vec2 pos; Vec2 pos;
Vec2 velocity; Vec2 velocity;
f32 exists;
f32 velocity_falloff;
Vec3 color;
}; };
#if IsLanguageC #if IsLanguageC

View File

@ -1695,7 +1695,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
// Rects // Rects
u64 rects_count = ArenaCount(frame->rects_arena, UI_DRect); u64 rects_count = ArenaCount(frame->rects_arena, UI_DRect);
G_ResourceHandle rects_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromArena(frame->rects_arena)); G_ResourceHandle rects_buff = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromArena(frame->rects_arena));
G_StructuredBufferRef rects_ro = G_PushStructuredBufferRef(frame->gpu_arena, rects_buff, UI_DRect); G_StructuredBufferRef rects_ro = G_PushStructuredBufferRef(frame->gpu_arena, rects_buff, UI_DRect);
// Params // Params
@ -1708,7 +1708,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
params.cursor_pos = frame->cursor_pos; params.cursor_pos = frame->cursor_pos;
params.aa = TweakFloat("UI anti-aliasing", 1, 0, 1); params.aa = TweakFloat("UI anti-aliasing", 1, 0, 1);
} }
G_ResourceHandle params_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(&params)); G_ResourceHandle params_buff = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromStruct(&params));
G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, UI_DParams); G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, UI_DParams);
// Constants // Constants
@ -1721,6 +1721,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
//- Dispatch shaders //- Dispatch shaders
//- Clear pass //- Clear pass
{ {
G_ClearRenderTarget(frame->cl, draw_target, VEC4(0, 0, 0, 0)); G_ClearRenderTarget(frame->cl, draw_target, VEC4(0, 0, 0, 0));
} }