tile sprite testing

This commit is contained in:
jacob 2026-01-09 15:37:23 -06:00
parent d332315c16
commit 86b12bf909
22 changed files with 476 additions and 125 deletions

View File

@ -1,8 +1,4 @@
//
// Aseprite (.ase) file parser
//
// DEFLATE decoder based on Handmade Hero's png parser
//
////////////////////////////////////////////////////////////
//~ Shared constants

View File

@ -769,14 +769,16 @@ u64 MixU64s(u64 seed_a, u64 seed_b)
}
#if IsLanguageC
#define Fnv64Basis 0xCBF29CE484222325
// FNV-1a parameters for different hash sizes:
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
Inline u64 HashFnv64(u64 seed, String s)
Inline u64 HashFnv64(u64 seed, String str)
{
u64 hash = seed;
for (u64 i = 0; i < s.len; ++i)
for (u64 i = 0; i < str.len; ++i)
{
hash ^= (u8)s.text[i];
hash ^= (u8)str.text[i];
hash *= 0x100000001B3;
}
return hash;

View File

@ -497,6 +497,38 @@ String PathFromString(Arena *arena, String str, u8 path_delimiter)
return result;
}
////////////////////////////////////////////////////////////
//~ Hash helpers
u64 HashStringEx(u64 seed, String str)
{
u64 result = HashFnv64(seed, str);
result = MixU64(result);
return result;
}
u64 HashString(String str)
{
return HashStringEx(Fnv64Basis, str);
}
u64 HashF_(String fmt, ...)
{
u64 result = 0;
TempArena scratch = BeginScratchNoConflict();
{
va_list args;
va_start(args, fmt);
{
String str = FormatString(scratch.arena, fmt, FmtArgsFromVaList(scratch.arena, args));
result = HashString(str);
}
va_end(args);
}
EndScratch(scratch);
return result;
}
////////////////////////////////////////////////////////////
//~ String list helpers

View File

@ -102,6 +102,14 @@ b32 StringEndsWith(String str, String substring);
String StringFromArray(Arena *arena, StringArray a);
String PathFromString(Arena *arena, String str, u8 path_delimiter);
////////////////////////////////////////////////////////////
//~ Hash helpers
u64 HashStringEx(u64 seed, String str);
u64 HashString(String str);
u64 HashF_(String fmt, ...);
#define HashF(fmt_cstr, ...) HashF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd)
////////////////////////////////////////////////////////////
//~ Trimming helpers

View File

