ase refactor wip

This commit is contained in:
jacob 2026-01-30 00:29:52 -06:00
parent ff7e0b2167
commit d44a845a8d
6 changed files with 978 additions and 235 deletions

View File

@ -1,3 +1,512 @@
// DEFLATE decoder based on Handmade Hero's png parser
////////////////////////////////////////////////////////////
//~ Inflate
//- Bitbuff
u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits)
{
Assert(nbits <= 32);
u64 cur_byte = bb->cur_bit >> 3;
u8 bit_index = bb->cur_bit % 8;
u64 nbytes = (nbits + bit_index + 7) >> 3;
u64 val64 = 0;
CopyBytes(&val64, &bb->data[cur_byte], nbytes);
u32 val32 = (u32)(val64 >> bit_index);
val32 &= U32Max >> (32 - nbits);
return val32;
}
u32 ASE_ConsumeBits(ASE_Bitbuff *bb, u32 nbits)
{
u32 val = ASE_PeekBits(bb, nbits);
bb->cur_bit += nbits;
return val;
}
void ASE_SkipBits(ASE_Bitbuff *bb, u32 nbits)
{
bb->cur_bit += nbits;
}
//- Reverse bits
u32 ASE_ReverseBits(u32 v, u32 bit_count)
{
// 7 & 15 seem to be the most common bit_counts, so a
// more optimal path is laid out for them.
if (bit_count == 15)
{
u32 b1 = v & 0xFF;
b1 = (b1 & 0xF0) >> 4 | (b1 & 0x0F) << 4;
b1 = (b1 & 0xCC) >> 2 | (b1 & 0x33) << 2;
b1 = (b1 & 0xAA) >> 1 | (b1 & 0x55) << 1;
u32 b2 = (v & 0xFF00) >> 8;
b2 = (b2 & 0xF0) >> 4 | (b2 & 0x0F) << 4;
b2 = (b2 & 0xCC) >> 2 | (b2 & 0x33) << 2;
b2 = (b2 & 0xAA) >> 1 | (b2 & 0x55) << 1;
b2 >>= 1;
return (b1 << 7) | b2;
}
else if (bit_count == 7)
{
v = (v & 0xF0) >> 4 | (v & 0x0F) << 4;
v = (v & 0xCC) >> 2 | (v & 0x33) << 2;
v = (v & 0xAA) >> 1 | (v & 0x55) << 1;
return v >> 1;
}
else
{
u32 result = 0;
for (u32 i = 0; i <= (bit_count / 2); ++i)
{
u32 inv = (bit_count - (i + 1));
result |= ((v >> i) & 0x1) << inv;
result |= ((v >> inv) & 0x1) << i;
}
return result;
}
}
//- Dict
ASE_HuffDict ASE_InitHuffDict(Arena *arena, u32 max_code_bits, u32 *bl_counts, u32 bl_counts_count)
{
ASE_HuffDict result = Zi;
result.max_code_bits = max_code_bits;
result.entries_count = (1 << max_code_bits);
result.entries = PushStructsNoZero(arena, ASE_HuffEntry, result.entries_count);
u32 code_length_hist[ASE_HuffBitCount] = Zi;
for (u32 bl_count_index = 0; bl_count_index < bl_counts_count; ++bl_count_index)
{
u32 count = bl_counts[bl_count_index];
Assert(count <= countof(code_length_hist));
++code_length_hist[count];
}
u32 next_code[ASE_HuffBitCount] = Zi;
next_code[0] = 0;
code_length_hist[0] = 0;
for (u32 code_index = 1; code_index < countof(next_code); ++code_index)
{
next_code[code_index] = ((next_code[code_index - 1] + code_length_hist[code_index - 1]) << 1);
}
for (u32 bl_count_index = 0; bl_count_index < bl_counts_count; ++bl_count_index)
{
u32 code_bits = bl_counts[bl_count_index];
if (code_bits)
{
Assert(code_bits < countof(next_code));
u32 code = next_code[code_bits]++;
u32 arbitrary_bits = result.max_code_bits - code_bits;
u32 entry_count = (1 << arbitrary_bits);
// TODO: Optimize this. It's bloating load times.
for (u32 entry_index = 0; entry_index < entry_count; ++entry_index)
{
u32 base_index = (code << arbitrary_bits) | entry_index;
u32 index = ASE_ReverseBits(base_index, result.max_code_bits);
ASE_HuffEntry *entry = &result.entries[index];
entry->symbol = (u16)bl_count_index;
entry->bits_used = (u16)code_bits;
}
}
}
return result;
}
u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb)
{
u32 index = ASE_PeekBits(bb, huffman->max_code_bits);
Assert(index < huffman->entries_count);
ASE_HuffEntry *entry = &huffman->entries[index];
u16 result = entry->symbol;
ASE_SkipBits(bb, entry->bits_used);
Assert(entry->bits_used > 0);
return result;
}
//- Inflate
void ASE_Inflate(u8 *dst, u8 *encoded)
{
TempArena scratch = BeginScratchNoConflict();
ASE_Bitbuff bb = Zi;
bb.data = encoded;
// ZLIB header
u32 cm = ASE_ConsumeBits(&bb, 4);
u32 cinfo = ASE_ConsumeBits(&bb, 4);
Assert(cm == 8);
Assert(cinfo == 7);
u32 fcheck = ASE_ConsumeBits(&bb, 5);
u32 fdict = ASE_ConsumeBits(&bb, 1);
u32 flevl = ASE_ConsumeBits(&bb, 2);
Assert(fdict == 0);
u8 cmf = (u8)(cm | (cinfo << 4));
u8 flg = fcheck | (fdict << 5) | (flevl << 6);
Assert(((cmf * 256) + flg) % 31 == 0);
u8 bfinal = 0;
while (!bfinal)
{
bfinal = ASE_ConsumeBits(&bb, 1);
u8 btype = ASE_ConsumeBits(&bb, 2);
switch (btype)
{
case ASE_BlockType_Uncompressed:
{
ASE_SkipBits(&bb, (8 - (bb.cur_bit % 8)) % 8);
i16 len = ASE_ConsumeBits(&bb, 16);
i16 nlen = ASE_ConsumeBits(&bb, 16);
Assert(len == ~nlen); // Validation
while (len-- > 0)
{
*dst++ = ASE_ConsumeBits(&bb, 8);
}
} break;
case ASE_BlockType_CompressedFixed:
case ASE_BlockType_CompressedDynamic:
{
TempArena temp = BeginTempArena(scratch.arena);
{
//- Decode huffman table
u32 lit_len_dist_table[512] = Zi;
u32 hlit = 0;
u32 hdist = 0;
{
if (btype == ASE_BlockType_CompressedDynamic)
{
// Read huffman table
hlit = ASE_ConsumeBits(&bb, 5) + 257;
hdist = ASE_ConsumeBits(&bb, 5) + 1;
u32 hclen = ASE_ConsumeBits(&bb, 4) + 4;
// Init dict huffman (hclen)
u32 hclen_bl_counts[19] = Zi;
for (u32 i = 0; i < hclen; ++i)
{
u32 code = ASE_HuffHclenOrder[i];
hclen_bl_counts[code] = ASE_ConsumeBits(&bb, 3);
}
ASE_HuffDict dict_huffman = ASE_InitHuffDict(temp.arena, 7, hclen_bl_counts, countof(hclen_bl_counts));
// Decode dict huffman
u32 lit_len_count = 0;
u32 len_count = hlit + hdist;
Assert(len_count <= countof(lit_len_dist_table));
while (lit_len_count < len_count)
{
u32 rep_count = 1;
u32 rep_val = 0;
u32 encoded_len = ASE_DecodeHuffDict(&dict_huffman, &bb);
if (encoded_len <= 15)
{
rep_val = encoded_len;
}
else if (encoded_len == 16)
{
rep_count = 3 + ASE_ConsumeBits(&bb, 2);
Assert(lit_len_count > 0);
rep_val = lit_len_dist_table[lit_len_count - 1];
}
else if (encoded_len == 17)
{
rep_count = 3 + ASE_ConsumeBits(&bb, 3);
}
else if (encoded_len == 18)
{
rep_count = 11 + ASE_ConsumeBits(&bb, 7);
}
else
{
// Invalid len
Assert(0);
}
while (rep_count--)
{
lit_len_dist_table[lit_len_count++] = rep_val;
}
}
Assert(lit_len_count == len_count);
}
else
{
// Decode fixed table
hlit = 288;
hdist = 32;
u32 index = 0;
for (u32 i = 0; i < countof(ASE_HuffBlCounts); ++i)
{
u32 bit_count = ASE_HuffBlCounts[i][1];
u32 last_valuie = ASE_HuffBlCounts[i][0];
while (index <= last_valuie)
{
lit_len_dist_table[index++] = bit_count;
}
}
}
}
//- Decode
ASE_HuffDict lit_len_huffman = ASE_InitHuffDict(temp.arena, 15, lit_len_dist_table, hlit);
ASE_HuffDict dist_huffman = ASE_InitHuffDict(temp.arena, 15, lit_len_dist_table + hlit, hdist);
for (;;)
{
u32 lit_len = ASE_DecodeHuffDict(&lit_len_huffman, &bb);
if (lit_len <= 255)
{
*dst++ = lit_len & 0xFF;
}
else if (lit_len >= 257)
{
u32 length_index = (lit_len - 257);
ASE_HuffEntry length_entry = ASE_HuffLenTable[length_index];
u32 length = length_entry.symbol;
if (length_entry.bits_used > 0)
{
u32 extra_bits = ASE_ConsumeBits(&bb, length_entry.bits_used);
length += extra_bits;
}
u32 dist_index = ASE_DecodeHuffDict(&dist_huffman, &bb);
ASE_HuffEntry dist_entry = ASE_HuffDistTable[dist_index];
u32 distance = dist_entry.symbol;
if (dist_entry.bits_used > 0)
{
u32 extra_bits = ASE_ConsumeBits(&bb, dist_entry.bits_used);
distance += extra_bits;
}
u8 *src = dst - distance;
while (length--)
{
*dst++ = *src++;
}
}
else
{
break;
}
}
}
EndTempArena(temp);
} break;
case ASE_BlockType_Reserved:
{
// TODO
Assert(0);
} break;
}
}
EndScratch(scratch);
}
////////////////////////////////////////////////////////////
//~ Meta
ASE_Meta ASE_DecodeMeta(Arena *arena, String encoded)
{
ASE_Meta result = Zi;
return result;
}
////////////////////////////////////////////////////////////
//~ Image
ASE_Image ASE_PushBlankImage(Arena *arena, Rng2 bounds)
{
ASE_Image result = Zi;
i64 pixels_count = AreaFromVec2(DimsFromRng2(bounds));
if (pixels_count > 0)
{
result.bounds = bounds;
result.pixels = PushStructs(arena, u32, pixels_count);
}
return result;
}
ASE_Image ASE_DecompressImageFromCel(Arena *arena, ASE_Cel *cel)
{
ASE_Image result = Zi;
result.bounds = cel->bounds;
result.opacity = cel->opacity;
i64 pixels_count = AreaFromVec2(DimsFromRng2(result.bounds));
result.pixels = PushStructsNoZero(arena, u32, pixels_count);
ASE_Inflate((u8 *)result.pixels, cel->encoded.text);
return result;
}
////////////////////////////////////////////////////////////
//~ Blend
u32 ASE_BlendPixel(u32 src, u32 dst, u8 opacity)
{
u32 result = 0;
u32 dst_a = (dst >> 24) & 0xff;
u32 src_a = (src >> 24) & 0xff;
src_a = (u8)MulNormalizedU8(src_a, opacity);
u32 a = src_a + dst_a - MulNormalizedU8(src_a, dst_a);
if (a != 0)
{
u32 dst_r = (dst & 0xff);
u32 dst_g = (dst >> 8) & 0xff;
u32 dst_b = (dst >> 16) & 0xff;
u32 src_r = (src & 0xff);
u32 src_g = (src >> 8) & 0xff;
u32 src_b = (src >> 16) & 0xff;
u32 r = dst_r + (src_r - dst_r) * src_a / a;
u32 g = dst_g + (src_g - dst_g) * src_a / a;
u32 b = dst_b + (src_b - dst_b) * src_a / a;
result = (r << 0) | (g << 8) | (b << 16) | (a << 24);
}
return result;
}
void ASE_BlendImage(ASE_Image src_img, ASE_Image dst_img)
{
u8 opacity_u8 = (u8)(src_img.opacity * 255.0f);
Vec2 src_dims = DimsFromRng2(src_img.bounds);
Vec2 dst_dims = DimsFromRng2(dst_img.bounds);
Rng2 blend_bounds = IntersectRng2(src_img.bounds, dst_img.bounds);
Vec2 blend_dims = DimsFromRng2(blend_bounds);
if (!IsRng2Empty(blend_bounds))
{
for (i32 blend_y = blend_bounds.p0.y; blend_y < blend_bounds.p1.y; ++blend_y)
{
i32 src_y = blend_y - src_img.bounds.p0.y;
i32 dst_y = blend_y - dst_img.bounds.p0.y;
i32 src_offset = src_y * src_dims.x;
i32 dst_offset = dst_y * dst_dims.x;
for (i32 blend_x = blend_bounds.p0.x; blend_x < blend_bounds.p1.x; ++blend_x)
{
i32 src_x = blend_x - src_img.bounds.p0.x;
i32 dst_x = blend_x - dst_img.bounds.p0.x;
u32 *src_pixel = &src_img.pixels[src_x + src_offset];
u32 *dst_pixel = &dst_img.pixels[dst_x + dst_offset];
*dst_pixel = ASE_BlendPixel(*src_pixel, *dst_pixel, opacity_u8);
}
}
}
}
// ////////////////////////////////////////////////////////////
// //~ Query
// u32 ASE_FirstPixelFromCel(ASE_Cel *cel)
// {
// u32 result = 0;
// return result;
// }
// ////////////////////////////////////////////////////////////
// //~ Rasterize
// void ASE_RasterizeCel(ASE_Cel *cel, u32 *dst_pixels, Vec2 dst_dims)
// {
// TempArena scratch = BeginScratchNoConflict();
// // ASE_Layer *layer = cel->layer;
// // ASE_Meta *meta = layer->meta;
// // u8 opacity = (cel->opacity / 255.0f) * (layer->opacity / 255.0f) * 255.0f;
// // // Adjust bounds to ensure pixels outside of frame boundaries
// // // aren't processed (aseprite keeps chunks outside of frame
// // // around in project file).
// // {
// // i32 frame_right = cel_width + frame_left;
// // i32 frame_bottom = frame_top + cel_height;
// // if (frame_left < 0)
// // {
// // cel_left += -frame_left;
// // frame_left = 0;
// // }
// // if (frame_top < 0)
// // {
// // cel_top += -frame_top;
// // frame_top = 0;
// // }
// // if (frame_right > (i32)frame_width)
// // {
// // cel_right -= (frame_right - frame_width);
// // frame_right = frame_width;
// // }
// // if (frame_bottom > (i32)frame_height)
// // {
// // cel_bottom -= (frame_bottom - frame_height);
// // frame_bottom = frame_height;
// // }
// // }
// i64 src_pixels_count = DimsFromRng2(cel->bounds).x * DimsFromRng2(cel->bounds).y;
// u32 *src_pixels = PushStructsNoZero(scratch.arena, u32, src_pixels_count);
// {
// }
// u8 opacity_u8 = (u8)(cel->opacity * 255.0f);
// Rng2 bounds = IntersectRng2(cel->bounds, RNG2(Vec2(0, 0), dst_dims));
// Vec2 dims = DimsFromRng2(bounds);
// for (i32 cel_y = 0; cel_y < dims.y; ++cel_y)
// {
// i32 dst_y = bounds.p0.y + cel_y;
// i32 cel_offset = cel_y * cel_width;
// i32 dst_offset = dst_y * dst_width;
// for (i32 cel_x = 0; cel_x < dims.x; ++cel_x)
// {
// i32 dst_x = bounds.p0.x + cel_x;
// u32 *src_pixel = &cel->pixels[cel_x + cel_offset];
// u32 *dst_pixel = &dst_pixels[dst_x + dst_offset];
// *dst_pixel = ASE_Blend(*src_pixel, *dst_pixel, opacity_u8);
// }
// }
// EndScratch(scratch);
// }
// // DEFLATE decoder based on Handmade Hero's png parser // // DEFLATE decoder based on Handmade Hero's png parser
// //////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////

