sprite layer refactor

This commit is contained in:
jacob 2025-09-04 16:33:51 -05:00
parent 938f4e701c
commit 82c81a6280
14 changed files with 371 additions and 126 deletions

View File

@ -21,13 +21,13 @@ if not exist meta.exe (
)
::- Program build
if not "%norun%"=="1" (
if not "%nobuild%"=="1" (
echo ======== Build ========
%program_build_cmd%
set "rc=!errorlevel!"
if !rc! NEQ 0 (
if !rc! EQU %meta_rebuild_code% (
del meta.exe
if !rc! EQU %meta_rebuild_code% (
goto meta_build
)
exit /b !rc!

View File

@ -791,6 +791,12 @@ ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded)
ASE_Header ase_header;
BB_ReadBytes(&br, StringFromStruct(&ase_header));
if (ase_header.magic != 0xA5E0)
{
ASE_PushError(arena, &result.errors, Lit("Not a valid aseprite file"));
goto abort;
}
u64 frame_width = ase_header.width;
u64 frame_height = ase_header.height;
@ -801,13 +807,13 @@ ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded)
ASE_MakeDimensionsSquareish(&ase_header, &frames_x, &frames_y, &image_width, &image_height);
u32 num_frames = 0;
ASE_Frame *frame_head = 0;
ASE_Frame *first_frame = 0;
u32 num_spans = 0;
ASE_Span *span_head = 0;
ASE_Span *first_span = 0;
u32 num_slice_keys = 0;
ASE_SliceKey *slice_key_head = 0;
ASE_SliceKey *first_slice_key = 0;
//- Iterate frames
for (u16 i = 0; i < ase_header.frames; ++i)
@ -823,8 +829,8 @@ ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded)
}
ASE_Frame *frame = PushStruct(arena, ASE_Frame);
frame->next = frame_head;
frame_head = frame;
frame->next = first_frame;
first_frame = frame;
frame->index = i;
frame->duration = frame_header.frame_duration_ms / 1000.0;
@ -872,8 +878,8 @@ ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded)
for (u16 k = 0; k < frame_span_count; ++k)
{
ASE_Span *span = PushStruct(arena, ASE_Span);
span->next = span_head;
span_head = span;
span->next = first_span;
first_span = span;
span->start = BB_ReadUBits(&br, 16);
span->end = BB_ReadUBits(&br, 16);
@ -892,8 +898,8 @@ ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded)
case ASE_ChunkKind_Slice:
{
ASE_SliceKey *slice_key = PushStruct(arena, ASE_SliceKey);
slice_key->next = slice_key_head;
slice_key_head = slice_key;
slice_key->next = first_slice_key;
first_slice_key = slice_key;
u32 num_slices = BB_ReadUBits(&br, 32);
slice_key->num_slices = num_slices;
@ -908,8 +914,8 @@ ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded)
for (u32 k = 0; k < num_slices; ++k)
{
ASE_Slice *slice = PushStruct(arena, ASE_Slice);
slice->next = slice_key->slice_head;
slice_key->slice_head = slice;
slice->next = slice_key->first_slice;
slice_key->first_slice = slice;
u32 start = BB_ReadUBits(&br, 32);
i32 x = BB_ReadIBits(&br, 32);
@ -952,9 +958,11 @@ ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded)
result.num_frames = num_frames;
result.num_spans = num_spans;
result.num_slice_keys = num_slice_keys;
result.frame_head = frame_head;
result.span_head = span_head;
result.slice_key_head = slice_key_head;
result.first_frame = first_frame;
result.first_span = first_span;
result.first_slice_key = first_slice_key;
abort:
if (result.errors.count <= 0)
{

View File

@ -34,7 +34,7 @@ Struct(ASE_SliceKey)
{
String name;
u32 num_slices;
ASE_Slice *slice_head;
ASE_Slice *first_slice;
ASE_SliceKey *next;
};
@ -70,9 +70,9 @@ Struct(ASE_DecodedSheet)
u32 num_frames;
u32 num_spans;
u32 num_slice_keys;
ASE_Frame *frame_head;
ASE_Span *span_head;
ASE_SliceKey *slice_key_head;
ASE_Frame *first_frame;
ASE_Span *first_span;
ASE_SliceKey *first_slice_key;
ASE_ErrorList errors;
b32 success;
};

View File