@ -13,7 +13,7 @@ String TweakEx(Arena *arena, TweakVar desc, b32 update_existing)
{
String result = Zi;
Arena *perm = PermArena();
u64 hash = MixU64(HashFnv64(Fnv64Basis, desc.name));
u64 hash = HashString(desc.name);
TweakVarEntryBin *bin = &Base.tweak.entry_bins[hash % countof(Base.tweak.entry_bins)];
LockTicketMutex(&Base.tweak.tm);
{

View File

@ -1,8 +1,3 @@
////////////////////////////////////////////////////////////
//~ Hash types
#define Fnv64Basis 0xCBF29CE484222325
////////////////////////////////////////////////////////////
//~ Mergesort types
@ -41,27 +36,6 @@ Struct(Dict)
DictEntry *last;
};
////////////////////////////////////////////////////////////
//~ Hash utils
#define HashF(fmt_cstr, ...) HashF_(StringFromCstrNoLimit(fmt_cstr), __VA_ARGS__, FmtEnd)
Inline u64 HashF_(String fmt, ...)
{
u64 result = 0;
TempArena scratch = BeginScratchNoConflict();
{
va_list args;
va_start(args, fmt);
{
String str = FormatString(scratch.arena, fmt, FmtArgsFromVaList(scratch.arena, args));
result = HashFnv64(Fnv64Basis, str);
}
va_end(args);
}
EndScratch(scratch);
return result;
}
////////////////////////////////////////////////////////////
//~ Mergesort utils

View File

@ -91,7 +91,7 @@ Struct(GC_Run)
};
////////////////////////////////////////////////////////////
//~ Cmd types
//~ Async cmd types
Struct(GC_Cmd)
{

View File

@ -19,10 +19,40 @@ void G_BootstrapCommon(void)
G.quad_indices = G_IdxBuff16(quad_indices);
}
// Init point sampler
// Init point clamp sampler
{
G_ResourceHandle pt_sampler = G_PushSampler(gpu_perm, cl, .filter = G_Filter_MinMagMipPoint);
G.basic_sampler = G_PushSamplerStateRef(gpu_perm, pt_sampler);
G_ResourceHandle pt_sampler = G_PushSampler(
gpu_perm, cl,
.filter = G_Filter_MinMagMipPoint,
.x = G_AddressMode_Clamp,
.y = G_AddressMode_Clamp,
.z = G_AddressMode_Clamp,
);
G.basic_point_clamp_sampler = G_PushSamplerStateRef(gpu_perm, pt_sampler);
}
// Init point wrap sampler
{
G_ResourceHandle pt_sampler = G_PushSampler(
gpu_perm, cl,
.filter = G_Filter_MinMagMipPoint,
.x = G_AddressMode_Wrap,
.y = G_AddressMode_Wrap,
.z = G_AddressMode_Wrap,
);
G.basic_point_wrap_sampler = G_PushSamplerStateRef(gpu_perm, pt_sampler);
}
// Init blank texture
{
G_ResourceHandle blank_tex = G_PushTexture2D(
gpu_perm, cl,
G_Format_R8G8B8A8_Uint,
VEC2I32(8, 8),
G_Layout_AnyQueue_ShaderRead_CopyRead_CopyWrite_Present,
.flags = G_ResourceFlag_ZeroMemory
);
G.blank_tex = G_PushTexture2DRef(gpu_perm, blank_tex);
}
// Init noise texture
@ -46,7 +76,6 @@ void G_BootstrapCommon(void)
noise_data.text, noise_dims,
RNG3I32(VEC3I32(0, 0, 0), noise_dims)
);
G.basic_noise = G_PushTexture3DRef(gpu_perm, noise_tex);
}
@ -101,9 +130,19 @@ G_IndexBufferDesc G_QuadIndices(void)
return G.quad_indices;
}
G_SamplerStateRef G_BasicSampler(void)
G_SamplerStateRef G_BasicPointClampSampler(void)
{
return G.basic_sampler;
return G.basic_point_clamp_sampler;
}
G_SamplerStateRef G_BasicPointWrapSampler(void)
{
return G.basic_point_wrap_sampler;
}
G_Texture2DRef G_BlankTexture2D(void)
{
return G.blank_tex;
}
G_Texture3DRef G_BasicNoiseTexture(void)

View File

@ -5,7 +5,9 @@ Struct(G_Ctx)
{
// Common shared resources
G_IndexBufferDesc quad_indices;
G_SamplerStateRef basic_sampler;
G_SamplerStateRef basic_point_clamp_sampler;
G_SamplerStateRef basic_point_wrap_sampler;
G_Texture2DRef blank_tex;
G_Texture3DRef basic_noise;
};
@ -26,22 +28,20 @@ void G_BootstrapCommon(void);
//~ Utils
//- Arena
G_ArenaHandle G_PermArena(void);
//- Push resource from cpu
G_ResourceHandle G_PushBufferFromCpuCopy_(G_ArenaHandle gpu_arena, G_CommandListHandle cl, String src, G_BufferDesc desc);
#define G_PushBufferFromCpuCopy(_arena, _cl, _src, ...) \
G_PushBufferFromCpuCopy_((_arena), (_cl), (_src), (G_BufferDesc) { .size = (_src).len, __VA_ARGS__ })
//- Viewport / scissor
Rng3 G_ViewportFromTexture(G_ResourceHandle texture);
Rng2 G_ScissorFromTexture(G_ResourceHandle texture);
//- Shared resources
G_IndexBufferDesc G_QuadIndices(void);
G_SamplerStateRef G_BasicSampler(void);
G_SamplerStateRef G_BasicPointClampSampler(void);
G_SamplerStateRef G_BasicPointWrapSampler(void);
G_Texture2DRef G_BlankTexture2D(void);
G_Texture3DRef G_BasicNoiseTexture(void);

View File

