From 00629aa988eb40f78d19d0880c72199a4625fd0d Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 1 May 2024 15:50:50 -0500 Subject: [PATCH] merge texture & sheet into 'sprite' --- res/graphics/white.ase | 3 - src/app.c | 10 +- src/common.h | 7 +- src/config.h | 23 - src/draw.c | 57 +-- src/draw.h | 10 +- src/entity.h | 6 +- src/font.c | 3 +- src/font.h | 4 +- src/game.c | 57 +-- src/game.h | 4 +- src/renderer.h | 4 +- src/renderer_d3d11.c | 16 +- src/sheet.c | 841 ------------------------------------ src/sheet.h | 70 --- src/{texture.c => sprite.c} | 394 +++++++++++++---- src/sprite.h | 87 ++++ src/sys_win32.c | 2 + src/texture.h | 51 --- src/user.c | 42 +- src/user.h | 4 +- 21 files changed, 488 insertions(+), 1207 deletions(-) delete mode 100644 res/graphics/white.ase delete mode 100644 src/sheet.c delete mode 100644 src/sheet.h rename src/{texture.c => sprite.c} (66%) create mode 100644 src/sprite.h delete mode 100644 src/texture.h diff --git a/res/graphics/white.ase b/res/graphics/white.ase deleted file mode 100644 index 182ac67a..00000000 --- a/res/graphics/white.ase +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2424e08c9bf7bdcfa17b02d27cef0b62078d01d627b6b1ef0992201bb6977c6b -size 334 diff --git a/src/app.c b/src/app.c index b6369ec3..a47565fd 100644 --- a/src/app.c +++ b/src/app.c @@ -11,9 +11,8 @@ #include "resource.h" #include "asset_cache.h" #include "font.h" -#include "texture.h" +#include "sprite.h" #include "ttf.h" -#include "sheet.h" #include "mixer.h" #include "sound.h" #include "util.h" @@ -210,13 +209,12 @@ void app_entry_point(void) struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup(&work_sr); struct ttf_startup_receipt ttf_sr = ttf_startup(); struct font_startup_receipt font_sr = font_startup(&work_sr, &renderer_sr, &asset_cache_sr, &ttf_sr, &resource_sr); - struct texture_startup_receipt texture_sr = texture_startup(&renderer_sr, &resource_sr); - struct sheet_startup_receipt sheet_sr = sheet_startup(&resource_sr); + struct sprite_startup_receipt sprite_sr = sprite_startup(&renderer_sr, &resource_sr); struct mixer_startup_receipt mixer_sr = mixer_startup(); struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr); struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr); - struct game_startup_receipt game_sr = game_startup(&mixer_sr, &sheet_sr, &sound_sr); - struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &texture_sr, &draw_sr, &game_sr, &asset_cache_sr, &mixer_sr, &window); + struct game_startup_receipt game_sr = game_startup(&mixer_sr, &sprite_sr, &sound_sr); + struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &game_sr, &asset_cache_sr, &mixer_sr, &window); struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr); (UNUSED)user_sr; diff --git a/src/common.h b/src/common.h index 1981d84e..21ddfd38 100644 --- a/src/common.h +++ b/src/common.h @@ -370,12 +370,7 @@ struct renderer_handle { * Tag structs * ========================== */ -struct texture_tag { - u128 hash; - struct string path; -}; - -struct sheet_tag { +struct sprite_tag { u128 hash; struct string path; }; diff --git a/src/config.h b/src/config.h index 26044b10..2998cccf 100644 --- a/src/config.h +++ b/src/config.h @@ -39,29 +39,6 @@ #define USER_INTERP_OFFSET_TICK_RATIO 1.1 #define USER_INTERP_ENABLED 1 -/* ========================== * - * Cache limits - * - * (Size of caches before evicting starts) - * ========================== */ - -/* NOTE: Total memory budget should be half of envisioned hard cache memory - * budget. This is because eviction can happen in parallel, so theoretically - * cache entries can load and exist in memory at the same time the old evicted - * record is still around. */ -#define TOTAL_CACHE_MEMORY_BUDGET (MEGABYTE(256)) - -#define TEXTURE_CACHE_MEMORY_BUDGET (MEGABYTE(128)) -#define SHEET_CACHE_MEMORY_BUDGET (MEGABYTE(128)) - -/* Sum of cache budgets must = total cache memory budget */ -CT_ASSERT( - ( - SHEET_CACHE_MEMORY_BUDGET + - TEXTURE_CACHE_MEMORY_BUDGET - ) == TOTAL_CACHE_MEMORY_BUDGET -); - /* ========================== * * Settings * ========================== */ diff --git a/src/draw.c b/src/draw.c index 5c4d5e18..d542acca 100644 --- a/src/draw.c +++ b/src/draw.c @@ -3,10 +3,10 @@ #include "math.h" #include "font.h" #include "scratch.h" -#include "texture.h" +#include "sprite.h" GLOBAL struct { - struct texture_tag solid_white; + struct renderer_handle solid_white; } G = { 0 }, DEBUG_ALIAS(G, G_draw); /* ========================== * @@ -18,7 +18,8 @@ struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *render { (UNUSED)renderer_sr; (UNUSED)font_sr; - G.solid_white = texture_tag_from_path(STR("res/graphics/white.ase")); + u32 pixel_white = 0xFFFFFFFF; + G.solid_white = renderer_texture_alloc((struct image_rgba) { .width = 1, .height = 1, .pixels = &pixel_white } ); return (struct draw_startup_receipt) { 0 }; } @@ -26,7 +27,7 @@ struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *render * Texture * ========================== */ -INTERNAL void draw_texture_quad_internal(struct renderer_canvas *canvas, struct clip_rect clip, u32 tint, struct quad quad) +INTERNAL void draw_sprite_quad_internal(struct renderer_canvas *canvas, struct clip_rect clip, u32 tint, struct quad quad) { struct texture_shader_vertex *vertices = NULL; vidx *indices = NULL; @@ -68,17 +69,17 @@ INTERNAL void draw_texture_quad_internal(struct renderer_canvas *canvas, struct indices[5] = offset + 3; } -void draw_texture_quad(struct renderer_canvas *canvas, struct draw_texture_params params, struct quad quad) +void draw_sprite_quad(struct renderer_canvas *canvas, struct draw_sprite_params params, struct quad quad) { - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = params.texture_tag }); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .sprite = params.sprite }); - draw_texture_quad_internal(canvas, params.clip, params.tint, quad); + draw_sprite_quad_internal(canvas, params.clip, params.tint, quad); } -void draw_texture_rect(struct renderer_canvas *canvas, struct draw_texture_params params, struct rect rect) +void draw_sprite_rect(struct renderer_canvas *canvas, struct draw_sprite_params params, struct rect rect) { struct quad quad = quad_from_rect(rect); - draw_texture_quad(canvas, params, quad); + draw_sprite_quad(canvas, params, quad); } /* ========================== * @@ -117,7 +118,7 @@ void draw_solid_poly(struct renderer_canvas *canvas, struct v2_array array, u32 return; } - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white }); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = G.solid_white }); draw_solid_poly_internal(canvas, array, color); } @@ -145,15 +146,15 @@ void draw_solid_circle(struct renderer_canvas *canvas, struct v2 pos, f32 radius void draw_solid_quad(struct renderer_canvas *canvas, struct quad quad, u32 color) { - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white }); - draw_texture_quad_internal(canvas, CLIP_ALL, color, quad); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = G.solid_white }); + draw_sprite_quad_internal(canvas, CLIP_ALL, color, quad); } void draw_solid_rect(struct renderer_canvas *canvas, struct rect rect, u32 color) { - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white }); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = G.solid_white }); struct quad quad = quad_from_rect(rect); - draw_texture_quad_internal(canvas, CLIP_ALL, color, quad); + draw_sprite_quad_internal(canvas, CLIP_ALL, color, quad); } /* ========================== * @@ -162,16 +163,16 @@ void draw_solid_rect(struct renderer_canvas *canvas, struct rect rect, u32 color void draw_solid_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, u32 color) { - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white }); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = G.solid_white }); struct quad quad = quad_from_line(start, end, thickness); - draw_texture_quad_internal(canvas, CLIP_ALL, color, quad); + draw_sprite_quad_internal(canvas, CLIP_ALL, color, quad); } void draw_solid_ray(struct renderer_canvas *canvas, struct v2 pos, struct v2 rel, f32 thickness, u32 color) { - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white }); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = G.solid_white }); struct quad quad = quad_from_ray(pos, rel, thickness); - draw_texture_quad_internal(canvas, CLIP_ALL, color, quad); + draw_sprite_quad_internal(canvas, CLIP_ALL, color, quad); } void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array, b32 loop, f32 thickness, u32 color) @@ -180,18 +181,18 @@ void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array, return; } - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white }); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = G.solid_white }); for (u64 i = 1; i < array.count; ++i) { struct v2 p1 = array.points[i - 1]; struct v2 p2 = array.points[i]; struct quad q = quad_from_line(p1, p2, thickness); - draw_texture_quad_internal(canvas, CLIP_ALL, color, q); + draw_sprite_quad_internal(canvas, CLIP_ALL, color, q); } if (loop && array.count > 2) { struct v2 p1 = array.points[array.count - 1]; struct v2 p2 = array.points[0]; struct quad q = quad_from_line(p1, p2, thickness); - draw_texture_quad_internal(canvas, CLIP_ALL, color, q); + draw_sprite_quad_internal(canvas, CLIP_ALL, color, q); } } @@ -210,7 +211,7 @@ void draw_solid_rect_line(struct renderer_canvas *canvas, struct rect rect, f32 void draw_solid_arrow_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, f32 arrowhead_height, u32 color) { - renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white }); + renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = G.solid_white }); const f32 head_width_ratio = 0.5f; /* Width of arrowhead relative to its length */ @@ -237,7 +238,7 @@ void draw_solid_arrow_line(struct renderer_canvas *canvas, struct v2 start, stru draw_solid_poly_internal(canvas, head_points_v2_array, color); struct quad line_quad = quad_from_line(start, head_start, thickness); - draw_texture_quad_internal(canvas, CLIP_ALL, color, line_quad); + draw_sprite_quad_internal(canvas, CLIP_ALL, color, line_quad); } void draw_solid_arrow_ray(struct renderer_canvas *canvas, struct v2 pos, struct v2 rel, f32 thickness, f32 arrowhead_height, u32 color) @@ -281,18 +282,18 @@ void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 p struct clip_rect clip = { { - glyph->atlas_rect.x / font->image_size.x, - glyph->atlas_rect.y / font->image_size.y + glyph->atlas_rect.x / (f32)font->image_width, + glyph->atlas_rect.y / (f32)font->image_height }, { - (glyph->atlas_rect.x + glyph->atlas_rect.width) / font->image_size.x, - (glyph->atlas_rect.y + glyph->atlas_rect.height) / font->image_size.y + (glyph->atlas_rect.x + glyph->atlas_rect.width) / (f32)font->image_width, + (glyph->atlas_rect.y + glyph->atlas_rect.height) / (f32)font->image_height } }; struct quad quad = quad_from_rect(RECT(x, y, width, height)); - draw_texture_quad_internal(canvas, clip, 0xFFFFFFFF, quad); + draw_sprite_quad_internal(canvas, clip, 0xFFFFFFFF, quad); draw_pos.x += glyph->advance * scale; } diff --git a/src/draw.h b/src/draw.h index e15e91b6..6f6f07c9 100644 --- a/src/draw.h +++ b/src/draw.h @@ -6,14 +6,14 @@ struct font; struct renderer_startup_receipt; struct font_startup_receipt; -#define DRAW_TEXTURE_PARAMS(...) ((struct draw_texture_params) { \ +#define DRAW_SPRITE_PARAMS(...) ((struct draw_sprite_params) { \ .tint = COLOR_WHITE, \ .clip = CLIP_ALL, \ __VA_ARGS__ \ }) -struct draw_texture_params { - struct texture_tag texture_tag; +struct draw_sprite_params { + struct sprite_tag sprite; struct clip_rect clip; u32 tint; }; @@ -22,8 +22,8 @@ struct draw_startup_receipt { i32 _; }; struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *renderer_sr, struct font_startup_receipt *font_sr); -void draw_texture_quad(struct renderer_canvas *canvas, struct draw_texture_params params, struct quad quad); -void draw_texture_rect(struct renderer_canvas *canvas, struct draw_texture_params params, struct rect rect); +void draw_sprite_quad(struct renderer_canvas *canvas, struct draw_sprite_params params, struct quad quad); +void draw_sprite_rect(struct renderer_canvas *canvas, struct draw_sprite_params params, struct rect rect); void draw_solid_poly(struct renderer_canvas *canvas, struct v2_array array, u32 color); void draw_solid_circle(struct renderer_canvas *canvas, struct v2 pos, f32 radius, u32 color, u32 detail); diff --git a/src/entity.h b/src/entity.h index 3e88d397..e70b7131 100644 --- a/src/entity.h +++ b/src/entity.h @@ -1,8 +1,7 @@ #ifndef ENTITY_H #define ENTITY_H -#include "texture.h" -#include "sheet.h" +#include "sprite.h" #include "mixer.h" enum entity_prop { @@ -66,8 +65,7 @@ struct entity { /* ====================================================================== */ /* Sprite */ - struct texture_tag sprite_texture_tag; - struct sheet_tag sprite_sheet_tag; + struct sprite_tag sprite; struct string sprite_span_name; struct xform sprite_quad_xform; u32 sprite_tint; diff --git a/src/font.c b/src/font.c index 336f0a6f..6955c34c 100644 --- a/src/font.c +++ b/src/font.c @@ -135,7 +135,8 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams) /* Set font data */ font->image_renderer_handle = image_renderer_handle; - font->image_size = V2(result.image_data.width, result.image_data.height); + font->image_width = result.image_data.width; + font->image_height = result.image_data.height; font->glyphs_count = result.glyphs_count; font->point_size = point_size; diff --git a/src/font.h b/src/font.h index 8cf1aefa..1472b598 100644 --- a/src/font.h +++ b/src/font.h @@ -1,7 +1,6 @@ #ifndef FONT_H #define FONT_H -#include "texture.h" #include "util.h" struct asset; @@ -22,7 +21,8 @@ struct font_glyph { struct font { struct renderer_handle image_renderer_handle; - struct v2 image_size; + u32 image_width; + u32 image_height; f32 point_size; u16 glyphs_count; struct font_glyph *glyphs; diff --git a/src/game.c b/src/game.c index 872c4a80..93917e82 100644 --- a/src/game.c +++ b/src/game.c @@ -2,7 +2,7 @@ #include "sys.h" #include "util.h" #include "world.h" -#include "sheet.h" +#include "sprite.h" #include "sound.h" #include "mixer.h" #include "math.h" @@ -35,7 +35,7 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(game_shutdown); INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg); struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr, - struct sheet_startup_receipt *sheet_sr, + struct sprite_startup_receipt *sheet_sr, struct sound_startup_receipt *sound_sr) { (UNUSED)mixer_sr; @@ -153,16 +153,16 @@ INTERNAL void recalculate_world_xform_recurse(struct entity *parent) } #if 0 -INTERNAL struct v2 sheet_size_meters(struct sheet_tag s) +INTERNAL struct v2 sprite_sheet_size_meters(struct sprite_tag s) { struct v2 size = { 0 }; - struct sheet_scope *scope = sheet_scope_begin(); + struct sprite_scope *scope = sprite_scope_begin(); { - struct sheet *sheet = sheet_from_tag_await(scope, s); + struct sheet *sheet = sprite_sheet_from_tag_await(scope, s); size.x = sheet->frame_size.x / (f32)PIXELS_PER_UNIT; size.y = sheet->frame_size.y / (f32)PIXELS_PER_UNIT; } - sheet_scope_end(scope); + sprite_scope_end(scope); return size; } #endif @@ -177,7 +177,7 @@ INTERNAL void game_update(void) * Begin frame cache scopes * ========================== */ - struct sheet_scope *sheet_frame_scope = sheet_scope_begin(); + struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); /* TODO: remove this (testing) */ /* Initialize entities */ @@ -196,15 +196,10 @@ INTERNAL void game_update(void) e->valid = true; e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size); - struct string sprite_name = STR("res/graphics/tim.ase"); - struct string sprite_span_name = STR("UNARMED"); - struct texture_tag sprite_texture_tag = texture_tag_from_path(sprite_name); - struct sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name); - struct v2 sprite_pos = V2(0, 0); f32 sprite_rot = 0; struct v2 sprite_size = V2(0.5f, 0.5f); - // struct v2 sprite_size = sheet_size_meters(sprite_sheet_tag); + // struct v2 sprite_size = sprite_sheet_size_meters(sprite_sheet_tag); struct v2 sprite_pivot; { @@ -219,9 +214,8 @@ INTERNAL void game_update(void) sprite_xf = xform_scale(sprite_xf, sprite_size); e->sprite_quad_xform = sprite_xf; - e->sprite_texture_tag = sprite_texture_tag; - e->sprite_sheet_tag = sprite_sheet_tag; - e->sprite_span_name = sprite_span_name; + e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase"));; + e->sprite_span_name = STR("UNARMED"); e->sprite_tint = COLOR_WHITE; entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); @@ -248,15 +242,10 @@ INTERNAL void game_update(void) e->valid = true; e->rel_xform = XFORM_TRS(.t = pos, .r = r, .s = size); - struct string sprite_name = STR("res/graphics/tim.ase"); - struct string sprite_span_name = STR("UNARMED"); - struct texture_tag sprite_texture_tag = texture_tag_from_path(sprite_name); - struct sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name); - struct v2 sprite_pos = V2(0, 0); f32 sprite_rot = 0; struct v2 sprite_size = V2(0.5f, 0.5f); - // struct v2 sprite_size = sheet_size_meters(sprite_sheet_tag); + // struct v2 sprite_size = sprite_sheet_size_meters(sprite_sheet_tag); struct v2 sprite_pivot; { @@ -271,9 +260,8 @@ INTERNAL void game_update(void) sprite_xf = xform_scale(sprite_xf, sprite_size); e->sprite_quad_xform = sprite_xf; - e->sprite_texture_tag = sprite_texture_tag; - e->sprite_sheet_tag = sprite_sheet_tag; - e->sprite_span_name = sprite_span_name; + e->sprite = sprite_tag_from_path(STR("res/graphics/tim.ase"));; + e->sprite_span_name = STR("UNARMED"); e->sprite_tint = RGBA_F(0.5, 0.5, 0, 1); //entity_enable_prop(e, ENTITY_PROP_PLAYER_CONTROLLED); @@ -318,16 +306,11 @@ INTERNAL void game_update(void) e->valid = true; e->rel_xform = XFORM_POS(V2(-3, -3)); - struct string sprite_name = STR("res/graphics/sound.ase"); - struct texture_tag sprite_texture_tag = texture_tag_from_path(sprite_name); - struct sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name); - struct v2 sprite_size = V2(0.25f, 0.25f); - // struct v2 sprite_size = sheet_size_meters(sprite_sheet_tag); + // struct v2 sprite_size = sprite_sheet_size_meters(sprite_sheet_tag); e->sprite_quad_xform = xform_with_scale(XFORM_IDENT, sprite_size); - e->sprite_texture_tag = sprite_texture_tag; - e->sprite_sheet_tag = sprite_sheet_tag; + e->sprite = sprite_tag_from_path(STR("res/graphics/sound.ase"));; e->sprite_tint = RGBA_F(1, 1, 0, 1); entity_enable_prop(e, ENTITY_PROP_TEST_SOUND_EMITTER); @@ -409,11 +392,11 @@ INTERNAL void game_update(void) f64 time_in_frame = ent->animation_time_in_frame + G.world.dt; u64 span_frame_offset = ent->animation_frame; - struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, ent->sprite_sheet_tag); - struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name); + struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, ent->sprite); + struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); u64 frame_index = span.start + span_frame_offset; - struct sheet_frame frame = sheet_get_frame(sheet, frame_index); + struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, frame_index); while (time_in_frame > frame.duration) { time_in_frame -= frame.duration; ++frame_index; @@ -421,7 +404,7 @@ INTERNAL void game_update(void) /* Loop animation */ frame_index = span.start; } - frame = sheet_get_frame(sheet, frame_index); + frame = sprite_sheet_get_frame(sheet, frame_index); } span_frame_offset = frame_index - span.start; @@ -604,7 +587,7 @@ INTERNAL void game_update(void) * End frame cache scopes * ========================== */ - sheet_scope_end(sheet_frame_scope); + sprite_scope_end(sprite_frame_scope); scratch_end(scratch); } diff --git a/src/game.h b/src/game.h index bb642afc..a4cf04cd 100644 --- a/src/game.h +++ b/src/game.h @@ -3,7 +3,7 @@ struct world; struct mixer_startup_receipt; -struct sheet_startup_receipt; +struct sprite_startup_receipt; struct sound_startup_receipt; enum game_cmd_kind { @@ -32,7 +32,7 @@ struct game_cmd_array { struct game_startup_receipt { i32 _; }; struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr, - struct sheet_startup_receipt *sheet_sr, + struct sprite_startup_receipt *sheet_sr, struct sound_startup_receipt *sound_sr); void game_get_latest_tick(struct world *dest); diff --git a/src/renderer.h b/src/renderer.h index ea89cabc..369f23e7 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -22,8 +22,8 @@ enum shader_kind { }; struct texture_shader_parameters { - struct renderer_handle texture_handle; /* Overrides texture_tag */ - struct texture_tag texture_tag; + struct renderer_handle texture_handle; /* Overrides sprite */ + struct sprite_tag sprite; }; struct texture_shader_vertex { diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index d5a57298..17e58b6d 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -7,7 +7,7 @@ #include "math.h" #include "inc.h" #include "tar.h" -#include "texture.h" +#include "sprite.h" #include #define CINTERFACE @@ -59,8 +59,8 @@ struct dx11_buffer { struct renderer_cmd { struct dx11_shader *shader; - struct renderer_handle texture_handle; /* Overrides texture_tag */ - struct texture_tag texture_tag; + struct renderer_handle texture_handle; /* Overrides sprite */ + struct sprite_tag sprite; /* Associated buffer data */ u32 vertex_count; @@ -704,13 +704,13 @@ void renderer_canvas_ensure_texture_cmd(struct renderer_canvas *canvas, struct t struct renderer_cmd *last_cmd = canvas->cpu_cmd_store.cmd_last; if (!last_cmd || last_cmd->shader->kind != SHADER_TEXTURE || !handle_eq(last_cmd->texture_handle, params.texture_handle) || - !texture_tag_eq(last_cmd->texture_tag, params.texture_tag)) { + !sprite_tag_eq(last_cmd->sprite, params.sprite)) { /* Command parameters are not the same, insert new command */ struct renderer_cmd *cmd = arena_push(&canvas->cpu_cmd_store.arena, struct renderer_cmd); *cmd = (struct renderer_cmd){ .shader = &G.shaders[SHADER_TEXTURE], .texture_handle = params.texture_handle, - .texture_tag = params.texture_tag + .sprite = params.sprite }; if (!canvas->cpu_cmd_store.cmd_first) { @@ -841,7 +841,7 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou { __prof; - struct texture_scope *texture_scope = texture_scope_begin(); + struct sprite_scope *sprite_scope = sprite_scope_begin(); /* Resize back buffer */ if (!v2_eq(G.backbuffer_size, screen_size)) { @@ -881,7 +881,7 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou struct renderer_handle texture_handle; if (handle_is_nil(cmd->texture_handle)) { - texture_handle = texture_from_tag_async(texture_scope, cmd->texture_tag)->renderer_handle; + texture_handle = sprite_texture_from_tag_async(sprite_scope, cmd->sprite)->renderer_handle; } else { texture_handle = cmd->texture_handle; } @@ -925,7 +925,7 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou } renderer_capture_image_for_profiler(viewport.width, viewport.height); - texture_scope_end(texture_scope); + sprite_scope_end(sprite_scope); } /* ========================== * diff --git a/src/sheet.c b/src/sheet.c deleted file mode 100644 index 6fd58bc4..00000000 --- a/src/sheet.c +++ /dev/null @@ -1,841 +0,0 @@ -#include "sheet.h" -#include "arena.h" -#include "log.h" -#include "sys.h" -#include "scratch.h" -#include "resource.h" -#include "ase.h" -#include "util.h" -#include "work.h" -#include "atomic.h" -#include "thread_local.h" -#include "app.h" - -#define SHEET_ARENA_RESERVE MEGABYTE(64) -#define SHEET_LOOKUP_TABLE_BUCKET_RATIO 2.0 - -#define TCTX_ARENA_RESERVE MEGABYTE(64) -#define CACHE_BUCKETS_COUNT 1024 - -#define MAX_LOADER_THREADS 4 - -/* How long between evictor thread scans */ -#define EVICTOR_CYCLE_INTERVAL (RESOURCE_RELOADING ? 0.100 : 0.500) - -/* Time a cache entry spends unused until it's considered evictable (rounded up to multiple of of EVICTOR_CYCLE_INTERVAL) */ -#define EVICTOR_GRACE_PERIOD 10.000 - -/* ========================== * - * Loader cmd structs - * ========================== */ - -struct loader_cmd { - struct loader_cmd *next; - struct loader_cmd *next_free; - - struct cache_node *cache_node; - struct sheet_tag tag; - u8 tag_path_buff[4096]; -}; - -/* ========================== * - * Cache structs - * ========================== */ - -enum cache_node_state { - CACHE_NODE_STATE_NONE, - CACHE_NODE_STATE_QUEUED, - CACHE_NODE_STATE_WORKING, - CACHE_NODE_STATE_LOADED -}; - -struct cache_node_refcount { - i32 count; /* Number of scopes currently holding a reference to this sheet */ - u32 last_modified_cycle; /* Last time that refcount was modified */ -}; -CT_ASSERT(sizeof(struct cache_node_refcount) == 8); /* Must fit into 64 bit atomic */ - -struct cache_node { - u128 hash; - struct atomic_u32 state; - struct atomic_u64 refcount_struct; /* Cast eval to `cache_node_refcount` */ - - /* Allocated data */ - u64 memory_usage; - struct arena arena; - struct sheet *sheet; - - /* Hash list */ - struct cache_node *next_hash; - struct cache_node *prev_hash; - - /* Free list */ - struct cache_node *next_free; - -#if RESOURCE_RELOADING - struct sys_datetime initial_resource_file_modified_time; - u64 tag_path_len; - u8 tag_path[4096]; -#endif -}; - -struct cache_bucket { - struct sys_rw_mutex rw_mutex; - struct cache_node *first; -}; - -struct cache { - struct atomic_u64 memory_usage; - struct arena arena; - struct cache_bucket *buckets; - struct sys_mutex node_pool_mutex; - struct cache_node *node_pool_first_free; -}; - -struct sheet_scope_reference { - struct cache_node *cache_node; - struct sheet_scope_reference *next_hash; - struct sheet_scope_reference *next_free; -}; - -/* ========================== * - * Global state - * ========================== */ - -GLOBAL struct { - /* Cache */ - struct cache cache; - - /* Loader threads */ - b32 loaders_shutdown; - struct sys_mutex loaders_mutex; - struct sys_condition_variable loaders_cv; - struct arena loader_cmd_arena; - struct loader_cmd *first_free_loader_cmd; - struct loader_cmd *first_loader_cmd; - struct loader_cmd *last_loader_cmd; - u64 loader_threads_count; - struct sys_thread loader_threads[MAX_LOADER_THREADS]; - - /* Evictor thread */ - struct atomic_u32 evictor_cycle; - b32 evictor_shutdown; - struct sys_mutex evictor_mutex; - struct sys_condition_variable evictor_cv; - - struct sys_thread evictor_thread; -} G = { 0 }, DEBUG_ALIAS(G, G_sheet); - -GLOBAL READONLY struct sheet g_sheet_nil = { 0 }; -GLOBAL READONLY struct sheet g_sheet_loading = { - .loading = true -}; - -/* ========================== * - * Thread local state - * ========================== */ - -struct sheet_tctx { - struct arena arena; - struct sheet_scope *first_free_scope; - struct sheet_scope_reference *first_free_reference; -}; - -INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(sheet_tctx_alloc, vtctx) -{ - struct sheet_tctx *tctx = (struct sheet_tctx *)vtctx; - tctx->arena = arena_alloc(MEGABYTE(64)); -} - -INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(sheet_tctx_release, vtctx) -{ - struct sheet_tctx *tctx = (struct sheet_tctx *)vtctx; - arena_release(&tctx->arena); -} - -GLOBAL THREAD_LOCAL_VAR_DEF(tl_sheet_tctx, struct sheet_tctx, sheet_tctx_alloc, sheet_tctx_release); - -/* ========================== * - * Startup - * ========================== */ - -INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sheet_shutdown); -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg); -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg); - -struct sheet_startup_receipt sheet_startup(struct resource_startup_receipt *resource_sr) -{ - (UNUSED)resource_sr; - - G.cache.node_pool_mutex = sys_mutex_alloc(); - G.cache.arena = arena_alloc(GIGABYTE(64)); - G.cache.buckets = arena_push_array_zero(&G.cache.arena, struct cache_bucket, CACHE_BUCKETS_COUNT); - for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) { - G.cache.buckets[i].rw_mutex = sys_rw_mutex_alloc(); - } - - G.loader_cmd_arena = arena_alloc(GIGABYTE(64)); - G.loaders_mutex = sys_mutex_alloc(); - G.loaders_cv = sys_condition_variable_alloc(); - - G.evictor_mutex = sys_mutex_alloc(); - G.evictor_cv = sys_condition_variable_alloc(); - - { - struct temp_arena scratch = scratch_begin_no_conflict(); - G.loader_threads_count = clamp_i64(1, MAX_LOADER_THREADS, sys_num_logical_processors() - 1); - for (u64 i = 0; i < G.loader_threads_count; ++i) { - struct string thread_name = string_format(scratch.arena, - STR("[P0] Sheet loader %F"), - FMT_UINT(i)); - G.loader_threads[i] = sys_thread_alloc(sheet_loader_thread_entry_point, NULL, thread_name); - } - scratch_end(scratch); - } - G.evictor_thread = sys_thread_alloc(sheet_evictor_thread_entry_point, NULL, STR("[P0] Sheet evictor")); - - app_register_exit_callback(&sheet_shutdown); - - return (struct sheet_startup_receipt) { 0 }; -} - -INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sheet_shutdown) -{ - __prof; - - /* Signal loaders shutdown */ - sys_mutex_lock(&G.loaders_mutex); - { - G.loaders_shutdown = true; - sys_condition_variable_broadcast(&G.loaders_cv); - } - sys_mutex_unlock(&G.loaders_mutex); - - /* Signal evictor shutdown */ - sys_mutex_lock(&G.evictor_mutex); - { - G.evictor_shutdown = true; - sys_condition_variable_broadcast(&G.evictor_cv); - } - sys_mutex_unlock(&G.evictor_mutex); - - - /* Wait on threads */ - for (u64 i = 0; i < G.loader_threads_count; ++i) { - sys_thread_wait_release(&G.loader_threads[i]); - } - sys_thread_wait_release(&G.evictor_thread); -} - -/* ========================== * - * Tag - * ========================== */ - -struct sheet_tag sheet_tag_from_path(struct string path) -{ - struct sheet_tag res = { 0 }; - res.hash = HASH_FNV128_BASIS; - res.hash = hash_fnv128(res.hash, BUFFER_FROM_STRING(path)); - res.path = path; - return res; -} - -/* ========================== * - * Refcount - * ========================== */ - -INTERNAL void node_refcount_add(struct cache_node *n, i32 amount) -{ - u32 evictor_cycle = atomic_u32_eval(&G.evictor_cycle); - struct atomic_u64 *refcount_atomic = &n->refcount_struct; - u64 old_refcount_uncast = atomic_u64_eval(refcount_atomic); - do { - struct cache_node_refcount new_refcount = *(struct cache_node_refcount *)&old_refcount_uncast; - new_refcount.count += amount; - new_refcount.last_modified_cycle = evictor_cycle; - u64 v = atomic_u64_eval_compare_exchange(refcount_atomic, old_refcount_uncast, *(u64 *)&new_refcount); - if (v != old_refcount_uncast) { - old_refcount_uncast = v; - } else { - break; - } - } while (true); -} - -/* ========================== * - * Load - * ========================== */ - -INTERNAL struct sheet init_sheet_from_ase_result(struct arena *arena, struct ase_decode_sheet_result ase) -{ - struct sheet sheet = { 0 }; - - ASSERT(ase.num_frames >= 1); - - /* Init frames */ - sheet.image_size = ase.image_size; - sheet.frame_size = ase.frame_size; - sheet.frames = arena_push_array_zero(arena, struct sheet_frame, ase.num_frames); - sheet.frames_count = ase.num_frames; - for (struct ase_frame *ase_frame = ase.frame_head; ase_frame; ase_frame = ase_frame->next) { - u32 index = ase_frame->index; - sheet.frames[index] = (struct sheet_frame) { - .index = index, - .duration = ase_frame->duration, - .clip = ase_frame->clip - }; - } - - /* Init spans */ - sheet.spans_count = ase.num_spans; - if (ase.num_spans > 0) { - sheet.spans_dict = fixed_dict_init(arena, (u64)(ase.num_spans * SHEET_LOOKUP_TABLE_BUCKET_RATIO)); - for (struct ase_span *ase_span = ase.span_head; ase_span; ase_span = ase_span->next) { - struct string name = string_copy(arena, ase_span->name); - struct sheet_span *span = arena_push(arena, struct sheet_span); - *span = (struct sheet_span) { - .name = name, - .start = ase_span->start, - .end = ase_span->end - }; - fixed_dict_set(arena, &sheet.spans_dict, name, span); - } - - } - - return sheet; -} - -INTERNAL void sheet_load(struct cache_node *n, struct sheet_tag tag) -{ - __prof; - struct temp_arena scratch = scratch_begin_no_conflict(); - - atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_WORKING); - struct string path = tag.path; - - logf_info("Loading sheet \"%F\"", FMT_STR(path)); - sys_timestamp_t start_ts = sys_timestamp(); - - ASSERT(string_ends_with(path, STR(".ase"))); - - n->arena = arena_alloc(SHEET_ARENA_RESERVE); - { - /* Decode */ - struct ase_decode_sheet_result decoded = { 0 }; - if (resource_exists(path)) { - struct resource sheet_rs = resource_open(path); - decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); -#if RESOURCE_RELOADING - n->initial_resource_file_modified_time = sys_file_get_time(sheet_rs.file).modified; -#endif - resource_close(sheet_rs); - - /* Initialize */ - n->sheet = arena_push(&n->arena, struct sheet); - *n->sheet = init_sheet_from_ase_result(&n->arena, decoded); - } else { - n->sheet = &g_sheet_nil; - logf_error("Resource \"%F\" not found", path); - } - } -#if RESOURCE_RELOADING - u64 cpy_len = min_u64(tag.path.len, ARRAY_COUNT(n->tag_path)); - n->tag_path_len = cpy_len; - MEMCPY(n->tag_path, tag.path.text, cpy_len); -#endif - arena_set_readonly(&n->arena); - n->memory_usage = n->arena.committed; - atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage); - - logf_info("Finished loading sheet \"%F\" in %F seconds (cache size: %F bytes).", - FMT_STR(path), - FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)), - FMT_UINT(n->memory_usage)); - - - atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_LOADED); - - scratch_end(scratch); -} - -/* ========================== * - * Scope - * ========================== */ - -INTERNAL void scope_ensure_reference(struct sheet_scope *scope, struct cache_node *cache_node, u64 cache_bucket_index) -{ - __prof; - struct sheet_scope_reference **ref_next = &scope->reference_buckets[cache_bucket_index]; - struct sheet_scope_reference *ref = *ref_next; - while (ref) { - if (ref->cache_node == cache_node) { - /* Scope already references node */ - break; - } else { - ref_next = &ref->next_hash; - ref = *ref_next; - } - } - - if (!ref) { - /* Increment refcount */ - node_refcount_add(cache_node, 1); - /* Add reference to scope */ - struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); - if (tctx->first_free_reference) { - ref = tctx->first_free_reference; - tctx->first_free_reference = ref->next_free; - MEMZERO_STRUCT(ref); - } else { - ref = arena_push_zero(&tctx->arena, struct sheet_scope_reference); - } - ref->cache_node = cache_node; - *ref_next = ref; - } -} - -struct sheet_scope *sheet_scope_begin(void) -{ - struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); - - struct sheet_scope *res = NULL; - if (tctx->first_free_scope) { - res = tctx->first_free_scope; - tctx->first_free_scope = res->next_free; - MEMZERO(res->reference_buckets, sizeof(*res->reference_buckets) * CACHE_BUCKETS_COUNT); - *res = (struct sheet_scope) { - .reference_buckets = res->reference_buckets - }; - } else { - res = arena_push_zero(&tctx->arena, struct sheet_scope); - res->reference_buckets = arena_push_array_zero(&tctx->arena, struct sheet_scope_reference *, CACHE_BUCKETS_COUNT); - } - - return res; -} - -void sheet_scope_end(struct sheet_scope *scope) -{ - struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx); - for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) { - struct sheet_scope_reference *ref = scope->reference_buckets[i]; - while (ref) { - /* Decrement refcount */ - node_refcount_add(ref->cache_node, -1); - /* Add reference to free list */ - ref->next_free = tctx->first_free_reference; - tctx->first_free_reference = ref; - ref = ref->next_hash; - } - } - scope->next_free = tctx->first_free_scope; - tctx->first_free_scope = scope; -} - -/* ========================== * - * Cache interface - * ========================== */ - -INTERNAL struct cache_node *node_lookup_touch(struct sheet_scope *scope, struct sheet_tag tag) -{ - __prof; - - struct cache_node *n = NULL; - struct cache_node *nonmatching = NULL; - struct cache_node **nonmatching_next = NULL; - - u64 cache_bucket_index = tag.hash % CACHE_BUCKETS_COUNT; - struct cache_bucket *bucket = &G.cache.buckets[cache_bucket_index]; - - /* Lookup */ - /* TODO: Spinlock */ - sys_rw_mutex_lock_shared(&bucket->rw_mutex); - { - nonmatching_next = &bucket->first; - n = *nonmatching_next; - while (n) { - if (n->hash == tag.hash) { - scope_ensure_reference(scope, n, cache_bucket_index); - break; - } else { - nonmatching = n; - nonmatching_next = &nonmatching->next_hash; - n = *nonmatching_next; - } - } - } - sys_rw_mutex_unlock_shared(&bucket->rw_mutex); - - /* Allocate new node if necessary */ - if (!n) { - __profscope(node_lookup_allocate); - sys_rw_mutex_lock_exclusive(&bucket->rw_mutex); - { - /* Alloc node */ - sys_mutex_lock(&G.cache.node_pool_mutex); - { - if (G.cache.node_pool_first_free) { - n = G.cache.node_pool_first_free; - G.cache.node_pool_first_free = n->next_free; - MEMZERO_STRUCT(n); - } else { - n = arena_push_zero(&G.cache.arena, struct cache_node); - } - } - sys_mutex_unlock(&G.cache.node_pool_mutex); - /* Init node and add to bucket */ - scope_ensure_reference(scope, n, cache_bucket_index); - *nonmatching_next = n; - if (nonmatching) { - nonmatching->next_hash = n; - n->prev_hash = nonmatching; - } - n->hash = tag.hash; - } - sys_rw_mutex_unlock_exclusive(&bucket->rw_mutex); - } - - return n; -} - -INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct sheet_tag tag, b32 await) -{ - struct sheet *res = &g_sheet_loading; - struct cache_node *n = node_lookup_touch(scope, tag); - - u32 state = atomic_u32_eval(&n->state); - if (state == CACHE_NODE_STATE_LOADED) { - res = n->sheet; - } else if (state == CACHE_NODE_STATE_NONE) { - if (atomic_u32_eval_compare_exchange(&n->state, CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_QUEUED) == CACHE_NODE_STATE_NONE) { - /* Node is new, load sheet */ - if (await) { - sheet_load(n, tag); - res = n->sheet; - } else { - sys_mutex_lock(&G.loaders_mutex); - { - /* Allocate cmd */ - struct loader_cmd *cmd = NULL; - if (G.first_free_loader_cmd) { - cmd = G.first_free_loader_cmd; - G.first_free_loader_cmd = cmd->next_free; - MEMZERO_STRUCT(cmd); - } else { - cmd = arena_push_zero(&G.loader_cmd_arena, struct loader_cmd); - } - - /* Initialize cmd */ - cmd->cache_node = n; - cmd->tag = tag; - { - u64 copy_len = min_u64(tag.path.len, ARRAY_COUNT(cmd->tag_path_buff)); - cmd->tag.path.text = cmd->tag_path_buff; - MEMCPY(cmd->tag.path.text, tag.path.text, copy_len); - } - - /* Add cmd to queue */ - *(G.last_loader_cmd ? &G.last_loader_cmd->next : &G.first_loader_cmd) = cmd; - G.last_loader_cmd = cmd; - - /* Cmd holds reference to node */ - node_refcount_add(n, 1); - - /* Signal work ready */ - sys_condition_variable_signal(&G.loaders_cv); - } - sys_mutex_unlock(&G.loaders_mutex); - } - - } - } - - return res; -} - -struct sheet *sheet_from_tag_await(struct sheet_scope *scope, struct sheet_tag tag) -{ - __prof; - return sheet_from_tag_internal(scope, tag, true); -} - -struct sheet *sheet_from_tag_async(struct sheet_scope *scope, struct sheet_tag tag) -{ - __prof; - return sheet_from_tag_internal(scope, tag, false); -} - -/* ========================== * - * Sheet data - * ========================== */ - -struct sheet_span sheet_get_span(struct sheet *sheet, struct string name) -{ - struct sheet_span res = { 0 }; - if (sheet->spans_count > 0) { - struct sheet_span *entry = fixed_dict_get(&sheet->spans_dict, name); - if (entry) { - res = *entry; - } - } - return res; -} - -struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index) -{ - if (sheet->frames_count > 0) { - index = min_u32(sheet->frames_count - 1, index); - return sheet->frames[index]; - } else { - return (struct sheet_frame) { - .index = 0, - .duration = 0.1, - .clip = CLIP_ALL - }; - } -} - -/* ========================== * - * Loader thread - * ========================== */ - -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg) -{ - __prof; - (UNUSED)arg; - - b32 abort = false; - while (!abort) { - sys_mutex_lock(&G.loaders_mutex); - - if (G.loaders_shutdown) { - /* Exit thread */ - abort = true; - } else if (!G.first_loader_cmd) { - /* Wait for work */ - sys_condition_variable_wait(&G.loaders_cv, &G.loaders_mutex); - - } - - while (G.first_loader_cmd && !G.loaders_shutdown) { - /* Pull cmd from queue */ - struct loader_cmd *cmd = G.first_loader_cmd; - G.first_loader_cmd = cmd->next; - if (G.last_loader_cmd == cmd) { - G.last_loader_cmd = NULL; - } - - /* Do work (temporarily unlock) */ - sys_mutex_unlock(&G.loaders_mutex); - { - sheet_load(cmd->cache_node, cmd->tag); - } - sys_mutex_lock(&G.loaders_mutex); - - /* Free cmd */ - cmd->next_free = G.first_free_loader_cmd; - G.first_free_loader_cmd = cmd; - - /* Cmd no longer references node */ - node_refcount_add(cmd->cache_node, -1); - } - - sys_mutex_unlock(&G.loaders_mutex); - } -} - -/* ========================== * - * Evictor thread - * ========================== */ - -struct evict_node { - b32 force_evict; - struct cache_node_refcount refcount; - struct cache_node *cache_node; - struct cache_bucket *cache_bucket; - struct evict_node *next_consider; - struct evict_node *next_consider_lru; - struct evict_node *next_evicted; - -}; - -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg) -{ - (UNUSED)arg; - - b32 abort = false; - while (!abort) { - struct temp_arena scratch = scratch_begin_no_conflict(); - struct evict_node *head_consider = NULL; - struct evict_node *head_consider_lru = NULL; - struct evict_node *head_evicted = NULL; - - sys_mutex_lock(&G.evictor_mutex); - { - /* Thread shutdown */ - if (G.evictor_shutdown) { - abort = true; - } else { - /* Wait */ - sys_condition_variable_wait_time(&G.evictor_cv, &G.evictor_mutex, EVICTOR_CYCLE_INTERVAL); - } - - if (!G.evictor_shutdown) { - u32 cur_cycle = *atomic_u32_raw(&G.evictor_cycle); - - /* Scan for evictable nodes */ - b32 cache_over_budget = atomic_u64_eval(&G.cache.memory_usage) > SHEET_CACHE_MEMORY_BUDGET; - if (cache_over_budget || RESOURCE_RELOADING) { - __profscope(eviction_scan); - for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) { - struct cache_bucket *bucket = &G.cache.buckets[i]; - sys_rw_mutex_lock_shared(&bucket->rw_mutex); - { - struct cache_node *n = bucket->first; - while (n) { - b32 consider_for_eviction = false; - b32 force_evict = false; - u64 refcount_uncast = atomic_u64_eval(&n->refcount_struct); - struct cache_node_refcount refcount = *(struct cache_node_refcount *)&refcount_uncast; - if (refcount.count <= 0) { -#if RESOURCE_RELOADING - /* Check if file changed for resource reloading */ - if (!consider_for_eviction) { - struct string path = string_from_cstr_len((char *)n->tag_path, n->tag_path_len); - b32 file_changed = false; - struct sys_datetime current_file_time; - { - struct sys_file file = sys_file_open_read(path); - current_file_time = sys_file_get_time(file).modified; - sys_file_close(file); - } - file_changed = MEMCMP_STRUCT(&n->initial_resource_file_modified_time, ¤t_file_time) != 0; - if (file_changed) { - logf_info("Resource file for sheet \"%F\" has changed. Evicting to allow for reloading.", FMT_STR(path)); - consider_for_eviction = true; - force_evict = true; - } - } -#endif - - /* Check usage time */ -#if RESOURCE_RELOADING - /* Only check conditional if * RESOURCE_RELOADING is enabled, - * since over-budget is assumed to be * true otherwise */ - if (cache_over_budget) -#endif - { - u32 last_used_cycle = refcount.last_modified_cycle; - f64 time_since_use = (f64)(cur_cycle - last_used_cycle) * EVICTOR_CYCLE_INTERVAL; - if (time_since_use > EVICTOR_GRACE_PERIOD) { - /* Cache is over budget and node hasn't been referenced in a while */ - consider_for_eviction = true; - } - } - - } - - /* Add node to evict list */ - if (consider_for_eviction) { - struct evict_node *evict_node = arena_push_zero(scratch.arena, struct evict_node); - evict_node->cache_node = n; - evict_node->cache_bucket = bucket; - evict_node->refcount = refcount; - evict_node->force_evict = force_evict; - evict_node->next_consider = head_consider; - head_consider = evict_node; - } - - n = n->next_hash; - } - sys_rw_mutex_unlock_shared(&bucket->rw_mutex); - } - } - } - - /* Sort evict nodes by usage time */ - if (head_consider) { - /* TODO: Optimize sort if necessary. Currently O(n^2). */ - __profscope(eviction_sort); - for (struct evict_node *en = head_consider; en; en = en->next_consider) { - u32 last_modified_cycle = en->refcount.last_modified_cycle; - struct evict_node *prev = NULL; - struct evict_node *next = head_consider_lru; - while (next && !(last_modified_cycle <= next->refcount.last_modified_cycle || en->force_evict)) { - prev = next; - next = next->next_consider_lru; - } - if (prev) { - prev->next_consider_lru = en; - } else { - head_consider_lru = en; - } - en->next_consider_lru = next; - } - } - - /* Remove evictable nodes from cache table until under budget */ - if (head_consider_lru) { - __profscope(eviction_cache_removal); - b32 stop_evicting = false; - for (struct evict_node *en = head_consider_lru; en && !stop_evicting; en = en->next_consider_lru) { - struct cache_bucket *bucket = en->cache_bucket; - struct cache_node *n = en->cache_node; - sys_rw_mutex_lock_exclusive(&bucket->rw_mutex); - { - struct cache_node_refcount refcount = *(struct cache_node_refcount *)atomic_u64_raw(&n->refcount_struct); - if (refcount.count > 0 || ((refcount.last_modified_cycle != en->refcount.last_modified_cycle) && !en->force_evict)) { - /* Cache node has been referenced since scan, skip eviction. */ - } else if (en->force_evict || atomic_u64_eval(&G.cache.memory_usage) > SHEET_CACHE_MEMORY_BUDGET) { - /* Remove from cache bucket */ - if (n->prev_hash) { - n->prev_hash->next_hash = n->next_hash; - } else { - bucket->first = n->next_hash; - } - if (n->next_hash) { - n->next_hash->prev_hash = n->prev_hash; - } - atomic_u64_eval_add(&G.cache.memory_usage, -((i64)n->memory_usage)); - /* Add to evicted list */ - en->next_evicted = head_evicted; - head_evicted = en; - } else { - /* Cache is no longer over budget or force evicting, stop iteration */ - stop_evicting = true; - } - } - sys_rw_mutex_unlock_exclusive(&bucket->rw_mutex); - } - } - - if (head_evicted) { - /* Release evicted node memory */ - { - __profscope(eviction_memory_release); - for (struct evict_node *en = head_evicted; en; en = en->next_evicted) { - struct cache_node *n = en->cache_node; - arena_release(&n->arena); - } - } - - /* Add evicted nodes to free list */ - sys_mutex_lock(&G.cache.node_pool_mutex); - { - __profscope(eviction_free_list_append); - for (struct evict_node *en = head_evicted; en; en = en->next_evicted) { - struct cache_node *n = en->cache_node; - n->next_free = G.cache.node_pool_first_free; - G.cache.node_pool_first_free = n; - } - } - sys_mutex_unlock(&G.cache.node_pool_mutex); - } - } - atomic_u32_inc_eval(&G.evictor_cycle); - } - sys_mutex_unlock(&G.evictor_mutex); - scratch_end(scratch); - } -} diff --git a/src/sheet.h b/src/sheet.h deleted file mode 100644 index 21aa03b2..00000000 --- a/src/sheet.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef SHEET_H -#define SHEET_H - -#include "util.h" - -struct sheet_frame; -struct resource_startup_receipt; - -struct sheet { - b32 loading; - struct v2 image_size; - struct v2 frame_size; - u32 frames_count; - struct sheet_frame *frames; - u32 spans_count; - struct fixed_dict spans_dict; -}; - -/* ========================== * - * Startup - * ========================== */ - -struct sheet_startup_receipt { i32 _; }; -struct sheet_startup_receipt sheet_startup(struct resource_startup_receipt *resource_sr); - -/* ========================== * - * Tag - * ========================== */ - -struct sheet_tag sheet_tag_from_path(struct string path); - -/* ========================== * - * Scope - * ========================== */ - -struct sheet_scope { - struct sheet_scope_reference **reference_buckets; - struct sheet_scope *next_free; -}; - -struct sheet_scope *sheet_scope_begin(void); -void sheet_scope_end(struct sheet_scope *scope); - -/* ========================== * - * Load - * ========================== */ - -struct sheet *sheet_from_tag_await(struct sheet_scope *scope, struct sheet_tag tag); -struct sheet *sheet_from_tag_async(struct sheet_scope *scope, struct sheet_tag tag); - -/* ========================== * - * Inspect - * ========================== */ - -struct sheet_span { - struct string name; - u32 start; - u32 end; -}; - -struct sheet_frame { - u32 index; - f64 duration; - struct clip_rect clip; -}; - -struct sheet_span sheet_get_span(struct sheet *sheet, struct string name); -struct sheet_frame sheet_get_frame(struct sheet *sheet, u32 index); - -#endif diff --git a/src/texture.c b/src/sprite.c similarity index 66% rename from src/texture.c rename to src/sprite.c index 4076c112..5a908a69 100644 --- a/src/texture.c +++ b/src/sprite.c @@ -1,4 +1,4 @@ -#include "texture.h" +#include "sprite.h" #include "arena.h" #include "log.h" #include "sys.h" @@ -12,10 +12,7 @@ #include "app.h" #include "renderer.h" -/* Arena only used to store texture struct at the moment. Actual image data is allocated on GPU. */ -#define TEXTURE_ARENA_RESERVE MEGABYTE(1) - -#define TCTX_ARENA_RESERVE MEGABYTE(64) +#define CACHE_MEMORY_BUDGET (MEGABYTE(256)) #define CACHE_BUCKETS_COUNT 1024 #define MAX_LOADER_THREADS 4 @@ -26,6 +23,14 @@ /* Time a cache entry spends unused until it's considered evictable (rounded up to multiple of of EVICTOR_CYCLE_INTERVAL) */ #define EVICTOR_GRACE_PERIOD 10.000 +#define TCTX_ARENA_RESERVE MEGABYTE(64) + +/* Texture arena only used to store texture struct at the moment. Actual image data is allocated on GPU. */ +#define TEXTURE_ARENA_RESERVE MEGABYTE(1) + +#define SHEET_ARENA_RESERVE MEGABYTE(64) +#define SHEET_SPAN_LOOKUP_TABLE_BUCKET_RATIO 2.0 + /* ========================== * * Loader cmd structs * ========================== */ @@ -35,7 +40,7 @@ struct loader_cmd { struct loader_cmd *next_free; struct cache_node *cache_node; - struct texture_tag tag; + struct sprite_tag tag; u8 tag_path_buff[512]; }; @@ -43,6 +48,11 @@ struct loader_cmd { * Cache structs * ========================== */ +enum cache_node_kind { + CACHE_NODE_KIND_TEXTURE, + CACHE_NODE_KIND_SHEET +}; + enum cache_node_state { CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_QUEUED, @@ -51,20 +61,26 @@ enum cache_node_state { }; struct cache_node_refcount { - i32 count; /* Number of scopes currently holding a reference to this texture */ + i32 count; /* Number of scopes currently holding a reference to this node */ u32 last_modified_cycle; /* Last time that refcount was modified */ }; CT_ASSERT(sizeof(struct cache_node_refcount) == 8); /* Must fit into 64 bit atomic */ +struct cache_node_hash { + u128 v; +}; + struct cache_node { - u128 hash; + enum cache_node_kind kind; + struct cache_node_hash hash; struct atomic_u32 state; struct atomic_u64 refcount_struct; /* Cast eval to `cache_node_refcount` */ /* Allocated data */ u64 memory_usage; struct arena arena; - struct texture *texture; + struct sprite_texture *texture; + struct sprite_sheet *sheet; /* Hash list */ struct cache_node *next_hash; @@ -93,10 +109,10 @@ struct cache { struct cache_node *node_pool_first_free; }; -struct texture_scope_reference { +struct sprite_scope_reference { struct cache_node *cache_node; - struct texture_scope_reference *next_hash; - struct texture_scope_reference *next_free; + struct sprite_scope_reference *next_hash; + struct sprite_scope_reference *next_free; }; /* ========================== * @@ -104,9 +120,11 @@ struct texture_scope_reference { * ========================== */ GLOBAL struct { - struct arena perm_texture_arena; - struct texture *nil_texture; - struct texture *loading_texture; + struct arena perm_arena; + struct sprite_texture *nil_texture; + struct sprite_texture *loading_texture; + struct sprite_sheet *nil_sheet; + struct sprite_sheet *loading_sheet; /* Cache */ struct cache cache; @@ -129,31 +147,31 @@ GLOBAL struct { struct sys_condition_variable evictor_cv; struct sys_thread evictor_thread; -} G = { 0 }, DEBUG_ALIAS(G, G_texture); +} G = { 0 }, DEBUG_ALIAS(G, G_sprite); /* ========================== * * Thread local state * ========================== */ -struct texture_tctx { +struct sprite_tctx { struct arena arena; - struct texture_scope *first_free_scope; - struct texture_scope_reference *first_free_reference; + struct sprite_scope *first_free_scope; + struct sprite_scope_reference *first_free_reference; }; -INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(texture_tctx_alloc, vtctx) +INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(sprite_tctx_alloc, vtctx) { - struct texture_tctx *tctx = (struct texture_tctx *)vtctx; + struct sprite_tctx *tctx = (struct sprite_tctx *)vtctx; tctx->arena = arena_alloc(MEGABYTE(64)); } -INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(texture_tctx_release, vtctx) +INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(sprite_tctx_release, vtctx) { - struct texture_tctx *tctx = (struct texture_tctx *)vtctx; + struct sprite_tctx *tctx = (struct sprite_tctx *)vtctx; arena_release(&tctx->arena); } -GLOBAL THREAD_LOCAL_VAR_DEF(tl_texture_tctx, struct texture_tctx, texture_tctx_alloc, texture_tctx_release); +GLOBAL THREAD_LOCAL_VAR_DEF(tl_sprite_tctx, struct sprite_tctx, sprite_tctx_alloc, sprite_tctx_release); /* ========================== * * Purple-black image @@ -197,35 +215,36 @@ INTERNAL struct image_rgba generate_purple_black_image(struct arena *arena, u32 * Startup * ========================== */ -INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(texture_shutdown); -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_loader_thread_entry_point, arg); -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_evictor_thread_entry_point, arg); +INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sprite_shutdown); +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_loader_thread_entry_point, arg); +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg); -struct texture_startup_receipt texture_startup(struct renderer_startup_receipt *renderer_sr, - struct resource_startup_receipt *resource_sr) +struct sprite_startup_receipt sprite_startup(struct renderer_startup_receipt *renderer_sr, + struct resource_startup_receipt *resource_sr) { (UNUSED)renderer_sr; (UNUSED)resource_sr; + G.perm_arena = arena_alloc(MEGABYTE(1)); { - G.perm_texture_arena = arena_alloc(MEGABYTE(1)); - + /* Init loading texture */ + G.loading_texture = arena_push_zero(&G.perm_arena, struct sprite_texture); /* Init nil texture */ - G.nil_texture = arena_push_zero(&G.perm_texture_arena, struct texture); + G.nil_texture = arena_push_zero(&G.perm_arena, struct sprite_texture); + G.nil_texture->loaded = true; { struct temp_arena scratch = scratch_begin_no_conflict(); struct image_rgba purple_black_image = generate_purple_black_image(scratch.arena, 64, 64); G.nil_texture->renderer_handle = renderer_texture_alloc(purple_black_image); scratch_end(scratch); } - - /* Init loading texture */ - G.loading_texture = arena_push_zero(&G.perm_texture_arena, struct texture); - G.loading_texture->loading = true; - - arena_set_readonly(&G.perm_texture_arena); + /* Init loading sheet */ + G.loading_sheet = arena_push_zero(&G.perm_arena, struct sprite_sheet); + /* Init nil sheet */ + G.nil_sheet = arena_push_zero(&G.perm_arena, struct sprite_sheet); + G.nil_sheet->loaded = true; } - + arena_set_readonly(&G.perm_arena); G.cache.node_pool_mutex = sys_mutex_alloc(); G.cache.arena = arena_alloc(GIGABYTE(64)); @@ -246,20 +265,20 @@ struct texture_startup_receipt texture_startup(struct renderer_startup_receipt * G.loader_threads_count = clamp_i64(1, MAX_LOADER_THREADS, sys_num_logical_processors() - 1); for (u64 i = 0; i < G.loader_threads_count; ++i) { struct string thread_name = string_format(scratch.arena, - STR("[P0] Texture loader %F"), + STR("[P0] Sprite loader %F"), FMT_UINT(i)); - G.loader_threads[i] = sys_thread_alloc(texture_loader_thread_entry_point, NULL, thread_name); + G.loader_threads[i] = sys_thread_alloc(sprite_loader_thread_entry_point, NULL, thread_name); } scratch_end(scratch); } - G.evictor_thread = sys_thread_alloc(texture_evictor_thread_entry_point, NULL, STR("[P0] Texture evictor")); + G.evictor_thread = sys_thread_alloc(sprite_evictor_thread_entry_point, NULL, STR("[P0] Sprite evictor")); - app_register_exit_callback(&texture_shutdown); + app_register_exit_callback(&sprite_shutdown); - return (struct texture_startup_receipt) { 0 }; + return (struct sprite_startup_receipt) { 0 }; } -INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(texture_shutdown) +INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sprite_shutdown) { __prof; @@ -291,25 +310,30 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(texture_shutdown) * Tag * ========================== */ -struct texture_tag texture_tag_from_path(struct string path) +struct sprite_tag sprite_tag_from_path(struct string path) { - struct texture_tag res = { 0 }; + struct sprite_tag res = { 0 }; res.hash = HASH_FNV128_BASIS; res.hash = hash_fnv128(res.hash, BUFFER_FROM_STRING(path)); res.path = path; return res; } -b32 texture_tag_is_nil(struct texture_tag tag) +b32 sprite_tag_is_nil(struct sprite_tag tag) { return tag.hash == 0; } -b32 texture_tag_eq(struct texture_tag t1, struct texture_tag t2) +b32 sprite_tag_eq(struct sprite_tag t1, struct sprite_tag t2) { return t1.hash == t2.hash; } +INTERNAL struct cache_node_hash cache_node_hash_from_tag_hash(u128 tag_hash, enum cache_node_kind kind) +{ + return (struct cache_node_hash) { .v = hash_fnv128(tag_hash, BUFFER(1, (u8 *)&kind)) }; +} + /* ========================== * * Refcount * ========================== */ @@ -336,7 +360,7 @@ INTERNAL void node_refcount_add(struct cache_node *n, i32 amount) * Load * ========================== */ -INTERNAL void texture_load(struct cache_node *n, struct texture_tag tag) +INTERNAL void cache_node_load_texture(struct cache_node *n, struct sprite_tag tag) { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); @@ -344,14 +368,15 @@ INTERNAL void texture_load(struct cache_node *n, struct texture_tag tag) atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_WORKING); struct string path = tag.path; - logf_info("Loading texture \"%F\"", FMT_STR(path)); + logf_info("Loading sprite texture \"%F\"", FMT_STR(path)); sys_timestamp_t start_ts = sys_timestamp(); ASSERT(string_ends_with(path, STR(".ase"))); + ASSERT(n->kind == CACHE_NODE_KIND_TEXTURE); /* TODO: Arena probably overkill. Just using it to store texture struct. */ n->arena = arena_alloc(TEXTURE_ARENA_RESERVE); - u64 texture_memory_size = 0; + u64 memory_size = 0; { /* Decode */ struct ase_decode_image_result decoded = { 0 }; @@ -364,14 +389,16 @@ INTERNAL void texture_load(struct cache_node *n, struct texture_tag tag) resource_close(texture_rs); /* Initialize */ - n->texture = arena_push(&n->arena, struct texture); - n->texture->size = V2(decoded.image.width, decoded.image.height); + n->texture = arena_push(&n->arena, struct sprite_texture); + n->texture->width = decoded.image.width; + n->texture->height = decoded.image.height; n->texture->renderer_handle = renderer_texture_alloc(decoded.image); + n->texture->valid = true; + n->texture->loaded = true; /* TODO: Query renderer for more accurate texture size in VRAM */ - texture_memory_size += (decoded.image.width * decoded.image.height) * sizeof(*decoded.image.pixels); + memory_size += (decoded.image.width * decoded.image.height) * sizeof(*decoded.image.pixels); } else { - n->texture = G.nil_texture; - logf_error("Resource \"%F\" not found", path); + logf_error("Sprite \"%F\" not found", path); } #if RESOURCE_RELOADING u64 cpy_len = min_u64(tag.path.len, ARRAY_COUNT(n->tag_path)); @@ -380,10 +407,10 @@ INTERNAL void texture_load(struct cache_node *n, struct texture_tag tag) #endif } arena_set_readonly(&n->arena); - n->memory_usage = n->arena.committed + texture_memory_size; + n->memory_usage = n->arena.committed + memory_size; atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage); - logf_info("Finished loading texture \"%F\" in %F seconds (cache size: %F bytes).", + logf_info("Finished loading sprite texture \"%F\" in %F seconds (cache size: %F bytes).", FMT_STR(path), FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)), FMT_UINT(n->memory_usage)); @@ -393,15 +420,110 @@ INTERNAL void texture_load(struct cache_node *n, struct texture_tag tag) scratch_end(scratch); } +INTERNAL struct sprite_sheet init_sheet_from_ase_result(struct arena *arena, struct ase_decode_sheet_result ase) +{ + struct sprite_sheet sheet = { 0 }; + + ASSERT(ase.num_frames >= 1); + + /* Init frames */ + sheet.image_size = ase.image_size; + sheet.frame_size = ase.frame_size; + sheet.frames = arena_push_array_zero(arena, struct sprite_sheet_frame, ase.num_frames); + sheet.frames_count = ase.num_frames; + for (struct ase_frame *ase_frame = ase.frame_head; ase_frame; ase_frame = ase_frame->next) { + u32 index = ase_frame->index; + sheet.frames[index] = (struct sprite_sheet_frame) { + .index = index, + .duration = ase_frame->duration, + .clip = ase_frame->clip + }; + } + + /* Init spans */ + sheet.spans_count = ase.num_spans; + if (ase.num_spans > 0) { + sheet.spans_dict = fixed_dict_init(arena, (u64)(ase.num_spans * SHEET_SPAN_LOOKUP_TABLE_BUCKET_RATIO)); + for (struct ase_span *ase_span = ase.span_head; ase_span; ase_span = ase_span->next) { + struct string name = string_copy(arena, ase_span->name); + struct sprite_sheet_span *span = arena_push(arena, struct sprite_sheet_span); + *span = (struct sprite_sheet_span) { + .name = name, + .start = ase_span->start, + .end = ase_span->end + }; + fixed_dict_set(arena, &sheet.spans_dict, name, span); + } + + } + + return sheet; +} + +INTERNAL void cache_node_load_sheet(struct cache_node *n, struct sprite_tag tag) +{ + __prof; + struct temp_arena scratch = scratch_begin_no_conflict(); + + atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_WORKING); + struct string path = tag.path; + + logf_info("Loading sprite sheet \"%F\"", FMT_STR(path)); + sys_timestamp_t start_ts = sys_timestamp(); + + ASSERT(string_ends_with(path, STR(".ase"))); + ASSERT(n->kind == CACHE_NODE_KIND_SHEET); + + n->arena = arena_alloc(SHEET_ARENA_RESERVE); + { + /* Decode */ + struct ase_decode_sheet_result decoded = { 0 }; + if (resource_exists(path)) { + struct resource sheet_rs = resource_open(path); + decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes); +#if RESOURCE_RELOADING + n->initial_resource_file_modified_time = sys_file_get_time(sheet_rs.file).modified; +#endif + resource_close(sheet_rs); + + /* Initialize */ + n->sheet = arena_push(&n->arena, struct sprite_sheet); + *n->sheet = init_sheet_from_ase_result(&n->arena, decoded); + n->sheet->loaded = true; + n->sheet->valid = true; + } else { + logf_error("Sprite \"%F\" not found", path); + } + } +#if RESOURCE_RELOADING + u64 cpy_len = min_u64(tag.path.len, ARRAY_COUNT(n->tag_path)); + n->tag_path_len = cpy_len; + MEMCPY(n->tag_path, tag.path.text, cpy_len); +#endif + arena_set_readonly(&n->arena); + n->memory_usage = n->arena.committed; + atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage); + + logf_info("Finished loading sprite sheet \"%F\" in %F seconds (cache size: %F bytes).", + FMT_STR(path), + FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)), + FMT_UINT(n->memory_usage)); + + + atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_LOADED); + + scratch_end(scratch); +} + /* ========================== * * Scope * ========================== */ -INTERNAL void scope_ensure_reference(struct texture_scope *scope, struct cache_node *cache_node, u64 cache_bucket_index) +INTERNAL void scope_ensure_reference(struct sprite_scope *scope, struct cache_node *cache_node, u64 cache_bucket_index) { __prof; - struct texture_scope_reference **ref_next = &scope->reference_buckets[cache_bucket_index]; - struct texture_scope_reference *ref = *ref_next; + struct sprite_scope_reference **ref_next = &scope->reference_buckets[cache_bucket_index]; + struct sprite_scope_reference *ref = *ref_next; while (ref) { if (ref->cache_node == cache_node) { /* Scope already references node */ @@ -416,44 +538,44 @@ INTERNAL void scope_ensure_reference(struct texture_scope *scope, struct cache_n /* Increment refcount */ node_refcount_add(cache_node, 1); /* Add reference to scope */ - struct texture_tctx *tctx = thread_local_var_eval(&tl_texture_tctx); + struct sprite_tctx *tctx = thread_local_var_eval(&tl_sprite_tctx); if (tctx->first_free_reference) { ref = tctx->first_free_reference; tctx->first_free_reference = ref->next_free; MEMZERO_STRUCT(ref); } else { - ref = arena_push_zero(&tctx->arena, struct texture_scope_reference); + ref = arena_push_zero(&tctx->arena, struct sprite_scope_reference); } ref->cache_node = cache_node; *ref_next = ref; } } -struct texture_scope *texture_scope_begin(void) +struct sprite_scope *sprite_scope_begin(void) { - struct texture_tctx *tctx = thread_local_var_eval(&tl_texture_tctx); + struct sprite_tctx *tctx = thread_local_var_eval(&tl_sprite_tctx); - struct texture_scope *res = NULL; + struct sprite_scope *res = NULL; if (tctx->first_free_scope) { res = tctx->first_free_scope; tctx->first_free_scope = res->next_free; MEMZERO(res->reference_buckets, sizeof(*res->reference_buckets) * CACHE_BUCKETS_COUNT); - *res = (struct texture_scope) { + *res = (struct sprite_scope) { .reference_buckets = res->reference_buckets }; } else { - res = arena_push_zero(&tctx->arena, struct texture_scope); - res->reference_buckets = arena_push_array_zero(&tctx->arena, struct texture_scope_reference *, CACHE_BUCKETS_COUNT); + res = arena_push_zero(&tctx->arena, struct sprite_scope); + res->reference_buckets = arena_push_array_zero(&tctx->arena, struct sprite_scope_reference *, CACHE_BUCKETS_COUNT); } return res; } -void texture_scope_end(struct texture_scope *scope) +void sprite_scope_end(struct sprite_scope *scope) { - struct texture_tctx *tctx = thread_local_var_eval(&tl_texture_tctx); + struct sprite_tctx *tctx = thread_local_var_eval(&tl_sprite_tctx); for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) { - struct texture_scope_reference *ref = scope->reference_buckets[i]; + struct sprite_scope_reference *ref = scope->reference_buckets[i]; while (ref) { /* Decrement refcount */ node_refcount_add(ref->cache_node, -1); @@ -471,7 +593,7 @@ void texture_scope_end(struct texture_scope *scope) * Cache interface * ========================== */ -INTERNAL struct cache_node *node_lookup_touch(struct texture_scope *scope, struct texture_tag tag) +INTERNAL struct cache_node *node_lookup_touch(struct sprite_scope *scope, struct sprite_tag tag, enum cache_node_kind kind) { __prof; @@ -479,7 +601,8 @@ INTERNAL struct cache_node *node_lookup_touch(struct texture_scope *scope, struc struct cache_node *nonmatching = NULL; struct cache_node **nonmatching_next = NULL; - u64 cache_bucket_index = tag.hash % CACHE_BUCKETS_COUNT; + struct cache_node_hash hash = cache_node_hash_from_tag_hash(tag.hash, kind); + u64 cache_bucket_index = hash.v % CACHE_BUCKETS_COUNT; struct cache_bucket *bucket = &G.cache.buckets[cache_bucket_index]; /* Lookup */ @@ -489,7 +612,7 @@ INTERNAL struct cache_node *node_lookup_touch(struct texture_scope *scope, struc nonmatching_next = &bucket->first; n = *nonmatching_next; while (n) { - if (n->hash == tag.hash) { + if (n->hash.v == hash.v) { scope_ensure_reference(scope, n, cache_bucket_index); break; } else { @@ -525,7 +648,10 @@ INTERNAL struct cache_node *node_lookup_touch(struct texture_scope *scope, struc nonmatching->next_hash = n; n->prev_hash = nonmatching; } - n->hash = tag.hash; + n->hash = cache_node_hash_from_tag_hash(tag.hash, kind); + n->kind = kind; + n->texture = G.nil_texture; + n->sheet = G.nil_sheet; } sys_rw_mutex_unlock_exclusive(&bucket->rw_mutex); } @@ -533,20 +659,37 @@ INTERNAL struct cache_node *node_lookup_touch(struct texture_scope *scope, struc return n; } -INTERNAL struct texture *texture_from_tag_internal(struct texture_scope *scope, struct texture_tag tag, b32 await) +INTERNAL void *data_from_tag_internal(struct sprite_scope *scope, struct sprite_tag tag, enum cache_node_kind kind, b32 await) { - struct texture *res = G.loading_texture; - struct cache_node *n = node_lookup_touch(scope, tag); + /* TODO: Replace switch statements */ + void *res = NULL; + switch (kind) { + case CACHE_NODE_KIND_TEXTURE: { res = G.loading_texture; } break; + case CACHE_NODE_KIND_SHEET: { res = G.loading_sheet; } break; + } + + struct cache_node *n = node_lookup_touch(scope, tag, kind); u32 state = atomic_u32_eval(&n->state); if (state == CACHE_NODE_STATE_LOADED) { - res = n->texture; + switch (kind) { + case CACHE_NODE_KIND_TEXTURE: { res = n->texture; } break; + case CACHE_NODE_KIND_SHEET: { res = n->sheet; } break; + } } else if (state == CACHE_NODE_STATE_NONE) { if (atomic_u32_eval_compare_exchange(&n->state, CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_QUEUED) == CACHE_NODE_STATE_NONE) { /* Node is new, load texture */ if (await) { - texture_load(n, tag); - res = n->texture; + switch (kind) { + case CACHE_NODE_KIND_TEXTURE: { + cache_node_load_texture(n, tag); + res = n->texture; + } break; + case CACHE_NODE_KIND_SHEET: { + cache_node_load_sheet(n, tag); + res = n->sheet; + } break; + } } else { sys_mutex_lock(&G.loaders_mutex); { @@ -588,23 +731,71 @@ INTERNAL struct texture *texture_from_tag_internal(struct texture_scope *scope, return res; } -struct texture *texture_from_tag_await(struct texture_scope *scope, struct texture_tag tag) +/* ========================== * + * Texture + * ========================== */ + +struct sprite_texture *sprite_texture_from_tag_await(struct sprite_scope *scope, struct sprite_tag tag) { __prof; - return texture_from_tag_internal(scope, tag, true); + return (struct sprite_texture *)data_from_tag_internal(scope, tag, CACHE_NODE_KIND_TEXTURE, true); } -struct texture *texture_from_tag_async(struct texture_scope *scope, struct texture_tag tag) +struct sprite_texture *sprite_texture_from_tag_async(struct sprite_scope *scope, struct sprite_tag tag) { __prof; - return texture_from_tag_internal(scope, tag, false); + return (struct sprite_texture *)data_from_tag_internal(scope, tag, CACHE_NODE_KIND_TEXTURE, false); +} + +/* ========================== * + * Sheet + * ========================== */ + +struct sprite_sheet *sprite_sheet_from_tag_await(struct sprite_scope *scope, struct sprite_tag tag) +{ + __prof; + return (struct sprite_sheet *)data_from_tag_internal(scope, tag, CACHE_NODE_KIND_SHEET, true); +} + +struct sprite_sheet *sprite_sheet_from_tag_async(struct sprite_scope *scope, struct sprite_tag tag) +{ + __prof; + return (struct sprite_sheet *)data_from_tag_internal(scope, tag, CACHE_NODE_KIND_SHEET, false); +} + +struct sprite_sheet_span sprite_sheet_get_span(struct sprite_sheet *sheet, struct string name) +{ + __prof; + struct sprite_sheet_span res = { 0 }; + if (sheet->spans_count > 0) { + struct sprite_sheet_span *entry = fixed_dict_get(&sheet->spans_dict, name); + if (entry) { + res = *entry; + } + } + return res; +} + +struct sprite_sheet_frame sprite_sheet_get_frame(struct sprite_sheet *sheet, u32 index) +{ + __prof; + if (sheet->frames_count > 0) { + index = min_u32(sheet->frames_count - 1, index); + return sheet->frames[index]; + } else { + return (struct sprite_sheet_frame) { + .index = 0, + .duration = 0.1, + .clip = CLIP_ALL + }; + } } /* ========================== * * Loader thread * ========================== */ -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_loader_thread_entry_point, arg) +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_loader_thread_entry_point, arg) { __prof; (UNUSED)arg; @@ -633,7 +824,15 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_loader_thread_entry_point, arg) /* Do work (temporarily unlock) */ sys_mutex_unlock(&G.loaders_mutex); { - texture_load(cmd->cache_node, cmd->tag); + struct cache_node *n = cmd->cache_node; + switch (n->kind) { + case CACHE_NODE_KIND_TEXTURE: { + cache_node_load_texture(n, cmd->tag); + } break; + case CACHE_NODE_KIND_SHEET: { + cache_node_load_sheet(n, cmd->tag); + } break; + } } sys_mutex_lock(&G.loaders_mutex); @@ -664,7 +863,7 @@ struct evict_node { }; -INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_evictor_thread_entry_point, arg) +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) { (UNUSED)arg; @@ -689,7 +888,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_evictor_thread_entry_point, arg u32 cur_cycle = *atomic_u32_raw(&G.evictor_cycle); /* Scan for evictable nodes */ - b32 cache_over_budget = atomic_u64_eval(&G.cache.memory_usage) > TEXTURE_CACHE_MEMORY_BUDGET; + b32 cache_over_budget = atomic_u64_eval(&G.cache.memory_usage) > CACHE_MEMORY_BUDGET; if (cache_over_budget || RESOURCE_RELOADING) { __profscope(eviction_scan); for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) { @@ -716,7 +915,14 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_evictor_thread_entry_point, arg } file_changed = MEMCMP_STRUCT(&n->initial_resource_file_modified_time, ¤t_file_time) != 0; if (file_changed) { - logf_info("Resource file for texture \"%F\" has changed. Evicting to allow for reloading.", FMT_STR(path)); + switch (n->kind) { + case CACHE_NODE_KIND_TEXTURE: { + logf_info("Resource file for sprite texture \"%F\" has changed. Evicting to allow for reloading.", FMT_STR(path)); + } break; + case CACHE_NODE_KIND_SHEET: { + logf_info("Resource file for sprite sheet \"%F\" has changed. Evicting to allow for reloading.", FMT_STR(path)); + } break; + } consider_for_eviction = true; force_evict = true; } @@ -791,7 +997,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_evictor_thread_entry_point, arg struct cache_node_refcount refcount = *(struct cache_node_refcount *)atomic_u64_raw(&n->refcount_struct); if (refcount.count > 0 || ((refcount.last_modified_cycle != en->refcount.last_modified_cycle) && !en->force_evict)) { /* Cache node has been referenced since scan, skip eviction. */ - } else if (en->force_evict || atomic_u64_eval(&G.cache.memory_usage) > TEXTURE_CACHE_MEMORY_BUDGET) { + } else if (en->force_evict || atomic_u64_eval(&G.cache.memory_usage) > CACHE_MEMORY_BUDGET) { /* Remove from cache bucket */ if (n->prev_hash) { n->prev_hash->next_hash = n->next_hash; @@ -820,7 +1026,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_evictor_thread_entry_point, arg __profscope(eviction_memory_release); for (struct evict_node *en = head_evicted; en; en = en->next_evicted) { struct cache_node *n = en->cache_node; - if (n->texture->valid) { + if (n->kind == CACHE_NODE_KIND_TEXTURE && n->texture->valid) { renderer_texture_release(n->texture->renderer_handle); } arena_release(&n->arena); diff --git a/src/sprite.h b/src/sprite.h new file mode 100644 index 00000000..c02e0d82 --- /dev/null +++ b/src/sprite.h @@ -0,0 +1,87 @@ +#ifndef SPRITE_H +#define SPRITE_H + +#include "util.h" + +struct renderer_startup_receipt; +struct resource_startup_receipt; + +/* ========================== * + * Startup + * ========================== */ + +struct sprite_startup_receipt { i32 _; }; +struct sprite_startup_receipt sprite_startup(struct renderer_startup_receipt *renderer_sr, + struct resource_startup_receipt *resource_sr); + +/* ========================== * + * Tag + * ========================== */ + +struct sprite_tag sprite_tag_from_path(struct string path); +b32 sprite_tag_is_nil(struct sprite_tag tag); +b32 sprite_tag_eq(struct sprite_tag t1, struct sprite_tag t2); + +/* ========================== * + * Scope + * ========================== */ + +struct sprite_scope { + struct sprite_scope_reference **reference_buckets; + struct sprite_scope *next_free; +}; + +struct sprite_scope *sprite_scope_begin(void); +void sprite_scope_end(struct sprite_scope *scope); + +/* ========================== * + * Texture + * ========================== */ + +struct sprite_texture { + b32 loaded; + b32 valid; + struct renderer_handle renderer_handle; + u32 width; + u32 height; +}; + +struct sprite_texture *sprite_texture_from_tag_await(struct sprite_scope *scope, struct sprite_tag tag); +struct sprite_texture *sprite_texture_from_tag_async(struct sprite_scope *scope, struct sprite_tag tag); + +/* ========================== * + * Sheet + * ========================== */ + +struct sprite_sheet { + b32 loaded; + b32 valid; + struct v2 image_size; + struct v2 frame_size; + u32 frames_count; + struct sprite_sheet_frame *frames; + u32 spans_count; + struct fixed_dict spans_dict; +}; + +struct sprite_sheet_span { + struct string name; + u32 start; + u32 end; +}; + +struct sprite_sheet_frame { + u32 index; + f64 duration; + struct clip_rect clip; +}; + +/* Load */ +struct sprite_sheet *sprite_sheet_from_tag_await(struct sprite_scope *scope, struct sprite_tag tag); +struct sprite_sheet *sprite_sheet_from_tag_async(struct sprite_scope *scope, struct sprite_tag tag); + +/* Index */ +struct sprite_sheet_span sprite_sheet_get_span(struct sprite_sheet *sheet, struct string name); +struct sprite_sheet_frame sprite_sheet_get_frame(struct sprite_sheet *sheet, u32 index); + +#endif diff --git a/src/sys_win32.c b/src/sys_win32.c index b5d6089d..57442da8 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -565,6 +565,7 @@ struct sys_file_map sys_file_map_open_read(struct sys_file file) ); if (!map_handle) { + ASSERT(false); return (struct sys_file_map) { 0 }; } @@ -584,6 +585,7 @@ struct sys_file_map sys_file_map_open_read(struct sys_file file) } } else { /* File is empty */ + ASSERT(false); return (struct sys_file_map) { 0 }; } diff --git a/src/texture.h b/src/texture.h deleted file mode 100644 index 3b82a7ee..00000000 --- a/src/texture.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef TEXTURE_H -#define TEXTURE_H - -#include "util.h" - -struct renderer_startup_receipt; -struct resource_startup_receipt; - -struct texture { - b32 loading; - b32 valid; - struct renderer_handle renderer_handle; - struct v2 size; -}; - -/* ========================== * - * Startup - * ========================== */ - -struct texture_startup_receipt { i32 _; }; -struct texture_startup_receipt texture_startup(struct renderer_startup_receipt *renderer_sr, - struct resource_startup_receipt *resource_sr); - -/* ========================== * - * Tag - * ========================== */ - -struct texture_tag texture_tag_from_path(struct string path); -b32 texture_tag_is_nil(struct texture_tag tag); -b32 texture_tag_eq(struct texture_tag t1, struct texture_tag t2); - -/* ========================== * - * Scope - * ========================== */ - -struct texture_scope { - struct texture_scope_reference **reference_buckets; - struct texture_scope *next_free; -}; - -struct texture_scope *texture_scope_begin(void); -void texture_scope_end(struct texture_scope *scope); - -/* ========================== * - * Load - * ========================== */ - -struct texture *texture_from_tag_await(struct texture_scope *scope, struct texture_tag tag); -struct texture *texture_from_tag_async(struct texture_scope *scope, struct texture_tag tag); - -#endif diff --git a/src/user.c b/src/user.c index da610749..b0e7866f 100644 --- a/src/user.c +++ b/src/user.c @@ -1,7 +1,7 @@ #include "user.h" #include "renderer.h" #include "font.h" -#include "texture.h" +#include "sprite.h" #include "draw.h" #include "intrinsics.h" #include "app.h" @@ -104,7 +104,7 @@ INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event); struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, struct renderer_startup_receipt *renderer_sr, struct font_startup_receipt *font_sr, - struct texture_startup_receipt *texture_sr, + struct sprite_startup_receipt *sprite_sr, struct draw_startup_receipt *draw_sr, struct game_startup_receipt *game_sr, struct asset_cache_startup_receipt *asset_cache_sr, @@ -114,7 +114,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, (UNUSED)work_sr; (UNUSED)renderer_sr; (UNUSED)font_sr; - (UNUSED)texture_sr; + (UNUSED)sprite_sr; (UNUSED)draw_sr; (UNUSED)game_sr; (UNUSED)asset_cache_sr; @@ -401,8 +401,7 @@ INTERNAL void user_update(void) * ========================== */ /* TODO: Remove texture scope once renderer is rewritten to work with tags */ - struct texture_scope *texture_frame_scope = texture_scope_begin(); - struct sheet_scope *sheet_frame_scope = sheet_scope_begin(); + struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); /* ========================== * * Produce interpolated tick @@ -759,23 +758,23 @@ INTERNAL void user_update(void) b32 skip_debug_draw_transform = ent == active_camera; /* Draw sprite */ - if (!texture_tag_is_nil(ent->sprite_texture_tag)) { + if (!sprite_tag_is_nil(ent->sprite)) { /* Draw texture */ - struct texture_tag texture_tag = ent->sprite_texture_tag; + struct sprite_tag sprite = ent->sprite; /* TODO: Remove this once renderer is rewritten to work with tags */ - struct texture *texture = texture_from_tag_async(texture_frame_scope, texture_tag); + struct sprite_texture *texture = sprite_texture_from_tag_async(sprite_frame_scope, sprite); struct xform xf = xform_mul(ent->world_xform, ent->sprite_quad_xform); struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf); u32 tint = ent->sprite_tint; - struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, ent->sprite_sheet_tag); - struct sheet_frame frame = { 0 }; + struct sprite_sheet *sheet = sprite_sheet_from_tag_async(sprite_frame_scope, sprite); + struct sprite_sheet_frame frame = { 0 }; { - struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name); + struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name); u64 frame_index = span.start + ent->animation_frame; - frame = sheet_get_frame(sheet, frame_index); + frame = sprite_sheet_get_frame(sheet, frame_index); if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) { f64 time_in_frame = ent->animation_time_in_frame; while (time_in_frame > frame.duration) { @@ -785,17 +784,17 @@ INTERNAL void user_update(void) /* Loop animation */ frame_index = span.start; } - frame = sheet_get_frame(sheet, frame_index); + frame = sprite_sheet_get_frame(sheet, frame_index); } } } - struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture_tag = texture_tag, .tint = tint, .clip = frame.clip); + struct draw_sprite_params params = DRAW_SPRITE_PARAMS(.sprite = sprite, .tint = tint, .clip = frame.clip); /* TODO: Check for texture loading as well */ - if (!sheet->loading && !texture->loading) { + if (sheet->loaded && texture->loaded) { /* TODO: Fade in a placeholder or something instead of drawing nothing. */ - draw_texture_quad(G.world_canvas, params, quad); + draw_sprite_quad(G.world_canvas, params, quad); } #if 0 @@ -912,13 +911,13 @@ INTERNAL void user_update(void) struct v2 crosshair_pos = G.viewport_cursor; u32 tint = RGBA_F(1, 1, 1, 1); - struct texture_tag crosshair_tag = texture_tag_from_path(STR("res/graphics/crosshair.ase")); - struct texture *t = texture_from_tag_async(texture_frame_scope, crosshair_tag); + struct sprite_tag crosshair_tag = sprite_tag_from_path(STR("res/graphics/crosshair.ase")); + struct sprite_texture *t = sprite_texture_from_tag_async(sprite_frame_scope, crosshair_tag); - struct v2 size = t->size; + struct v2 size = V2(t->width, t->height); struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size); struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf); - draw_texture_quad(G.viewport_canvas, DRAW_TEXTURE_PARAMS(.texture_tag = crosshair_tag, .tint = tint), quad); + draw_sprite_quad(G.viewport_canvas, DRAW_SPRITE_PARAMS(.sprite = crosshair_tag, .tint = tint), quad); struct rect cursor_clip = RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size); cursor_clip.pos = v2_add(cursor_clip.pos, v2_mul(size, 0.5f)); @@ -1082,8 +1081,7 @@ INTERNAL void user_update(void) * End frame cache scopes * ========================== */ - sheet_scope_end(sheet_frame_scope); - texture_scope_end(texture_frame_scope); + sprite_scope_end(sprite_frame_scope); scratch_end(scratch); } diff --git a/src/user.h b/src/user.h index fd080958..4e92a9d7 100644 --- a/src/user.h +++ b/src/user.h @@ -5,7 +5,7 @@ struct sys_window; struct work_startup_receipt; struct renderer_startup_receipt; struct font_startup_receipt; -struct texture_startup_receipt; +struct sprite_startup_receipt; struct draw_startup_receipt; struct game_startup_receipt; struct asset_cache_startup_receipt; @@ -37,7 +37,7 @@ struct user_startup_receipt { i32 _; }; struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, struct renderer_startup_receipt *renderer_sr, struct font_startup_receipt *font_sr, - struct texture_startup_receipt *texture_sr, + struct sprite_startup_receipt *sprite_sr, struct draw_startup_receipt *draw_sr, struct game_startup_receipt *game_sr, struct asset_cache_startup_receipt *asset_cache_sr,