View File

@ -1,9 +1,126 @@
// DEFLATE decoder based on Handmade Hero's png parser
////////////////////////////////////////////////////////////
//~ Inflate types
#define ASE_HuffBitCount 16
Struct(ASE_HuffEntry)
{
u16 symbol;
u16 bits_used;
};
Struct(ASE_HuffDict)
{
u32 max_code_bits;
u32 entries_count;
ASE_HuffEntry *entries;
};
Global Readonly u32 ASE_HuffHclenOrder[] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
Global Readonly ASE_HuffEntry ASE_HuffLenTable[] = {
{3, 0}, // 257
{4, 0}, // 258
{5, 0}, // 259
{6, 0}, // 260
{7, 0}, // 261
{8, 0}, // 262
{9, 0}, // 263
{10, 0}, // 264
{11, 1}, // 265
{13, 1}, // 266
{15, 1}, // 267
{17, 1}, // 268
{19, 2}, // 269
{23, 2}, // 270
{27, 2}, // 271
{31, 2}, // 272
{35, 3}, // 273
{43, 3}, // 274
{51, 3}, // 275
{59, 3}, // 276
{67, 4}, // 277
{83, 4}, // 278
{99, 4}, // 279
{115, 4}, // 280
{131, 5}, // 281
{163, 5}, // 282
{195, 5}, // 283
{227, 5}, // 284
{258, 0}, // 285
};
Global Readonly ASE_HuffEntry ASE_HuffDistTable[] = {
{1, 0}, // 0
{2, 0}, // 1
{3, 0}, // 2
{4, 0}, // 3
{5, 1}, // 4
{7, 1}, // 5
{9, 2}, // 6
{13, 2}, // 7
{17, 3}, // 8
{25, 3}, // 9
{33, 4}, // 10
{49, 4}, // 11
{65, 5}, // 12
{97, 5}, // 13
{129, 6}, // 14
{193, 6}, // 15
{257, 7}, // 16
{385, 7}, // 17
{513, 8}, // 18
{769, 8}, // 19
{1025, 9}, // 20
{1537, 9}, // 21
{2049, 10}, // 22
{3073, 10}, // 23
{4097, 11}, // 24
{6145, 11}, // 25
{8193, 12}, // 26
{12289, 12}, // 27
{16385, 13}, // 28
{24577, 13}, // 29
};
Global Readonly u32 ASE_HuffBlCounts[][2] = {
{143, 8},
{255, 9},
{279, 7},
{287, 8},
{319, 5},
};
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
};
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Meta types //~ Meta types
Struct (ASE_Cel) Struct (ASE_Cel)
{ {
i32 _; f32 opacity;
i64 frame_idx;
i64 linked_frame_idx;
String encoded;
Rng2 bounds;
}; };
Struct(ASE_Layer) Struct(ASE_Layer)
@ -12,7 +129,6 @@ Struct(ASE_Layer)
ASE_Layer *prev; ASE_Layer *prev;
String name; String name;
i64 cels_count;
ASE_Cel *cels; ASE_Cel *cels;
}; };
@ -40,28 +156,68 @@ Struct(ASE_Meta)
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Query types //~ Image types
Struct(ASE_SinglePixelResult) Struct(ASE_Image)
{ {
Vec2 pos; f32 opacity;
u32 v; Rng2 bounds;
u32 *pixels;
}; };
////////////////////////////////////////////////////////////
//~ Inflate
//- Bitbuff
u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits);
u32 ASE_ConsumeBits(ASE_Bitbuff *bb, u32 nbits);
void ASE_SkipBits(ASE_Bitbuff *bb, u32 nbits);
//- Reverse bits
u32 ASE_ReverseBits(u32 v, u32 bit_count);
//- Dict
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);
//- Inflate
void ASE_Inflate(u8 *dst, u8 *encoded);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Meta //~ Meta
ASE_Meta ASE_DecodeMeta(Arena *arena, String encoded); ASE_Meta ASE_DecodeMeta(Arena *arena, String encoded);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Query //~ Image
ASE_SinglePixelResult ASE_SinglePixelFromCel(ASE_Cel *cel); ASE_Image ASE_PushBlankImage(Arena *arena, Rng2 bounds);
ASE_Image ASE_DecompressImageFromCel(Arena *arena, ASE_Cel *cel);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Rasterize //~ Blend
void ASE_RasterizeCel(ASE_Cel *cel, u32 *dst_pixels, Vec2 dst_dims); u32 ASE_BlendPixel(u32 src, u32 dst, u8 opacity);
void ASE_BlendImage(ASE_Image src_img, ASE_Image dst_img);
// ////////////////////////////////////////////////////////////
// //~ Query
// u32 ASE_FirstPixelFromCel(ASE_Cel *cel);
// ////////////////////////////////////////////////////////////
// //~ Rasterize
// void ASE_RasterizeCel(ASE_Cel *cel, u32 *dst_pixels, Vec2 dst_dims);