@ -271,6 +271,7 @@ Enum(G_Layout)
G_Layout_DirectComputeQueue_ShaderReadWrite, // D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS
G_Layout_DirectComputeQueue_ShaderRead, // D3D12_BARRIER_LAYOUT_SHADER_RESOURCE
G_Layout_DirectComputeQueue_CopyRead, // D3D12_BARRIER_LAYOUT_COPY_SOURCE
G_Layout_DirectComputeQueue_CopyWrite, // D3D12_BARRIER_LAYOUT_COPY_DEST
//////////////////////////////
//- Direct queue
@ -281,6 +282,7 @@ Enum(G_Layout)
G_Layout_DirectQueue_ShaderReadWrite, // D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS
G_Layout_DirectQueue_ShaderRead, // D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE
G_Layout_DirectQueue_CopyRead, // D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE
G_Layout_DirectQueue_CopyWrite, // D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST
G_Layout_DirectQueue_DepthStencilRead_DepthStencilWrite, // D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE
G_Layout_DirectQueue_DepthStencilRead, // D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_READ
@ -295,6 +297,7 @@ Enum(G_Layout)
G_Layout_ComputeQueue_ShaderReadWrite, // D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_UNORDERED_ACCESS
G_Layout_ComputeQueue_ShaderRead, // D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE
G_Layout_ComputeQueue_CopyRead, // D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE
G_Layout_ComputeQueue_CopyWrite, // D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST
};
// Barrier will execute after previous stages specified by `stage_prev`, and before next stages specified by `stage_next`.

View File

@ -425,11 +425,13 @@ D3D12_BARRIER_LAYOUT G_D12_BarrierLayoutFromLayout(G_Layout layout)
[G_Layout_DirectComputeQueue_ShaderRead_CopyRead] = D3D12_BARRIER_LAYOUT_GENERIC_READ,
[G_Layout_DirectComputeQueue_ShaderRead] = D3D12_BARRIER_LAYOUT_SHADER_RESOURCE,
[G_Layout_DirectComputeQueue_CopyRead] = D3D12_BARRIER_LAYOUT_COPY_SOURCE,
[G_Layout_DirectComputeQueue_CopyWrite] = D3D12_BARRIER_LAYOUT_COPY_DEST,
[G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite] = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON,
[G_Layout_DirectQueue_ShaderReadWrite] = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS,
[G_Layout_DirectQueue_ShaderRead_CopyRead_DepthStencilRead] = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ,
[G_Layout_DirectQueue_ShaderRead] = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE,
[G_Layout_DirectQueue_CopyRead] = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE,
[G_Layout_DirectQueue_CopyWrite] = D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST,
[G_Layout_DirectQueue_DepthStencilRead_DepthStencilWrite] = D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE,
[G_Layout_DirectQueue_DepthStencilRead] = D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_READ,
[G_Layout_DirectQueue_RenderTargetWrite] = D3D12_BARRIER_LAYOUT_RENDER_TARGET,
@ -438,6 +440,7 @@ D3D12_BARRIER_LAYOUT G_D12_BarrierLayoutFromLayout(G_Layout layout)
[G_Layout_ComputeQueue_ShaderRead_CopyRead] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_GENERIC_READ,
[G_Layout_ComputeQueue_ShaderRead] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_SHADER_RESOURCE,
[G_Layout_ComputeQueue_CopyRead] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_SOURCE,
[G_Layout_ComputeQueue_CopyWrite] = D3D12_BARRIER_LAYOUT_COMPUTE_QUEUE_COPY_DEST,
};
return translate[layout];
};
@ -447,7 +450,7 @@ D3D12_BARRIER_LAYOUT G_D12_BarrierLayoutFromLayout(G_Layout layout)
G_D12_Pipeline *G_D12_PipelineFromDesc(G_D12_PipelineDesc desc)
{
u64 hash = MixU64(HashFnv64(Fnv64Basis, StringFromStruct(&desc)));
u64 hash = HashString(StringFromStruct(&desc));
// Fetch pipeline from cache
G_D12_Pipeline *pipeline = 0;

View File

@ -283,7 +283,8 @@ void BuildEntryPoint(WaveLaneCtx *lane)
for (StringListNode *n = check_files.first; n; n = n->next)
{
String file = n->s;
new_metahash = MixU64s(HashFnv64(new_metahash, file), OS_LastWriteTimestampFromPath(file));
new_metahash = MixU64s(new_metahash, OS_LastWriteTimestampFromPath(file));
new_metahash = HashStringEx(new_metahash , file);
}
}

