diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index 8d055215..04d4d1c9 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -206,30 +206,28 @@ INTERNAL void init_shader_table(void) /* Triangle shader layout */ G.shader_info[SHADER_TRIANGLE] = (struct dx11_shader_desc) { - SHADER_TRIANGLE, - "res/shaders/triangle.hlsl", - sizeof(struct triangle_shader_vertex), - { + .kind = SHADER_TRIANGLE, + .name_cstr = "res/shaders/triangle.hlsl", + .vertex_size = sizeof(struct triangle_shader_vertex), + .input_layout_desc = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } - }, - { 0 } + } }; /* Grid shader layout */ G.shader_info[SHADER_GRID] = (struct dx11_shader_desc) { - SHADER_GRID, - "res/shaders/grid.hlsl", - sizeof(struct grid_shader_vertex), - { + .kind = SHADER_GRID, + .name_cstr = "res/shaders/grid.hlsl", + .vertex_size = sizeof(struct grid_shader_vertex), + .input_layout_desc = { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "THICKNESS", 0, DXGI_FORMAT_R32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "SPACING", 0, DXGI_FORMAT_R32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "OFFSET", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } - }, - { 0 } + } }; G.shader_info_lookup = fixed_dict_init(&G.arena, SHADER_INFO_LOOKUP_BINS); @@ -371,7 +369,7 @@ INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_d } #if RESOURCE_RELOADING -INTERNAL RESOURCE_WATCH_CALLBACK_DEF(shader_resource_watch_callback, info) +INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, info) { struct string name = info->name; struct dx11_shader_desc *desc = (struct dx11_shader_desc *)fixed_dict_get(&G.shader_info_lookup, name); diff --git a/src/resource.c b/src/resource.c index 8aef2d5c..3f973acf 100644 --- a/src/resource.c +++ b/src/resource.c @@ -123,13 +123,6 @@ b32 resource_exists(struct string path) #endif } -#if !RESOURCES_EMBEDDED -struct sys_file_time resource_get_time(struct resource *res_ptr) -{ - return sys_file_get_time(res_ptr->_file); -} -#endif - /* ========================== * * Watch * ========================== */ diff --git a/src/resource.h b/src/resource.h index 70780cd4..b5b8dfed 100644 --- a/src/resource.h +++ b/src/resource.h @@ -33,20 +33,14 @@ b32 resource_exists(struct string path); #define resource_get_data(res_ptr) (res_ptr)->_data -#if RESOURCES_EMBEDDED - #define resource_get_time(res_ptr) (struct sys_file_time) ZI -#else - struct sys_file_time resource_get_time(struct resource *res_ptr); -#endif - #if RESOURCES_EMBEDDED #define resource_get_name(res_ptr) (res_ptr)->_file_name #else #define resource_get_name(res_ptr) STRING((res_ptr)->_file_name_len, (res_ptr)->_file_name_text) #endif -#define RESOURCE_WATCH_CALLBACK_DEF(func_name, arg_info) void func_name(struct sys_watch_info *arg_info) -typedef RESOURCE_WATCH_CALLBACK_DEF(resource_watch_callback, info); +#define RESOURCE_WATCH_CALLBACK_FUNC_DEF(func_name, arg_info) void func_name(struct sys_watch_info *arg_info) +typedef RESOURCE_WATCH_CALLBACK_FUNC_DEF(resource_watch_callback, info); #if RESOURCE_RELOADING void resource_register_watch_callback(resource_watch_callback *callback); diff --git a/src/sprite.c b/src/sprite.c index 691007c4..829ab5f0 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -12,6 +12,7 @@ #include "app.h" #include "renderer.h" #include "math.h" +#include "rand.h" #define CACHE_MEMORY_BUDGET (MEGABYTE(256)) #define CACHE_BINS_COUNT 1024 @@ -50,7 +51,9 @@ struct load_cmd { enum cache_node_kind { CACHE_NODE_KIND_TEXTURE, - CACHE_NODE_KIND_SHEET + CACHE_NODE_KIND_SHEET, + + NUM_CACHE_NODE_KINDS }; enum cache_node_state { @@ -83,14 +86,14 @@ struct cache_node { struct sprite_sheet *sheet; /* Hash list */ - struct cache_node *next_hash; - struct cache_node *prev_hash; + struct cache_node *next_in_bin; + struct cache_node *prev_in_bin; /* Free list */ struct cache_node *next_free; #if RESOURCE_RELOADING - struct sys_datetime initial_resource_file_modified_time; + struct atomic_i32 out_of_date; /* Has the resource changed since this node was loaded */ u64 tag_path_len; u8 tag_path[4096]; #endif @@ -111,7 +114,7 @@ struct cache { struct sprite_scope_reference { struct cache_node *cache_node; - struct sprite_scope_reference *next_hash; + struct sprite_scope_reference *next_in_bin; struct sprite_scope_reference *next_free; }; @@ -151,12 +154,18 @@ struct sprite_tctx { struct arena arena; struct sprite_scope *first_free_scope; struct sprite_scope_reference *first_free_reference; +#if RTC + u32 thread_id; +#endif }; INTERNAL THREAD_LOCAL_VAR_ALLOC_FUNC_DEF(sprite_tctx_alloc, vtctx) { struct sprite_tctx *tctx = (struct sprite_tctx *)vtctx; tctx->arena = arena_alloc(MEGABYTE(64)); +#if RTC + tctx->thread_id = sys_thread_id(); +#endif } INTERNAL THREAD_LOCAL_VAR_RELEASE_FUNC_DEF(sprite_tctx_release, vtctx) @@ -213,6 +222,10 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(sprite_shutdown); INTERNAL WORK_TASK_FUNC_DEF(sprite_load_task, arg); INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg); +#if RESOURCE_RELOADING +INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(sprite_resource_watch_callback, info); +#endif + struct sprite_startup_receipt sprite_startup(struct renderer_startup_receipt *renderer_sr, struct resource_startup_receipt *resource_sr) { @@ -262,6 +275,7 @@ struct sprite_startup_receipt sprite_startup(struct renderer_startup_receipt *re G.evictor_thread = sys_thread_alloc(sprite_evictor_thread_entry_point, NULL, LIT("[P2] Sprite evictor")); app_register_exit_callback(&sprite_shutdown); + resource_register_watch_callback(&sprite_resource_watch_callback); return (struct sprite_startup_receipt) { 0 }; } @@ -357,9 +371,6 @@ INTERNAL void cache_node_load_texture(struct cache_node *n, struct sprite_tag ta if (resource_exists(path)) { struct resource texture_rs = resource_open(path); decoded = ase_decode_image(scratch.arena, resource_get_data(&texture_rs)); -#if RESOURCE_RELOADING - n->initial_resource_file_modified_time = resource_get_time(&texture_rs).modified; -#endif resource_close(&texture_rs); /* Initialize */ @@ -661,9 +672,6 @@ INTERNAL void cache_node_load_sheet(struct cache_node *n, struct sprite_tag tag) if (resource_exists(path)) { struct resource sheet_rs = resource_open(path); decoded = ase_decode_sheet(scratch.arena, resource_get_data(&sheet_rs)); -#if RESOURCE_RELOADING - n->initial_resource_file_modified_time = resource_get_time(&sheet_rs).modified; -#endif resource_close(&sheet_rs); /* Initialize */ @@ -700,36 +708,44 @@ INTERNAL void cache_node_load_sheet(struct cache_node *n, struct sprite_tag tag) * Scope * ========================== */ -INTERNAL void scope_ensure_reference(struct sprite_scope *scope, struct cache_node *cache_node, u64 cache_bin_index) +/* Returns the slot at which the reference pointer should exist in the sprite scope. + * If the pointed to slot points to NULL, then the reference does not exist in the scope for the node. */ +INTERNAL struct sprite_scope_reference **scope_get_reference_slot(struct sprite_scope *scope, struct cache_node *cache_node, u64 cache_bin_index) { - __prof; - struct sprite_scope_reference **ref_next = &scope->reference_bins[cache_bin_index]; - struct sprite_scope_reference *ref = *ref_next; - while (ref) { - if (ref->cache_node == cache_node) { - /* Scope already references node */ + sys_thread_assert(scope->tctx->thread_id); + + struct sprite_scope_reference **ref_slot = &scope->reference_bins[cache_bin_index]; + while (*ref_slot) { + if ((*ref_slot)->cache_node == cache_node) { + /* Found reference in scope */ break; } else { - ref_next = &ref->next_hash; - ref = *ref_next; + ref_slot = &(*ref_slot)->next_in_bin; } } + return ref_slot; +} - if (!ref) { - /* Increment refcount */ - node_refcount_add(cache_node, 1); - /* Add reference to scope */ - struct sprite_tctx *tctx = thread_local_var_eval(&tl_sprite_tctx); - if (tctx->first_free_reference) { - ref = tctx->first_free_reference; - tctx->first_free_reference = ref->next_free; - MEMZERO_STRUCT(ref); - } else { - ref = arena_push_zero(&tctx->arena, struct sprite_scope_reference); - } - ref->cache_node = cache_node; - *ref_next = ref; +INTERNAL struct sprite_scope_reference *scope_reference_alloc(struct sprite_scope *scope, struct cache_node *cache_node) +{ + sys_thread_assert(scope->tctx->thread_id); + + /* Increment refcount */ + node_refcount_add(cache_node, 1); + + /* Add reference to scope */ + struct sprite_tctx *tctx = scope->tctx; + struct sprite_scope_reference *ref; + 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 sprite_scope_reference); } + ref->cache_node = cache_node; + + return ref; } struct sprite_scope *sprite_scope_begin(void) @@ -748,13 +764,15 @@ struct sprite_scope *sprite_scope_begin(void) res = arena_push_zero(&tctx->arena, struct sprite_scope); res->reference_bins = arena_push_array_zero(&tctx->arena, struct sprite_scope_reference *, CACHE_BINS_COUNT); } + res->tctx = tctx; return res; } void sprite_scope_end(struct sprite_scope *scope) { - struct sprite_tctx *tctx = thread_local_var_eval(&tl_sprite_tctx); + sys_thread_assert(scope->tctx->thread_id); + struct sprite_tctx *tctx = scope->tctx; for (u64 i = 0; i < CACHE_BINS_COUNT; ++i) { struct sprite_scope_reference *ref = scope->reference_bins[i]; while (ref) { @@ -763,7 +781,7 @@ void sprite_scope_end(struct sprite_scope *scope) /* Add reference to free list */ ref->next_free = tctx->first_free_reference; tctx->first_free_reference = ref; - ref = ref->next_hash; + ref = ref->next_in_bin; } } scope->next_free = tctx->first_free_scope; @@ -793,12 +811,33 @@ INTERNAL struct cache_node *node_lookup_touch(struct sprite_scope *scope, struct nonmatching_next = &bin->first; n = *nonmatching_next; while (n) { + b32 match = false; if (n->hash.v == hash.v) { - scope_ensure_reference(scope, n, cache_bin_index); + struct sprite_scope_reference **ref_slot = scope_get_reference_slot(scope, n, cache_bin_index); +#if RESOURCE_RELOADING + if (*ref_slot) { + match = true; + } else { + if (atomic_i32_eval(&n->out_of_date)) { + /* If node is out of date and the scope doesn't already hold a reference to it, then ignore node */ + } else { + match = true; + *ref_slot = scope_reference_alloc(scope, n); + } + } +#else + if (!(*ref_slot)) { + *ref_slot = scope_reference_alloc(scope, n); + } + match = true; +#endif + } + + if (match) { break; } else { nonmatching = n; - nonmatching_next = &nonmatching->next_hash; + nonmatching_next = &nonmatching->next_in_bin; n = *nonmatching_next; } } @@ -822,12 +861,16 @@ INTERNAL struct cache_node *node_lookup_touch(struct sprite_scope *scope, struct } sys_mutex_unlock(&pool_lock); } + /* Init node and add to bin */ - scope_ensure_reference(scope, n, cache_bin_index); + struct sprite_scope_reference **ref_slot = scope_get_reference_slot(scope, n, cache_bin_index); + if (!(*ref_slot)) { + *ref_slot = scope_reference_alloc(scope, n); + } *nonmatching_next = n; if (nonmatching) { - nonmatching->next_hash = n; - n->prev_hash = nonmatching; + nonmatching->next_in_bin = n; + n->prev_in_bin = nonmatching; } n->hash = cache_node_hash_from_tag_hash(tag.hash, kind); n->kind = kind; @@ -847,6 +890,7 @@ INTERNAL void *data_from_tag_internal(struct sprite_scope *scope, struct sprite_ switch (kind) { case CACHE_NODE_KIND_TEXTURE: { res = G.loading_texture; } break; case CACHE_NODE_KIND_SHEET: { res = G.loading_sheet; } break; + default: { sys_panic(LIT("Unknown sprite cache node kind")); } break; } struct cache_node *n = node_lookup_touch(scope, tag, kind); @@ -856,6 +900,7 @@ INTERNAL void *data_from_tag_internal(struct sprite_scope *scope, struct sprite_ switch (kind) { case CACHE_NODE_KIND_TEXTURE: { res = n->texture; } break; case CACHE_NODE_KIND_SHEET: { res = n->sheet; } break; + default: { sys_panic(LIT("Unknown sprite cache node kind")); } break; } } else if (state == CACHE_NODE_STATE_NONE) { if (atomic_u32_eval_compare_exchange(&n->state, CACHE_NODE_STATE_NONE, CACHE_NODE_STATE_QUEUED) == CACHE_NODE_STATE_NONE) { @@ -870,6 +915,7 @@ INTERNAL void *data_from_tag_internal(struct sprite_scope *scope, struct sprite_ cache_node_load_sheet(n, tag); res = n->sheet; } break; + default: { sys_panic(LIT("Unknown sprite cache node kind")); } break; } } else { struct sys_lock lock = sys_mutex_lock_e(&G.load_cmds_mutex); @@ -1021,6 +1067,7 @@ INTERNAL WORK_TASK_FUNC_DEF(sprite_load_task, arg) case CACHE_NODE_KIND_SHEET: { cache_node_load_sheet(n, cmd->tag); } break; + default: { sys_panic(LIT("Unknown sprite cache node kind")); } break; } /* Free cmd */ @@ -1033,6 +1080,34 @@ INTERNAL WORK_TASK_FUNC_DEF(sprite_load_task, arg) } } +/* ========================== * + * Resource watch + * ========================== */ + +#if RESOURCE_RELOADING + +INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(sprite_resource_watch_callback, info) +{ + struct string name = info->name; + struct sprite_tag tag = sprite_tag_from_path(name); + for (u64 kind = 0; kind < NUM_CACHE_NODE_KINDS; ++kind) { + struct cache_node_hash hash = cache_node_hash_from_tag_hash(tag.hash, kind); + u64 cache_bin_index = hash.v % CACHE_BINS_COUNT; + struct cache_bin *bin = &G.cache.bins[cache_bin_index]; + struct sys_lock lock = sys_mutex_lock_s(&bin->mutex); + { + for (struct cache_node *n = bin->first; n; n = n->next_in_bin) { + if (n->hash.v == hash.v) { + atomic_i32_eval_exchange(&n->out_of_date, 1); + } + } + } + sys_mutex_unlock(&lock); + } +} + +#endif + /* ========================== * * Evictor thread * ========================== */ @@ -1080,15 +1155,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) /* Check if file changed for resource reloading */ if (!consider_for_eviction) { struct string path = STRING(n->tag_path_len, n->tag_path); - 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 = !MEMEQ_STRUCT(&n->initial_resource_file_modified_time, ¤t_file_time); - if (file_changed) { + if (atomic_i32_eval(&n->out_of_date)) { switch (n->kind) { case CACHE_NODE_KIND_TEXTURE: { logf_info("Resource file for sprite texture \"%F\" has changed. Evicting to allow for reloading.", FMT_STR(path)); @@ -1096,6 +1163,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) case CACHE_NODE_KIND_SHEET: { logf_info("Resource file for sprite sheet \"%F\" has changed. Evicting to allow for reloading.", FMT_STR(path)); } break; + default: { sys_panic(LIT("Unknown sprite cache node kind")); } break; } consider_for_eviction = true; force_evict = true; @@ -1124,7 +1192,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) head_consider = evict_node; } - n = n->next_hash; + n = n->next_in_bin; } } sys_mutex_unlock(&bin_lock); @@ -1166,13 +1234,13 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg) /* Cache node has been referenced since scan, skip eviction. */ } else if (atomic_u64_eval(&G.cache.memory_usage) > CACHE_MEMORY_BUDGET || en->force_evict) { /* Remove from cache bin */ - if (n->prev_hash) { - n->prev_hash->next_hash = n->next_hash; + if (n->prev_in_bin) { + n->prev_in_bin->next_in_bin = n->next_in_bin; } else { - bin->first = n->next_hash; + bin->first = n->next_in_bin; } - if (n->next_hash) { - n->next_hash->prev_hash = n->prev_hash; + if (n->next_in_bin) { + n->next_in_bin->prev_in_bin = n->prev_in_bin; } atomic_u64_eval_add_i64(&G.cache.memory_usage, -((i64)n->memory_usage)); /* Add to evicted list */ diff --git a/src/sprite.h b/src/sprite.h index 270f6f27..97ece973 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -32,6 +32,7 @@ b32 sprite_tag_eq(struct sprite_tag t1, struct sprite_tag t2); * ========================== */ struct sprite_scope { + struct sprite_tctx *tctx; struct sprite_scope_reference **reference_bins; struct sprite_scope *next_free; };