View File

@ -190,14 +190,14 @@ f32 UnwindAngleF32(f32 a)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Float lerp //~ Float lerp
f32 LerpF32(f32 val0, f32 val1, f32 t) f32 LerpF32(f32 a, f32 b, f32 t)
{ {
return val0 + ((val1 - val0) * t); return a + ((b - a) * t);
} }
f64 LerpF64(f64 val0, f64 val1, f64 t) f64 LerpF64(f64 a, f64 b, f64 t)
{ {
return val0 + ((val1 - val0) * t); return a + ((b - a) * t);
} }
f32 LerpAngleF32(f32 a, f32 b, f32 t) f32 LerpAngleF32(f32 a, f32 b, f32 t)
@ -209,24 +209,24 @@ f32 LerpAngleF32(f32 a, f32 b, f32 t)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Int lerp //~ Int lerp
i32 LerpI32(i32 val0, i32 val1, f32 t) i32 LerpI32(i32 a, i32 b, f32 t)
{ {
return val0 + RoundF32((f32)(val1 - val0) * t); return a + RoundF32((f32)(b - a) * t);
} }
i64 LerpI64(i64 val0, i64 val1, f64 t) i64 LerpI64(i64 a, i64 b, f64 t)
{ {
return val0 + RoundF64((f64)(val1 - val0) * t); return a + RoundF64((f64)(b - a) * t);
} }
i32 LerpU32(u32 val0, u32 val1, f32 t) i32 LerpU32(u32 a, u32 b, f32 t)
{ {
return val0 + RoundF32((f32)(val1 - val0) * t); return a + RoundF32((f32)(b - a) * t);
} }
i64 LerpU64(u64 val0, u64 val1, f64 t) i64 LerpU64(u64 a, u64 b, f64 t)
{ {
return val0 + RoundF64((f64)(val1 - val0) * t); return a + RoundF64((f64)(b - a) * t);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -336,12 +336,12 @@ Vec4 PremulFromSrgb(Vec4 srgb)
return premul; return premul;
} }
Vec4 LerpSrgb(Vec4 v0, Vec4 v1, f32 t) Vec4 LerpSrgb(Vec4 a, Vec4 b, f32 t)
{ {
Vec4 result = Zi; Vec4 result = Zi;
Vec4 v0_lin = LinearFromSrgb(v0); Vec4 a_lin = LinearFromSrgb(a);
Vec4 v1_lin = LinearFromSrgb(v1); Vec4 b_lin = LinearFromSrgb(b);
Vec4 lerp_lin = LerpVec4(v0_lin, v1_lin, t); Vec4 lerp_lin = LerpVec4(a_lin, b_lin, t);
result = SrgbFromLinear(lerp_lin); result = SrgbFromLinear(lerp_lin);
return result; return result;
} }
@ -548,14 +548,11 @@ f32 AngleFromVec2(Vec2 v)
return ArcTan2F32(v.y, v.x); return ArcTan2F32(v.y, v.x);
} }
f32 AngleFromVec2Dirs(Vec2 dir1, Vec2 dir2) //- Area
{
return ArcTan2F32(WedgeVec2(dir1, dir2), DotVec2(dir1, dir2));
}
f32 AngleFromVec2Points(Vec2 pt1, Vec2 pt2) f32 AreaFromVec2(Vec2 v)
{ {
return AngleFromVec2(SubVec2(pt2, pt1)); return AbsF32(v.x * v.y);
} }
//- Closest point //- Closest point
@ -571,21 +568,21 @@ Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p)
//- Lerp //- Lerp
// Interpolate position vectors // Interpolate position vectors
Vec2 LerpVec2(Vec2 v0, Vec2 v1, f32 t) Vec2 LerpVec2(Vec2 a, Vec2 b, f32 t)
{ {
return VEC2(LerpF32(v0.x, v1.x, t), LerpF32(v0.y, v1.y, t)); return VEC2(LerpF32(a.x, b.x, t), LerpF32(a.y, b.y, t));
} }
Vec2 LerpVec2Vec2(Vec2 v0, Vec2 v1, Vec2 t) Vec2 LerpVec2Vec2(Vec2 a, Vec2 b, Vec2 t)
{ {
return VEC2(LerpF32(v0.x, v1.x, t.x), LerpF32(v0.y, v1.y, t.y)); return VEC2(LerpF32(a.x, b.x, t.x), LerpF32(a.y, b.y, t.y));
} }
// Interpolate direction vectors (spherical lerp) // Interpolate direction vectors (spherical lerp)
Vec2 SlerpVec2(Vec2 v0, Vec2 v1, f32 t) Vec2 SlerpVec2(Vec2 a, Vec2 b, f32 t)
{ {
f32 rot = LerpAngleF32(AngleFromVec2(v0), AngleFromVec2(v1), t); f32 rot = LerpAngleF32(AngleFromVec2(a), AngleFromVec2(b), t);
f32 len = LerpF32(Vec2Len(v0), Vec2Len(v1), t); f32 len = LerpF32(Vec2Len(a), Vec2Len(b), t);
return MulVec2(Vec2FromAngle(rot), len); return MulVec2(Vec2FromAngle(rot), len);
} }
@ -639,13 +636,13 @@ Vec4 MulVec4Vec4(Vec4 a, Vec4 b)
//- Lerp //- Lerp
Vec4 LerpVec4(Vec4 v0, Vec4 v1, f32 t) Vec4 LerpVec4(Vec4 a, Vec4 b, f32 t)
{ {
Vec4 result = Zi; Vec4 result = Zi;
result.x = LerpF32(v0.x, v1.x, t); result.x = LerpF32(a.x, b.x, t);
result.y = LerpF32(v0.y, v1.y, t); result.y = LerpF32(a.y, b.y, t);
result.z = LerpF32(v0.z, v1.z, t); result.z = LerpF32(a.z, b.z, t);
result.w = LerpF32(v0.w, v1.w, t); result.w = LerpF32(a.w, b.w, t);
return result; return result;
} }
@ -683,6 +680,11 @@ f32 NormRng(Rng r, f32 v)
//- Rng2 //- Rng2
b32 IsRng2Empty(Rng2 r)
{
return r.p0.x >= r.p1.x || r.p0.y >= r.p1.y;
}
Vec2 DimsFromRng2(Rng2 r) Vec2 DimsFromRng2(Rng2 r)
{ {
Vec2 result = Zi; Vec2 result = Zi;
@ -719,6 +721,16 @@ Rng2 UnionRng2(Rng2 a, Rng2 b)
return result; return result;
} }
Rng2 IntersectRng2(Rng2 a, Rng2 b)
{
Rng2 result = Zi;
result.p0.x = MaxF32(a.p0.x, b.p0.x);
result.p0.y = MaxF32(a.p0.y, b.p0.y);
result.p1.x = MinF32(a.p1.x, b.p1.x);
result.p1.y = MinF32(a.p1.y, b.p1.y);
return result;
}
Rng2 AddRng2Vec2(Rng2 r, Vec2 v) Rng2 AddRng2Vec2(Rng2 r, Vec2 v)
{ {
Rng2 result = Zi; Rng2 result = Zi;
@ -745,6 +757,11 @@ Rng2 DivRng2Vec2(Rng2 r, Vec2 v)
//- Rng2I32 //- Rng2I32
b32 IsRng2I32Empty(Rng2I32 r)
{
return r.p0.x >= r.p1.x || r.p0.y >= r.p1.y;
}
Vec2I32 DimsFromRng2I32(Rng2I32 r) Vec2I32 DimsFromRng2I32(Rng2I32 r)
{ {
Vec2I32 result = Zi; Vec2I32 result = Zi;
@ -765,10 +782,20 @@ Vec2I32 CenterFromRng2I32(Rng2I32 r)
Rng2I32 UnionRng2I32(Rng2I32 a, Rng2I32 b) Rng2I32 UnionRng2I32(Rng2I32 a, Rng2I32 b)
{ {
Rng2I32 result = Zi; Rng2I32 result = Zi;
result.p0.x = MinF32(a.p0.x, b.p0.x); result.p0.x = MinI32(a.p0.x, b.p0.x);
result.p0.y = MinF32(a.p0.y, b.p0.y); result.p0.y = MinI32(a.p0.y, b.p0.y);
result.p1.x = MaxF32(a.p1.x, b.p1.x); result.p1.x = MaxI32(a.p1.x, b.p1.x);
result.p1.y = MaxF32(a.p1.y, b.p1.y); result.p1.y = MaxI32(a.p1.y, b.p1.y);
return result;
}
Rng2I32 IntersectRng2I32(Rng2I32 a, Rng2I32 b)
{
Rng2I32 result = Zi;
result.p0.x = MaxI32(a.p0.x, b.p0.x);
result.p0.y = MaxI32(a.p0.y, b.p0.y);
result.p1.x = MinI32(a.p1.x, b.p1.x);
result.p1.y = MinI32(a.p1.y, b.p1.y);
return result; return result;
} }
@ -799,9 +826,9 @@ Rng2I32 DivRng2I32Vec2I32(Rng2I32 r, Vec2I32 v)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Affine //~ Affine
b32 MatchAffine(Affine af1, Affine af2) b32 MatchAffine(Affine af0, Affine af1)
{ {
return MatchVec2(af1.og, af2.og) && MatchVec2(af1.bx, af2.bx) && MatchVec2(af1.by, af2.by); return MatchVec2(af0.og, af1.og) && MatchVec2(af0.bx, af1.bx) && MatchVec2(af0.by, af1.by);
} }
//- Initialization //- Initialization
@ -1085,17 +1112,17 @@ Mat4x4 Mat4x4FromOrtho(f32 left, f32 right, f32 bottom, f32 top, f32 near_z, f32
return m; return m;
} }
Mat4x4 MulMat4x4(Mat4x4 m1, Mat4x4 m2) Mat4x4 MulMat4x4(Mat4x4 m0, Mat4x4 m1)
{ {
f32 a00 = m1.e[0][0], a01 = m1.e[0][1], a02 = m1.e[0][2], a03 = m1.e[0][3], f32 a00 = m0.e[0][0], a01 = m0.e[0][1], a02 = m0.e[0][2], a03 = m0.e[0][3],
a10 = m1.e[1][0], a11 = m1.e[1][1], a12 = m1.e[1][2], a13 = m1.e[1][3], a10 = m0.e[1][0], a11 = m0.e[1][1], a12 = m0.e[1][2], a13 = m0.e[1][3],
a20 = m1.e[2][0], a21 = m1.e[2][1], a22 = m1.e[2][2], a23 = m1.e[2][3], a20 = m0.e[2][0], a21 = m0.e[2][1], a22 = m0.e[2][2], a23 = m0.e[2][3],
a30 = m1.e[3][0], a31 = m1.e[3][1], a32 = m1.e[3][2], a33 = m1.e[3][3], a30 = m0.e[3][0], a31 = m0.e[3][1], a32 = m0.e[3][2], a33 = m0.e[3][3],
b00 = m2.e[0][0], b01 = m2.e[0][1], b02 = m2.e[0][2], b03 = m2.e[0][3], b00 = m1.e[0][0], b01 = m1.e[0][1], b02 = m1.e[0][2], b03 = m1.e[0][3],
b10 = m2.e[1][0], b11 = m2.e[1][1], b12 = m2.e[1][2], b13 = m2.e[1][3], b10 = m1.e[1][0], b11 = m1.e[1][1], b12 = m1.e[1][2], b13 = m1.e[1][3],
b20 = m2.e[2][0], b21 = m2.e[2][1], b22 = m2.e[2][2], b23 = m2.e[2][3], b20 = m1.e[2][0], b21 = m1.e[2][1], b22 = m1.e[2][2], b23 = m1.e[2][3],
b30 = m2.e[3][0], b31 = m2.e[3][1], b32 = m2.e[3][2], b33 = m2.e[3][3]; b30 = m1.e[3][0], b31 = m1.e[3][1], b32 = m1.e[3][2], b33 = m1.e[3][3];
Mat4x4 result; Mat4x4 result;
result.e[0][0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03; result.e[0][0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03;

View File

@ -308,17 +308,17 @@ f32 UnwindAngleF32(f32 a);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Float lerp //~ Float lerp
f32 LerpF32(f32 val0, f32 val1, f32 t); f32 LerpF32(f32 a, f32 b, f32 t);
f64 LerpF64(f64 val0, f64 val1, f64 t); f64 LerpF64(f64 a, f64 b, f64 t);
f32 LerpAngleF32(f32 a, f32 b, f32 t); f32 LerpAngleF32(f32 a, f32 b, f32 t);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Int lerp //~ Int lerp
i32 LerpI32(i32 val0, i32 val1, f32 t); i32 LerpI32(i32 a, i32 b, f32 t);
i64 LerpI64(i64 val0, i64 val1, f64 t); i64 LerpI64(i64 a, i64 b, f64 t);
i32 LerpU32(u32 val0, u32 val1, f32 t); i32 LerpU32(u32 a, u32 b, f32 t);
i64 LerpU64(u64 val0, u64 val1, f64 t); i64 LerpU64(u64 a, u64 b, f64 t);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Smoothstep //~ Smoothstep
@ -340,7 +340,7 @@ Vec4 SrgbFromLinear(Vec4 lin);
Vec4 PremulFromLinear(Vec4 lin); Vec4 PremulFromLinear(Vec4 lin);
Vec4 PremulFromSrgb(Vec4 srgb); Vec4 PremulFromSrgb(Vec4 srgb);
Vec4 LerpSrgb(Vec4 v0, Vec4 v1, f32 t); Vec4 LerpSrgb(Vec4 a, Vec4 b, f32 t);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Vec2 //~ Vec2
@ -391,16 +391,17 @@ Vec2 RotateVec2(Vec2 v, f32 a);
Vec2 RotateVec2Vec2(Vec2 a, Vec2 b); Vec2 RotateVec2Vec2(Vec2 a, Vec2 b);
Vec2 Vec2FromAngle(f32 a); Vec2 Vec2FromAngle(f32 a);
f32 AngleFromVec2(Vec2 v); f32 AngleFromVec2(Vec2 v);
f32 AngleFromVec2Dirs(Vec2 dir1, Vec2 dir2);
f32 AngleFromVec2Points(Vec2 pt1, Vec2 pt2); //- Area
f32 DimsFromVec2(Vec2 v);
//- Closest point //- Closest point
Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p); Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p);
//- Lerp //- Lerp
Vec2 LerpVec2(Vec2 v0, Vec2 v1, f32 t); Vec2 LerpVec2(Vec2 a, Vec2 b, f32 t);
Vec2 LerpVec2Vec2(Vec2 v0, Vec2 v1, Vec2 t); Vec2 LerpVec2Vec2(Vec2 a, Vec2 b, Vec2 t);
Vec2 SlerpVec2(Vec2 v0, Vec2 v1, f32 t); Vec2 SlerpVec2(Vec2 a, Vec2 b, f32 t);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Vec2I32 //~ Vec2I32
@ -418,7 +419,7 @@ Vec4 MulVec4(Vec4 v, f32 s);
Vec4 MulVec4Vec4(Vec4 a, Vec4 b); Vec4 MulVec4Vec4(Vec4 a, Vec4 b);
//- Lerp //- Lerp
Vec4 LerpVec4(Vec4 v0, Vec4 v1, f32 t); Vec4 LerpVec4(Vec4 a, Vec4 b, f32 t);
//- Conversion //- Conversion
Vec4 Vec4FromU32(u32 v); Vec4 Vec4FromU32(u32 v);
@ -432,18 +433,22 @@ f32 NormRng(Rng r, f32 v);
#define Norm(min, max, v) NormRng(RNG((min), (max)), (v)) #define Norm(min, max, v) NormRng(RNG((min), (max)), (v))
//- Rng2 //- Rng2
b32 IsRng2Empty(Rng2 r);
Vec2 DimsFromRng2(Rng2 r); Vec2 DimsFromRng2(Rng2 r);
Vec2 CenterFromRng2(Rng2 r); Vec2 CenterFromRng2(Rng2 r);
Vec2 NormRng2(Rng2 r, Vec2 v); Vec2 NormRng2(Rng2 r, Vec2 v);
Rng2 UnionRng2(Rng2 a, Rng2 b); Rng2 UnionRng2(Rng2 a, Rng2 b);
Rng2 IntersectRng2(Rng2 a, Rng2 b);
Rng2 AddRng2Vec2(Rng2 r, Vec2 v); Rng2 AddRng2Vec2(Rng2 r, Vec2 v);
Rng2 MulRng2Vec2(Rng2 a, Vec2 v); Rng2 MulRng2Vec2(Rng2 a, Vec2 v);
Rng2 DivRng2Vec2(Rng2 a, Vec2 v); Rng2 DivRng2Vec2(Rng2 a, Vec2 v);
//- Rng2I32 //- Rng2I32
b32 IsRng2I32Empty(Rng2I32 r);
Vec2I32 DimsFromRng2I32(Rng2I32 r); Vec2I32 DimsFromRng2I32(Rng2I32 r);
Vec2I32 CenterFromRng2I32(Rng2I32 r); Vec2I32 CenterFromRng2I32(Rng2I32 r);
Rng2I32 UnionRng2I32(Rng2I32 a, Rng2I32 b); Rng2I32 UnionRng2I32(Rng2I32 a, Rng2I32 b);
Rng2I32 IntersectRng2I32(Rng2I32 a, Rng2I32 b);
Rng2I32 AddRng2I32Vec2I32(Rng2I32 r, Vec2I32 v); Rng2I32 AddRng2I32Vec2I32(Rng2I32 r, Vec2I32 v);
Rng2I32 MulRng2I32Vec2I32(Rng2I32 a, Vec2I32 v); Rng2I32 MulRng2I32Vec2I32(Rng2I32 a, Vec2I32 v);
Rng2I32 DivRng2I32Vec2I32(Rng2I32 a, Vec2I32 v); Rng2I32 DivRng2I32Vec2I32(Rng2I32 a, Vec2I32 v);
@ -454,7 +459,7 @@ Rng2I32 DivRng2I32Vec2I32(Rng2I32 a, Vec2I32 v);
#define AffineIdentity ((Affine) { .bx = { .x = 1 }, .by = { .y = 1 } }) #define AffineIdentity ((Affine) { .bx = { .x = 1 }, .by = { .y = 1 } })
#define CompAffineIdentity { .bx = { .x = 1 }, .by = { .y = 1 } } #define CompAffineIdentity { .bx = { .x = 1 }, .by = { .y = 1 } }
b32 MatchAffine(Affine af1, Affine af2); b32 MatchAffine(Affine af0, Affine af1);
//- Initialization //- Initialization
Affine AffineFromPos(Vec2 v); Affine AffineFromPos(Vec2 v);
@ -516,5 +521,5 @@ SoftSpring MakeSpring(f32 hertz, f32 damping_ratio, f32 dt);
Mat4x4 Mat4x4FromAffine(Affine af); Mat4x4 Mat4x4FromAffine(Affine af);
Mat4x4 Mat4x4FromOrtho(f32 left, f32 right, f32 bottom, f32 top, f32 near_z, f32 far_z); Mat4x4 Mat4x4FromOrtho(f32 left, f32 right, f32 bottom, f32 top, f32 near_z, f32 far_z);
Mat4x4 MulMat4x4(Mat4x4 m1, Mat4x4 m2); Mat4x4 MulMat4x4(Mat4x4 m0, Mat4x4 m1);
Mat4x4 ProjectMat4x4View(Affine view, f32 viewport_width, f32 viewport_height); Mat4x4 ProjectMat4x4View(Affine view, f32 viewport_width, f32 viewport_height);

View File

@ -107,18 +107,38 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
for (i64 slice_idx = 0; slice_idx < sheet->slices_count; ++slice_idx) for (i64 slice_idx = 0; slice_idx < sheet->slices_count; ++slice_idx)
{ {
SPR_SliceEntry *slice = &sheet->slices[slice_idx]; SPR_SliceEntry *slice = &sheet->slices[slice_idx];
slice->bounds = Rng2Empty;
for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind) for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind)
{ {
slice->rays[ray_kind] = XformIdentity; slice->rays[ray_kind] = XformIdentity;
} }
} }
//- Compute slice bounds
for (ASE_Layer *ase_layer = sheet->meta.first_layer; ase_layer; ase_layer = ase_layer->next)
{
SPR_LayerKind kind = SPR_LayerKindFromName(ase_layer->name);
if (kind == SPR_LayerKind_Visual)
{
for (i64 slice_idx = 0; slice_idx < sheet->meta.frames_count; ++slice_idx)
{
ASE_Cel *cel = &ase_layer->cels[slice_idx];
SPR_SliceEntry *slice = &sheet->slices[slice_idx];
slice->bounds = UnionRng2(slice->bounds, cel->bounds);
}
}
}
//- Push async rasterization commands //- Push async rasterization commands
if (sheet->meta.frames_count > 0) if (sheet->meta.frames_count > 0)
{ {
Lock cmds_lock = LockE(&SPR.submit.mutex); Lock cmds_lock = LockE(&SPR.submit.mutex);
{ {
i64 submit_count = 0;
for (i64 slice_idx = 0; slice_idx < sheet->meta.frames_count; ++slice_idx) for (i64 slice_idx = 0; slice_idx < sheet->meta.frames_count; ++slice_idx)
{
SPR_SliceEntry *slice = &sheet->slices[slice_idx];
if (!IsRng2Empty(slice->bounds))
{ {
SPR_CmdNode *cmd_node = SPR.submit.first_free; SPR_CmdNode *cmd_node = SPR.submit.first_free;
if (cmd_node) if (cmd_node)
@ -133,15 +153,21 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
cmd_node->cmd.sheet = sheet; cmd_node->cmd.sheet = sheet;
cmd_node->cmd.slice_idx = slice_idx; cmd_node->cmd.slice_idx = slice_idx;
SllQueuePush(SPR.submit.first, SPR.submit.last, cmd_node); SllQueuePush(SPR.submit.first, SPR.submit.last, cmd_node);
++SPR.submit.count; submit_count += 1;
} }
Atomic32FetchSet(&SPR.new_cmds_present, 1); }
if (submit_count > 0)
{
SPR.submit.count += submit_count;
SignalAsyncTick(); SignalAsyncTick();
} }
}
Unlock(&cmds_lock); Unlock(&cmds_lock);
} }
//- Compute rays //- Compute rays
{
TempArena scratch = BeginScratchNoConflict();
for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind) for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind)
{ {
String ray_name = SPR_NameFromRayKind(ray_kind); String ray_name = SPR_NameFromRayKind(ray_kind);
@ -155,21 +181,27 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
{ {
ASE_Cel *ase_cel = &ase_layer->cels[slice_idx]; ASE_Cel *ase_cel = &ase_layer->cels[slice_idx];
SPR_SliceEntry *slice = &sheet->slices[slice_idx]; SPR_SliceEntry *slice = &sheet->slices[slice_idx];
ASE_SinglePixelResult ray_pix = ASE_SinglePixelFromCel(ase_cel); ASE_Image image = ASE_DecompressImageFromCel(scratch.arena, ase_cel);
u32 alpha = (ray_pix.v >> 24) & 0xFF; if (!IsRng2Empty(image.bounds))
{
u32 ray_pix = image.pixels[0];
u32 alpha = (ray_pix >> 24) & 0xFF;
if (alpha > 0) if (alpha > 0)
{ {
// TODO: Different quantization so that 128 equals 0, instead of approximately 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_x = (((f32)((ray_pix >> 0) & 0xFF) / 255.0) * 2.0) - 1;
f32 rot_y = (((f32)((ray_pix.v >> 8) & 0xFF) / 255.0) * 2.0) - 1; f32 rot_y = (((f32)((ray_pix >> 8) & 0xFF) / 255.0) * 2.0) - 1;
Vec2 rot = NormVec2(VEC2(rot_x, rot_y)); Vec2 rot = NormVec2(VEC2(rot_x, rot_y));
slice->rays[ray_kind].r = rot; slice->rays[ray_kind].r = rot;
slice->rays[ray_kind].t = ray_pix.pos; slice->rays[ray_kind].t = ase_cel->bounds.p0;
} }
} }
} }
} }
} }
}
EndScratch(scratch);
}
//- Init spans //- Init spans
sheet->span_bins_count = 256; sheet->span_bins_count = 256;
@ -207,9 +239,12 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
////////////////////////////// //////////////////////////////
//- Fetch span //- Fetch span
SPR_SpanEntry *span = 0; // FIXME: Ensure slices_count always > 0
i64 span_start = 0;
i64 span_end = sheet->slices_count;
b32 span_matched = 0; b32 span_matched = 0;
{ {
SPR_SpanEntry *span = 0;
if (sheet->ok) if (sheet->ok)
{ {
SPR_SpanBin *span_bin = &sheet->span_bins[span_key.v % sheet->span_bins_count]; SPR_SpanBin *span_bin = &sheet->span_bins[span_key.v % sheet->span_bins_count];
@ -224,9 +259,13 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
} }
if (!span) if (!span)
{ {
// FIXME: Ensure first span always exists in sheet
span = sheet->first_span; span = sheet->first_span;
} }
if (span)
{
span_start = span->start;
span_end = span->end;
}
} }
////////////////////////////// //////////////////////////////
@ -235,7 +274,7 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
SPR_SliceEntry *slice = 0; SPR_SliceEntry *slice = 0;
{ {
// FIXME: Ensure span->end is never <= span->start // FIXME: Ensure span->end is never <= span->start
i64 slice_idx = span->start + (frame_seq % (span->end - span->start)); i64 slice_idx = span_start + (frame_seq % (span_end - span_start));
slice = &sheet->slices[slice_idx]; slice = &sheet->slices[slice_idx];
} }
@ -258,13 +297,13 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
{ {
result.tex = slice->atlas->tex_ref; result.tex = slice->atlas->tex_ref;
result.tex_rect_uv = slice->atlas_rect_uv; result.tex_rect_uv = slice->atlas_rect_uv;
result.dims = slice->dims; result.bounds = slice->bounds;
} }
else else
{ {
result.tex = SPR.unready_tex; result.tex = SPR.unready_tex;
result.tex_rect_uv = RNG2(VEC2(0, 0), VEC2(1, 1)); result.tex_rect_uv = RNG2(VEC2(0, 0), VEC2(1, 1));
result.dims = SPR.unready_tex_dims; result.bounds = RNG2(VEC2(0, 0), SPR.unready_tex_dims);
} }
// Fill rays // Fill rays
StaticAssert(countof(result.rays) == countof(slice->rays)); StaticAssert(countof(result.rays) == countof(slice->rays));
@ -286,16 +325,14 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
// TODO: Distribute rasterization accross wave // TODO: Distribute rasterization accross wave
if (lane->idx == 0) if (lane->idx == 0)
{ {
if (Atomic32Fetch(&SPR.new_cmds_present))
{
Atomic32Set(&SPR.new_cmds_present, 0);
i64 cmds_count = 0; i64 cmds_count = 0;
SPR_Cmd *cmds = 0; SPR_Cmd *cmds = 0;
{ {
Lock lock = LockE(&SPR.submit.mutex); Lock lock = LockE(&SPR.submit.mutex);
if (SPR.submit.count > 0)
{ {
cmds_count = SPR.submit.count; cmds_count = SPR.submit.count;
cmds = PushStructsNoZero(frame_arena, SPR_Cmd, SPR.submit.count); cmds = PushStructsNoZero(frame_arena, SPR_Cmd, cmds_count);
i64 cmd_idx = 0; i64 cmd_idx = 0;
for (SPR_CmdNode *n = SPR.submit.first; n; n = n->next) for (SPR_CmdNode *n = SPR.submit.first; n; n = n->next)
{ {
@ -304,9 +341,9 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
} }
// Reset submission queue // Reset submission queue
SPR.submit.first_free = SPR.submit.first; SPR.submit.first_free = SPR.submit.first;
SPR.submit.count = 0;
SPR.submit.first = 0; SPR.submit.first = 0;
SPR.submit.last = 0; SPR.submit.last = 0;
SPR.submit.count = 0;
} }
Unlock(&lock); Unlock(&lock);
} }
@ -316,6 +353,7 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_AsyncCopy); G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_AsyncCopy);
for (i64 cmd_idx = 0; cmd_idx < cmds_count; ++cmd_idx) for (i64 cmd_idx = 0; cmd_idx < cmds_count; ++cmd_idx)
{ {
TempArena scratch = BeginScratchNoConflict();
SPR_Cmd *cmd = &cmds[cmd_idx]; SPR_Cmd *cmd = &cmds[cmd_idx];
SPR_SheetEntry *sheet = cmd->sheet; SPR_SheetEntry *sheet = cmd->sheet;
ASE_Meta meta = sheet->meta; ASE_Meta meta = sheet->meta;
@ -325,18 +363,27 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
// LogInfoF("Rasterizing sprite sheet %F \"%F\" (%F bytes)", FmtHandle(sheet->key.r), FmtString(name), FmtUint(encoded.len)); // LogInfoF("Rasterizing sprite sheet %F \"%F\" (%F bytes)", FmtHandle(sheet->key.r), FmtString(name), FmtUint(encoded.len));
SPR_SliceEntry *slice = &sheet->slices[cmd->slice_idx]; SPR_SliceEntry *slice = &sheet->slices[cmd->slice_idx];
Vec2 slice_dims = DimsFromRng2(slice->bounds);
////////////////////////////// //////////////////////////////
//- Rasterize //- Composite
u32 *pixels = PushStructs(frame_arena, u32, slice->dims.x * slice->dims.y); ASE_Image composite = ASE_PushBlankImage(frame_arena, slice->bounds);
for (ASE_Layer *ase_layer = sheet->meta.last_layer; ase_layer; ase_layer = ase_layer->prev) for (ASE_Layer *ase_layer = sheet->meta.last_layer; ase_layer; ase_layer = ase_layer->prev)
{ {
SPR_LayerKind kind = SPR_LayerKindFromName(ase_layer->name); SPR_LayerKind kind = SPR_LayerKindFromName(ase_layer->name);
if (kind == SPR_LayerKind_Visual) if (kind == SPR_LayerKind_Visual)
{ {
ASE_Cel *cel = &ase_layer->cels[cmd->slice_idx]; ASE_Cel *cel = &ase_layer->cels[cmd->slice_idx];
ASE_RasterizeCel(cel, pixels, slice->dims); {
TempArena temp = BeginTempArena(scratch.arena);
{
// TODO: Reuse decompressed images for linked cels
ASE_Image image = ASE_DecompressImageFromCel(temp.arena, cel);
ASE_BlendImage(image, composite);
}
EndTempArena(temp);
}
} }
} }
@ -353,7 +400,7 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
if (!atlas) if (!atlas)
{ {
atlas = PushStruct(perm, SPR_Atlas); atlas = PushStruct(perm, SPR_Atlas);
i32 atlas_size = MaxI32(1024, NextPow2U64(MaxI32(slice->dims.x, slice->dims.y))); i32 atlas_size = MaxI32(1024, NextPow2U64(MaxI32(slice_dims.x, slice_dims.y)));
atlas->dims = VEC2I32(atlas_size, atlas_size); atlas->dims = VEC2I32(atlas_size, atlas_size);
{ {
G_ArenaHandle gpu_perm = G_PermArena(); G_ArenaHandle gpu_perm = G_PermArena();
@ -370,14 +417,14 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
} }
// Determine pos in atlas // Determine pos in atlas
pos_in_atlas = atlas->cur_pos; pos_in_atlas = atlas->cur_pos;
atlas->cur_row_height = MaxI32(atlas->cur_row_height, slice->dims.y); atlas->cur_row_height = MaxI32(atlas->cur_row_height, slice_dims.y);
if (pos_in_atlas.x + slice->dims.x > atlas->dims.x); if (pos_in_atlas.x + slice_dims.x > atlas->dims.x);
{ {
atlas->cur_pos.x = 0; atlas->cur_pos.x = 0;
atlas->cur_pos.y += atlas->cur_row_height; atlas->cur_pos.y += atlas->cur_row_height;
atlas->cur_row_height = slice->dims.y; atlas->cur_row_height = slice_dims.y;
} }
atlas->cur_pos.x += slice->dims.x; atlas->cur_pos.x += slice_dims.x;
if (atlas->cur_pos.x < atlas->dims.x && atlas->cur_pos.y < atlas->dims.y) if (atlas->cur_pos.x < atlas->dims.x && atlas->cur_pos.y < atlas->dims.y)
{ {
can_use_atlas = 1; can_use_atlas = 1;
@ -390,7 +437,7 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
// Fill slice_entry atlas info // Fill slice_entry atlas info
{ {
Rng2I32 atlas_rect = RNG2I32(pos_in_atlas, AddVec2I32(pos_in_atlas, Vec2I32FromVec(slice->dims))); Rng2I32 atlas_rect = RNG2I32(pos_in_atlas, AddVec2I32(pos_in_atlas, Vec2I32FromVec(slice_dims)));
slice->atlas = atlas; slice->atlas = atlas;
slice->atlas_rect_uv.p0.x = (f32)atlas_rect.p0.x / (f32)atlas->dims.x; 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.p0.y = (f32)atlas_rect.p0.y / (f32)atlas->dims.x;
@ -402,12 +449,13 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
G_CopyCpuToTexture( G_CopyCpuToTexture(
cl, cl,
atlas->tex, VEC3I32(pos_in_atlas.x, pos_in_atlas.y, 0), atlas->tex, VEC3I32(pos_in_atlas.x, pos_in_atlas.y, 0),
pixels, VEC3I32(slice->dims.x, slice->dims.y, 1), composite.pixels, VEC3I32(slice_dims.x, slice_dims.y, 1),
RNG3I32( RNG3I32(
VEC3I32(0, 0, 0), VEC3I32(0, 0, 0),
VEC3I32(slice->dims.x, slice->dims.y, 1) VEC3I32(slice_dims.x, slice_dims.y, 1)
) )
); );
EndScratch(scratch);
} }
i64 completion_target = G_CommitCommandList(cl); i64 completion_target = G_CommitCommandList(cl);
@ -422,5 +470,4 @@ void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
} }
} }
} }
}
} }