BIN
src/pp/pp_res/sprite/tiles.ase (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/pp/pp_res/sprite/tiles_real.ase (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1221,7 +1221,7 @@ void V_TickForever(WaveLaneCtx *lane)
{
UI_BuildColumnEx(panel->key);
}
UI_Push(Tag, panel->key.hash);
UI_Push(Tag, panel->key.v);
panel_dfs->cp = UI_PushCP(panel->key);
if (!panel->is_organizing_panel && !panel->is_viewport_panel)
@ -1325,7 +1325,7 @@ void V_TickForever(WaveLaneCtx *lane)
for (DrawableTab *tab = first_drawable_tab; tab; tab = tab->next)
{
UI_Push(Tag, tab->key.hash);
UI_Push(Tag, tab->key.v);
UI_BoxReport tab_rep = UI_ReportsFromKey(tab->key).draw;
if (tab == first_drawable_tab)
@ -1538,7 +1538,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_SetNext(Flags, UI_BoxFlag_CaptureMouse);
UI_PushCP(UI_BuildColumn());
{
UI_Push(Tag, window->key.hash);
UI_Push(Tag, window->key.v);
if (window->is_tile_window)
{
for (S_TileKind tile_kind = 0; tile_kind < S_TileKind_COUNT; ++tile_kind)
@ -1679,7 +1679,7 @@ void V_TickForever(WaveLaneCtx *lane)
palette->key = UI_KeyF("command palette");
UI_Checkpoint palette_cp = UI_PushCP(UI_NilKey);
{
UI_Push(Tag, palette->key.hash);
UI_Push(Tag, palette->key.v);
UI_Key titlebar_key = UI_KeyF("title bar");
UI_BoxReports titlebar_reps = UI_ReportsFromKey(titlebar_key);
UI_BoxReports palette_reps = UI_ReportsFromKey(palette->key);
@ -1880,7 +1880,7 @@ void V_TickForever(WaveLaneCtx *lane)
UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_CaptureMouse);
UI_PushCP(UI_BuildRowEx(item->key));
{
UI_Push(Tag, item->key.hash);
UI_Push(Tag, item->key.v);
// Begin spacer
UI_BuildSpacer(UI_PIX(spacing, 1), Axis_X);
@ -2816,7 +2816,7 @@ void V_TickForever(WaveLaneCtx *lane)
if (0)
// if (0)
{
for (S_Ent *bullet = S_FirstEnt(world); bullet->valid; bullet = S_NextEnt(bullet))
{
@ -3163,6 +3163,8 @@ void V_TickForever(WaveLaneCtx *lane)
params.xf = frame->xf;
params.seed = RandU64FromState(&frame->rand);
params.pt_wrap_sampler = G_BasicPointWrapSampler();
params.selection_mode = frame->selection_mode;
params.equipped_tile = frame->equipped_tile;
@ -3190,27 +3192,27 @@ void V_TickForever(WaveLaneCtx *lane)
params.cells = gpu_cells_ref;
params.stains = gpu_stains_ref;
params.drynesses = gpu_drynesses_ref;
// Fill tile textures
{
ResourceKey sheet_resource = ResourceKeyFromStore(&P_Resources, Lit("sprite/tiles.ase"));
for (S_TileKind tile_kind = 0; tile_kind < S_TileKind_COUNT; ++tile_kind)
{
String tile_name = S_TileNameFromKind(tile_kind);
SPR_SheetKey sheet = SPR_SheetKeyFromResource(sheet_resource);
SPR_Slice tile_slice = SPR_SliceFromSheet(sheet, tile_name);
params.tile_slices[tile_kind] = tile_slice;
}
}
}
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);
// Fill tile textures
{
ResourceKey tiles_resource = ResourceKeyFromStore(&P_Resources, Lit("sprite/tiles.ase"));
for (S_TileKind tile_kind = 0; tile_kind < S_TileKind_COUNT; ++tile_kind)
{
String tile_name = S_TileNameFromKind(tile_kind);
SPR_Key sprite_key = SPR_KeyFromResource(tiles_resource);
SPR_Slice tile_slice = SPR_SliceFromKey(sprite_key, tile_name);
params.tile_slices[tile_kind] = tile_slice;
}
}
// Upload tiles
if (tiles_dirty)
{
LogDebugF("Uploading tiles to gpu");
G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite);
G_DumbMemoryLayoutSync(frame->cl, gpu_tiles, G_Layout_DirectQueue_CopyWrite);
G_CopyCpuToTexture(
frame->cl,
gpu_tiles, VEC3I32(0, 0, 0),

View File

@ -88,8 +88,8 @@ ComputeShader2D(V_BackdropCS, 8, 8)
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));
Vec2 bounds_screen_p0 = mul(params.xf.world_to_ui, Vec3(-half_bounds_size, -half_bounds_size, 1));
Vec2 bounds_screen_p1 = mul(params.xf.world_to_ui, 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) &&
@ -125,8 +125,8 @@ ComputeShader2D(V_BackdropCS, 8, 8)
}
// 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));
Vec2 grid_screen_p0 = mul(params.xf.world_to_ui, Vec3(floor(world_pos), 1));
Vec2 grid_screen_p1 = mul(params.xf.world_to_ui, 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));
@ -137,27 +137,9 @@ ComputeShader2D(V_BackdropCS, 8, 8)
result = grid_color;
}
}
// Tile
{
switch (tile)
{
default: break;
case S_TileKind_Floor:
{
result = Color_Blue;
} break;
case S_TileKind_Wall:
{
// result = Color_Red;
result = Color_Black;
} break;
}
}
// Axis
{
Vec2 zero_screen = mul(params.xf.world_to_draw, Vec3(0, 0, 1));
Vec2 zero_screen = mul(params.xf.world_to_ui, 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)
@ -186,6 +168,44 @@ ComputeShader2D(V_BackdropCS, 8, 8)
// Tile test
// TODO: Remove this
{
if (tile == S_TileKind_Floor)
{
SamplerState wrap_sampler = G_Dereference(params.pt_wrap_sampler);
SPR_Slice slice = params.tile_slices[tile];
Texture2D<Vec4> tile_tex = G_Dereference<Vec4>(slice.tex);
result = tile_tex.Sample(wrap_sampler, world_pos);
}
else if (tile == S_TileKind_Wall)
{
result = Color_Black;
}
// switch (tile)
// {
// default: break;
// case S_TileKind_Floor:
// {
// result = Color_Blue;
// } break;
// case S_TileKind_Wall:
// {
// // result = Color_Red;
// result = Color_Black;
// } break;
// }
}
// TODO: Remove this
// Cells test
{
@ -370,7 +390,6 @@ ComputeShader(V_SimParticlesCS, 64)
u64 seed0 = MixU64(emitter.seed + particle.seq);
u64 seed1 = MixU64(seed0);
f32 rand_speed = (f32)((seed0 >> 0) & 0xFFFF) / (f32)0xFFFF;
f32 rand_angle = (f32)((seed0 >> 16) & 0xFFFF) / (f32)0xFFFF;
f32 rand_offset = (f32)((seed0 >> 32) & 0xFFFF) / (f32)0xFFFF;

View File

@ -53,9 +53,10 @@ Struct(V_GpuParams)
G_Texture2DRef target_ro;
G_RWTexture2DRef target_rw;
V_Xforms xf;
u64 seed;
G_SamplerStateRef pt_wrap_sampler;
V_SelectionMode selection_mode;
S_TileKind equipped_tile;

View File

@ -11,23 +11,110 @@ void SPR_Bootstrap(void)
////////////////////////////////////////////////////////////
//~ Key helpers
SPR_Key SPR_KeyFromResource(ResourceKey resource)
SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource)
{
SPR_Key result = Zi;
SPR_SheetKey result = Zi;
result.r = resource;
return result;
}
////////////////////////////////////////////////////////////
//~ Lookup
SPR_Slice SPR_SliceFromKey(SPR_Key key, String slice_name)
SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet, String slice_name)
{
SPR_Slice result = Zi;
// u64 hash = key.hash;
// hash = Mi
u64 hash = sheet.r.v;
hash = HashStringEx(hash, slice_name);
// SPR_SliceBin *bin = &SPR.slice_bins[
i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy);
// Search for existing entry
b32 found = 0;
SPR_SliceBin *bin = &SPR.slice_bins[hash % countof(SPR.slice_bins)];
{
Lock bin_lock = LockS(&bin->mutex);
{
SPR_SliceEntry *entry = bin->first;
for (; entry; entry = entry->next)
{
if (entry->hash == hash)
{
break;
}
}
if (entry)
{
if (completion >= Atomic64Fetch(&entry->async_copy_completion_target))
{
result = entry->slice;
}
found = 1;
}
}
Unlock(&bin_lock);
}
// Push new entry
if (!found)
{
Lock submit_lock = LockE(&SPR.submit.mutex);
Lock bin_lock = LockE(&bin->mutex);
{
SPR_SliceEntry *entry = bin->first;
for (; entry; entry = entry->next)
{
if (entry->hash == hash)
{
break;
}
}
if (entry)
{
if (completion >= Atomic64Fetch(&entry->async_copy_completion_target))
{
result = entry->slice;
}
found = 1;
}
else
{
Arena *perm = PermArena();
entry = PushStruct(perm, SPR_SliceEntry);
entry->hash = hash;
Atomic64FetchSet(&entry->async_copy_completion_target, I64Max);
entry->sheet = sheet;
entry->slice_name = PushString(perm, slice_name);
SllStackPush(bin->first, entry);
SPR_CmdNode *n = SPR.submit.first_free;
if (n)
{
ZeroStruct(n);
}
else
{
n = PushStruct(perm, SPR_CmdNode);
}
n->cmd.entry = entry;
SllQueuePush(SPR.submit.first, SPR.submit.last, n);
++SPR.submit.count;
Atomic32FetchSet(&SPR.new_cmds_present, 1);
SignalAsyncTick();
}
}
Unlock(&bin_lock);
Unlock(&submit_lock);
}
if (G_IsRefNil(result.tex))
{
result.tex = G_BlankTexture2D();
result.uv_rect.p0 = VEC2(0, 0);
result.uv_rect.p1 = VEC2(1, 1);
}
return result;
}
@ -37,4 +124,117 @@ SPR_Slice SPR_SliceFromKey(SPR_Key key, String slice_name)
void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
{
Arena *perm = PermArena();
SPR_AsyncCtx *async = &SPR.async;
Arena *frame_arena = base_async_lane_frame->arena;
// TODO: Go wide
if (lane->idx == 0)
{
if (Atomic32Fetch(&SPR.new_cmds_present))
{
Atomic32Set(&SPR.new_cmds_present, 0);
SPR_CmdNode *first_cmd_node = 0;
SPR_CmdNode *last_cmd_node = 0;
u64 cmds_count = 0;
{
Lock lock = LockE(&SPR.submit.mutex);
{
first_cmd_node = SPR.submit.first;
last_cmd_node = SPR.submit.last;
cmds_count = SPR.submit.count;
SPR.submit.first = 0;
SPR.submit.last = 0;
}
Unlock(&lock);
}
if (cmds_count > 0)
{
for (SPR_CmdNode *n = first_cmd_node; n; n = n->next)
{
SPR_Cmd cmd = n->cmd;
SPR_SliceEntry *slice_entry = cmd.entry;
SPR_SheetBin *sheet_bin = &async->sheet_bins[slice_entry->sheet.r.v % countof(async->sheet_bins)];
SPR_SheetEntry *sheet = sheet_bin->first;
for (; sheet; sheet = sheet->next)
{
if (sheet->key.r.v == slice_entry->sheet.r.v)
{
break;
}
}
// Decode sheet
// TODO: Distribute chunk decoding accross wave
// TODO: Use atlas allocator and separate slices into unique textures
// TODO: Reuse command list for all uploads
if (!sheet)
{
sheet = PushStruct(perm, SPR_SheetEntry);
sheet->key = slice_entry->sheet;
SllStackPush(sheet_bin->first, sheet);
String encoded = DataFromResource(sheet->key.r);
String name = NameFromResource(sheet->key.r);
LogInfoF("Decoding sprite sheet \"%F\" (%F bytes)", FmtString(name), FmtUint(encoded.len));
ASE_DecodedImage decoded_image = ASE_DecodeImage(frame_arena, encoded);
ASE_DecodedSheet decoded_sheet = ASE_DecodeSheet(frame_arena, encoded);
if (decoded_image.ok)
{
G_ResourceHandle gpu_resource = Zi;
G_ArenaHandle gpu_perm = G_PermArena();
G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_AsyncCopy);
{
Vec3I32 dims = Zi;
dims.x = decoded_image.width;
dims.y = decoded_image.height;
dims.z = 1;
gpu_resource = G_PushTexture2D(
gpu_perm, cl,
G_Format_R8G8B8A8_Unorm_Srgb,
dims,
G_Layout_AnyQueue_ShaderRead_CopyRead_CopyWrite_Present
);
G_CopyCpuToTexture(
cl,
gpu_resource, VEC3I32(0, 0, 0),
decoded_image.pixels, dims,
RNG3I32(
VEC3I32(0, 0, 0),
dims
)
);
}
i64 completion_target = G_CommitCommandList(cl);
sheet->async_copy_completion_target = completion_target;
sheet->tex = G_PushTexture2DRef(gpu_perm, gpu_resource);
// LogDebugF("Decoded with ref: %F", FmtUint(slice_entry->slice.tex.v));
}
else
{
// TODO: Use 'missing' texture
sheet->tex = G_BlankTexture2D();
sheet->async_copy_completion_target = 0;
}
}
slice_entry->slice.tex = sheet->tex;
// FIXME: Real uv
slice_entry->slice.uv_rect.p0 = VEC2(0, 0);
slice_entry->slice.uv_rect.p1= VEC2(1, 1);
Atomic64Set(&slice_entry->async_copy_completion_target, sheet->async_copy_completion_target);
}
// Free cmds
Lock lock = LockE(&SPR.submit.mutex);
{
last_cmd_node->next = SPR.submit.first_free;
SPR.submit.first_free = first_cmd_node;
}
Unlock(&lock);
}
}
}
}

