power_play/src/sprite/sprite.c
2026-01-27 13:54:40 -06:00

275 lines
7.6 KiB
C

SPR_Ctx SPR = Zi;
////////////////////////////////////////////////////////////
//~ Bootstrap
void SPR_Bootstrap(void)
{
OnAsyncTick(SPR_TickAsync);
}
////////////////////////////////////////////////////////////
//~ Key helpers
SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource)
{
SPR_SheetKey result = Zi;
result.r = resource;
return result;
}
SPR_SpanKey SPR_SpanKeyFromName(String name)
{
SPR_SpanKey result = Zi;
result.v = HashString(name);
return result;
}
SPR_LayerKey SPR_LayerKeyFromName(String name)
{
SPR_LayerKey result = Zi;
result.v = HashString(name);
return result;
}
////////////////////////////////////////////////////////////
//~ Lookup
SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet, SPR_SpanKey span, i64 frame_seq)
{
SPR_Slice result = Zi;
return result;
}
Xform SPR_XformFromSheet(SPR_SheetKey sheet, SPR_LayerKey layer, SPR_SpanKey span, i64 frame_seq)
{
Xform result = Zi;
return result;
}
// SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet, SPR_SpanKey span, i64 frame_seq)
// {
// // TODO: Ability to specify desired alpha modes (Straight, Premultiplied, Opaque)
// SPR_Slice result = Zi;
// // FIXME
// u64 hash = sheet.r.v;
// hash = HashStringEx(hash, slice_name);
// 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)
// {
// SllStackPop(SPR.submit.first_free);
// 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;
// }
////////////////////////////////////////////////////////////
//~ Async
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;
SPR.submit.count = 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);
}
}
}
}