@ -279,35 +279,35 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
//- Singly linked list stack (first & next pointers)
#define StackPush_N(f,n,next) ((n)->next=(f), (f)=(n))
#define StackPop_N(f,next) ((f)=(f)->next)
#define StackPush(f,n) StackPush_N(f,n,next)
#define StackPop(f) StackPop_N(f,next)
#define StackPushN(f,n,next) ((n)->next=(f), (f)=(n))
#define StackPopN(f,next) ((f)=(f)->next)
#define StackPush(f,n) StackPushN(f,n,next)
#define StackPop(f) StackPopN(f,next)
//- Singly linked list queue (first, last, & next pointers)
#define QueuePush_NZ(nil,f,l,n,next) (CheckNil(nil,f) ? \
#define QueuePushNZ(nil,f,l,n,next) (CheckNil(nil,f) ? \
((f)=(l)=(n),SetNil(nil,(n)->next)) : \
((l)->next=(n),(l)=(n),SetNil(nil,(n)->next)))
#define QueuePushFront_NZ(nil,f,l,n,next) (CheckNil(nil,f) ? \
#define QueuePushFrontNZ(nil,f,l,n,next) (CheckNil(nil,f) ? \
((f)=(l)=(n),SetNil(nil,(n)->next)) : \
((n)->next=(f),(f)=(n)))
#define QueuePop_NZ(nil,f,l,next) ((f)==(l) ? \
#define QueuePopNZ(nil,f,l,next) ((f)==(l) ? \
(SetNil(nil,f),SetNil(nil,l)) : \
((f)=(f)->next))
#define QueuePush_N(f,l,n,next) QueuePush_NZ(0,f,l,n,next)
#define QueuePushFront_N(f,l,n,next) QueuePushFront_NZ(0,f,l,n,next)
#define QueuePop_N(f,l,next) QueuePop_NZ(0,f,l,next)
#define QueuePush(f,l,n) QueuePush_NZ(0,f,l,n,next)
#define QueuePushFront(f,l,n) QueuePushFront_NZ(0,f,l,n,next)
#define QueuePop(f,l) QueuePop_NZ(0,f,l,next)
#define QueuePushN(f,l,n,next) QueuePushNZ(0,f,l,n,next)
#define QueuePushFrontN(f,l,n,next) QueuePushFrontNZ(0,f,l,n,next)
#define QueuePopN(f,l,next) QueuePopNZ(0,f,l,next)
#define QueuePush(f,l,n) QueuePushNZ(0,f,l,n,next)
#define QueuePushFront(f,l,n) QueuePushFrontNZ(0,f,l,n,next)
#define QueuePop(f,l) QueuePopNZ(0,f,l,next)
//- Doubly linked list (first, last, next, & prev pointers)
#define DllInsert_NPZ(nil,f,l,p,n,next,prev) (CheckNil(nil,f) ? \
#define DllInsertNPZ(nil,f,l,p,n,next,prev) (CheckNil(nil,f) ? \
((f) = (l) = (n), SetNil(nil,(n)->next), SetNil(nil,(n)->prev)) : \
CheckNil(nil,p) ? \
((n)->next = (f), (f)->prev = (n), (f) = (n), SetNil(nil,(n)->prev)) : \
@ -315,23 +315,23 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
((l)->next = (n), (n)->prev = (l), (l) = (n), SetNil(nil, (n)->next)) : \
(((!CheckNil(nil,p) && CheckNil(nil,(p)->next)) ? (0) : ((p)->next->prev = (n))), ((n)->next = (p)->next), ((p)->next = (n)), ((n)->prev = (p))))
#define DllRemove_NPZ(nil,f,l,n,next,prev) (((n) == (f) ? (f) = (n)->next : (0)), \
#define DllRemoveNPZ(nil,f,l,n,next,prev) (((n) == (f) ? (f) = (n)->next : (0)), \
((n) == (l) ? (l) = (l)->prev : (0)), \
(CheckNil(nil,(n)->prev) ? (0) : \
((n)->prev->next = (n)->next)), \
(CheckNil(nil,(n)->next) ? (0) : \
((n)->next->prev = (n)->prev)))
#define DllPushBack_NPZ(nil,f,l,n,next,prev) DllInsert_NPZ(nil,f,l,l,n,next,prev)
#define DllPushFront_NPZ(nil,f,l,n,next,prev) DllInsert_NPZ(nil,l,f,f,n,prev,next)
#define DllInsert_NP(f,l,p,n,next,prev) DllInsert_NPZ(0,f,l,p,n,next,prev)
#define DllPushBack_NP(f,l,n,next,prev) DllPushBack_NPZ(0,f,l,n,next,prev)
#define DllPushFront_NP(f,l,n,next,prev) DllPushFront_NPZ(0,f,l,n,next,prev)
#define DllRemove_NP(f,l,n,next,prev) DllRemove_NPZ(0,f,l,n,next,prev)
#define DllInsert(f,l,p,n) DllInsert_NPZ(0,f,l,p,n,next,prev)
#define DllPushBack(f,l,n) DllPushBack_NPZ(0,f,l,n,next,prev)
#define DllPushFront(f,l,n) DllPushFront_NPZ(0,f,l,n,next,prev)
#define DllRemove(f,l,n) DllRemove_NPZ(0,f,l,n,next,prev)
#define DllPushBackNPZ(nil,f,l,n,next,prev) DllInsertNPZ(nil,f,l,l,n,next,prev)
#define DllPushFrontNPZ(nil,f,l,n,next,prev) DllInsertNPZ(nil,l,f,f,n,prev,next)
#define DllInsertNP(f,l,p,n,next,prev) DllInsertNPZ(0,f,l,p,n,next,prev)
#define DllPushBackNP(f,l,n,next,prev) DllPushBackNPZ(0,f,l,n,next,prev)
#define DllPushFrontNP(f,l,n,next,prev) DllPushFrontNPZ(0,f,l,n,next,prev)
#define DllRemoveNP(f,l,n,next,prev) DllRemoveNPZ(0,f,l,n,next,prev)
#define DllInsert(f,l,p,n) DllInsertNPZ(0,f,l,p,n,next,prev)
#define DllPushBack(f,l,n) DllPushBackNPZ(0,f,l,n,next,prev)
#define DllPushFront(f,l,n) DllPushFrontNPZ(0,f,l,n,next,prev)
#define DllRemove(f,l,n) DllRemoveNPZ(0,f,l,n,next,prev)
////////////////////////////////
//~ Color helper macros