View File

@ -55,7 +55,7 @@ Struct(SPR_Slice)
G_Texture2DRef tex; G_Texture2DRef tex;
Rng2 tex_rect_uv; Rng2 tex_rect_uv;
Vec2 dims; Rng2 bounds;
b32 exists; b32 exists;
b32 ready; b32 ready;
@ -73,8 +73,8 @@ Struct(SPR_SliceEntry)
Xform rays[SPR_RayKind_COUNT]; Xform rays[SPR_RayKind_COUNT];
SPR_Atlas *atlas; SPR_Atlas *atlas;
Vec2 dims;
Rng2 atlas_rect_uv; Rng2 atlas_rect_uv;
Rng2 bounds;
Atomic64 atlas_copy_completion_target; Atomic64 atlas_copy_completion_target;
}; };
@ -154,14 +154,13 @@ Struct(SPR_Ctx)
i64 atlases_count; i64 atlases_count;
SPR_Atlas *first_atlas; SPR_Atlas *first_atlas;
Atomic32 new_cmds_present;
struct struct
{ {
Mutex mutex; Mutex mutex;
SPR_CmdNode *first; SPR_CmdNode *first;
SPR_CmdNode *last; SPR_CmdNode *last;
SPR_CmdNode *first_free; SPR_CmdNode *first_free;
u64 count; i64 count;
} submit; } submit;
SPR_AsyncCtx async; SPR_AsyncCtx async;