View File

@ -1,9 +1,83 @@
////////////////////////////////////////////////////////////
//~ Key types
Struct(SPR_SheetKey)
{
ResourceKey r;
};
////////////////////////////////////////////////////////////
//~ Cache types
Struct(SPR_SliceEntry)
{
SPR_SliceEntry *next;
SPR_SheetKey sheet;
u64 hash;
Atomic64 async_copy_completion_target;
String slice_name;
SPR_Slice slice;
};
Struct(SPR_SliceBin)
{
Mutex mutex;
SPR_SliceEntry *first;
};
Struct(SPR_SheetEntry)
{
SPR_SheetEntry *next;
SPR_SheetKey key;
i64 async_copy_completion_target;
G_Texture2DRef tex;
};
Struct(SPR_SheetBin)
{
SPR_SheetEntry *first;
};
////////////////////////////////////////////////////////////
//~ Async cmd types
Struct(SPR_Cmd)
{
SPR_SliceEntry *entry;
};
Struct(SPR_CmdNode)
{
SPR_CmdNode *next;
SPR_Cmd cmd;
};
////////////////////////////////////////////////////////////
//~ State types
Struct(SPR_AsyncCtx)
{
SPR_SheetBin sheet_bins[Kibi(16)];
};
Struct(SPR_Ctx)
{
i32 _;
SPR_SliceBin slice_bins[Kibi(16)];
Atomic32 new_cmds_present;
struct
{
Mutex mutex;
SPR_CmdNode *first;
SPR_CmdNode *last;
SPR_CmdNode *first_free;
u64 count;
} submit;
SPR_AsyncCtx async;
};
extern SPR_Ctx SPR;
@ -16,12 +90,12 @@ void SPR_Bootstrap(void);
////////////////////////////////////////////////////////////
//~ Key helpers
SPR_Key SPR_KeyFromResource(ResourceKey resource);
SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource);
////////////////////////////////////////////////////////////
//~ Lookup
SPR_Slice SPR_SliceFromKey(SPR_Key key, String slice_name);
SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet, String slice_name);
////////////////////////////////////////////////////////////
//~ Async

