From 82c81a62809350a709f357a50aba146545f55a43 Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 4 Sep 2025 16:33:51 -0500 Subject: [PATCH] sprite layer refactor --- build.bat | 4 +- src/ase/ase.c | 36 +++--- src/ase/ase.h | 8 +- src/base/base.h | 50 ++++---- src/base/base_arena.h | 16 +-- src/base/base_bitbuff.c | 8 +- src/base/base_bitbuff.h | 6 +- src/base/base_resource.c | 51 ++++---- src/meta/meta.c | 16 +-- src/meta/meta_lay.c | 10 +- src/meta/meta_lay.h | 6 - src/platform/platform.h | 5 - src/sprite/sprite.c | 243 ++++++++++++++++++++++++++++++++++++++- src/sprite/sprite.h | 38 +++--- 14 files changed, 371 insertions(+), 126 deletions(-) diff --git a/build.bat b/build.bat index 952d6f4b..75f7df9c 100644 --- a/build.bat +++ b/build.bat @@ -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 ( + del meta.exe if !rc! EQU %meta_rebuild_code% ( - del meta.exe goto meta_build ) exit /b !rc! diff --git a/src/ase/ase.c b/src/ase/ase.c index 3e8a6f06..2536f260 100644 --- a/src/ase/ase.c +++ b/src/ase/ase.c @@ -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) { diff --git a/src/ase/ase.h b/src/ase/ase.h index 1c957b22..865be406 100644 --- a/src/ase/ase.h +++ b/src/ase/ase.h @@ -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; }; diff --git a/src/base/base.h b/src/base/base.h index 77cc8c53..90d6d70c 100644 --- a/src/base/base.h +++ b/src/base/base.h @@ -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 diff --git a/src/base/base_arena.h b/src/base/base_arena.h index 36794e14..0f33a530 100644 --- a/src/base/base_arena.h +++ b/src/base/base_arena.h @@ -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; diff --git a/src/base/base_bitbuff.c b/src/base/base_bitbuff.c index 39433af6..6f225e28 100644 --- a/src/base/base_bitbuff.c +++ b/src/base/base_bitbuff.c @@ -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); diff --git a/src/base/base_bitbuff.h b/src/base/base_bitbuff.h index 214d0738..84768ae9 100644 --- a/src/base/base_bitbuff.h +++ b/src/base/base_bitbuff.h @@ -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); diff --git a/src/base/base_resource.c b/src/base/base_resource.c index 6ad33979..6d626566 100644 --- a/src/base/base_resource.c +++ b/src/base/base_resource.c @@ -5,34 +5,37 @@ SharedResourceState shared_resource_state = ZI; void InitBaseResources(String archive) { - SharedResourceState *g = &shared_resource_state; - if (archive.len <= 0) return; - - BB_Buff bb = BB_BuffFromString(archive); - BB_Reader br = BB_ReaderFromBuff(&bb); - - u64 magic = BB_ReadUBits(&br, 64); - Assert(magic == ResourceEmbeddedMagic); - - /* Create & insert entries */ - u64 num_entries = BB_ReadUBits(&br, 64); - for (u64 i = 0; i < num_entries; ++i) + if (archive.len > 0) { - u64 name_start = BB_ReadUBits(&br, 64); - u64 name_len = BB_ReadUBits(&br, 64); - u64 data_start = BB_ReadUBits(&br, 64); - u64 data_len = BB_ReadUBits(&br, 64); + SharedResourceState *g = &shared_resource_state; + Arena *perm = PermArena(); - ResourceEntry *entry = PushStruct(PermArena, 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); + BB_Buff bb = BB_BuffFromString(archive); + BB_Reader br = BB_ReaderFromBuff(&bb); - 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); + u64 magic = BB_ReadUBits(&br, 64); + Assert(magic == ResourceEmbeddedMagic); + + /* Create & insert entries */ + u64 num_entries = BB_ReadUBits(&br, 64); + for (u64 i = 0; i < num_entries; ++i) + { + u64 name_start = BB_ReadUBits(&br, 64); + u64 name_len = BB_ReadUBits(&br, 64); + u64 data_start = BB_ReadUBits(&br, 64); + u64 data_len = BB_ReadUBits(&br, 64); + + 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]; + QueuePushN(bin->first, bin->last, entry, next_in_bin); + QueuePushN(g->first_entry, g->last_entry, entry, next); + } + g->entries_count = num_entries; } - g->entries_count = num_entries; } //////////////////////////////// diff --git a/src/meta/meta.c b/src/meta/meta.c index b543202e..583d0ddc 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -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), diff --git a/src/meta/meta_lay.c b/src/meta/meta_lay.c index da05d750..132f4745 100644 --- a/src/meta/meta_lay.c +++ b/src/meta/meta_lay.c @@ -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; } diff --git a/src/meta/meta_lay.h b/src/meta/meta_lay.h index a76d3cbe..bf3055b5 100644 --- a/src/meta/meta_lay.h +++ b/src/meta/meta_lay.h @@ -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", diff --git a/src/platform/platform.h b/src/platform/platform.h index a38bf2b6..36cca614 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -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); diff --git a/src/sprite/sprite.c b/src/sprite/sprite.c index 8c025048..a923aa83 100644 --- a/src/sprite/sprite.c +++ b/src/sprite/sprite.c @@ -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; } diff --git a/src/sprite/sprite.h b/src/sprite/sprite.h index 206bd000..c1c5774a 100644 --- a/src/sprite/sprite.h +++ b/src/sprite/sprite.h @@ -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;