texture cache w/ eviction & reloading
This commit is contained in:
parent
42a2d97d3d
commit
cf3d678699
BIN
res/graphics/white.ase
(Stored with Git LFS)
Normal file
BIN
res/graphics/white.ase
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -20,6 +20,7 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "math.h"
|
#include "math.h"
|
||||||
|
#include "renderer.h"
|
||||||
|
|
||||||
struct exit_callback {
|
struct exit_callback {
|
||||||
app_exit_callback_func *func;
|
app_exit_callback_func *func;
|
||||||
@ -209,8 +210,8 @@ void app_entry_point(void)
|
|||||||
struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup(&work_sr);
|
struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup(&work_sr);
|
||||||
struct ttf_startup_receipt ttf_sr = ttf_startup();
|
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 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(&work_sr, &renderer_sr, &asset_cache_sr, &resource_sr);
|
struct texture_startup_receipt texture_sr = texture_startup(&renderer_sr, &resource_sr);
|
||||||
struct sheet_startup_receipt sheet_sr = sheet_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
struct sheet_startup_receipt sheet_sr = sheet_startup(&resource_sr);
|
||||||
struct mixer_startup_receipt mixer_sr = mixer_startup();
|
struct mixer_startup_receipt mixer_sr = mixer_startup();
|
||||||
struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
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 draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr);
|
||||||
|
|||||||
@ -240,7 +240,7 @@ INTERNAL u16 huffman_decode(struct huffman *huffman, struct bitbuf *bb)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL void inflate(struct arena *arena, u8 *dest, u8 *encoded)
|
INTERNAL void inflate(u8 *dest, u8 *encoded)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct bitbuf bb = { encoded };
|
struct bitbuf bb = { encoded };
|
||||||
@ -273,7 +273,7 @@ INTERNAL void inflate(struct arena *arena, u8 *dest, u8 *encoded)
|
|||||||
|
|
||||||
case BLOCK_TYPE_COMPRESSED_FIXED:
|
case BLOCK_TYPE_COMPRESSED_FIXED:
|
||||||
case BLOCK_TYPE_COMPRESSED_DYNAMIC: {
|
case BLOCK_TYPE_COMPRESSED_DYNAMIC: {
|
||||||
struct temp_arena scratch = scratch_begin(arena);
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
u32 lit_len_dist_table[512] = { 0 };
|
u32 lit_len_dist_table[512] = { 0 };
|
||||||
u32 hlit;
|
u32 hlit;
|
||||||
u32 hdist;
|
u32 hdist;
|
||||||
@ -692,7 +692,7 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct buff
|
|||||||
cel->height = br_read_u16(&br);
|
cel->height = br_read_u16(&br);
|
||||||
|
|
||||||
cel->pixels = arena_push_array(scratch.arena, u32, cel->width * cel->height);
|
cel->pixels = arena_push_array(scratch.arena, u32, cel->width * cel->height);
|
||||||
inflate(scratch.arena, (u8 *)cel->pixels, br.at);
|
inflate((u8 *)cel->pixels, br.at);
|
||||||
|
|
||||||
br_seek_to(&br, chunk_end_pos);
|
br_seek_to(&br, chunk_end_pos);
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
30
src/common.h
30
src/common.h
@ -8,12 +8,6 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Configurable constants
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Compiler headers
|
* Compiler headers
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -368,6 +362,24 @@ struct buffer {
|
|||||||
u8 *data;
|
u8 *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct renderer_handle {
|
||||||
|
u64 v[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Tag structs
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct texture_tag {
|
||||||
|
u128 hash;
|
||||||
|
struct string path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sheet_tag {
|
||||||
|
u128 hash;
|
||||||
|
struct string path;
|
||||||
|
};
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Buffer utils
|
* Buffer utils
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -566,6 +578,12 @@ INLINE void __prof_zone_cleanup_func(TracyCZoneCtx *__tracy_ctx) { TracyCZoneEnd
|
|||||||
|
|
||||||
#endif /* PROFILING */
|
#endif /* PROFILING */
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Configurable constants
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
22
src/config.h
22
src/config.h
@ -39,6 +39,28 @@
|
|||||||
#define USER_INTERP_OFFSET_TICK_RATIO 1.1
|
#define USER_INTERP_OFFSET_TICK_RATIO 1.1
|
||||||
#define USER_INTERP_ENABLED 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
|
* Settings
|
||||||
|
|||||||
45
src/draw.c
45
src/draw.c
@ -3,9 +3,10 @@
|
|||||||
#include "math.h"
|
#include "math.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
|
#include "texture.h"
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
struct renderer_handle solid_white;
|
struct texture_tag solid_white;
|
||||||
} G = { 0 }, DEBUG_ALIAS(G, G_draw);
|
} G = { 0 }, DEBUG_ALIAS(G, G_draw);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -17,20 +18,7 @@ struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *render
|
|||||||
{
|
{
|
||||||
(UNUSED)renderer_sr;
|
(UNUSED)renderer_sr;
|
||||||
(UNUSED)font_sr;
|
(UNUSED)font_sr;
|
||||||
|
G.solid_white = texture_tag_from_path(STR("res/graphics/white.ase"));
|
||||||
/* Generate solid white texture */
|
|
||||||
{
|
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
||||||
struct image_rgba image_data = {
|
|
||||||
.width = 1,
|
|
||||||
.height = 1,
|
|
||||||
.pixels = arena_push(scratch.arena, u32)
|
|
||||||
};
|
|
||||||
image_data.pixels[0] = COLOR_WHITE;
|
|
||||||
G.solid_white = renderer_texture_alloc(image_data);
|
|
||||||
scratch_end(scratch);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (struct draw_startup_receipt) { 0 };
|
return (struct draw_startup_receipt) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +70,7 @@ INTERNAL void draw_texture_quad_internal(struct renderer_canvas *canvas, struct
|
|||||||
|
|
||||||
void draw_texture_quad(struct renderer_canvas *canvas, struct draw_texture_params params, struct quad quad)
|
void draw_texture_quad(struct renderer_canvas *canvas, struct draw_texture_params params, struct quad quad)
|
||||||
{
|
{
|
||||||
ASSERT(params.texture);
|
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) { .texture = params.texture->renderer_handle });
|
|
||||||
|
|
||||||
draw_texture_quad_internal(canvas, params.clip, params.tint, quad);
|
draw_texture_quad_internal(canvas, params.clip, params.tint, quad);
|
||||||
}
|
}
|
||||||
@ -130,7 +117,7 @@ void draw_solid_poly(struct renderer_canvas *canvas, struct v2_array array, u32
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture = G.solid_white });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white });
|
||||||
draw_solid_poly_internal(canvas, array, color);
|
draw_solid_poly_internal(canvas, array, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,13 +145,13 @@ 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)
|
void draw_solid_quad(struct renderer_canvas *canvas, struct quad quad, u32 color)
|
||||||
{
|
{
|
||||||
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture = G.solid_white });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white });
|
||||||
draw_texture_quad_internal(canvas, CLIP_ALL, color, quad);
|
draw_texture_quad_internal(canvas, CLIP_ALL, color, quad);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_solid_rect(struct renderer_canvas *canvas, struct rect rect, u32 color)
|
void draw_solid_rect(struct renderer_canvas *canvas, struct rect rect, u32 color)
|
||||||
{
|
{
|
||||||
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture = G.solid_white });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white });
|
||||||
struct quad quad = quad_from_rect(rect);
|
struct quad quad = quad_from_rect(rect);
|
||||||
draw_texture_quad_internal(canvas, CLIP_ALL, color, quad);
|
draw_texture_quad_internal(canvas, CLIP_ALL, color, quad);
|
||||||
}
|
}
|
||||||
@ -175,14 +162,14 @@ 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)
|
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 = G.solid_white });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white });
|
||||||
struct quad quad = quad_from_line(start, end, thickness);
|
struct quad quad = quad_from_line(start, end, thickness);
|
||||||
draw_texture_quad_internal(canvas, CLIP_ALL, color, quad);
|
draw_texture_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)
|
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 = G.solid_white });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white });
|
||||||
struct quad quad = quad_from_ray(pos, rel, thickness);
|
struct quad quad = quad_from_ray(pos, rel, thickness);
|
||||||
draw_texture_quad_internal(canvas, CLIP_ALL, color, quad);
|
draw_texture_quad_internal(canvas, CLIP_ALL, color, quad);
|
||||||
}
|
}
|
||||||
@ -193,7 +180,7 @@ void draw_solid_poly_line(struct renderer_canvas *canvas, struct v2_array array,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture = G.solid_white });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white });
|
||||||
for (u64 i = 1; i < array.count; ++i) {
|
for (u64 i = 1; i < array.count; ++i) {
|
||||||
struct v2 p1 = array.points[i - 1];
|
struct v2 p1 = array.points[i - 1];
|
||||||
struct v2 p2 = array.points[i];
|
struct v2 p2 = array.points[i];
|
||||||
@ -223,7 +210,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)
|
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 = G.solid_white });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_tag = G.solid_white });
|
||||||
|
|
||||||
const f32 head_width_ratio = 0.5f; /* Width of arrowhead relative to its length */
|
const f32 head_width_ratio = 0.5f; /* Width of arrowhead relative to its length */
|
||||||
|
|
||||||
@ -270,7 +257,7 @@ void draw_text(struct renderer_canvas *canvas, struct font *font, struct v2 pos,
|
|||||||
|
|
||||||
void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 pos, f32 scale, struct string str)
|
void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 pos, f32 scale, struct string str)
|
||||||
{
|
{
|
||||||
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture = font->texture.renderer_handle });
|
renderer_canvas_ensure_texture_cmd(canvas, (struct texture_shader_parameters) { .texture_handle = font->image_renderer_handle });
|
||||||
|
|
||||||
struct v2 draw_pos = pos;
|
struct v2 draw_pos = pos;
|
||||||
draw_pos.y += font->point_size * scale;
|
draw_pos.y += font->point_size * scale;
|
||||||
@ -294,13 +281,13 @@ void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 p
|
|||||||
|
|
||||||
struct clip_rect clip = {
|
struct clip_rect clip = {
|
||||||
{
|
{
|
||||||
glyph->atlas_rect.x / font->texture.size.x,
|
glyph->atlas_rect.x / font->image_size.x,
|
||||||
glyph->atlas_rect.y / font->texture.size.y
|
glyph->atlas_rect.y / font->image_size.y
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
(glyph->atlas_rect.x + glyph->atlas_rect.width) / font->texture.size.x,
|
(glyph->atlas_rect.x + glyph->atlas_rect.width) / font->image_size.x,
|
||||||
(glyph->atlas_rect.y + glyph->atlas_rect.height) / font->texture.size.y
|
(glyph->atlas_rect.y + glyph->atlas_rect.height) / font->image_size.y
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,8 +11,9 @@ struct font_startup_receipt;
|
|||||||
.clip = CLIP_ALL, \
|
.clip = CLIP_ALL, \
|
||||||
__VA_ARGS__ \
|
__VA_ARGS__ \
|
||||||
})
|
})
|
||||||
|
|
||||||
struct draw_texture_params {
|
struct draw_texture_params {
|
||||||
struct texture *texture;
|
struct texture_tag texture_tag;
|
||||||
struct clip_rect clip;
|
struct clip_rect clip;
|
||||||
u32 tint;
|
u32 tint;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef ENTITY_H
|
#ifndef ENTITY_H
|
||||||
#define ENTITY_H
|
#define ENTITY_H
|
||||||
|
|
||||||
#include "mixer.h"
|
#include "texture.h"
|
||||||
#include "sheet.h"
|
#include "sheet.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ struct entity {
|
|||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
/* Sprite */
|
/* Sprite */
|
||||||
|
|
||||||
struct string sprite_name;
|
struct texture_tag sprite_texture_tag;
|
||||||
struct sheet_tag sprite_sheet_tag;
|
struct sheet_tag sprite_sheet_tag;
|
||||||
struct string sprite_span_name;
|
struct string sprite_span_name;
|
||||||
struct xform sprite_quad_xform;
|
struct xform sprite_quad_xform;
|
||||||
|
|||||||
@ -121,7 +121,7 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
|
|||||||
resource_close(res);
|
resource_close(res);
|
||||||
|
|
||||||
/* Send texture to GPU */
|
/* Send texture to GPU */
|
||||||
struct renderer_handle texture_renderer_handle = renderer_texture_alloc(result.image_data);
|
struct renderer_handle image_renderer_handle = renderer_texture_alloc(result.image_data);
|
||||||
|
|
||||||
/* Allocate store memory */
|
/* Allocate store memory */
|
||||||
struct font *font = NULL;
|
struct font *font = NULL;
|
||||||
@ -134,10 +134,8 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Set font data */
|
/* Set font data */
|
||||||
font->texture = (struct texture) {
|
font->image_renderer_handle = image_renderer_handle;
|
||||||
.renderer_handle = texture_renderer_handle,
|
font->image_size = V2(result.image_data.width, result.image_data.height);
|
||||||
.size = V2(result.image_data.width, result.image_data.height),
|
|
||||||
};
|
|
||||||
font->glyphs_count = result.glyphs_count;
|
font->glyphs_count = result.glyphs_count;
|
||||||
font->point_size = point_size;
|
font->point_size = point_size;
|
||||||
|
|
||||||
|
|||||||
@ -21,8 +21,9 @@ struct font_glyph {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct font {
|
struct font {
|
||||||
|
struct renderer_handle image_renderer_handle;
|
||||||
|
struct v2 image_size;
|
||||||
f32 point_size;
|
f32 point_size;
|
||||||
struct texture texture;
|
|
||||||
u16 glyphs_count;
|
u16 glyphs_count;
|
||||||
struct font_glyph *glyphs;
|
struct font_glyph *glyphs;
|
||||||
u16 *lookup;
|
u16 *lookup;
|
||||||
|
|||||||
66
src/game.c
66
src/game.c
@ -152,6 +152,21 @@ INTERNAL void recalculate_world_xform_recurse(struct entity *parent)
|
|||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
INTERNAL struct v2 sheet_size_meters(struct sheet_tag s)
|
||||||
|
{
|
||||||
|
struct v2 size = { 0 };
|
||||||
|
struct sheet_scope *scope = sheet_scope_begin();
|
||||||
|
{
|
||||||
|
struct sheet *sheet = 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);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
INTERNAL void game_update(void)
|
INTERNAL void game_update(void)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
@ -183,22 +198,13 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
struct string sprite_name = STR("res/graphics/tim.ase");
|
struct string sprite_name = STR("res/graphics/tim.ase");
|
||||||
struct string sprite_span_name = STR("UNARMED");
|
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 sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name);
|
||||||
|
|
||||||
f32 meters_width, meters_height;
|
|
||||||
{
|
|
||||||
struct sheet_scope *scope = sheet_scope_begin();
|
|
||||||
{
|
|
||||||
struct sheet *sheet = sheet_from_tag_await(scope, sprite_sheet_tag);
|
|
||||||
meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT;
|
|
||||||
meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT;
|
|
||||||
}
|
|
||||||
sheet_scope_end(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct v2 sprite_pos = V2(0, 0);
|
struct v2 sprite_pos = V2(0, 0);
|
||||||
f32 sprite_rot = 0;
|
f32 sprite_rot = 0;
|
||||||
struct v2 sprite_size = V2(meters_width, meters_height);
|
struct v2 sprite_size = V2(0.5f, 0.5f);
|
||||||
|
// struct v2 sprite_size = sheet_size_meters(sprite_sheet_tag);
|
||||||
|
|
||||||
struct v2 sprite_pivot;
|
struct v2 sprite_pivot;
|
||||||
{
|
{
|
||||||
@ -213,7 +219,7 @@ INTERNAL void game_update(void)
|
|||||||
sprite_xf = xform_scale(sprite_xf, sprite_size);
|
sprite_xf = xform_scale(sprite_xf, sprite_size);
|
||||||
e->sprite_quad_xform = sprite_xf;
|
e->sprite_quad_xform = sprite_xf;
|
||||||
|
|
||||||
e->sprite_name = sprite_name;
|
e->sprite_texture_tag = sprite_texture_tag;
|
||||||
e->sprite_sheet_tag = sprite_sheet_tag;
|
e->sprite_sheet_tag = sprite_sheet_tag;
|
||||||
e->sprite_span_name = sprite_span_name;
|
e->sprite_span_name = sprite_span_name;
|
||||||
e->sprite_tint = COLOR_WHITE;
|
e->sprite_tint = COLOR_WHITE;
|
||||||
@ -244,22 +250,13 @@ INTERNAL void game_update(void)
|
|||||||
|
|
||||||
struct string sprite_name = STR("res/graphics/tim.ase");
|
struct string sprite_name = STR("res/graphics/tim.ase");
|
||||||
struct string sprite_span_name = STR("UNARMED");
|
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 sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name);
|
||||||
|
|
||||||
f32 meters_width, meters_height;
|
|
||||||
{
|
|
||||||
struct sheet_scope *scope = sheet_scope_begin();
|
|
||||||
{
|
|
||||||
struct sheet *sheet = sheet_from_tag_await(scope, sprite_sheet_tag);
|
|
||||||
meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT;
|
|
||||||
meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT;
|
|
||||||
}
|
|
||||||
sheet_scope_end(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct v2 sprite_pos = V2(0, 0);
|
struct v2 sprite_pos = V2(0, 0);
|
||||||
f32 sprite_rot = 0;
|
f32 sprite_rot = 0;
|
||||||
struct v2 sprite_size = V2(meters_width, meters_height);
|
struct v2 sprite_size = V2(0.5f, 0.5f);
|
||||||
|
// struct v2 sprite_size = sheet_size_meters(sprite_sheet_tag);
|
||||||
|
|
||||||
struct v2 sprite_pivot;
|
struct v2 sprite_pivot;
|
||||||
{
|
{
|
||||||
@ -274,7 +271,7 @@ INTERNAL void game_update(void)
|
|||||||
sprite_xf = xform_scale(sprite_xf, sprite_size);
|
sprite_xf = xform_scale(sprite_xf, sprite_size);
|
||||||
e->sprite_quad_xform = sprite_xf;
|
e->sprite_quad_xform = sprite_xf;
|
||||||
|
|
||||||
e->sprite_name = sprite_name;
|
e->sprite_texture_tag = sprite_texture_tag;
|
||||||
e->sprite_sheet_tag = sprite_sheet_tag;
|
e->sprite_sheet_tag = sprite_sheet_tag;
|
||||||
e->sprite_span_name = sprite_span_name;
|
e->sprite_span_name = sprite_span_name;
|
||||||
e->sprite_tint = RGBA_F(0.5, 0.5, 0, 1);
|
e->sprite_tint = RGBA_F(0.5, 0.5, 0, 1);
|
||||||
@ -322,23 +319,14 @@ INTERNAL void game_update(void)
|
|||||||
e->rel_xform = XFORM_POS(V2(-3, -3));
|
e->rel_xform = XFORM_POS(V2(-3, -3));
|
||||||
|
|
||||||
struct string sprite_name = STR("res/graphics/sound.ase");
|
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 sheet_tag sprite_sheet_tag = sheet_tag_from_path(sprite_name);
|
||||||
|
|
||||||
f32 meters_width, meters_height;
|
struct v2 sprite_size = V2(0.25f, 0.25f);
|
||||||
{
|
// struct v2 sprite_size = sheet_size_meters(sprite_sheet_tag);
|
||||||
struct sheet_scope *scope = sheet_scope_begin();
|
|
||||||
{
|
|
||||||
struct sheet *sheet = sheet_from_tag_await(scope, sprite_sheet_tag);
|
|
||||||
meters_width = sheet->frame_size.x / (f32)PIXELS_PER_UNIT;
|
|
||||||
meters_height = sheet->frame_size.y / (f32)PIXELS_PER_UNIT;
|
|
||||||
}
|
|
||||||
sheet_scope_end(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct v2 sprite_size = V2(meters_width, meters_height);
|
|
||||||
e->sprite_quad_xform = xform_with_scale(XFORM_IDENT, sprite_size);
|
e->sprite_quad_xform = xform_with_scale(XFORM_IDENT, sprite_size);
|
||||||
|
|
||||||
e->sprite_name = sprite_name;
|
e->sprite_texture_tag = sprite_texture_tag;
|
||||||
e->sprite_sheet_tag = sprite_sheet_tag;
|
e->sprite_sheet_tag = sprite_sheet_tag;
|
||||||
e->sprite_tint = RGBA_F(1, 1, 0, 1);
|
e->sprite_tint = RGBA_F(1, 1, 0, 1);
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#define RENDERER_H
|
#define RENDERER_H
|
||||||
|
|
||||||
struct sys_window;
|
struct sys_window;
|
||||||
struct texture;
|
|
||||||
|
|
||||||
#define RENDERER_TEXTURE_MAX_WIDTH 16384
|
#define RENDERER_TEXTURE_MAX_WIDTH 16384
|
||||||
#define RENDERER_TEXTURE_MAX_HEIGHT 16384
|
#define RENDERER_TEXTURE_MAX_HEIGHT 16384
|
||||||
@ -11,10 +10,6 @@ typedef u32 vidx;
|
|||||||
|
|
||||||
struct renderer_canvas;
|
struct renderer_canvas;
|
||||||
|
|
||||||
struct renderer_handle {
|
|
||||||
u64 v[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Shaders
|
* Shaders
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -27,7 +22,8 @@ enum shader_kind {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct texture_shader_parameters {
|
struct texture_shader_parameters {
|
||||||
struct renderer_handle texture;
|
struct renderer_handle texture_handle; /* Overrides texture_tag */
|
||||||
|
struct texture_tag texture_tag;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct texture_shader_vertex {
|
struct texture_shader_vertex {
|
||||||
@ -80,7 +76,7 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct renderer_handle renderer_texture_alloc(struct image_rgba data);
|
struct renderer_handle renderer_texture_alloc(struct image_rgba data);
|
||||||
|
|
||||||
void renderer_texture_release(struct renderer_handle handle);
|
void renderer_texture_release(struct renderer_handle handle);
|
||||||
|
b32 renderer_texture_is_nil(struct renderer_handle handle);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "math.h"
|
#include "math.h"
|
||||||
#include "inc.h"
|
#include "inc.h"
|
||||||
#include "tar.h"
|
#include "tar.h"
|
||||||
|
#include "texture.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#define CINTERFACE
|
#define CINTERFACE
|
||||||
@ -57,7 +58,9 @@ struct dx11_buffer {
|
|||||||
|
|
||||||
struct renderer_cmd {
|
struct renderer_cmd {
|
||||||
struct dx11_shader *shader;
|
struct dx11_shader *shader;
|
||||||
struct renderer_handle texture;
|
|
||||||
|
struct renderer_handle texture_handle; /* Overrides texture_tag */
|
||||||
|
struct texture_tag texture_tag;
|
||||||
|
|
||||||
/* Associated buffer data */
|
/* Associated buffer data */
|
||||||
u32 vertex_count;
|
u32 vertex_count;
|
||||||
@ -275,9 +278,15 @@ INTERNAL void *handle_data(struct renderer_handle handle)
|
|||||||
|
|
||||||
INTERNAL b32 handle_eq(struct renderer_handle h1, struct renderer_handle h2)
|
INTERNAL b32 handle_eq(struct renderer_handle h1, struct renderer_handle h2)
|
||||||
{
|
{
|
||||||
|
CT_ASSERT(sizeof(struct renderer_handle) == 8);
|
||||||
return h1.v[0] == h2.v[0];
|
return h1.v[0] == h2.v[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INTERNAL b32 handle_is_nil(struct renderer_handle h)
|
||||||
|
{
|
||||||
|
return h.v[0] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Shader
|
* Shader
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -693,12 +702,15 @@ u32 renderer_canvas_push_vertices(struct renderer_canvas *canvas, u8 **vertices_
|
|||||||
void renderer_canvas_ensure_texture_cmd(struct renderer_canvas *canvas, struct texture_shader_parameters params)
|
void renderer_canvas_ensure_texture_cmd(struct renderer_canvas *canvas, struct texture_shader_parameters params)
|
||||||
{
|
{
|
||||||
struct renderer_cmd *last_cmd = canvas->cpu_cmd_store.cmd_last;
|
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, params.texture)) {
|
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)) {
|
||||||
/* Command parameters are not the same, insert new command */
|
/* Command parameters are not the same, insert new command */
|
||||||
struct renderer_cmd *cmd = arena_push(&canvas->cpu_cmd_store.arena, struct renderer_cmd);
|
struct renderer_cmd *cmd = arena_push(&canvas->cpu_cmd_store.arena, struct renderer_cmd);
|
||||||
*cmd = (struct renderer_cmd){
|
*cmd = (struct renderer_cmd){
|
||||||
.shader = &G.shaders[SHADER_TEXTURE],
|
.shader = &G.shaders[SHADER_TEXTURE],
|
||||||
.texture = params.texture
|
.texture_handle = params.texture_handle,
|
||||||
|
.texture_tag = params.texture_tag
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!canvas->cpu_cmd_store.cmd_first) {
|
if (!canvas->cpu_cmd_store.cmd_first) {
|
||||||
@ -829,6 +841,8 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
|
|||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
|
|
||||||
|
struct texture_scope *texture_scope = texture_scope_begin();
|
||||||
|
|
||||||
/* Resize back buffer */
|
/* Resize back buffer */
|
||||||
if (!v2_eq(G.backbuffer_size, screen_size)) {
|
if (!v2_eq(G.backbuffer_size, screen_size)) {
|
||||||
resize_backbuffer(screen_size);
|
resize_backbuffer(screen_size);
|
||||||
@ -864,7 +878,13 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
|
|||||||
for (struct renderer_cmd *cmd = canvas->gpu_cmd_store.cmd_first; cmd; cmd = cmd->next) {
|
for (struct renderer_cmd *cmd = canvas->gpu_cmd_store.cmd_first; cmd; cmd = cmd->next) {
|
||||||
struct dx11_shader *shader = cmd->shader;
|
struct dx11_shader *shader = cmd->shader;
|
||||||
struct dx11_buffer *buffer = &canvas->buffers[shader->kind];
|
struct dx11_buffer *buffer = &canvas->buffers[shader->kind];
|
||||||
struct renderer_handle texture_handle = cmd->texture;
|
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
texture_handle = cmd->texture_handle;
|
||||||
|
}
|
||||||
|
|
||||||
/* Activate shader */
|
/* Activate shader */
|
||||||
if (shader != last_shader) {
|
if (shader != last_shader) {
|
||||||
@ -904,6 +924,8 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
|
|||||||
__profframe(0);
|
__profframe(0);
|
||||||
}
|
}
|
||||||
renderer_capture_image_for_profiler(viewport.width, viewport.height);
|
renderer_capture_image_for_profiler(viewport.width, viewport.height);
|
||||||
|
|
||||||
|
texture_scope_end(texture_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -964,6 +986,11 @@ void renderer_texture_release(struct renderer_handle handle)
|
|||||||
handle_release(handle);
|
handle_release(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b32 renderer_texture_is_nil(struct renderer_handle handle)
|
||||||
|
{
|
||||||
|
return handle_is_nil(handle);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Profiling frame capture
|
* Profiling frame capture
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -1054,9 +1081,11 @@ INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height)
|
INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height)
|
||||||
{
|
{
|
||||||
(UNUSED)width;
|
(UNUSED)width;
|
||||||
(UNUSED)height;
|
(UNUSED)height;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -19,7 +19,6 @@ struct resource_startup_receipt resource_startup(void)
|
|||||||
{
|
{
|
||||||
#if RESOURCES_EMBEDDED
|
#if RESOURCES_EMBEDDED
|
||||||
struct buffer embedded_data = inc_res_tar();
|
struct buffer embedded_data = inc_res_tar();
|
||||||
//struct buffer embedded_data = ((struct buffer) { (u8 *)(_incbin_res_tar_end) - (u8 *)(_incbin_res_tar_start), (u8 *)_incbin_res_tar_start });;
|
|
||||||
G.arena = arena_alloc(GIGABYTE(64));
|
G.arena = arena_alloc(GIGABYTE(64));
|
||||||
if (embedded_data.size <= 0) {
|
if (embedded_data.size <= 0) {
|
||||||
sys_panic(STR("No embedded resources found"));
|
sys_panic(STR("No embedded resources found"));
|
||||||
@ -55,7 +54,7 @@ struct resource resource_open(struct string path)
|
|||||||
.bytes = entry ? entry->buff : BUFFER(0, 0)
|
.bytes = entry ? entry->buff : BUFFER(0, 0)
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
struct sys_file file = sys_file_open_read(path);
|
struct sys_file file = sys_file_open_read_wait(path);
|
||||||
struct sys_file_map file_map = sys_file_map_open_read(file);
|
struct sys_file_map file_map = sys_file_map_open_read(file);
|
||||||
struct buffer bytes = sys_file_map_data(file_map);
|
struct buffer bytes = sys_file_map_data(file_map);
|
||||||
return (struct resource) {
|
return (struct resource) {
|
||||||
|
|||||||
190
src/sheet.c
190
src/sheet.c
@ -4,14 +4,12 @@
|
|||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "asset_cache.h"
|
|
||||||
#include "ase.h"
|
#include "ase.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "work.h"
|
#include "work.h"
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "thread_local.h"
|
#include "thread_local.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "intrinsics.h"
|
|
||||||
|
|
||||||
#define SHEET_ARENA_RESERVE MEGABYTE(64)
|
#define SHEET_ARENA_RESERVE MEGABYTE(64)
|
||||||
#define SHEET_LOOKUP_TABLE_BUCKET_RATIO 2.0
|
#define SHEET_LOOKUP_TABLE_BUCKET_RATIO 2.0
|
||||||
@ -21,13 +19,10 @@
|
|||||||
|
|
||||||
#define MAX_LOADER_THREADS 4
|
#define MAX_LOADER_THREADS 4
|
||||||
|
|
||||||
/* Size of cache memory until evictor starts evicting */
|
|
||||||
#define CACHE_MEMORY_BUDGET MEGABYTE(8)
|
|
||||||
|
|
||||||
/* How long between evictor thread scans */
|
/* How long between evictor thread scans */
|
||||||
#define EVICTOR_CYCLE_INTERVAL 0.500
|
#define EVICTOR_CYCLE_INTERVAL (RESOURCE_RELOADING ? 0.100 : 0.500)
|
||||||
|
|
||||||
/* Time a cache entry spends unused until it's considered evictable (at granularity of EVICTOR_CYCLE_INTERVAL) */
|
/* 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 EVICTOR_GRACE_PERIOD 10.000
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -168,12 +163,8 @@ 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_loader_thread_entry_point, arg);
|
||||||
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg);
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg);
|
||||||
|
|
||||||
struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr,
|
struct sheet_startup_receipt sheet_startup(struct resource_startup_receipt *resource_sr)
|
||||||
struct asset_cache_startup_receipt *asset_cache_sr,
|
|
||||||
struct resource_startup_receipt *resource_sr)
|
|
||||||
{
|
{
|
||||||
(UNUSED)work_sr;
|
|
||||||
(UNUSED)asset_cache_sr;
|
|
||||||
(UNUSED)resource_sr;
|
(UNUSED)resource_sr;
|
||||||
|
|
||||||
G.cache.node_pool_mutex = sys_mutex_alloc();
|
G.cache.node_pool_mutex = sys_mutex_alloc();
|
||||||
@ -201,7 +192,7 @@ struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr,
|
|||||||
}
|
}
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
G.evictor_thread = sys_thread_alloc(sheet_evictor_thread_entry_point, NULL, STR("[P0] Sheet evictor thread"));
|
G.evictor_thread = sys_thread_alloc(sheet_evictor_thread_entry_point, NULL, STR("[P0] Sheet evictor"));
|
||||||
|
|
||||||
app_register_exit_callback(&sheet_shutdown);
|
app_register_exit_callback(&sheet_shutdown);
|
||||||
|
|
||||||
@ -249,6 +240,28 @@ struct sheet_tag sheet_tag_from_path(struct string path)
|
|||||||
return res;
|
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
|
* Load
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -315,9 +328,6 @@ INTERNAL void sheet_load(struct cache_node *n, struct sheet_tag tag)
|
|||||||
decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes);
|
decoded = ase_decode_sheet(scratch.arena, sheet_rs.bytes);
|
||||||
#if RESOURCE_RELOADING
|
#if RESOURCE_RELOADING
|
||||||
n->initial_resource_file_modified_time = sys_file_get_time(sheet_rs.file).modified;
|
n->initial_resource_file_modified_time = sys_file_get_time(sheet_rs.file).modified;
|
||||||
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
|
#endif
|
||||||
resource_close(sheet_rs);
|
resource_close(sheet_rs);
|
||||||
|
|
||||||
@ -329,14 +339,20 @@ INTERNAL void sheet_load(struct cache_node *n, struct sheet_tag tag)
|
|||||||
logf_error("Resource \"%F\" not found", path);
|
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);
|
arena_set_readonly(&n->arena);
|
||||||
n->memory_usage = n->arena.committed;
|
n->memory_usage = n->arena.committed;
|
||||||
atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage);
|
atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage);
|
||||||
|
|
||||||
logf_info("Finished loading sheet \"%F\" in %F seconds (final size: %F bytes).",
|
logf_info("Finished loading sheet \"%F\" in %F seconds (cache size: %F bytes).",
|
||||||
FMT_STR(path),
|
FMT_STR(path),
|
||||||
FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)),
|
FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)),
|
||||||
FMT_UINT(n->arena.pos));
|
FMT_UINT(n->memory_usage));
|
||||||
|
|
||||||
|
|
||||||
atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_LOADED);
|
atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_LOADED);
|
||||||
|
|
||||||
@ -364,19 +380,7 @@ INTERNAL void scope_ensure_reference(struct sheet_scope *scope, struct cache_nod
|
|||||||
|
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
/* Increment refcount */
|
/* Increment refcount */
|
||||||
u32 evictor_cycle = atomic_u32_eval(&G.evictor_cycle);
|
node_refcount_add(cache_node, 1);
|
||||||
u64 old_refcount_uncast = atomic_u64_eval(&cache_node->refcount_struct);
|
|
||||||
do {
|
|
||||||
struct cache_node_refcount new_refcount = *(struct cache_node_refcount *)&old_refcount_uncast;
|
|
||||||
new_refcount.count += 1;
|
|
||||||
new_refcount.last_modified_cycle = evictor_cycle;
|
|
||||||
u64 v = atomic_u64_eval_compare_exchange(&cache_node->refcount_struct, old_refcount_uncast, *(u64 *)&new_refcount);
|
|
||||||
if (v != old_refcount_uncast) {
|
|
||||||
old_refcount_uncast = v;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
/* Add reference to scope */
|
/* Add reference to scope */
|
||||||
struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx);
|
struct sheet_tctx *tctx = thread_local_var_eval(&tl_sheet_tctx);
|
||||||
if (tctx->first_free_reference) {
|
if (tctx->first_free_reference) {
|
||||||
@ -418,20 +422,7 @@ void sheet_scope_end(struct sheet_scope *scope)
|
|||||||
struct sheet_scope_reference *ref = scope->reference_buckets[i];
|
struct sheet_scope_reference *ref = scope->reference_buckets[i];
|
||||||
while (ref) {
|
while (ref) {
|
||||||
/* Decrement refcount */
|
/* Decrement refcount */
|
||||||
struct cache_node *cache_node = ref->cache_node;
|
node_refcount_add(ref->cache_node, -1);
|
||||||
u32 evictor_cycle = atomic_u32_eval(&G.evictor_cycle);
|
|
||||||
u64 old_refcount_uncast = atomic_u64_eval(&cache_node->refcount_struct);
|
|
||||||
do {
|
|
||||||
struct cache_node_refcount new_refcount = *(struct cache_node_refcount *)&old_refcount_uncast;
|
|
||||||
new_refcount.count -= 1;
|
|
||||||
new_refcount.last_modified_cycle = evictor_cycle;
|
|
||||||
u64 v = atomic_u64_eval_compare_exchange(&cache_node->refcount_struct, old_refcount_uncast, *(u64 *)&new_refcount);
|
|
||||||
if (v != old_refcount_uncast) {
|
|
||||||
old_refcount_uncast = v;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
/* Add reference to free list */
|
/* Add reference to free list */
|
||||||
ref->next_free = tctx->first_free_reference;
|
ref->next_free = tctx->first_free_reference;
|
||||||
tctx->first_free_reference = ref;
|
tctx->first_free_reference = ref;
|
||||||
@ -530,8 +521,9 @@ INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct
|
|||||||
if (G.first_free_loader_cmd) {
|
if (G.first_free_loader_cmd) {
|
||||||
cmd = G.first_free_loader_cmd;
|
cmd = G.first_free_loader_cmd;
|
||||||
G.first_free_loader_cmd = cmd->next_free;
|
G.first_free_loader_cmd = cmd->next_free;
|
||||||
|
MEMZERO_STRUCT(cmd);
|
||||||
} else {
|
} else {
|
||||||
cmd = arena_push(&G.loader_cmd_arena, struct loader_cmd);
|
cmd = arena_push_zero(&G.loader_cmd_arena, struct loader_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize cmd */
|
/* Initialize cmd */
|
||||||
@ -547,6 +539,9 @@ INTERNAL struct sheet *sheet_from_tag_internal(struct sheet_scope *scope, struct
|
|||||||
*(G.last_loader_cmd ? &G.last_loader_cmd->next : &G.first_loader_cmd) = cmd;
|
*(G.last_loader_cmd ? &G.last_loader_cmd->next : &G.first_loader_cmd) = cmd;
|
||||||
G.last_loader_cmd = cmd;
|
G.last_loader_cmd = cmd;
|
||||||
|
|
||||||
|
/* Cmd holds reference to node */
|
||||||
|
node_refcount_add(n, 1);
|
||||||
|
|
||||||
/* Signal work ready */
|
/* Signal work ready */
|
||||||
sys_condition_variable_signal(&G.loaders_cv);
|
sys_condition_variable_signal(&G.loaders_cv);
|
||||||
}
|
}
|
||||||
@ -641,6 +636,9 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_loader_thread_entry_point, arg)
|
|||||||
/* Free cmd */
|
/* Free cmd */
|
||||||
cmd->next_free = G.first_free_loader_cmd;
|
cmd->next_free = G.first_free_loader_cmd;
|
||||||
G.first_free_loader_cmd = 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);
|
sys_mutex_unlock(&G.loaders_mutex);
|
||||||
@ -684,11 +682,10 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!G.evictor_shutdown) {
|
if (!G.evictor_shutdown) {
|
||||||
sys_timestamp_t cur_timestamp = sys_timestamp();
|
u32 cur_cycle = *atomic_u32_raw(&G.evictor_cycle);
|
||||||
f64 cur_time = sys_timestamp_seconds(cur_timestamp);
|
|
||||||
|
|
||||||
/* Scan for evictable nodes */
|
/* Scan for evictable nodes */
|
||||||
b32 cache_over_budget = atomic_u64_eval(&G.cache.memory_usage) > CACHE_MEMORY_BUDGET;
|
b32 cache_over_budget = atomic_u64_eval(&G.cache.memory_usage) > SHEET_CACHE_MEMORY_BUDGET;
|
||||||
if (cache_over_budget || RESOURCE_RELOADING) {
|
if (cache_over_budget || RESOURCE_RELOADING) {
|
||||||
__profscope(eviction_scan);
|
__profscope(eviction_scan);
|
||||||
for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) {
|
for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) {
|
||||||
@ -699,61 +696,58 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg)
|
|||||||
while (n) {
|
while (n) {
|
||||||
b32 consider_for_eviction = false;
|
b32 consider_for_eviction = false;
|
||||||
b32 force_evict = false;
|
b32 force_evict = false;
|
||||||
if (atomic_u32_eval(&n->state) == CACHE_NODE_STATE_LOADED) {
|
u64 refcount_uncast = atomic_u64_eval(&n->refcount_struct);
|
||||||
u64 refcount_uncast = atomic_u64_eval(&n->refcount_struct);
|
struct cache_node_refcount refcount = *(struct cache_node_refcount *)&refcount_uncast;
|
||||||
struct cache_node_refcount refcount = *(struct cache_node_refcount *)&refcount_uncast;
|
if (refcount.count <= 0) {
|
||||||
if (refcount.count <= 0) {
|
|
||||||
#if RESOURCE_RELOADING
|
#if RESOURCE_RELOADING
|
||||||
/* Check if file changed for resource reloading */
|
/* Check if file changed for resource reloading */
|
||||||
if (!consider_for_eviction) {
|
if (!consider_for_eviction) {
|
||||||
struct string path = string_from_cstr_len((char *)n->tag_path, n->tag_path_len);
|
struct string path = string_from_cstr_len((char *)n->tag_path, n->tag_path_len);
|
||||||
b32 file_changed = false;
|
b32 file_changed = false;
|
||||||
if (!sys_is_file(path)) {
|
struct sys_datetime current_file_time;
|
||||||
file_changed = true;
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
if (cache_over_budget) /* Only check conditional if RESOURCE_RELOADING, since over-budget is assumed to be true otherwise */
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
f64 last_used_time = (f64)refcount.last_modified_cycle * EVICTOR_CYCLE_INTERVAL;
|
struct sys_file file = sys_file_open_read(path);
|
||||||
if (cur_time - last_used_time > EVICTOR_GRACE_PERIOD) {
|
current_file_time = sys_file_get_time(file).modified;
|
||||||
/* Cache is over budget and node hasn't been referenced in a while */
|
sys_file_close(file);
|
||||||
consider_for_eviction = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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);
|
sys_rw_mutex_unlock_shared(&bucket->rw_mutex);
|
||||||
}
|
}
|
||||||
@ -793,7 +787,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sheet_evictor_thread_entry_point, arg)
|
|||||||
struct cache_node_refcount refcount = *(struct cache_node_refcount *)atomic_u64_raw(&n->refcount_struct);
|
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)) {
|
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. */
|
/* Cache node has been referenced since scan, skip eviction. */
|
||||||
} else if (en->force_evict || atomic_u64_eval(&G.cache.memory_usage) > CACHE_MEMORY_BUDGET) {
|
} else if (en->force_evict || atomic_u64_eval(&G.cache.memory_usage) > SHEET_CACHE_MEMORY_BUDGET) {
|
||||||
/* Remove from cache bucket */
|
/* Remove from cache bucket */
|
||||||
if (n->prev_hash) {
|
if (n->prev_hash) {
|
||||||
n->prev_hash->next_hash = n->next_hash;
|
n->prev_hash->next_hash = n->next_hash;
|
||||||
|
|||||||
12
src/sheet.h
12
src/sheet.h
@ -3,17 +3,9 @@
|
|||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
struct asset;
|
|
||||||
struct sheet_frame;
|
struct sheet_frame;
|
||||||
struct work_startup_receipt;
|
|
||||||
struct asset_cache_startup_receipt;
|
|
||||||
struct resource_startup_receipt;
|
struct resource_startup_receipt;
|
||||||
|
|
||||||
struct sheet_tag {
|
|
||||||
u128 hash;
|
|
||||||
struct string path;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sheet {
|
struct sheet {
|
||||||
b32 loading;
|
b32 loading;
|
||||||
struct v2 image_size;
|
struct v2 image_size;
|
||||||
@ -29,9 +21,7 @@ struct sheet {
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct sheet_startup_receipt { i32 _; };
|
struct sheet_startup_receipt { i32 _; };
|
||||||
struct sheet_startup_receipt sheet_startup(struct work_startup_receipt *work_sr,
|
struct sheet_startup_receipt sheet_startup(struct resource_startup_receipt *resource_sr);
|
||||||
struct asset_cache_startup_receipt *asset_cache_srm,
|
|
||||||
struct resource_startup_receipt *resource_sr);
|
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Tag
|
* Tag
|
||||||
|
|||||||
@ -215,6 +215,7 @@ b32 sys_is_dir(struct string path);
|
|||||||
void sys_mkdir(struct string path);
|
void sys_mkdir(struct string path);
|
||||||
|
|
||||||
struct sys_file sys_file_open_read(struct string path);
|
struct sys_file sys_file_open_read(struct string path);
|
||||||
|
struct sys_file sys_file_open_read_wait(struct string path); /* Waits until file is not being used by another program */
|
||||||
struct sys_file sys_file_open_write(struct string path);
|
struct sys_file sys_file_open_write(struct string path);
|
||||||
struct sys_file sys_file_open_append(struct string path);
|
struct sys_file sys_file_open_append(struct string path);
|
||||||
void sys_file_close(struct sys_file file);
|
void sys_file_close(struct sys_file file);
|
||||||
|
|||||||
@ -363,9 +363,8 @@ struct sys_file sys_file_open_read(struct string path)
|
|||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
|
|
||||||
|
|
||||||
/* TODO: Handle errors / non-existing file (handle == INVALID_HANDLE_VALUE) */
|
wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
|
||||||
HANDLE handle = CreateFileW(
|
HANDLE handle = CreateFileW(
|
||||||
path_wstr,
|
path_wstr,
|
||||||
GENERIC_READ,
|
GENERIC_READ,
|
||||||
@ -375,16 +374,43 @@ struct sys_file sys_file_open_read(struct string path)
|
|||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
struct sys_file file = (struct sys_file) { (u64)handle };
|
struct sys_file file = (struct sys_file) { (u64)handle };
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sys_file sys_file_open_read_wait(struct string path)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
|
wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
|
||||||
|
|
||||||
|
i32 delay_ms = 1;
|
||||||
|
HANDLE handle;
|
||||||
|
while ((handle = CreateFileW(path_wstr, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
|
||||||
|
if (GetLastError() == ERROR_SHARING_VIOLATION) {
|
||||||
|
__profscope(file_share_conflict_delay);
|
||||||
|
Sleep(delay_ms);
|
||||||
|
if (delay_ms < 1024) {
|
||||||
|
delay_ms *= 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
return (struct sys_file) { (u64)handle };
|
||||||
|
}
|
||||||
|
|
||||||
struct sys_file sys_file_open_write(struct string path)
|
struct sys_file sys_file_open_write(struct string path)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
|
wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
|
||||||
|
|
||||||
|
/* TODO: Handle errors / non-existing file (handle == INVALID_HANDLE_VALUE) */
|
||||||
HANDLE handle = CreateFileW(
|
HANDLE handle = CreateFileW(
|
||||||
path_wstr,
|
path_wstr,
|
||||||
GENERIC_WRITE,
|
GENERIC_WRITE,
|
||||||
@ -394,10 +420,9 @@ struct sys_file sys_file_open_write(struct string path)
|
|||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
/* TODO: Handle errors / non-existing file (handle == INVALID_HANDLE_VALUE) */
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
struct sys_file file = (struct sys_file) { (u64)handle };
|
return (struct sys_file) { (u64)handle };
|
||||||
return file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sys_file sys_file_open_append(struct string path)
|
struct sys_file sys_file_open_append(struct string path)
|
||||||
@ -405,6 +430,7 @@ struct sys_file sys_file_open_append(struct string path)
|
|||||||
__prof;
|
__prof;
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
|
wchar_t *path_wstr = wstr_from_string(scratch.arena, path);
|
||||||
|
/* TODO: Handle errors / non-existing file (handle == INVALID_HANDLE_VALUE) */
|
||||||
HANDLE handle = CreateFileW(
|
HANDLE handle = CreateFileW(
|
||||||
path_wstr,
|
path_wstr,
|
||||||
FILE_APPEND_DATA,
|
FILE_APPEND_DATA,
|
||||||
@ -414,7 +440,6 @@ struct sys_file sys_file_open_append(struct string path)
|
|||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
/* TODO: Handle errors / non-existing file (handle == INVALID_HANDLE_VALUE) */
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
struct sys_file file = (struct sys_file) { (u64)handle };
|
struct sys_file file = (struct sys_file) { (u64)handle };
|
||||||
return file;
|
return file;
|
||||||
@ -450,6 +475,7 @@ struct buffer sys_file_read_all(struct arena *arena, struct sys_file file)
|
|||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buff;
|
return buff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,26 +517,29 @@ struct sys_file_time sys_file_get_time(struct sys_file file)
|
|||||||
FILETIME ft_created;
|
FILETIME ft_created;
|
||||||
FILETIME ft_accessed;
|
FILETIME ft_accessed;
|
||||||
FILETIME ft_modified;
|
FILETIME ft_modified;
|
||||||
GetFileTime((HANDLE)file.handle, &ft_created, &ft_accessed, &ft_modified);
|
b32 success = !!GetFileTime((HANDLE)file.handle, &ft_created, &ft_accessed, &ft_modified);
|
||||||
|
if (success) {
|
||||||
|
/* Convert file times to local file time */
|
||||||
|
FileTimeToLocalFileTime(&ft_created, &ft_created);
|
||||||
|
FileTimeToLocalFileTime(&ft_accessed, &ft_accessed);
|
||||||
|
FileTimeToLocalFileTime(&ft_modified, &ft_modified);
|
||||||
|
|
||||||
/* Convert file times to local file time */
|
/* Convert local file times to system times */
|
||||||
FileTimeToLocalFileTime(&ft_created, &ft_created);
|
SYSTEMTIME st_created;
|
||||||
FileTimeToLocalFileTime(&ft_accessed, &ft_accessed);
|
SYSTEMTIME st_accessed;
|
||||||
FileTimeToLocalFileTime(&ft_modified, &ft_modified);
|
SYSTEMTIME st_modified;
|
||||||
|
FileTimeToSystemTime(&ft_created, &st_created);
|
||||||
|
FileTimeToSystemTime(&ft_accessed, &st_accessed);
|
||||||
|
FileTimeToSystemTime(&ft_modified, &st_modified);
|
||||||
|
|
||||||
/* Convert local file times to system times */
|
return (struct sys_file_time) {
|
||||||
SYSTEMTIME st_created;
|
.created = win32_time_to_sys_time(st_created),
|
||||||
SYSTEMTIME st_accessed;
|
.accessed = win32_time_to_sys_time(st_accessed),
|
||||||
SYSTEMTIME st_modified;
|
.modified = win32_time_to_sys_time(st_modified)
|
||||||
FileTimeToSystemTime(&ft_created, &st_created);
|
};
|
||||||
FileTimeToSystemTime(&ft_accessed, &st_accessed);
|
} else {
|
||||||
FileTimeToSystemTime(&ft_modified, &st_modified);
|
return (struct sys_file_time) { 0 };
|
||||||
|
}
|
||||||
return (struct sys_file_time) {
|
|
||||||
.created = win32_time_to_sys_time(st_created),
|
|
||||||
.accessed = win32_time_to_sys_time(st_accessed),
|
|
||||||
.modified = win32_time_to_sys_time(st_modified)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -536,7 +565,6 @@ struct sys_file_map sys_file_map_open_read(struct sys_file file)
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!map_handle) {
|
if (!map_handle) {
|
||||||
ASSERT(false);
|
|
||||||
return (struct sys_file_map) { 0 };
|
return (struct sys_file_map) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1839,6 +1867,7 @@ u32 sys_rand_u32(void)
|
|||||||
|
|
||||||
void sys_panic(struct string msg)
|
void sys_panic(struct string msg)
|
||||||
{
|
{
|
||||||
|
ASSERT(false);
|
||||||
if (atomic_i32_eval_compare_exchange(&G.panicking, 0, 1) == 0) {
|
if (atomic_i32_eval_compare_exchange(&G.panicking, 0, 1) == 0) {
|
||||||
log_panic(msg);
|
log_panic(msg);
|
||||||
|
|
||||||
|
|||||||
907
src/texture.c
907
src/texture.c
@ -1,29 +1,102 @@
|
|||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
#include "memory.h"
|
#include "log.h"
|
||||||
#include "renderer.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "asset_cache.h"
|
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "ase.h"
|
#include "ase.h"
|
||||||
#include "log.h"
|
#include "util.h"
|
||||||
|
#include "work.h"
|
||||||
|
#include "atomic.h"
|
||||||
|
#include "thread_local.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "renderer.h"
|
||||||
|
|
||||||
#define LOOKUP_TABLE_CAPACITY_FACTOR 2.0f
|
/* Arena only used to store texture struct at the moment. Actual image data is allocated on GPU. */
|
||||||
|
#define TEXTURE_ARENA_RESERVE MEGABYTE(1)
|
||||||
|
|
||||||
struct texture_task_params {
|
#define TCTX_ARENA_RESERVE MEGABYTE(64)
|
||||||
struct texture_task_params *next_free;
|
#define CACHE_BUCKETS_COUNT 1024
|
||||||
|
|
||||||
struct asset *asset;
|
#define MAX_LOADER_THREADS 4
|
||||||
u64 path_len;
|
|
||||||
char path_cstr[1024];
|
/* 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 texture_tag tag;
|
||||||
|
u8 tag_path_buff[512];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct texture_task_params_store {
|
/* ========================== *
|
||||||
struct texture_task_params *head_free;
|
* 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 texture */
|
||||||
|
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 arena arena;
|
||||||
struct sys_mutex mutex;
|
struct texture *texture;
|
||||||
|
|
||||||
|
/* 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 texture_scope_reference {
|
||||||
|
struct cache_node *cache_node;
|
||||||
|
struct texture_scope_reference *next_hash;
|
||||||
|
struct texture_scope_reference *next_free;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -31,67 +104,61 @@ struct texture_task_params_store {
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
struct texture_task_params_store params;
|
struct arena perm_texture_arena;
|
||||||
|
struct texture *nil_texture;
|
||||||
|
struct texture *loading_texture;
|
||||||
|
|
||||||
|
/* 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_texture);
|
} G = { 0 }, DEBUG_ALIAS(G, G_texture);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Startup
|
* Thread local state
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct texture_startup_receipt texture_startup(struct work_startup_receipt *work_sr,
|
struct texture_tctx {
|
||||||
struct renderer_startup_receipt *renderer_sr,
|
struct arena arena;
|
||||||
struct asset_cache_startup_receipt *asset_cache_sr,
|
struct texture_scope *first_free_scope;
|
||||||
struct resource_startup_receipt *resource_sr)
|
struct texture_scope_reference *first_free_reference;
|
||||||
|
};
|
||||||
|
|
||||||
|
INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(texture_tctx_alloc, vtctx)
|
||||||
{
|
{
|
||||||
(UNUSED)work_sr;
|
struct texture_tctx *tctx = (struct texture_tctx *)vtctx;
|
||||||
(UNUSED)renderer_sr;
|
tctx->arena = arena_alloc(MEGABYTE(64));
|
||||||
(UNUSED)asset_cache_sr;
|
|
||||||
(UNUSED)resource_sr;
|
|
||||||
|
|
||||||
G.params.arena = arena_alloc(GIGABYTE(64));
|
|
||||||
G.params.mutex = sys_mutex_alloc();
|
|
||||||
|
|
||||||
return (struct texture_startup_receipt) { 0 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(texture_tctx_release, vtctx)
|
||||||
|
{
|
||||||
|
struct texture_tctx *tctx = (struct texture_tctx *)vtctx;
|
||||||
|
arena_release(&tctx->arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLOBAL THREAD_LOCAL_VAR_DEF(tl_texture_tctx, struct texture_tctx, texture_tctx_alloc, texture_tctx_release);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Load task param store
|
* Purple-black image
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL struct texture_task_params *texture_task_params_alloc(void)
|
|
||||||
{
|
|
||||||
struct texture_task_params *p = NULL;
|
|
||||||
{
|
|
||||||
sys_mutex_lock(&G.params.mutex);
|
|
||||||
{
|
|
||||||
if (G.params.head_free) {
|
|
||||||
p = G.params.head_free;
|
|
||||||
G.params.head_free = p->next_free;
|
|
||||||
} else {
|
|
||||||
p = arena_push_zero(&G.params.arena, struct texture_task_params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sys_mutex_unlock(&G.params.mutex);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
INTERNAL void texture_task_params_release(struct texture_task_params *p)
|
|
||||||
{
|
|
||||||
sys_mutex_lock(&G.params.mutex);
|
|
||||||
{
|
|
||||||
p->next_free = G.params.head_free;
|
|
||||||
G.params.head_free = p;
|
|
||||||
}
|
|
||||||
sys_mutex_unlock(&G.params.mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================== *
|
|
||||||
* Default texture load
|
|
||||||
* ========================== */
|
|
||||||
|
|
||||||
#define DEFAULT_TEXTURE_NAME "__texture_default"
|
|
||||||
|
|
||||||
INTERNAL struct image_rgba generate_purple_black_image(struct arena *arena, u32 width, u32 height)
|
INTERNAL struct image_rgba generate_purple_black_image(struct arena *arena, u32 width, u32 height)
|
||||||
{
|
{
|
||||||
u32 *pixels = arena_push_array(arena, u32, width * height);
|
u32 *pixels = arena_push_array(arena, u32, width * height);
|
||||||
@ -126,160 +193,656 @@ 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);
|
||||||
|
|
||||||
|
struct texture_startup_receipt texture_startup(struct renderer_startup_receipt *renderer_sr,
|
||||||
|
struct resource_startup_receipt *resource_sr)
|
||||||
|
{
|
||||||
|
(UNUSED)renderer_sr;
|
||||||
|
(UNUSED)resource_sr;
|
||||||
|
|
||||||
|
{
|
||||||
|
G.perm_texture_arena = arena_alloc(MEGABYTE(1));
|
||||||
|
|
||||||
|
/* Init nil texture */
|
||||||
|
G.nil_texture = arena_push_zero(&G.perm_texture_arena, struct texture);
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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] Texture loader %F"),
|
||||||
|
FMT_UINT(i));
|
||||||
|
G.loader_threads[i] = sys_thread_alloc(texture_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"));
|
||||||
|
|
||||||
|
app_register_exit_callback(&texture_shutdown);
|
||||||
|
|
||||||
|
return (struct texture_startup_receipt) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(texture_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 texture_tag texture_tag_from_path(struct string path)
|
||||||
|
{
|
||||||
|
struct texture_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)
|
||||||
|
{
|
||||||
|
return tag.hash == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 texture_tag_eq(struct texture_tag t1, struct texture_tag t2)
|
||||||
|
{
|
||||||
|
return t1.hash == t2.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* 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
|
* Load
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL WORK_TASK_FUNC_DEF(texture_load_asset_task, vparams)
|
INTERNAL void texture_load(struct cache_node *n, struct texture_tag tag)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct texture_task_params *params = (struct texture_task_params *)vparams;
|
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
struct string path = string_from_cstr_len(params->path_cstr, params->path_len);
|
|
||||||
struct asset *asset = params->asset;
|
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 texture \"%F\"", FMT_STR(path));
|
||||||
sys_timestamp_t start_ts = sys_timestamp();
|
sys_timestamp_t start_ts = sys_timestamp();
|
||||||
|
|
||||||
b32 success = false;
|
|
||||||
struct string error_msg = STR("Unknown error");
|
|
||||||
|
|
||||||
ASSERT(string_ends_with(path, STR(".ase")));
|
ASSERT(string_ends_with(path, STR(".ase")));
|
||||||
if (resource_exists(path)) {
|
|
||||||
|
/* TODO: Arena probably overkill. Just using it to store texture struct. */
|
||||||
|
n->arena = arena_alloc(TEXTURE_ARENA_RESERVE);
|
||||||
|
u64 texture_memory_size = 0;
|
||||||
|
{
|
||||||
/* Decode */
|
/* Decode */
|
||||||
struct resource texture_rs = resource_open(path);
|
struct ase_decode_image_result decoded = { 0 };
|
||||||
struct ase_decode_image_result decoded = ase_decode_image(scratch.arena, texture_rs.bytes);
|
if (resource_exists(path)) {
|
||||||
resource_close(texture_rs);
|
struct resource texture_rs = resource_open(path);
|
||||||
|
decoded = ase_decode_image(scratch.arena, texture_rs.bytes);
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
n->initial_resource_file_modified_time = sys_file_get_time(texture_rs.file).modified;
|
||||||
|
#endif
|
||||||
|
resource_close(texture_rs);
|
||||||
|
|
||||||
/* Failure paths */
|
/* Initialize */
|
||||||
if (decoded.errors.count > 0) {
|
n->texture = arena_push(&n->arena, struct texture);
|
||||||
/* FIXME: Read all errors from decode */
|
n->texture->size = V2(decoded.image.width, decoded.image.height);
|
||||||
struct string msg = decoded.errors.first->msg;
|
n->texture->renderer_handle = renderer_texture_alloc(decoded.image);
|
||||||
if (msg.len > 0) {
|
/* TODO: Query renderer for more accurate texture size in VRAM */
|
||||||
error_msg = msg;
|
texture_memory_size += (decoded.image.width * decoded.image.height) * sizeof(*decoded.image.pixels);
|
||||||
}
|
|
||||||
goto abort;
|
|
||||||
} else if (decoded.image.width > RENDERER_TEXTURE_MAX_WIDTH || decoded.image.height > RENDERER_TEXTURE_MAX_HEIGHT) {
|
|
||||||
error_msg = string_format(scratch.arena,
|
|
||||||
STR("Image with dimensions %F x %F exceeds maximum allowed dimensions (%F x %F)"),
|
|
||||||
FMT_UINT(decoded.image.width),
|
|
||||||
FMT_UINT(decoded.image.height),
|
|
||||||
FMT_UINT(RENDERER_TEXTURE_MAX_WIDTH),
|
|
||||||
FMT_UINT(RENDERER_TEXTURE_MAX_HEIGHT));
|
|
||||||
goto abort;
|
|
||||||
} else {
|
} else {
|
||||||
success = true;
|
n->texture = G.nil_texture;
|
||||||
|
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 + texture_memory_size;
|
||||||
|
atomic_u64_eval_add(&G.cache.memory_usage, n->memory_usage);
|
||||||
|
|
||||||
struct image_rgba image_data = decoded.image;
|
logf_info("Finished loading 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));
|
||||||
|
|
||||||
/* Create renderer texture */
|
atomic_u32_eval_exchange(&n->state, CACHE_NODE_STATE_LOADED);
|
||||||
struct renderer_handle handle = renderer_texture_alloc(image_data);
|
|
||||||
|
|
||||||
/* Initialize texture & its data into store */
|
scratch_end(scratch);
|
||||||
struct texture *texture = NULL;
|
}
|
||||||
{
|
|
||||||
struct asset_cache_store store = asset_cache_store_open();
|
/* ========================== *
|
||||||
texture = arena_push(store.arena, struct texture);
|
* Scope
|
||||||
*texture = (struct texture) {
|
* ========================== */
|
||||||
.size = V2(image_data.width, image_data.height),
|
|
||||||
.renderer_handle = handle
|
INTERNAL void scope_ensure_reference(struct texture_scope *scope, struct cache_node *cache_node, u64 cache_bucket_index)
|
||||||
};
|
{
|
||||||
asset_cache_store_close(&store);
|
__prof;
|
||||||
|
struct texture_scope_reference **ref_next = &scope->reference_buckets[cache_bucket_index];
|
||||||
|
struct texture_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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logf_info("Finished loading texture \"%F\" in %F seconds",
|
if (!ref) {
|
||||||
FMT_STR(path),
|
/* Increment refcount */
|
||||||
FMT_FLOAT(sys_timestamp_seconds(sys_timestamp() - start_ts)));
|
node_refcount_add(cache_node, 1);
|
||||||
|
/* Add reference to scope */
|
||||||
|
struct texture_tctx *tctx = thread_local_var_eval(&tl_texture_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->cache_node = cache_node;
|
||||||
|
*ref_next = ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
asset_cache_mark_ready(asset, texture);
|
struct texture_scope *texture_scope_begin(void)
|
||||||
|
{
|
||||||
|
struct texture_tctx *tctx = thread_local_var_eval(&tl_texture_tctx);
|
||||||
|
|
||||||
|
struct texture_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) {
|
||||||
|
.reference_buckets = res->reference_buckets
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
success = false;
|
res = arena_push_zero(&tctx->arena, struct texture_scope);
|
||||||
error_msg = STR("Resource not found");
|
res->reference_buckets = arena_push_array_zero(&tctx->arena, struct texture_scope_reference *, CACHE_BUCKETS_COUNT);
|
||||||
goto abort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abort:
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
if (!success) {
|
void texture_scope_end(struct texture_scope *scope)
|
||||||
logf_error("Error loading texture \"%F\": %F", FMT_STR(path), FMT_STR(error_msg));
|
{
|
||||||
|
struct texture_tctx *tctx = thread_local_var_eval(&tl_texture_tctx);
|
||||||
|
for (u64 i = 0; i < CACHE_BUCKETS_COUNT; ++i) {
|
||||||
|
struct texture_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;
|
||||||
|
}
|
||||||
|
|
||||||
/* Generate purple & black image */
|
/* ========================== *
|
||||||
struct renderer_handle handle = { 0 };
|
* Cache interface
|
||||||
struct image_rgba default_image_data = generate_purple_black_image(scratch.arena, 64, 64);
|
* ========================== */
|
||||||
handle = renderer_texture_alloc(default_image_data);
|
|
||||||
|
|
||||||
/* Store */
|
INTERNAL struct cache_node *node_lookup_touch(struct texture_scope *scope, struct texture_tag tag)
|
||||||
struct texture *texture = NULL;
|
{
|
||||||
|
__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);
|
||||||
{
|
{
|
||||||
struct asset_cache_store store = asset_cache_store_open();
|
/* Alloc node */
|
||||||
texture = arena_push(store.arena, struct texture);
|
sys_mutex_lock(&G.cache.node_pool_mutex);
|
||||||
*texture = (struct texture) {
|
{
|
||||||
.renderer_handle = handle,
|
if (G.cache.node_pool_first_free) {
|
||||||
.size = V2(default_image_data.width, default_image_data.height)
|
n = G.cache.node_pool_first_free;
|
||||||
};
|
G.cache.node_pool_first_free = n->next_free;
|
||||||
asset_cache_store_close(&store);
|
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);
|
||||||
asset_cache_mark_ready(asset, texture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_task_params_release(params);
|
return n;
|
||||||
|
|
||||||
scratch_end(scratch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct asset *texture_load_asset(struct string path, b32 help)
|
INTERNAL struct texture *texture_from_tag_internal(struct texture_scope *scope, struct texture_tag tag, b32 await)
|
||||||
{
|
{
|
||||||
__prof;
|
struct texture *res = G.loading_texture;
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct cache_node *n = node_lookup_touch(scope, tag);
|
||||||
|
|
||||||
struct string key = string_cat(scratch.arena, path, STR("_tex"));
|
u32 state = atomic_u32_eval(&n->state);
|
||||||
u64 hash = asset_cache_hash(key);
|
if (state == CACHE_NODE_STATE_LOADED) {
|
||||||
b32 is_first_touch;
|
res = n->texture;
|
||||||
struct asset *asset = asset_cache_touch(key, hash, &is_first_touch);
|
} 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;
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_first_touch) {
|
|
||||||
/* Assemble task params */
|
|
||||||
struct texture_task_params *params = texture_task_params_alloc();
|
|
||||||
if (path.len > (sizeof(params->path_cstr) - 1)) {
|
|
||||||
sys_panic(string_format(scratch.arena,
|
|
||||||
STR("Texture path \"%F\" too long!"),
|
|
||||||
FMT_STR(path)));
|
|
||||||
}
|
}
|
||||||
cstr_buff_from_string(BUFFER_FROM_ARRAY(params->path_cstr), path);
|
|
||||||
params->path_len = path.len;
|
|
||||||
params->asset = asset;
|
|
||||||
|
|
||||||
/* Push task */
|
|
||||||
asset_cache_mark_loading(asset);
|
|
||||||
struct work_handle wh = { 0 };
|
|
||||||
if (help) {
|
|
||||||
wh = work_push_task_and_help(&texture_load_asset_task, params, WORK_PRIORITY_NORMAL);
|
|
||||||
} else {
|
|
||||||
wh = work_push_task(&texture_load_asset_task, params, WORK_PRIORITY_NORMAL);
|
|
||||||
}
|
|
||||||
asset_cache_set_work(asset, &wh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(scratch);
|
return res;
|
||||||
return asset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct texture *texture_load_async(struct string path)
|
struct texture *texture_from_tag_await(struct texture_scope *scope, struct texture_tag tag)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct asset *asset = texture_load_asset(path, false);
|
return texture_from_tag_internal(scope, tag, true);
|
||||||
struct texture *tex = (struct texture *)asset_cache_get_store_data(asset);
|
|
||||||
return tex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct texture *texture_from_tag_async(struct texture_scope *scope, struct texture_tag tag)
|
||||||
struct texture *texture_load(struct string path)
|
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct asset *asset = texture_load_asset(path, true);
|
return texture_from_tag_internal(scope, tag, false);
|
||||||
asset_cache_wait(asset);
|
}
|
||||||
struct texture *tex = (struct texture *)asset_cache_get_store_data(asset);
|
|
||||||
return tex;
|
/* ========================== *
|
||||||
|
* Loader thread
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(texture_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);
|
||||||
|
{
|
||||||
|
texture_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(texture_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) > TEXTURE_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 texture \"%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) > TEXTURE_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;
|
||||||
|
if (n->texture->valid) {
|
||||||
|
renderer_texture_release(n->texture->renderer_handle);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,42 +1,51 @@
|
|||||||
#ifndef TEXTURE_H
|
#ifndef TEXTURE_H
|
||||||
#define TEXTURE_H
|
#define TEXTURE_H
|
||||||
|
|
||||||
#include "renderer.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
struct asset;
|
|
||||||
struct work_startup_receipt;
|
|
||||||
struct renderer_startup_receipt;
|
struct renderer_startup_receipt;
|
||||||
struct asset_cache_startup_receipt;
|
|
||||||
struct resource_startup_receipt;
|
struct resource_startup_receipt;
|
||||||
|
|
||||||
struct texture_frame {
|
|
||||||
struct clip_rect clip;
|
|
||||||
u32 duration_ms;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct texture_slice {
|
|
||||||
struct clip_rect clip;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct texture_anim {
|
|
||||||
u32 frame_start;
|
|
||||||
u32 frame_end;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct texture {
|
struct texture {
|
||||||
|
b32 loading;
|
||||||
|
b32 valid;
|
||||||
struct renderer_handle renderer_handle;
|
struct renderer_handle renderer_handle;
|
||||||
struct v2 size;
|
struct v2 size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
struct texture_startup_receipt { i32 _; };
|
struct texture_startup_receipt { i32 _; };
|
||||||
struct texture_startup_receipt texture_startup(struct work_startup_receipt *work_sr,
|
struct texture_startup_receipt texture_startup(struct renderer_startup_receipt *renderer_sr,
|
||||||
struct renderer_startup_receipt *renderer_sr,
|
|
||||||
struct asset_cache_startup_receipt *asset_cache_sr,
|
|
||||||
struct resource_startup_receipt *resource_sr);
|
struct resource_startup_receipt *resource_sr);
|
||||||
|
|
||||||
struct asset *texture_load_asset(struct string path, b32 wait);
|
/* ========================== *
|
||||||
struct texture *texture_load_async(struct string path);
|
* Tag
|
||||||
struct texture *texture_load(struct string path);
|
* ========================== */
|
||||||
|
|
||||||
|
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
|
#endif
|
||||||
|
|||||||
174
src/user.c
174
src/user.c
@ -400,6 +400,8 @@ INTERNAL void user_update(void)
|
|||||||
* Begin frame cache scopes
|
* Begin frame cache scopes
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
|
/* 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 sheet_scope *sheet_frame_scope = sheet_scope_begin();
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -757,67 +759,66 @@ INTERNAL void user_update(void)
|
|||||||
b32 skip_debug_draw_transform = ent == active_camera;
|
b32 skip_debug_draw_transform = ent == active_camera;
|
||||||
|
|
||||||
/* Draw sprite */
|
/* Draw sprite */
|
||||||
if (ent->sprite_name.len > 0) {
|
if (!texture_tag_is_nil(ent->sprite_texture_tag)) {
|
||||||
struct string tex_name = ent->sprite_name;
|
|
||||||
|
|
||||||
/* Draw texture */
|
/* Draw texture */
|
||||||
struct texture *texture = texture_load_async(tex_name);
|
struct texture_tag texture_tag = ent->sprite_texture_tag;
|
||||||
if (texture) {
|
/* TODO: Remove this once renderer is rewritten to work with tags */
|
||||||
struct xform xf = xform_mul(ent->world_xform, ent->sprite_quad_xform);
|
struct texture *texture = texture_from_tag_async(texture_frame_scope, texture_tag);
|
||||||
struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf);
|
|
||||||
|
|
||||||
u32 tint = ent->sprite_tint;
|
struct xform xf = xform_mul(ent->world_xform, ent->sprite_quad_xform);
|
||||||
struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = texture, .tint = tint);
|
struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf);
|
||||||
|
|
||||||
struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, ent->sprite_sheet_tag);
|
u32 tint = ent->sprite_tint;
|
||||||
{
|
|
||||||
struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name);
|
struct sheet *sheet = sheet_from_tag_async(sheet_frame_scope, ent->sprite_sheet_tag);
|
||||||
u64 frame_index = span.start + ent->animation_frame;
|
struct sheet_frame frame = { 0 };
|
||||||
struct sheet_frame frame = sheet_get_frame(sheet, frame_index);
|
{
|
||||||
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) {
|
struct sheet_span span = sheet_get_span(sheet, ent->sprite_span_name);
|
||||||
f64 time_in_frame = ent->animation_time_in_frame;
|
u64 frame_index = span.start + ent->animation_frame;
|
||||||
while (time_in_frame > frame.duration) {
|
frame = sheet_get_frame(sheet, frame_index);
|
||||||
time_in_frame -= frame.duration;
|
if (entity_has_prop(ent, ENTITY_PROP_ANIMATING)) {
|
||||||
++frame_index;
|
f64 time_in_frame = ent->animation_time_in_frame;
|
||||||
if (frame_index > span.end) {
|
while (time_in_frame > frame.duration) {
|
||||||
/* Loop animation */
|
time_in_frame -= frame.duration;
|
||||||
frame_index = span.start;
|
++frame_index;
|
||||||
}
|
if (frame_index > span.end) {
|
||||||
frame = sheet_get_frame(sheet, frame_index);
|
/* Loop animation */
|
||||||
|
frame_index = span.start;
|
||||||
}
|
}
|
||||||
|
frame = sheet_get_frame(sheet, frame_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
params.clip = frame.clip;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: Check for texture loading as well */
|
struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture_tag = texture_tag, .tint = tint, .clip = frame.clip);
|
||||||
if (!sheet->loading) {
|
|
||||||
/* TODO: Fade in a placeholder or something instead of drawing nothing. */
|
/* TODO: Check for texture loading as well */
|
||||||
draw_texture_quad(G.world_canvas, params, quad);
|
if (!sheet->loading && !texture->loading) {
|
||||||
}
|
/* TODO: Fade in a placeholder or something instead of drawing nothing. */
|
||||||
|
draw_texture_quad(G.world_canvas, params, quad);
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (G.debug_draw && !skip_debug_draw) {
|
if (G.debug_draw && !skip_debug_draw) {
|
||||||
/* Debug draw sprite quad */
|
/* Debug draw sprite quad */
|
||||||
{
|
{
|
||||||
f32 thickness = 2.f;
|
f32 thickness = 2.f;
|
||||||
u32 color = RGBA_F(1, 1, 0, 0.25);
|
u32 color = RGBA_F(1, 1, 0, 0.25);
|
||||||
draw_solid_quad_line(G.world_canvas, quad, (thickness / PIXELS_PER_UNIT / G.world_view.zoom), color);
|
draw_solid_quad_line(G.world_canvas, quad, (thickness / PIXELS_PER_UNIT / G.world_view.zoom), color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug draw sprite transform */
|
/* Debug draw sprite transform */
|
||||||
{
|
{
|
||||||
debug_draw_xform(xf);
|
debug_draw_xform(xf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug draw sprite pivot */
|
/* Debug draw sprite pivot */
|
||||||
{
|
{
|
||||||
u32 color = RGBA_F(1, 0, 0, 1);
|
u32 color = RGBA_F(1, 0, 0, 1);
|
||||||
draw_solid_circle(G.world_canvas, ent->world_xform.og, 0.02, color, 20);
|
draw_solid_circle(G.world_canvas, ent->world_xform.og, 0.02, color, 20);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug draw info */
|
/* Debug draw info */
|
||||||
@ -911,14 +912,13 @@ INTERNAL void user_update(void)
|
|||||||
struct v2 crosshair_pos = G.viewport_cursor;
|
struct v2 crosshair_pos = G.viewport_cursor;
|
||||||
u32 tint = RGBA_F(1, 1, 1, 1);
|
u32 tint = RGBA_F(1, 1, 1, 1);
|
||||||
|
|
||||||
struct v2 size = V2(0, 0);
|
struct texture_tag crosshair_tag = texture_tag_from_path(STR("res/graphics/crosshair.ase"));
|
||||||
struct texture *t = texture_load_async(STR("res/graphics/crosshair.ase"));
|
struct texture *t = texture_from_tag_async(texture_frame_scope, crosshair_tag);
|
||||||
if (t) {
|
|
||||||
size = t->size;
|
struct v2 size = t->size;
|
||||||
struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size);
|
struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size);
|
||||||
struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf);
|
struct quad quad = quad_mul_xform(QUAD_UNIT_SQUARE_CENTERED, xf);
|
||||||
draw_texture_quad(G.viewport_canvas, DRAW_TEXTURE_PARAMS(.texture = t, .tint = tint), quad);
|
draw_texture_quad(G.viewport_canvas, DRAW_TEXTURE_PARAMS(.texture_tag = crosshair_tag, .tint = tint), quad);
|
||||||
}
|
|
||||||
|
|
||||||
struct rect cursor_clip = RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size);
|
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));
|
cursor_clip.pos = v2_add(cursor_clip.pos, v2_mul(size, 0.5f));
|
||||||
@ -989,49 +989,52 @@ INTERNAL void user_update(void)
|
|||||||
|
|
||||||
/* Debug draw info */
|
/* Debug draw info */
|
||||||
if (G.debug_draw) {
|
if (G.debug_draw) {
|
||||||
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
|
||||||
|
|
||||||
f32 spacing = 20;
|
f32 spacing = 20;
|
||||||
struct v2 pos = V2(10, 8);
|
struct v2 pos = V2(10, 8);
|
||||||
struct font *font = font_load(STR("res/fonts/fixedsys.ttf"), 12.0f);
|
struct font *font = font_load_async(STR("res/fonts/fixedsys.ttf"), 12.0f);
|
||||||
|
if (font) {
|
||||||
|
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("time: %F"), FMT_FLOAT((f64)G.time)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("time: %F"), FMT_FLOAT((f64)G.time)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_size: (%F, %F)"), FMT_FLOAT((f64)G.screen_size.x), FMT_FLOAT((f64)G.screen_size.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_size: (%F, %F)"), FMT_FLOAT((f64)G.screen_size.x), FMT_FLOAT((f64)G.screen_size.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_cursor: (%F, %F)"), FMT_FLOAT((f64)G.screen_cursor.x), FMT_FLOAT((f64)G.screen_cursor.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("screen_cursor: (%F, %F)"), FMT_FLOAT((f64)G.screen_cursor.x), FMT_FLOAT((f64)G.screen_cursor.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_screen_offset: (%F, %F)"), FMT_FLOAT((f64)G.viewport_screen_offset.x), FMT_FLOAT((f64)G.viewport_screen_offset.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_screen_offset: (%F, %F)"), FMT_FLOAT((f64)G.viewport_screen_offset.x), FMT_FLOAT((f64)G.viewport_screen_offset.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_size: (%F, %F)"), FMT_FLOAT((f64)G.viewport_size.x), FMT_FLOAT((f64)G.viewport_size.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_size: (%F, %F)"), FMT_FLOAT((f64)G.viewport_size.x), FMT_FLOAT((f64)G.viewport_size.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_center: (%F, %F)"), FMT_FLOAT((f64)G.viewport_center.x), FMT_FLOAT((f64)G.viewport_center.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_center: (%F, %F)"), FMT_FLOAT((f64)G.viewport_center.x), FMT_FLOAT((f64)G.viewport_center.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_cursor: (%F, %F)"), FMT_FLOAT((f64)G.viewport_cursor.x), FMT_FLOAT((f64)G.viewport_cursor.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("viewport_cursor: (%F, %F)"), FMT_FLOAT((f64)G.viewport_cursor.x), FMT_FLOAT((f64)G.viewport_cursor.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view.og: (%F, %F)"), FMT_FLOAT((f64)G.world_view.og.x), FMT_FLOAT((f64)G.world_view.og.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view.og: (%F, %F)"), FMT_FLOAT((f64)G.world_view.og.x), FMT_FLOAT((f64)G.world_view.og.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view rotation: %F"), FMT_FLOAT((f64)xform_get_rotation(G.world_view))));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view rotation: %F"), FMT_FLOAT((f64)xform_get_rotation(G.world_view))));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view scale: (%F, %F)"), FMT_FLOAT((f64)xform_get_scale(G.world_view).x), FMT_FLOAT((f64)xform_get_scale(G.world_view).x)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_view scale: (%F, %F)"), FMT_FLOAT((f64)xform_get_scale(G.world_view).x), FMT_FLOAT((f64)xform_get_scale(G.world_view).x)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_cursor: (%F, %F)"), FMT_FLOAT((f64)G.world_cursor.x), FMT_FLOAT((f64)G.world_cursor.y)));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("world_cursor: (%F, %F)"), FMT_FLOAT((f64)G.world_cursor.x), FMT_FLOAT((f64)G.world_cursor.y)));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("debug_camera: %F"), FMT_STR(G.debug_camera ? STR("true") : STR("false"))));
|
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("debug_camera: %F"), FMT_STR(G.debug_camera ? STR("true") : STR("false"))));
|
||||||
pos.y += spacing;
|
pos.y += spacing;
|
||||||
|
|
||||||
|
arena_temp_end(temp);
|
||||||
|
}
|
||||||
|
|
||||||
arena_temp_end(temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push game cmds */
|
/* Push game cmds */
|
||||||
@ -1080,6 +1083,7 @@ INTERNAL void user_update(void)
|
|||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
sheet_scope_end(sheet_frame_scope);
|
sheet_scope_end(sheet_frame_scope);
|
||||||
|
texture_scope_end(texture_frame_scope);
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user