ase refactor wip

This commit is contained in:
jacob 2026-01-29 21:10:17 -06:00
parent 9c0c649365
commit ff7e0b2167
5 changed files with 1678 additions and 1689 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,241 +1,328 @@
////////////////////////////////////////////////////////////
//~ Sheet types
//~ Meta types
Struct(ASE_Slice)
Struct (ASE_Cel)
{
u32 start;
Rng2I32 rect;
ASE_Slice *next;
};
Struct(ASE_Span)
{
String name;
u32 start;
u32 end;
ASE_Span *next;
};
Struct(ASE_Frame)
{
u32 index;
Rng2I32 rect;
f64 duration;
ASE_Frame *next;
};
Struct(ASE_SliceKey)
{
String name;
u32 slices_count;
ASE_Slice *first_slice;
ASE_SliceKey *next;
};
////////////////////////////////////////////////////////////
//~ Decoder result types
Struct(ASE_Error)
{
String msg;
ASE_Error *next;
};
Struct(ASE_ErrorList)
{
u64 count;
ASE_Error *first;
ASE_Error *last;
};
Struct(ASE_DecodedImage)
{
u32 width;
u32 height;
u32 *pixels; // Array of [width * height] pixels
ASE_ErrorList errors;
b32 ok;
};
Struct(ASE_DecodedSheet)
{
Vec2 image_size;
Vec2 frame_size;
u32 frames_count;
u32 spans_count;
u32 slice_keys_count;
ASE_Frame *first_frame;
ASE_Span *first_span;
ASE_SliceKey *first_slice_key;
ASE_ErrorList errors;
b32 ok;
};
////////////////////////////////////////////////////////////
//~ Inflator types
#define ASE_HuffBitCount 16
Struct(ASE_Bitbuff)
{
u8 *data;
u64 cur_bit;
};
Enum(ASE_BlockType)
{
ASE_BlockType_Uncompressed = 0,
ASE_BlockType_CompressedFixed = 1,
ASE_BlockType_CompressedDynamic = 2,
ASE_BlockType_Reserved = 3
};
Struct(ASE_HuffEntry)
{
u16 symbol;
u16 bits_used;
};
Struct(ASE_HuffDict)
{
u32 max_code_bits;
u32 entries_count;
ASE_HuffEntry *entries;
};
////////////////////////////////////////////////////////////
//~ Header types
Packed(Struct(ASE_Header)
{
u32 file_size;
u16 magic;
u16 frames;
u16 width;
u16 height;
u16 color_depth;
u32 flags;
u16 speed;
u32 _1;
u32 _2;
u8 palette_entry;
u8 _3[3];
u16 colors_count;
u8 pixel_width;
u8 pixel_height;
i16 grid_x;
i16 grid_y;
u16 grid_width;
u16 grid_height;
u8 _4[84];
});
Packed(Struct(ASE_FrameHeader)
{
u32 bytes;
u16 magic;
u16 chunks_old;
u16 frame_duration_ms;
u8 _[2];
u32 chunks_new;
});
////////////////////////////////////////////////////////////
//~ Image decoder types
Enum(ASE_ChunkKind)
{
ASE_ChunkKind_OldPalette1 = 0x0004,
ASE_ChunkKind_OldPalette2 = 0x0011,
ASE_ChunkKind_Layer = 0x2004,
ASE_ChunkKind_Cel = 0x2005,
ASE_ChunkKind_CelExtra = 0x2006,
ASE_ChunkKind_ColorProfile = 0x2007,
ASE_ChunkKind_ExternalFiles = 0x2008,
ASE_ChunkKind_Mask = 0x2016,
ASE_ChunkKind_Path = 0x2017,
ASE_ChunkKind_Tags = 0x2018,
ASE_ChunkKind_Palette = 0x2019,
ASE_ChunkKind_Userdata = 0x2020,
ASE_ChunkKind_Slice = 0x2022,
ASE_ChunkKind_Tileset = 0x2023
};
Enum(ASE_CelKind)
{
ASE_CelKind_RawImage = 0,
ASE_CelKind_Linked = 1,
ASE_CelKind_CompressedImage = 2,
ASE_CelKind_CompressedTilemap = 3
i32 _;
};
Struct(ASE_Layer)
{
u16 flags;
u16 type;
u16 child_level;
u16 blend_mode;
u8 opacity;
String name;
u32 tileset_index;
u32 index;
ASE_Layer *next;
ASE_Layer *prev;
String name;
i64 cels_count;
ASE_Cel *cels;
};
Struct(ASE_Cel)
Struct(ASE_Span)
{
u16 layer_index;
i16 x_pos;
i16 y_pos;
u8 opacity;
ASE_CelKind type;
i16 z_index;
ASE_Span *next;
ASE_Span *prev;
// Linked cel
u16 frame_pos;
String name;
i64 start;
i64 end;
};
// Compressed image
u32 width;
u32 height;
u32 *pixels;
Struct(ASE_Meta)
{
i64 frames_count;
u16 frame_index;
ASE_Cel *next;
i64 spans_count;
ASE_Span *first_span;
ASE_Span *last_span;
i64 layers_count;
ASE_Layer *first_layer;
ASE_Layer *last_layer;
};
////////////////////////////////////////////////////////////
//~ Ase bitbuff helpers
//~ Query types
u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits);
u32 ASE_ConsumeBits(ASE_Bitbuff *bb, u32 nbits);
void ASE_SkipBits(ASE_Bitbuff *bb, u32 nbits);
Struct(ASE_SinglePixelResult)
{
Vec2 pos;
u32 v;
};
////////////////////////////////////////////////////////////
//~ Inflate
//~ Meta
u32 ASE_ReverseBits(u32 v, u32 bit_count);
ASE_HuffDict ASE_InitHuffDict(Arena *arena, u32 max_code_bits, u32 *bl_counts, u32 bl_counts_count);
u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb);
void ASE_Inflate(u8 *dst, u8 *encoded);
ASE_Meta ASE_DecodeMeta(Arena *arena, String encoded);
////////////////////////////////////////////////////////////
//~ Error helpers
//~ Query
void ASE_PushError(Arena *arena, ASE_ErrorList *list, String msg_src);
ASE_SinglePixelResult ASE_SinglePixelFromCel(ASE_Cel *cel);
////////////////////////////////////////////////////////////
//~ Decode helpers
//~ Rasterize
u32 ASE_Blend(u32 src, u32 dst, u8 opacity);
void ASE_MakeDimensionsSquareish(ASE_Header *header, u32 *frames_x, u32 *frames_y, u64 *image_width, u64 *image_height);
void ASE_RasterizeCel(ASE_Cel *cel, u32 *dst_pixels, Vec2 dst_dims);
////////////////////////////////////////////////////////////
//~ Decode image
ASE_DecodedImage ASE_DecodeImage(Arena *arena, String encoded);
////////////////////////////////////////////////////////////
//~ Decode sheet
ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded);
// ////////////////////////////////////////////////////////////
// //~ Sheet types
// Struct(ASE_Slice)
// {
// u32 start;
// Rng2I32 rect;
// ASE_Slice *next;
// };
// Struct(ASE_Span)
// {
// String name;
// u32 start;
// u32 end;
// ASE_Span *next;
// };
// Struct(ASE_Frame)
// {
// u32 index;
// Rng2I32 rect;
// f64 duration;
// ASE_Frame *next;
// };
// Struct(ASE_SliceKey)
// {
// String name;
// u32 slices_count;
// ASE_Slice *first_slice;
// ASE_SliceKey *next;
// };
// ////////////////////////////////////////////////////////////
// //~ Decoder result types
// Struct(ASE_Error)
// {
// String msg;
// ASE_Error *next;
// };
// Struct(ASE_ErrorList)
// {
// u64 count;
// ASE_Error *first;
// ASE_Error *last;
// };
// Struct(ASE_DecodedImage)
// {
// u32 width;
// u32 height;
// u32 *pixels; // Array of [width * height] pixels
// ASE_ErrorList errors;
// b32 ok;
// };
// Struct(ASE_DecodedSheet)
// {
// Vec2 image_size;
// Vec2 frame_size;
// u32 frames_count;
// u32 spans_count;
// u32 slice_keys_count;
// ASE_Frame *first_frame;
// ASE_Span *first_span;
// ASE_SliceKey *first_slice_key;
// ASE_ErrorList errors;
// b32 ok;
// };
// ////////////////////////////////////////////////////////////
// //~ Inflator types
// #define ASE_HuffBitCount 16
// Struct(ASE_Bitbuff)
// {
// u8 *data;
// u64 cur_bit;
// };
// Enum(ASE_BlockType)
// {
// ASE_BlockType_Uncompressed = 0,
// ASE_BlockType_CompressedFixed = 1,
// ASE_BlockType_CompressedDynamic = 2,
// ASE_BlockType_Reserved = 3
// };
// Struct(ASE_HuffEntry)
// {
// u16 symbol;
// u16 bits_used;
// };
// Struct(ASE_HuffDict)
// {
// u32 max_code_bits;
// u32 entries_count;
// ASE_HuffEntry *entries;
// };
// ////////////////////////////////////////////////////////////
// //~ Header types
// Packed(Struct(ASE_Header)
// {
// u32 file_size;
// u16 magic;
// u16 frames;
// u16 width;
// u16 height;
// u16 color_depth;
// u32 flags;
// u16 speed;
// u32 _1;
// u32 _2;
// u8 palette_entry;
// u8 _3[3];
// u16 colors_count;
// u8 pixel_width;
// u8 pixel_height;
// i16 grid_x;
// i16 grid_y;
// u16 grid_width;
// u16 grid_height;
// u8 _4[84];
// });
// Packed(Struct(ASE_FrameHeader)
// {
// u32 bytes;
// u16 magic;
// u16 chunks_old;
// u16 frame_duration_ms;
// u8 _[2];
// u32 chunks_new;
// });
// ////////////////////////////////////////////////////////////
// //~ Image decoder types
// Enum(ASE_ChunkKind)
// {
// ASE_ChunkKind_OldPalette1 = 0x0004,
// ASE_ChunkKind_OldPalette2 = 0x0011,
// ASE_ChunkKind_Layer = 0x2004,
// ASE_ChunkKind_Cel = 0x2005,
// ASE_ChunkKind_CelExtra = 0x2006,
// ASE_ChunkKind_ColorProfile = 0x2007,
// ASE_ChunkKind_ExternalFiles = 0x2008,
// ASE_ChunkKind_Mask = 0x2016,
// ASE_ChunkKind_Path = 0x2017,
// ASE_ChunkKind_Tags = 0x2018,
// ASE_ChunkKind_Palette = 0x2019,
// ASE_ChunkKind_Userdata = 0x2020,
// ASE_ChunkKind_Slice = 0x2022,
// ASE_ChunkKind_Tileset = 0x2023
// };
// Enum(ASE_CelKind)
// {
// ASE_CelKind_RawImage = 0,
// ASE_CelKind_Linked = 1,
// ASE_CelKind_CompressedImage = 2,
// ASE_CelKind_CompressedTilemap = 3
// };
// Struct(ASE_Layer)
// {
// u16 flags;
// u16 type;
// u16 child_level;
// u16 blend_mode;
// u8 opacity;
// String name;
// u32 tileset_index;
// u32 index;
// ASE_Layer *next;
// };
// Struct(ASE_Cel)
// {
// u16 layer_index;
// i16 x_pos;
// i16 y_pos;
// u8 opacity;
// ASE_CelKind type;
// i16 z_index;
// // Linked cel
// u16 frame_pos;
// // Compressed image
// u32 width;
// u32 height;
// u32 *pixels;
// u16 frame_index;
// ASE_Cel *next;
// };
// ////////////////////////////////////////////////////////////
// //~ Ase bitbuff helpers
// u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits);
// u32 ASE_ConsumeBits(ASE_Bitbuff *bb, u32 nbits);
// void ASE_SkipBits(ASE_Bitbuff *bb, u32 nbits);
// ////////////////////////////////////////////////////////////
// //~ Inflate
// u32 ASE_ReverseBits(u32 v, u32 bit_count);
// ASE_HuffDict ASE_InitHuffDict(Arena *arena, u32 max_code_bits, u32 *bl_counts, u32 bl_counts_count);
// u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb);
// void ASE_Inflate(u8 *dst, u8 *encoded);
// ////////////////////////////////////////////////////////////
// //~ Error helpers
// void ASE_PushError(Arena *arena, ASE_ErrorList *list, String msg_src);
// ////////////////////////////////////////////////////////////
// //~ Decode helpers
// u32 ASE_Blend(u32 src, u32 dst, u8 opacity);
// void ASE_MakeDimensionsSquareish(ASE_Header *header, u32 *frames_x, u32 *frames_y, u64 *image_width, u64 *image_height);
// ////////////////////////////////////////////////////////////
// //~ Decode image
// ASE_DecodedImage ASE_DecodeImage(Arena *arena, String encoded);
// ////////////////////////////////////////////////////////////
// //~ Decode sheet
// ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded);

