241 lines
6.6 KiB
C
241 lines
6.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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Lookup
|
|
|
|
SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet, String slice_name)
|
|
{
|
|
SPR_Slice result = Zi;
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|