View File

@ -1,16 +1,8 @@
////////////////////////////////////////////////////////////
//~ Key types
Struct(SPR_Key)
{
u64 v;
};
////////////////////////////////////////////////////////////
//~ Slice types
Struct(SPR_Slice)
{
G_Texture2DRef tex;
Rng2 uv;
Rng2 uv_rect;
};

View File

@ -12,19 +12,19 @@ void UI_Bootstrap(void)
b32 UI_MatchKey(UI_Key a, UI_Key b)
{
return a.hash == b.hash;
return a.v == b.v;
}
b32 UI_IsKeyNil(UI_Key key)
{
return key.hash == 0;
return key.v == 0;
}
UI_Key UI_KeyFromString(String str)
{
u64 top_tag = UI_Top(Tag);
UI_Key key = Zi;
key.hash = HashFnv64(top_tag, str);
key.v = HashFnv64(top_tag, str);
return key;
}
@ -45,18 +45,17 @@ UI_Key UI_KeyF_(String fmt, ...)
UI_Key UI_RandKey(void)
{
u64 seed = ++UI.rand_key_seed;
UI_Key key = Zi;
key.hash = MixU64(seed);
key.v = RandU64FromState(&UI.rand);
return key;
}
UI_Box *UI_BoxFromKey(UI_Key key)
{
UI_Box *box = 0;
if (key.hash != 0)
if (key.v != 0)
{
UI_BoxBin *bin = &UI.box_bins[key.hash % countof(UI.box_bins)];
UI_BoxBin *bin = &UI.box_bins[key.v % countof(UI.box_bins)];
for (box = bin->first; box; box = box->next_in_bin)
{
if (UI_MatchKey(box->key, key))
@ -284,7 +283,7 @@ UI_Checkpoint UI_PushCP(UI_Key parent)
UI_Frame *frame = UI_CurrentFrame();
UI_Stack *stack = frame->top_stack;
stack->top_checkpoint.v += 1;
if (parent.hash != 0)
if (parent.v != 0)
{
UI_Push(Parent, parent);
}
@ -574,7 +573,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
box->key = UI_RootKey;
box->gen = 1;
UI.boxes_count += 1;
UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)];
UI_BoxBin *bin = &UI.box_bins[box->key.v % countof(UI.box_bins)];
bin->first = box;
bin->last = box;
UI.root_box = box;
@ -938,7 +937,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
UI_Key key = cmd.box.key;
UI_Box *box = 0;
{
UI_BoxBin *bin = &UI.box_bins[key.hash % countof(UI.box_bins)];
UI_BoxBin *bin = &UI.box_bins[key.v % countof(UI.box_bins)];
for (box = bin->first; box; box = box->next_in_bin)
{
if (UI_MatchKey(box->key, key))
@ -1069,7 +1068,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
{
UI_Box *box = prunes[prune_idx];
UI_Box *parent = box->parent;
UI_BoxBin *bin = &UI.box_bins[box->key.hash % countof(UI.box_bins)];
UI_BoxBin *bin = &UI.box_bins[box->key.v % countof(UI.box_bins)];
// Re-parent children
if (box->first)
{
@ -1704,7 +1703,7 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
params.target_size = draw_size;
params.target_ro = draw_target_ro;
params.rects = rects_ro;
params.sampler = G_BasicSampler();
params.sampler = G_BasicPointClampSampler();
params.cursor_pos = frame->cursor_pos;
params.aa = TweakFloat("UI anti-aliasing", 1, 0, 1);
}

View File

@ -6,7 +6,7 @@
Struct(UI_Key)
{
u64 hash;
u64 v;
};
////////////////////////////////////////////////////////////
@ -412,7 +412,7 @@ Struct(UI_Ctx)
UI_BoxBin box_bins[Kibi(256)];
UI_Box *first_free_box;
u64 rand_key_seed;
RandState rand;
i64 current_frame_tick;
UI_Frame frames[2];