BIN
src/pp/pp_res/sprite/bla3.ase (Stored with Git LFS)

Binary file not shown.

View File

@ -9,7 +9,7 @@ void SPR_Bootstrap(void)
}
////////////////////////////////////////////////////////////
//~ Key helpers
//~ Helpers
SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource)
{
@ -25,228 +25,255 @@ SPR_SpanKey SPR_SpanKeyFromName(String name)
return result;
}
SPR_MarkKey SPR_MarkKeyFromName(String name)
String SPR_NameFromRayKind(SPR_RayKind kind)
{
SPR_MarkKey result = Zi;
result.v = HashString(name);
PERSIST Readonly String names[SPR_RayKind_COUNT] = {
#define X(kind_name, layer_name, ...) [SPR_RayKind_##kind_name] = CompLit(#layer_name),
SPR_RayKindXMacro(X)
#undef X
};
String result = Zi;
if (kind >= 0 && kind < countof(names))
{
result = names[kind];
}
return result;
}
SPR_LayerKind SPR_LayerKindFromName(String name)
{
SPR_LayerKind result = SPR_LayerKind_Visual;
if (StringBeginsWith(name, Lit(".")) || StringBeginsWith(name, Lit("#")))
{
result = SPR_LayerKind_Hidden;
}
return result;
}
////////////////////////////////////////////////////////////
//~ Lookup
SPR_SheetEntry *SPR_SheetFromKey(SPR_SheetKey key)
{
SPR_SheetEntry *result = 0;
return result;
}
SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 frame_seq)
{
SPR_Slice result = Zi;
//////////////////////////////
//- Fetch sheet
// FIXME: Default nil result
// i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy);
// if (completion < Atomic64Fetch(&sheet->atlas_copy_completion_target))
SPR_SpanEntry *span = SPR_SpanEntryFromSheet(sheet_key, span_key);
if (span && span->slices_count > 0)
SPR_SheetEntry *sheet = 0;
{
SPR_SpanStatus status = Atomic32Fetch(&span->status);
if (status >= SPR_SpanStatus_Rasterized)
SPR_SheetBin *sheet_bin = &SPR.sheet_bins[sheet_key.r.v % countof(SPR.sheet_bins)];
Lock sheet_bin_lock = LockS(&sheet_bin->mutex);
{
i64 frame_idx = frame_seq % span->slices_count;
SPR_SliceEntry *slice_entry = &span->slices[frame_idx];
result.tex = slice_entry->atlas->tex;
result.tex_rect_uv = slice_entry->atlas_rect_uv;
result.dims = slice_entry->dims;
result.ready = 1;
}
result.exists = 1;
}
// SPR_SheetEntry *sheet = SPR_SheetFromKey(sheet_key, 1);
// {
// SPR_SpanEntry *span = 0;
// {
// SPR_SpanBin *span_bin = &sheet->span_bins[span_key.v % sheet->span_bins_count];
// span = span_bin->first;
// for (; span; span = span->next_in_bin)
// {
// if (span->key.v == span_key.v)
// {
// break;
// }
// }
// }
// if (span && span->slices_count > 0)
// {
// i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy);
// if (completion < Atomic64Fetch(&sheet->atlas_copy_completion_target))
// {
// i64 frame_idx = frame_seq % span->slices_count;
// SPR_SliceEntry *slice_entry = &span->slices[frame_idx];
// result.tex = slice_entry->atlas->tex;
// result.tex_rect_uv = slice_entry->atlas_rect_uv;
// result.dims = slice_entry->dims;
// result.ready = 1;
// }
// result.exists = 1;
// }
// }
return result;
}
SPR_Mark SPR_MarkFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, SPR_MarkKey mark_key, i64 frame_seq)
{
SPR_Mark result = Zi;
// SPR_SheetEntry *sheet = SPR_SheetFromKey(sheet_key, 0);
SPR_SpanEntry *span = SPR_SpanEntryFromSheet(sheet_key, span_key);
if (span && span->slices_count > 0)
{
SPR_SpanStatus status = Atomic32Fetch(&span->status);
if (status >= SPR_SpanStatus_Processed)
{
i64 frame_idx = frame_seq % span->slices_count;
SPR_MarkEntry *mark_entry = 0
for (sheet = sheet_bin->first; sheet; sheet = sheet->next_in_bin)
{
SPR_MarkBin *mark_bin = &span->mark_bins[mark_key.v % span->mark_bins_count];
mark_entry = mark_bin->first;
for (; mark_entry; mark_entry = mark_entry->next_in_bin)
if (sheet->key.r.v == sheet_key.r.v)
{
if (mark_entry->key.v == mark_key.v)
{
break;
}
break;
}
}
if (mark_entry)
}
Unlock(&sheet_bin_lock);
}
//////////////////////////////
//- Decode sheet
if (!sheet)
{
SPR_SheetBin *sheet_bin = &SPR.sheet_bins[sheet_key.r.v % countof(SPR.sheet_bins)];
Lock sheet_bin_lock = LockE(&sheet_bin->mutex);
{
for (sheet = sheet_bin->first; sheet; sheet = sheet->next_in_bin)
{
if (sheet->key.r.v == sheet_key.r.v)
{
break;
}
}
if (!sheet)
{
Arena *perm = PermArena();
sheet = PushStruct(perm, SPR_SheetEntry);
SllStackPushN(sheet_bin->first, sheet, next_in_bin);
sheet->key = sheet_key;
String sheet_data = DataFromResource(sheet->key.r);
String sheet_name = NameFromResource(sheet->key.r);
LogInfoF("Decoding sprite sheet %F \"%F\" (%F bytes)", FmtHandle(sheet->key.r.v), FmtString(sheet_name), FmtUint(sheet_data.len));
sheet->meta = ASE_DecodeMeta(perm, sheet_data);
//- Init slices
sheet->slices_count = MaxI64(sheet->meta.frames_count, 1);
sheet->slices = PushStructs(perm, SPR_SliceEntry, sheet->slices_count);
for (i64 slice_idx = 0; slice_idx < sheet->slices_count; ++slice_idx)
{
SPR_SliceEntry *slice = &sheet->slices[slice_idx];
for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind)
{
slice->rays[ray_kind] = XformIdentity;
}
}
//- Push async rasterization commands
if (sheet->meta.frames_count > 0)
{
Lock cmds_lock = LockE(&SPR.submit.mutex);
{
for (i64 slice_idx = 0; slice_idx < sheet->meta.frames_count; ++slice_idx)
{
SPR_CmdNode *cmd_node = SPR.submit.first_free;
if (cmd_node)
{
SllStackPop(SPR.submit.first_free);
ZeroStruct(cmd_node);
}
else
{
cmd_node = PushStruct(perm, SPR_CmdNode);
}
cmd_node->cmd.sheet = sheet;
cmd_node->cmd.slice_idx = slice_idx;
SllQueuePush(SPR.submit.first, SPR.submit.last, cmd_node);
++SPR.submit.count;
}
Atomic32FetchSet(&SPR.new_cmds_present, 1);
SignalAsyncTick();
}
Unlock(&cmds_lock);
}
//- Compute rays
for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind)
{
String ray_name = SPR_NameFromRayKind(ray_kind);
b32 match = 0;
for (ASE_Layer *ase_layer = sheet->meta.last_layer; ase_layer && !match; ase_layer = ase_layer->prev)
{
if (MatchString(ray_name, ase_layer->name))
{
match = 1;
for (i64 slice_idx = 0; slice_idx < sheet->meta.frames_count; ++slice_idx)
{
ASE_Cel *ase_cel = &ase_layer->cels[slice_idx];
SPR_SliceEntry *slice = &sheet->slices[slice_idx];
ASE_SinglePixelResult ray_pix = ASE_SinglePixelFromCel(ase_cel);
u32 alpha = (ray_pix.v >> 24) & 0xFF;
if (alpha > 0)
{
// TODO: Different quantization so that 128 equals 0, instead of approximately 0
f32 rot_x = (((f32)((ray_pix.v >> 0) & 0xFF) / 255.0) * 2.0) - 1;
f32 rot_y = (((f32)((ray_pix.v >> 8) & 0xFF) / 255.0) * 2.0) - 1;
Vec2 rot = NormVec2(VEC2(rot_x, rot_y));
slice->rays[ray_kind].r = rot;
slice->rays[ray_kind].t = ray_pix.pos;
}
}
}
}
}
//- Init spans
sheet->span_bins_count = 256;
sheet->span_bins = PushStructs(perm, SPR_SpanBin, sheet->span_bins_count);
for (ASE_Span *ase_span = sheet->meta.first_span; ase_span; ase_span = ase_span->next)
{
SPR_SpanKey new_span_key = { .v = HashString(ase_span->name) };
SPR_SpanBin *span_bin = &sheet->span_bins[new_span_key.v % sheet->span_bins_count];
SPR_SpanEntry *span = 0;
for (span = span_bin->first; span; span = span->next_in_bin)
{
if (span->key.v == new_span_key.v)
{
break;
}
}
if (!span)
{
span = PushStruct(perm, SPR_SpanEntry);
SllQueuePush(sheet->first_span, sheet->last_span, span);
SllStackPushN(span_bin->first, span, next_in_bin);
span->key = new_span_key;
span->start = ase_span->start;
span->end = ase_span->end;
}
}
// TODO: Proper validation
sheet->ok = 1;
}
}
Unlock(&sheet_bin_lock);
}
//////////////////////////////
//- Fetch span
SPR_SpanEntry *span = 0;
b32 span_matched = 0;
{
if (sheet->ok)
{
SPR_SpanBin *span_bin = &sheet->span_bins[span_key.v % sheet->span_bins_count];
for (span = span_bin->first; span; span = span->next_in_bin)
{
if (span->key.v == span_key.v)
{
span_matched = 1;
break;
}
}
}
if (!span)
{
// FIXME: Ensure first span always exists in sheet
span = sheet->first_span;
}
}
//////////////////////////////
//- Fetch slice
SPR_SliceEntry *slice = 0;
{
// FIXME: Ensure span->end is never <= span->start
i64 slice_idx = span->start + (frame_seq % (span->end - span->start));
slice = &sheet->slices[slice_idx];
}
//////////////////////////////
//- Compute result
SPR_Slice result = Zi;
{
result.exists = span_matched;
if (result.exists)
{
i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy);
if (completion >= Atomic64Fetch(&slice->atlas_copy_completion_target))
{
SPR_SliceEntry *slice_entry = &span->slices[frame_idx];
result.tex = slice_entry->atlas->tex;
result.tex_rect_uv = slice_entry->atlas_rect_uv;
result.dims = slice_entry->dims;
result.ready = 1;
}
}
result.exists = 1;
// Fill tex info
if (result.ready)
{
result.tex = slice->atlas->tex_ref;
result.tex_rect_uv = slice->atlas_rect_uv;
result.dims = slice->dims;
}
else
{
result.tex = SPR.unready_tex;
result.tex_rect_uv = RNG2(VEC2(0, 0), VEC2(1, 1));
result.dims = SPR.unready_tex_dims;
}
// Fill rays
StaticAssert(countof(result.rays) == countof(slice->rays));
CopyStructs(result.rays, slice->rays, countof(result.rays));
}
return result;
}
// SPR_SliceEntry SPR_SliceFromSheet(SPR_SheetKey sheet, SPR_SpanKey span, i64 frame_seq)
// {
// // TODO: Ability to specify desired alpha modes (Straight, Premultiplied, Opaque)
// SPR_SliceEntry 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->atlas_copy_completion_target))
// {
// result = entry->slice_entry;
// }
// 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->atlas_copy_completion_target))
// {
// result = entry->slice_entry;
// }
// found = 1;
// }
// else
// {
// Arena *perm = PermArena();
// entry = PushStruct(perm, SPR_SliceEntry);
// entry->hash = hash;
// Atomic64FetchSet(&entry->atlas_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
@ -291,306 +318,109 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
{
SPR_Cmd *cmd = &cmds[cmd_idx];
SPR_SheetEntry *sheet = cmd->sheet;
ASE_Meta *meta = &sheet->meta;
ASE_Meta meta = sheet->meta;
String encoded = DataFromResource(sheet->key.r);
String name = NameFromResource(sheet->key.r);
LogInfoF("Rasterizing sprite sheet %F \"%F\" (%F bytes)", FmtHandle(sheet->key.r), FmtString(name), FmtUint(encoded.len));
// String encoded = DataFromResource(sheet->key.r);
// String name = NameFromResource(sheet->key.r);
// LogInfoF("Rasterizing sprite sheet %F \"%F\" (%F bytes)", FmtHandle(sheet->key.r), FmtString(name), FmtUint(encoded.len));
for (ASE_Span *ase_span = meta.first_span; ase_span; ase_span = ase_span->next)
SPR_SliceEntry *slice = &sheet->slices[cmd->slice_idx];
//////////////////////////////
//- Rasterize
u32 *pixels = PushStructs(frame_arena, u32, slice->dims.x * slice->dims.y);
for (ASE_Layer *ase_layer = sheet->meta.last_layer; ase_layer; ase_layer = ase_layer->prev)
{
u64 span_hash = HashString(ase_span->name);
SPR_SpanBin *span_bin = &sheet->span_bins[span_hash % sheet->span_bins_count];
SPR_SpanEntry *span = span_bin->first;
for (; span; span = span->next_in_bin)
SPR_LayerKind kind = SPR_LayerKindFromName(ase_layer->name);
if (kind == SPR_LayerKind_Visual)
{
if (span->key.v == span_hash)
{
break;
}
}
if (span)
{
i64 slice_idx = 0;
for (ASE_Frame *ase_frame = span->first_frame; ase_frame && slice_idx < span->slices_count; ase_frame = ase_frame->next)
{
SPR_SliceEntry *slice_entry = &span->slice_entry[slice_idx];
// // Determine slice_entry bounds
// Rng2 frame_bounds = Rng2Empty;
// for (ASE_Layer *ase_layer = ase_frame->first_layer; ase_layer; ase_layer = ase_layer->next)
// {
// if (!StringBeginsWith(ase_layer->name, Lit(".")))
// {
// frame_bounds = UnionRng2(frame_bounds);
// }
// }
// Vec2 dims = DimsFromRng2(frame_bounds);
if (!IsRng2Empty(frame_bounds))
{
// Rasterize slice_entry
u32 *pixels = PushStructs(frame_arena, u32, slice_entry->dims.x * slice_entry->dims.y);
for (ASE_Layer *ase_layer = ase_frame->first_layer; ase_layer; ase_layer = ase_layer->next)
{
SPR_LayerKind kind = SPR_LayerKindFromName(ase_layer->name);
if (kind == SPR_LayerKind_Standard)
{
ASE_RasterizeLayer(ase_layer, pixels, slice_entry->dims);
}
}
// Allocate atlas rect
// TODO: Use a more efficient atlas packing algorithm for less wasted space
SPR_Atlas *atlas = SPR.first_atlas;
b32 can_use_atlas = 0;
Vec2I32 pos_in_atlas = Zi;
while (can_use_atlas == 0)
{
// Create atlas
if (!atlas)
{
Arena *perm = PermArena();
atlas = PushStruct(perm, SPR_Atlas);
i32 atlas_size = MaxI32(1024, NextPow2U64(MaxI32(slice_entry->dims.x, slice_entry->dims.y)));
atlas->dims = VEC2I32(atlas_size, atlas_size);
{
G_ArenaHandle gpu_perm = G_PermArena();
atlas->tex = G_PushTexture2D(
gpu_perm, cl,
G_Format_R8G8B8A8_Unorm_Srgb,
atlas->dims,
G_Layout_AnyQueue_ShaderRead_CopyRead_CopyWrite_Present,
);
atlas->tex_ref = G_PushTexture2DRef(gpu_perm, atlas->tex);
}
SllStackPush(SPR.first_atlas, atlas);
++SPR.atlases_count;
}
// Determine pos in atlas
pos_in_atlas = atlas->cur_pos;
atlas->cur_row_height = MaxI32(atlas->cur_row_height, slice_entry->dims.y);
if (pos_in_atlas.x + slice_entry->dims.x > atlas->dims.x);
{
atlas->cur_pos.x = 0;
atlas->cur_pos.y += atlas->cur_row_height;
atlas->cur_row_height = slice_entry->dims.y;
}
atlas->cur_pos.x += slice_entry->dims.x;
if (atlas->cur_pos.x < atlas->dims.x && atlas->cur_pos.y < atlas->dims.y)
{
can_use_atlas = 1;
}
else
{
atlas = 0;
}
}
// Fill slice_entry atlas info
slice_entry->atlas = atlas;
slice_entry->atlas_rect = RNG2I32(pos_in_atlas, AddVec2I32(pos_in_atlas, slice_entry->dims));
slice_entry->atlas_rect_uv.p0.x = (f32)slice_entry->atlas_rect.p0.x / (f32)atlas->dims.x;
slice_entry->atlas_rect_uv.p0.y = (f32)slice_entry->atlas_rect.p0.y / (f32)atlas->dims.x;
slice_entry->atlas_rect_uv.p1.x = (f32)slice_entry->atlas_rect.p1.x / (f32)atlas->dims.x;
slice_entry->atlas_rect_uv.p1.y = (f32)slice_entry->atlas_rect.p1.y / (f32)atlas->dims.x;
// Copy to atlas
G_CopyCpuToTexture(
cl,
atlas->tex, VEC3I32(pos_in_atlas.x, pos_in_atlas.y, 0),
pixels, VEC3I32(slice_entry->dims.x, slice_entry->dims.y, 1),
RNG3I32(
VEC3I32(0, 0, 0),
VEC3I32(slice_entry->dims.x, slice_entry->dims.y, 1)
)
);
}
++slice_idx;
}
ASE_Cel *cel = &ase_layer->cels[cmd->slice_idx];
ASE_RasterizeCel(cel, pixels, slice->dims);
}
}
//////////////////////////////
//- Write atlas
// TODO: Use a more efficient atlas packing algorithm for less wasted space
SPR_Atlas *atlas = SPR.first_atlas;
b32 can_use_atlas = 0;
Vec2I32 pos_in_atlas = Zi;
while (can_use_atlas == 0)
{
// Create atlas
if (!atlas)
{
atlas = PushStruct(perm, SPR_Atlas);
i32 atlas_size = MaxI32(1024, NextPow2U64(MaxI32(slice->dims.x, slice->dims.y)));
atlas->dims = VEC2I32(atlas_size, atlas_size);
{
G_ArenaHandle gpu_perm = G_PermArena();
atlas->tex = G_PushTexture2D(
gpu_perm, cl,
G_Format_R8G8B8A8_Unorm_Srgb,
atlas->dims,
G_Layout_AnyQueue_ShaderRead_CopyRead_CopyWrite_Present,
);
atlas->tex_ref = G_PushTexture2DRef(gpu_perm, atlas->tex);
}
SllStackPush(SPR.first_atlas, atlas);
++SPR.atlases_count;
}
// Determine pos in atlas
pos_in_atlas = atlas->cur_pos;
atlas->cur_row_height = MaxI32(atlas->cur_row_height, slice->dims.y);
if (pos_in_atlas.x + slice->dims.x > atlas->dims.x);
{
atlas->cur_pos.x = 0;
atlas->cur_pos.y += atlas->cur_row_height;
atlas->cur_row_height = slice->dims.y;
}
atlas->cur_pos.x += slice->dims.x;
if (atlas->cur_pos.x < atlas->dims.x && atlas->cur_pos.y < atlas->dims.y)
{
can_use_atlas = 1;
}
else
{
atlas = 0;
}
}
// Fill slice_entry atlas info
{
Rng2I32 atlas_rect = RNG2I32(pos_in_atlas, AddVec2I32(pos_in_atlas, Vec2I32FromVec(slice->dims)));
slice->atlas = atlas;
slice->atlas_rect_uv.p0.x = (f32)atlas_rect.p0.x / (f32)atlas->dims.x;
slice->atlas_rect_uv.p0.y = (f32)atlas_rect.p0.y / (f32)atlas->dims.x;
slice->atlas_rect_uv.p1.x = (f32)atlas_rect.p1.x / (f32)atlas->dims.x;
slice->atlas_rect_uv.p1.y = (f32)atlas_rect.p1.y / (f32)atlas->dims.x;
}
// Copy to atlas
G_CopyCpuToTexture(
cl,
atlas->tex, VEC3I32(pos_in_atlas.x, pos_in_atlas.y, 0),
pixels, VEC3I32(slice->dims.x, slice->dims.y, 1),
RNG3I32(
VEC3I32(0, 0, 0),
VEC3I32(slice->dims.x, slice->dims.y, 1)
)
);
}
i64 completion_target = G_CommitCommandList(cl);
// Update completion targets
//////////////////////////////
//- Update completion targets
for (i64 cmd_idx = 0; cmd_idx < cmds_count; ++cmd_idx)
{
SPR_Cmd *cmd = &cmds[cmd_idx];
SPR_SheetEntry *sheet = cmd->sheet;
Atomic64Set(&sheet->atlas_copy_completion_target, completion_target);
SPR_SliceEntry *slice = &cmd->sheet->slices[cmd->slice_idx];
Atomic64Set(&slice->atlas_copy_completion_target, completion_target);
}
// 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->atlas_copy_completion_target = completion_target;
// sheet->tex = G_PushTexture2DRef(gpu_perm, gpu_resource);
// // LogDebugF("Decoded with ref: %F", FmtUint(slice_entry->slice_entry.tex.v));
// }
// else
// {
// // TODO: Use 'missing' texture
// sheet->tex = G_BlankTexture2D();
// sheet->atlas_copy_completion_target = 0;
// }
// }
// slice_entry->slice_entry.tex = sheet->tex;
// // FIXME: Real uv
// slice_entry->slice_entry.uv_rect.p0 = VEC2(0, 0);
// slice_entry->slice_entry.uv_rect.p1= VEC2(1, 1);
// Atomic64Set(&slice_entry->atlas_copy_completion_target, sheet->atlas_copy_completion_target);
}
}
}
}
// 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->atlas_copy_completion_target = completion_target;
// sheet->tex = G_PushTexture2DRef(gpu_perm, gpu_resource);
// // LogDebugF("Decoded with ref: %F", FmtUint(slice_entry->slice_entry.tex.v));
// }
// else
// {
// // TODO: Use 'missing' texture
// sheet->tex = G_BlankTexture2D();
// sheet->atlas_copy_completion_target = 0;
// }
// }
// slice_entry->slice_entry.tex = sheet->tex;
// // FIXME: Real uv
// slice_entry->slice_entry.uv_rect.p0 = VEC2(0, 0);
// slice_entry->slice_entry.uv_rect.p1= VEC2(1, 1);
// Atomic64Set(&slice_entry->atlas_copy_completion_target, sheet->atlas_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