View File

@ -29,7 +29,7 @@ Struct(TempArena)
#define ScratchArenasPerCtx 2
Struct(ArenaCtx)
Struct(FiberArenaCtx)
{
Arena *perm_arena;
Arena *scratch_arenas[ScratchArenasPerCtx];
@ -40,7 +40,7 @@ Struct(ArenaCtx)
Struct(SharedArenaCtx)
{
ArenaCtx arena_contexts[MaxFibers];
FiberArenaCtx arena_contexts[MaxFibers];
};
extern SharedArenaCtx shared_arena_ctx;
@ -176,12 +176,12 @@ Inline void EndTempArena(TempArena temp)
}
////////////////////////////////
//~ Arena ctx operations
//~ Fiber arena ctx operations
Inline ArenaCtx *ArenaCtxFromFiberId(i16 fiber_id)
Inline FiberArenaCtx *FiberArenaCtxFromId(i16 fiber_id)
{
SharedArenaCtx *g = &shared_arena_ctx;
ArenaCtx *ctx = &g->arena_contexts[fiber_id];
FiberArenaCtx *ctx = &g->arena_contexts[fiber_id];
if (!ctx->perm_arena)
{
__profn("Initialize fiber arena ctx");
@ -194,7 +194,7 @@ Inline ArenaCtx *ArenaCtxFromFiberId(i16 fiber_id)
return ctx;
}
#define PermArena (ArenaCtxFromFiberId(FiberId())->perm_arena)
#define PermArena() (FiberArenaCtxFromId(FiberId())->perm_arena)
////////////////////////////////
//~ Scratch helpers
@ -215,7 +215,7 @@ Inline TempArena BeginScratch(Arena *potential_conflict)
/* Use `BeginScratchNoConflict` if no conflicts are present */
Assert(potential_conflict != 0);
ArenaCtx *ctx = ArenaCtxFromFiberId(FiberId());
FiberArenaCtx *ctx = FiberArenaCtxFromId(FiberId());
Arena *scratch_arena = ctx->scratch_arenas[0];
if (scratch_arena == potential_conflict)
{
@ -239,7 +239,7 @@ Inline TempArena BeginScratch(Arena *potential_conflict)
Inline TempArena BeginScratchNoConflict_(void)
{
ArenaCtx *ctx = ArenaCtxFromFiberId(FiberId());
FiberArenaCtx *ctx = FiberArenaCtxFromId(FiberId());
Arena *scratch_arena = ctx->scratch_arenas[0];
TempArena temp = BeginTempArena(scratch_arena);
return temp;

View File

@ -140,10 +140,10 @@ void BB_WriteAlignToNextByte(BB_Writer *bw)
bw->cur_bit += (8 - (bw->cur_bit & 7)) & 7;
}
void BB_WriteAlign(BB_Writer *bw, u64 align)
void BB_WriteAlignByte(BB_Writer *bw, u64 align)
{
BB_WriteAlignToNextByte(bw);
BB_WriteDebugMagic(bw, BB_DebugMagicKind_Align, align);
BB_WriteDebugMagic(bw, BB_DebugMagicKind_AlignByte, align);
if (align > 0)
{
u64 new_pos = (bw->cur_bit >> 3);
@ -471,10 +471,10 @@ void BB_ReadAlignToNextByte(BB_Reader *br)
br->cur_bit += (8 - (br->cur_bit & 7)) & 7;
}
void BB_ReadAlign(BB_Reader *br, u64 align)
void BB_ReadAlignByte(BB_Reader *br, u64 align)
{
BB_ReadAlignToNextByte(br);
BB_ReadDebugMagic(br, BB_DebugMagicKind_Align, align);
BB_ReadDebugMagic(br, BB_DebugMagicKind_AlignByte, align);
if (align > 0)
{
u64 new_pos = (br->cur_bit >> 3);

View File

@ -47,7 +47,7 @@ Struct(BB_Reader)
/* Magic numbers inserted to verify read/write type & length */
Enum(BB_DebugMagicKind)
{
BB_DebugMagicKind_Align = 0x20A4,
BB_DebugMagicKind_AlignByte = 0x20A4,
BB_DebugMagicKind_AlignToNextByte = 0x379A,
BB_DebugMagicKind_UBits = 0xCB4A,
BB_DebugMagicKind_IBits = 0xB30D,
@ -90,7 +90,7 @@ b32 BB_CheckWriterOverflowBits(BB_Writer *bw, u64 num_bits);
//- Align
void BB_WriteAlignToNextByte(BB_Writer *bw);
void BB_WriteAlign(BB_Writer *bw, u64 align);
void BB_WriteAlignByte(BB_Writer *bw, u64 align);
//- Bits
void BB_WriteUBitsNoMagic(BB_Writer *bw, u64 value, u8 num_bits);
@ -144,7 +144,7 @@ b32 BB_CheckReaderOverflowBits(BB_Reader *br, u64 num_bits);
//- Align
void BB_ReadAlignToNextByte(BB_Reader *br);
void BB_ReadAlign(BB_Reader *br, u64 align);
void BB_ReadAlignByte(BB_Reader *br, u64 align);
//- Bits
u64 BB_ReadUBitsNoMagic(BB_Reader *br, u8 num_bits);

View File

@ -4,9 +4,11 @@ SharedResourceState shared_resource_state = ZI;
//~ Startup
void InitBaseResources(String archive)
{
if (archive.len > 0)
{
SharedResourceState *g = &shared_resource_state;
if (archive.len <= 0) return;
Arena *perm = PermArena();
BB_Buff bb = BB_BuffFromString(archive);
BB_Reader br = BB_ReaderFromBuff(&bb);
@ -23,17 +25,18 @@ void InitBaseResources(String archive)
u64 data_start = BB_ReadUBits(&br, 64);
u64 data_len = BB_ReadUBits(&br, 64);
ResourceEntry *entry = PushStruct(PermArena, ResourceEntry);
ResourceEntry *entry = PushStruct(perm, ResourceEntry);
entry->name = STRING(name_len, archive.text + name_start);
entry->data = STRING(data_len, archive.text + data_start);
entry->hash = HashFnv64(Fnv64Basis, entry->name);
ResourceEntryBin *bin = &g->bins[entry->hash % NumResourceEntryBins];
QueuePush_N(bin->first, bin->last, entry, next_in_bin);
QueuePush_N(g->first_entry, g->last_entry, entry, next);
QueuePushN(bin->first, bin->last, entry, next_in_bin);
QueuePushN(g->first_entry, g->last_entry, entry, next);
}
g->entries_count = num_entries;
}
}
////////////////////////////////
//~ Resource helpers

View File

@ -477,8 +477,9 @@ void StartupMeta(void)
//- Generate archive file
String arc_out_file = F_GetFull(arena, Lit("resources.arc"));
if (ret == 0)
{
EchoLine(Lit("[Building ARC]"));
EchoLine(Lit("[Creating archive]"));
Struct(EntryNode)
{
EntryNode *next;
@ -555,14 +556,15 @@ void StartupMeta(void)
String file_data = F_DataFromFile(arena, en->file_name);
/* Write name */
BB_WriteAlign(&bw, 64);
BB_WriteAlignByte(&bw, 64);
u64 name_start = BB_GetNumBytesWritten(&bw) + 1;
BB_WriteString(&bw, en->entry_name);
u64 name_len = BB_GetNumBytesWritten(&bw) - name_start;
/* Write data */
BB_WriteAlign(&bw, 64);
u64 data_start = BB_GetNumBytesWritten(&bw) + 1;
BB_WriteAlignByte(&bw, 64);
/* FIXME: Why no +1 here? */
u64 data_start = BB_GetNumBytesWritten(&bw);
BB_WriteBytes(&bw, file_data);
u64 data_len = BB_GetNumBytesWritten(&bw) - data_start;
@ -601,7 +603,7 @@ void StartupMeta(void)
if (ret == 0)
{
EchoLine(Lit("[Compiling RC]"));
String cmd = StringF(arena, "rc -nologo -fo %F %F", FmtString(res_out_file), FmtString(rc_out_file));
String cmd = StringF(arena, "cmd /c rc.exe -nologo -fo %F %F", FmtString(res_out_file), FmtString(rc_out_file));
OS_CommandResult result = OS_RunCommand(arena, cmd);
String output = TrimWhitespace(result.output);
EchoLine(F_GetFileName(rc_out_file));
@ -618,7 +620,7 @@ void StartupMeta(void)
{
EchoLine(Lit("[Compiling C]"));
String cmd = StringF(arena,
"cl /c %F -Fo:%F %F %F %F %F",
"cmd /c cl.exe /c %F -Fo:%F %F %F %F %F",
FmtString(c_out_file),
FmtString(c_out_obj_file),
FmtString(StringFromList(arena, flags_msvc, Lit(" "))),
@ -640,7 +642,7 @@ void StartupMeta(void)
{
EchoLine(Lit("[Linking]"));
String cmd = StringF(arena,
"link %F %F /OUT:%F %F %F",
"cmd /c link.exe %F %F /OUT:%F %F %F",
FmtString(c_out_obj_file),
FmtString(res_out_file),
FmtString(exe_file),

View File

@ -56,7 +56,7 @@ M_TokenFile *M_PushTokenFile(Arena *arena, M_TokenFileList *l, String name, Stri
tf->name = name;
tf->data = data;
M_PushToken(arena, tf, Lit(""), M_TokenKind_Eol);
QueuePush_NZ(&M_NilTokenFile, l->first, l->last, tf, next);
QueuePushNZ(&M_NilTokenFile, l->first, l->last, tf, next);
++l->count;
return tf;
}
@ -72,7 +72,7 @@ M_Token *M_PushToken(Arena *arena, M_TokenFile *file, String s, M_TokenKind kind
{
t->file = file;
}
QueuePush_NZ(&M_NilToken, file->first_token, file->last_token, t, next);
QueuePushNZ(&M_NilToken, file->first_token, file->last_token, t, next);
++file->tokens_count;
return t;
}
@ -81,6 +81,8 @@ M_TokenFileList M_TokensFromSrcDirs(Arena *arena, StringList src_dirs)
{
TempArena scratch = BeginScratch(arena);
M_TokenFileList result = ZI;
result.first = &M_NilTokenFile;
result.last = &M_NilTokenFile;
/* Build token file list from src dirs */
Dict *dedup_dict = InitDict(scratch.arena, 1021);
@ -206,7 +208,7 @@ M_Layer *M_PushLayer(Arena *arena, M_LayerList *list, M_TokenFile *file)
*layer = M_NilLayer;
layer->valid = 1;
layer->file = file;
QueuePush_NZ(&M_NilLayer, list->first, list->last, layer, next);
QueuePushNZ(&M_NilLayer, list->first, list->last, layer, next);
++list->count;
return layer;
}
@ -223,7 +225,7 @@ M_Entry *M_PushEntry(Arena *arena, M_Layer *l, M_EntryKind kind, M_Token *entry_
e->arg_tokens[i] = args[i];
}
e->args_count = args_count;
QueuePush_NZ(&M_NilEntry, l->first, l->last, e, next);
QueuePushNZ(&M_NilEntry, l->first, l->last, e, next);
++l->count;
return e;
}

View File

@ -81,12 +81,6 @@ Enum(M_EntryKind)
M_EntryKind_EmbedDir,
};
Struct(M_EntryKindMatch)
{
char *match_cstr;
u64 max_args;
};
Global Readonly char *M_entry_kind_rules[] = {
[M_EntryKind_Layer] = "@Layer",
[M_EntryKind_Dep] = "@Dep",

View File

@ -370,8 +370,3 @@ String P_GetClipboardText(Arena *arena);
void P_SleepPrecise(i64 sleep_time_ns);
void P_SleepFrame(i64 last_frame_time_ns, i64 target_dt_ns);
////////////////////////////////
//~ @hookdecl Entry point (implemented per application)
void P_AppStartup(String args_str);

View File

@ -50,12 +50,187 @@ JobDef(S_LoadTextureJob, sig, _)
JobDef(S_LoadSheetJob, sig, _)
{
TempArena scratch = BeginScratchNoConflict();
Arena *perm = PermArena();
S_Entry *entry = sig->entry;
Resource resource = entry->resource;
b32 success = 1;
S_Sheet *sheet = &entry->sheet;
sheet->valid = 1;
String name = NameFromResource(resource);
String data = DataFromResource(resource);
ASE_DecodedSheet decoded = ASE_DecodeSheet(scratch.arena, data);
success = decoded.success;
if (success)
{
Vec2 image_size = decoded.image_size;
Vec2 frame_size = decoded.frame_size;
Vec2 frame_center = MulVec2(decoded.frame_size, 0.5f);
sheet->image_size = image_size;
sheet->frame_size = frame_size;
/* Init frames */
sheet->frames_count = decoded.num_frames;
sheet->frames = PushStructs(perm, S_Frame, sheet->frames_count);
for (ASE_Frame *src = decoded.first_frame; src; src = src->next)
{
S_Frame *dst = &sheet->frames[src->index];
dst->index = src->index;
dst->duration = src->duration;
dst->clip.p0.x = src->x1;
dst->clip.p0.y = src->y1;
dst->clip.p1.x = src->x2;
dst->clip.p1.y = src->y2;
}
/* Init spans */
sheet->spans_count = decoded.num_spans;
sheet->span_bins_count = MaxU32(AlignU64Pow2(sheet->spans_count * 2), 1);
sheet->spans = PushStructs(perm, S_Span, sheet->spans_count);
sheet->span_bins = PushStructs(perm, S_SpanBin, sheet->span_bins_count);
{
i32 span_index = 0;
for (ASE_Span *src = decoded.first_span; src; src = src->next)
{
S_Span *dst = &sheet->spans[span_index];
dst->hash = HashFnv64(Fnv64Basis, src->name);
dst->name = PushString(perm, src->name);
dst->start = src->start;
dst->end = src->end;
/* Insert span into bin */
{
S_SpanBin *bin = &sheet->span_bins[dst->hash % sheet->span_bins_count];
QueuePushN(bin->first, bin->last, dst, next_in_bin);
}
++span_index;
}
}
/* Init slice groups */
sheet->slice_groups_count = decoded.num_slice_keys;
sheet->slice_group_bins_count = MaxU32(AlignU64Pow2(sheet->slice_groups_count * 2), 1);
sheet->slice_groups = PushStructs(perm, S_SliceGroup, sheet->slice_groups_count);
sheet->slice_group_bins = PushStructs(perm, S_SliceGroupBin, sheet->slice_group_bins_count);
{
i32 group_index = 0;
for (ASE_SliceKey *src_group = decoded.first_slice_key; src_group; src_group = src_group->next)
{
S_SliceGroup *dst_group = &sheet->slice_groups[group_index];
dst_group->hash = HashFnv64(Fnv64Basis, src_group->name);
dst_group->name = PushString(perm, src_group->name);
/* Init slices */
dst_group->slices = PushStructs(perm, S_Slice, sheet->frames_count);
{
/* Fill is_original slices */
for (ASE_Slice *src_slice = src_group->first_slice; src_slice; src_slice = src_slice->next)
{
f32 x1_px = src_slice->x1;
f32 y1_px = src_slice->y1;
f32 x2_px = src_slice->x2;
f32 y2_px = src_slice->y2;
f32 width_px = x2_px - x1_px;
f32 height_px = y2_px - y1_px;
f32 x1 = (x1_px - frame_center.x) / frame_size.x;
f32 y1 = (y1_px - frame_center.y) / frame_size.y;
f32 x2 = (x2_px - frame_center.x) / frame_size.x;
f32 y2 = (y2_px - frame_center.y) / frame_size.y;
f32 width = x2 - x1;
f32 height = y2 - y1;
/* Rect */
Rect rect_px = RectFromScalar(x1_px, y1_px, width_px, height_px);
Rect rect = RectFromScalar(x1, y1, width, height);
/* Center */
Vec2 center_px = VEC2(x1_px + (width_px * 0.5f), y1_px + (height_px * 0.5f));
Vec2 center = VEC2(x1 + (width * 0.5f), y1 + (height * 0.5f));
/* Dir */
Vec2 dir_px = VEC2(center_px.x, -1);
Vec2 dir = VEC2(0, -1);
S_Slice *dst_slice = &dst_group->slices[src_slice->start];
dst_slice->is_original = 1;
dst_slice->rect_px = rect_px;
dst_slice->center_px = center_px;
dst_slice->dir_px = dir_px;
dst_slice->rect = rect;
dst_slice->center = center;
dst_slice->dir = dir;
}
/* Copy slices forward into frames without a slice */
{
S_Slice *origin = 0;
for (u32 frame_index = 0; frame_index < sheet->frames_count; ++frame_index)
{
S_Slice *slice = &dst_group->slices[frame_index];
if (slice->is_original)
{
origin = slice;
}
else
{
*slice = *origin;
slice->is_original = 0;
}
}
}
}
/* Insert group into bin */
{
S_SliceGroupBin *bin = &sheet->slice_group_bins[dst_group->hash % sheet->slice_group_bins_count];
QueuePushN(bin->first, bin->last, dst_group, next_in_bin);
}
++group_index;
}
}
/* Init slice ray directions */
{
String ray_suffix = Lit(".ray");
for (u32 slice_group_index = 0; slice_group_index < sheet->slice_groups_count; ++slice_group_index)
{
S_SliceGroup *group = &sheet->slice_groups[slice_group_index];
if (StringEndsWith(group->name, ray_suffix))
{
String point_slice_group_name = group->name;
point_slice_group_name.len -= ray_suffix.len;
u64 point_slice_group_hash = HashFnv64(Fnv64Basis, point_slice_group_name);
S_SliceGroupBin *bin = &sheet->slice_group_bins[point_slice_group_hash % sheet->slice_group_bins_count];
S_SliceGroup *point_slice_group = bin->first;
for (; point_slice_group; point_slice_group = point_slice_group->next_in_bin)
{
if (point_slice_group->hash == point_slice_group_hash)
{
break;
}
}
if (point_slice_group)
{
for (u32 frame_index = 0; frame_index < sheet->frames_count; ++frame_index)
{
S_Slice *point_slice = &point_slice_group->slices[frame_index];
S_Slice *ray_slice = &group->slices[frame_index];
Vec2 ray_end = ray_slice->center_px;
Vec2 ray_end_norm = ray_slice->center;
point_slice->dir_px = SubVec2(ray_end, point_slice->center_px);
point_slice->dir = SubVec2(ray_end_norm, point_slice->center);
point_slice->has_dir = 1;
}
}
}
}
}
}
sheet->loaded = 1;
AddCounter(&entry->sheet_load_counter, -1);
EndScratch(scratch);
}
@ -63,6 +238,7 @@ JobDef(S_LoadSheetJob, sig, _)
////////////////////////////////
//~ Cache
/* TODO: Per-fiber L1 cache */
S_Entry *S_FetchEntry(Resource resource, JobPool pool, S_FetchFlag flags)
{
S_SharedState *g = &S_shared_state;
@ -78,36 +254,35 @@ S_Entry *S_FetchEntry(Resource resource, JobPool pool, S_FetchFlag flags)
{
if (entry->resource.hash == resource.hash)
{
entry = entry;
break;
}
}
}
Unlock(&lock);
}
/* Entry not found, re-check & create */
/* Entry not found: lock, re-search, & create */
if (!entry)
{
Lock lock = LockE(&bin->mutex);
{
/* Re-check */
/* Re-search */
entry = bin->first;
for (; entry; entry = entry->next_in_bin)
{
if (entry->resource.hash == resource.hash)
{
entry = entry;
break;
}
}
/* Create */
if (!entry)
{
entry = PushStruct(PermArena, S_Entry);
Arena *perm = PermArena();
entry = PushStruct(perm, S_Entry);
entry->resource = resource;
AddCounter(&entry->texture_load_counter, 1);
AddCounter(&entry->sheet_load_counter, 1);
QueuePush_N(bin->first, bin->last, entry, next_in_bin);
QueuePushN(bin->first, bin->last, entry, next_in_bin);
}
}
Unlock(&lock);
@ -174,17 +349,73 @@ S_Sheet *S_SheetFromResourceAsync(Resource resource)
S_Span S_SpanFromName(S_Sheet *sheet, String name)
{
S_Span result = ZI;
u32 bins_count = sheet->span_bins_count;
if (bins_count > 0)
{
u64 name_hash = HashFnv64(Fnv64Basis, name);
S_SpanBin *bin = &sheet->span_bins[name_hash % bins_count];
S_Span *span = bin->first;
for (; span; span = span->next_in_bin)
{
if (span->hash == name_hash)
{
result = *span;
break;
}
}
}
return result;
}
S_Frame S_FrameFromIndex(S_Sheet *sheet, u64 index)
{
S_Frame result = ZI;
if (sheet->frames_count > 0)
{
result = sheet->frames[index % sheet->frames_count];
}
return result;
}
S_Slice S_SliceFromName(S_Sheet *sheet, String name, u64 frame_index)
{
S_Slice result = ZI;
b32 match = 0;
u32 bins_count = sheet->slice_group_bins_count;
if (bins_count > 0 && sheet->frames_count > 0)
{
u64 name_hash = HashFnv64(Fnv64Basis, name);
S_SliceGroupBin *bin = &sheet->slice_group_bins[name_hash % bins_count];
S_SliceGroup *group = bin->first;
for (; group; group = group->next_in_bin)
{
if (group->hash == name_hash)
{
result = group->slices[frame_index % sheet->frames_count];
match = 1;
break;
}
}
}
/* Return 'pivot' by default */
if (!match)
{
if (EqString(name, Lit("pivot")))
{
/* 'pivot' slice does not exist, return center */
result.center = VEC2(0, 0);
result.center_px = MulVec2(sheet->frame_size, 0.5f);
result.dir_px = VEC2(result.center_px.x, 0);
result.dir = VEC2(0, -0.5);
}
else
{
result = S_SliceFromName(sheet, Lit("pivot"), frame_index);
}
}
return result;
}

View File

@ -24,18 +24,26 @@ Struct(S_Frame)
Struct(S_Span)
{
S_Span *next_in_bin;
u64 hash;
String name;
u32 start;
u32 end;
};
Struct(S_SpanBin)
{
S_Span *first;
S_Span *last;
};
Struct(S_Slice)
{
/* If 1, this slice was not copied over from another frame in the sprite sheet */
b32 original;
b32 is_original;
/* If 1, the slice has a corresponding '.ray' slice affecting the 'dir' fields */
b32 has_ray;
b32 has_dir;
/* Values are in the range -0.5 (top / left edge) -> +0.5 (bottom / right edge) */
Rect rect;
@ -48,20 +56,18 @@ Struct(S_Slice)
Vec2 dir_px;
};
Struct(S_SliceArray)
Struct(S_SliceGroup)
{
u64 count;
S_SliceGroup *next_in_bin;
u64 hash;
String name;
S_Slice *slices;
};
Struct(S_SheetSliceGroup)
Struct(S_SliceGroupBin)
{
String name;
u64 per_frame_count;
/* 2d array of slices with length (num frames) * (num slices per frame).
* Index with [(frame index * num slices per frame) + slice index in frame] */
S_Slice *frame_slices;
S_SliceGroup *first;
S_SliceGroup *last;
};
Struct(S_Sheet)
@ -76,11 +82,15 @@ Struct(S_Sheet)
u32 spans_count;
S_Span *spans;
Dict *spans_dict;
u32 slice_groups_count;
S_SheetSliceGroup *slice_groups;
Dict *slice_groups_dict;
S_SliceGroup *slice_groups;
u32 span_bins_count;
S_SpanBin *span_bins;
u32 slice_group_bins_count;
S_SliceGroupBin *slice_group_bins;
};
extern Readonly S_Sheet S_NilSheet;