@ -3,56 +3,120 @@
Struct(SPR_SheetKey) { ResourceKey r; };
Struct(SPR_SpanKey) { u64 v; };
Struct(SPR_MarkKey) { u64 v; };
#define SPR_NilSheetKey ((SPR_SheetKey) { 0 })
#define SPR_NilSpanKey ((SPR_SpanKey) { 0 })
#define SPR_NilRayKey ((SPR_MarkKey) { 0 })
////////////////////////////////////////////////////////////
//~ Slice types
//~ Atlas types
Struct(SPR_Atlas)
{
SPR_Atlas *next;
Vec2I32 dims;
G_ResourceHandle tex;
G_Texture2DRef tex_ref;
Vec2I32 cur_pos;
i32 cur_row_height;
};
////////////////////////////////////////////////////////////
//~ Ray types
#define SPR_RayKindXMacro(X) \
X(Origin, .origin) \
X(Ap, .ap) \
/* ----------------------------- */
Enum(SPR_RayKind)
{
#define X(kind_name, ...) SPR_RayKind_##kind_name,
SPR_RayKindXMacro(X)
#undef X
SPR_RayKind_COUNT
};
////////////////////////////////////////////////////////////
//~ Layer types
Enum(SPR_LayerKind)
{
SPR_LayerKind_Visual,
SPR_LayerKind_Hidden,
};
////////////////////////////////////////////////////////////
//~ Lookup result types
Struct(SPR_Slice)
{
Xform rays[SPR_RayKind_COUNT];
G_Texture2DRef tex;
Rng2 uv_rect;
Rng2 tex_rect_uv;
Vec2 dims;
b32 exists;
b32 ready;
};
////////////////////////////////////////////////////////////
//~ Cache types
//- Slice
Struct(SPR_SliceEntry)
{
SPR_SliceEntry *next;
SPR_SliceEntry *next_in_bin;
SPR_SheetKey sheet;
u64 hash;
Xform rays[SPR_RayKind_COUNT];
SPR_Atlas *atlas;
Vec2 dims;
Rng2 atlas_rect_uv;
Atomic64 atlas_copy_completion_target;
String slice_name;
SPR_Slice slice;
};
Struct(SPR_SliceBin)
//- Span
Struct(SPR_SpanEntry)
{
Mutex mutex;
SPR_SliceEntry *first;
SPR_SpanEntry *next;
SPR_SpanEntry *next_in_bin;
SPR_SpanKey key;
i64 start;
i64 end;
};
Struct(SPR_SpanBin)
{
SPR_SpanEntry *first;
};
//- Sheet
Struct(SPR_SheetEntry)
{
SPR_SheetEntry *next;
SPR_SheetEntry *next_in_bin;
SPR_SheetKey key;
i64 atlas_copy_completion_target;
G_Texture2DRef tex;
i64 slices_count;
SPR_SliceEntry *slices;
i64 span_bins_count;
SPR_SpanBin *span_bins;
SPR_SpanEntry *first_span;
SPR_SpanEntry *last_span;
ASE_Meta meta;
b32 ok;
};
Struct(SPR_SheetBin)
{
Mutex mutex;
SPR_SheetEntry *first;
};
@ -61,7 +125,8 @@ Struct(SPR_SheetBin)
Struct(SPR_Cmd)
{
SPR_SliceEntry *entry;
SPR_SheetEntry *sheet;
i64 slice_idx;
};
Struct(SPR_CmdNode)
@ -75,12 +140,19 @@ Struct(SPR_CmdNode)
Struct(SPR_AsyncCtx)
{
SPR_SheetBin sheet_bins[Kibi(16)];
i32 _;
};
Struct(SPR_Ctx)
{
SPR_SliceBin slice_bins[Kibi(16)];
SPR_SheetBin sheet_bins[Kibi(16)];
// FIXME: Initialize this
G_Texture2DRef unready_tex;
Vec2 unready_tex_dims;
i64 atlases_count;
SPR_Atlas *first_atlas;
Atomic32 new_cmds_present;
struct
@ -103,18 +175,18 @@ extern SPR_Ctx SPR;
void SPR_Bootstrap(void);
////////////////////////////////////////////////////////////
//~ Key helpers
//~ Helpers
SPR_SheetKey SPR_SheetKeyFromResource(ResourceKey resource);
SPR_SpanKey SPR_SpanKeyFromName(String name);
SPR_MarkKey SPR_MarkKeyFromName(String name);
String SPR_NameFromRayKind(SPR_RayKind kind);
SPR_LayerKind SPR_LayerKindFromName(String name);
////////////////////////////////////////////////////////////
//~ Lookup helpers
//~ Lookup
SPR_SpanEntry *SPR_SpanEntryFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key);
SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 frame_seq);
SPR_Mark SPR_MarkFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, SPR_MarkKey ray_key, i64 frame_seq);
////////////////////////////////////////////////////////////
//~ Async