shader hot reloading

This commit is contained in:
jacob 2025-05-07 16:57:36 -05:00
parent 58b31f9192
commit abac85d324
15 changed files with 240 additions and 157 deletions

16
build.c
View File

@ -416,7 +416,6 @@ void OnBuild(StringList cli_args)
D_Tag executable_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/PowerPlay.exe"), FmtStr(out_bin_dir_path)), D_TagKind_File); D_Tag executable_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/PowerPlay.exe"), FmtStr(out_bin_dir_path)), D_TagKind_File);
D_Tag res_dir = D_TagFromPath(&perm, Lit("res"), D_TagKind_Dir); D_Tag res_dir = D_TagFromPath(&perm, Lit("res"), D_TagKind_Dir);
D_Tag shaders_dir = D_TagFromPath(&perm, Lit("src/shaders"), D_TagKind_Dir);
D_Tag icon_file = D_TagFromPath(&perm, Lit("icon.ico"), D_TagKind_File); D_Tag icon_file = D_TagFromPath(&perm, Lit("icon.ico"), D_TagKind_File);
D_Tag inc_src_file = D_TagFromPath(&perm, Lit("src/inc.c"), D_TagKind_File); D_Tag inc_src_file = D_TagFromPath(&perm, Lit("src/inc.c"), D_TagKind_File);
@ -658,12 +657,10 @@ void OnBuild(StringList cli_args)
AddSyncPoint(); AddSyncPoint();
D_TagList tar_input_dirs = { 0 }; D_TagList tar_input_dirs = { 0 };
{
D_TagListAppend(&perm, &tar_input_dirs, shaders_dir);
if (should_embed_res_dir) { if (should_embed_res_dir) {
D_TagListAppend(&perm, &tar_input_dirs, res_dir); D_TagListAppend(&perm, &tar_input_dirs, res_dir);
} }
}
for (D_TagListNode *n = tar_input_dirs.first; n; n = n->next) { for (D_TagListNode *n = tar_input_dirs.first; n; n = n->next) {
D_Tag input_dir = n->tag; D_Tag input_dir = n->tag;
@ -694,26 +691,19 @@ void OnBuild(StringList cli_args)
D_Tag rc_input_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/rc.rc"), FmtStr(out_inc_dir_path)), D_TagKind_File); D_Tag rc_input_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/rc.rc"), FmtStr(out_inc_dir_path)), D_TagKind_File);
{ {
D_Tag shaders_tar_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(shaders_dir))), D_TagKind_File);
D_Tag res_tar_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(res_dir))), D_TagKind_File); D_Tag res_tar_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(res_dir))), D_TagKind_File);
D_AddDependency(&store, rc_input_file, icon_file); D_AddDependency(&store, rc_input_file, icon_file);
if (should_embed_in_rc) { if (should_embed_in_rc && should_embed_res_dir) {
D_AddDependency(&store, rc_input_file, shaders_tar_file);
if (should_embed_res_dir) {
D_AddDependency(&store, rc_input_file, res_tar_file); D_AddDependency(&store, rc_input_file, res_tar_file);
} }
}
if (IsDirty(rc_input_file)) { if (IsDirty(rc_input_file)) {
D_ClearWrite(rc_input_file, Lit("")); D_ClearWrite(rc_input_file, Lit(""));
D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(icon_file)), FmtStr(Lit("ICON")), FmtStr(D_GetName(icon_file)))); D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(icon_file)), FmtStr(Lit("ICON")), FmtStr(D_GetName(icon_file))));
if (should_embed_in_rc) { if (should_embed_in_rc && should_embed_res_dir) {
D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(shaders_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(shaders_tar_file))));
if (should_embed_res_dir) {
D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(res_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(res_tar_file)))); D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(res_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(res_tar_file))));
} }
} }
} }
}
{ {
String rc_compile_args_fmt = StringFromStringLists(&perm, Lit(" "), rc_compile_args); String rc_compile_args_fmt = StringFromStringLists(&perm, Lit(" "), rc_compile_args);

View File

@ -116,8 +116,8 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
/* Decode */ /* Decode */
struct resource res = resource_open(path); struct resource res = resource_open(path);
struct ttf_decode_result result = ttf_decode(scratch.arena, res.data, point_size, g_font_codes, ARRAY_COUNT(g_font_codes)); struct ttf_decode_result result = ttf_decode(scratch.arena, resource_get_data(&res), point_size, g_font_codes, ARRAY_COUNT(g_font_codes));
resource_close(res); resource_close(&res);
/* Send texture to GPU */ /* Send texture to GPU */
struct renderer_texture texture = renderer_texture_alloc(RENDERER_TEXTURE_FORMAT_R8G8B8A8_UNORM, 0, V2I32(result.image_data.width, result.image_data.height), result.image_data.pixels); struct renderer_texture texture = renderer_texture_alloc(RENDERER_TEXTURE_FORMAT_R8G8B8A8_UNORM, 0, V2I32(result.image_data.width, result.image_data.height), result.image_data.pixels);

View File

@ -3,8 +3,8 @@
/* This is the file that actually includes binary data meant to be embedded in /* This is the file that actually includes binary data meant to be embedded in
* the executable. Embedded files should be added as dependencies to this source * the executable. Embedded files should be added as dependencies to this source
* file via the build system to ensure this unit is recompiled upon changes to * file via the build system to ensure this translation unit is recompiled upon
* an embedded file. */ * changes to an embedded file. */
#if RESOURCES_EMBEDDED #if RESOURCES_EMBEDDED
INCBIN_INCLUDE(res_tar, INCBIN_DIR "res.tar"); INCBIN_INCLUDE(res_tar, INCBIN_DIR "res.tar");
@ -13,9 +13,3 @@ struct string inc_res_tar(void)
return INCBIN_GET(res_tar); return INCBIN_GET(res_tar);
} }
#endif #endif
INCBIN_INCLUDE(shaders_tar, INCBIN_DIR "shaders.tar");
struct string inc_shaders_tar(void)
{
return INCBIN_GET(shaders_tar);
}

View File

@ -5,6 +5,4 @@
struct string inc_res_tar(void); struct string inc_res_tar(void);
#endif #endif
struct string inc_shaders_tar(void);
#endif #endif

View File

@ -8,12 +8,11 @@
#define MEMCPY_STRUCT(ptr_dst, ptr_src) MEMCPY((ptr_dst), (ptr_src), sizeof(*(ptr_dst))) #define MEMCPY_STRUCT(ptr_dst, ptr_src) MEMCPY((ptr_dst), (ptr_src), sizeof(*(ptr_dst)))
#define MEMCPY(dst, src, count) memcpy((dst), (src), (count)) #define MEMCPY(dst, src, count) memcpy((dst), (src), (count))
#define MEMCMP_STRUCT(p1, p2) MEMCMP((p1), (p2), sizeof(*p1))
#define MEMCMP(p1, p2, n) memcmp((p1), (p2), (n))
#define MEMEQ_STRUCT(p1, p2) MEMEQ((p1), (p2), sizeof(*p1)) #define MEMEQ_STRUCT(p1, p2) MEMEQ((p1), (p2), sizeof(*p1))
#define MEMEQ(p1, p2, n) (MEMCMP((p1), (p2), (n)) == 0) #define MEMEQ(p1, p2, n) (MEMCMP((p1), (p2), (n)) == 0)
#define MEMCMP(p1, p2, n) memcmp((p1), (p2), (n))
#define MEMSET(ptr, val, count) memset((ptr), (val), (count)) #define MEMSET(ptr, val, count) memset((ptr), (val), (count))
#if CRTLIB #if CRTLIB

View File

@ -1,4 +1,5 @@
#include "renderer.h" #include "renderer.h"
#include "resource.h"
#include "sys.h" #include "sys.h"
#include "memory.h" #include "memory.h"
#include "arena.h" #include "arena.h"
@ -6,7 +7,6 @@
#include "string.h" #include "string.h"
#include "math.h" #include "math.h"
#include "inc.h" #include "inc.h"
#include "tar.h"
#include "sprite.h" #include "sprite.h"
#include "log.h" #include "log.h"
@ -35,10 +35,15 @@
struct dx11_shader { struct dx11_shader {
enum shader_kind kind; enum shader_kind kind;
b32 valid; /* Is this shader allocated */
u32 vertex_size; u32 vertex_size;
ID3D11InputLayout *input_layout; ID3D11InputLayout *input_layout;
ID3D11VertexShader *vs; ID3D11VertexShader *vs;
ID3D11PixelShader *ps; ID3D11PixelShader *ps;
#if RESOURCE_RELOADING
struct sys_datetime last_modified_load_attempt; /* The last modified time of the shader src file that a load has been attempted on */
#endif
}; };
struct dx11_constant_buffer_data { struct dx11_constant_buffer_data {
@ -127,6 +132,7 @@ struct handle_store {
}; };
struct dx11_shader_desc { struct dx11_shader_desc {
enum shader_kind kind;
char *name_cstr; char *name_cstr;
u32 vertex_size; u32 vertex_size;
D3D11_INPUT_ELEMENT_DESC input_layout_desc[64]; /* NULL terminated array */ D3D11_INPUT_ELEMENT_DESC input_layout_desc[64]; /* NULL terminated array */
@ -134,7 +140,6 @@ struct dx11_shader_desc {
GLOBAL struct { GLOBAL struct {
struct arena arena; struct arena arena;
struct tar_archive shaders_archive; /* Tar archive including shader sources */
ID3D11Device *dev; ID3D11Device *dev;
ID3D11DeviceContext *devcon; ID3D11DeviceContext *devcon;
@ -192,26 +197,14 @@ INTERNAL void send_constant_buffer_data(ID3D11Buffer *buffer, struct mat4x4 vp)
* Shader * Shader
* ========================== */ * ========================== */
/* TODO: don't do fatal error, just don't use shader */
INTERNAL void process_shader_compilation_error(ID3DBlob *error_blob)
{
struct temp_arena scratch = scratch_begin_no_conflict();
struct string error_prefix = string_copy(scratch.arena, LIT("Failed to compile shader:\n"));
if (error_blob) {
char *compile_error_cstr = (char *)ID3D10Blob_GetBufferPointer(error_blob);
struct string error_msg = string_cat(scratch.arena, error_prefix, string_from_cstr_no_limit(compile_error_cstr));
sys_panic(error_msg);
}
scratch_end(scratch);
}
INTERNAL void init_shader_table(void) INTERNAL void init_shader_table(void)
{ {
MEMZERO_ARRAY(G.shader_info); MEMZERO_ARRAY(G.shader_info);
/* Triangle shader layout */ /* Triangle shader layout */
G.shader_info[SHADER_TRIANGLE] = (struct dx11_shader_desc) { G.shader_info[SHADER_TRIANGLE] = (struct dx11_shader_desc) {
"shaders/triangle.hlsl", SHADER_TRIANGLE,
"res/shaders/triangle.hlsl",
sizeof(struct triangle_shader_vertex), sizeof(struct triangle_shader_vertex),
{ {
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
@ -222,7 +215,8 @@ INTERNAL void init_shader_table(void)
/* Grid shader layout */ /* Grid shader layout */
G.shader_info[SHADER_GRID] = (struct dx11_shader_desc) { G.shader_info[SHADER_GRID] = (struct dx11_shader_desc) {
"shaders/grid.hlsl", SHADER_GRID,
"res/shaders/grid.hlsl",
sizeof(struct grid_shader_vertex), sizeof(struct grid_shader_vertex),
{ {
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
@ -234,16 +228,14 @@ INTERNAL void init_shader_table(void)
}; };
} }
INTERNAL void shader_init(struct dx11_shader *shader, enum shader_kind kind) /* If shader compilation fails, then error string is returned allocated on `arena` */
INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *shader, struct dx11_shader_desc *shader_desc, struct resource *src_res)
{ {
__prof; __prof;
MEMZERO_STRUCT(shader); struct temp_arena scratch = scratch_begin(arena);
struct string error_str = ZI;
struct temp_arena scratch = scratch_begin_no_conflict(); shader->kind = shader_desc->kind;
const struct dx11_shader_desc *shader_desc = &G.shader_info[kind];
struct string name = string_from_cstr_no_limit(shader_desc->name_cstr);
shader->kind = kind;
shader->vertex_size = shader_desc->vertex_size; shader->vertex_size = shader_desc->vertex_size;
u32 flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; u32 flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;
@ -254,31 +246,27 @@ INTERNAL void shader_init(struct dx11_shader *shader, enum shader_kind kind)
#endif #endif
/* Compile shader */ /* Compile shader */
ID3DBlob *vs_blob, *ps_blob; ID3DBlob *vs_blob = NULL;
ID3DBlob *ps_blob = NULL;
ID3DBlob *error_blob = NULL;
b32 success = false;
{ {
struct tar_entry *tar_entry = tar_get(&G.shaders_archive, name); struct string shader_src = resource_get_data(src_res);
if (!tar_entry) { logf_info("Compiling shader \"%F\"", FMT_STR(resource_get_name(src_res)));
sys_panic(string_format(scratch.arena,
LIT("Could not find shader \"%F\""),
FMT_STR(name)));
}
struct string shader_src = tar_entry->data;
logf_info("Compiling shader \"%F\"", FMT_STR(name));
/* Compile shader */ /* Compile shader */
/* TODO: pre-compile shaders w/ FXC? */ /* TODO: pre-compile shaders w/ FXC? */
ID3DBlob *error_blob; HRESULT hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, NULL, "vs_main", "vs_5_0", flags, 0, &vs_blob, &error_blob);
HRESULT v_res = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, NULL, "vs_main", "vs_5_0", flags, 0, &vs_blob, &error_blob); if (SUCCEEDED(hr)) {
if (FAILED(v_res)) { ID3D11Device_CreateVertexShader(G.dev, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), NULL, &shader->vs);
process_shader_compilation_error(error_blob); hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, NULL, "ps_main", "ps_5_0", flags, 0, &ps_blob, &error_blob);
if (SUCCEEDED(hr)) {
ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps);
success = true;
} }
HRESULT p_res = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, NULL, "ps_main", "ps_5_0", flags, 0, &ps_blob, &error_blob);
if (FAILED(p_res)) {
process_shader_compilation_error(error_blob);
} }
} }
if (success && !error_blob) {
/* Get number of device layout elements from NULL terminated array */ /* Get number of device layout elements from NULL terminated array */
u32 elem_count = 0; u32 elem_count = 0;
for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) { for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) {
@ -290,14 +278,94 @@ INTERNAL void shader_init(struct dx11_shader *shader, enum shader_kind kind)
/* Create device layout */ /* Create device layout */
ID3D11Device_CreateInputLayout(G.dev, shader_desc->input_layout_desc, elem_count, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &shader->input_layout); ID3D11Device_CreateInputLayout(G.dev, shader_desc->input_layout_desc, elem_count, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &shader->input_layout);
} else {
error_str = LIT("Unknown error");
if (error_blob) {
u64 error_cstr_len = ID3D10Blob_GetBufferSize(error_blob);
char *error_cstr = (char *)ID3D10Blob_GetBufferPointer(error_blob);
error_str = string_copy(arena, string_from_cstr(error_cstr, error_cstr_len));
}
}
/* Create shader */ if (vs_blob) {
ID3D11Device_CreateVertexShader(G.dev, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), NULL, &shader->vs);
ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps);
ID3D10Blob_Release(vs_blob); ID3D10Blob_Release(vs_blob);
}
if (ps_blob) {
ID3D10Blob_Release(ps_blob); ID3D10Blob_Release(ps_blob);
}
if (error_blob) {
ID3D10Blob_Release(error_blob);
}
shader->valid = true;
scratch_end(scratch);
return error_str;
}
INTERNAL void shader_release(struct dx11_shader *shader)
{
__prof;
if (shader->vs) {
ID3D11VertexShader_Release(shader->vs);
}
if (shader->ps) {
ID3D11PixelShader_Release(shader->ps);
}
if (shader->input_layout) {
ID3D11InputLayout_Release(shader->input_layout);
}
}
INTERNAL void reload_shaders(void)
{
__prof;
struct temp_arena scratch = scratch_begin_no_conflict();
for (u32 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) {
struct dx11_shader_desc *desc = &G.shader_info[i];
struct string name = string_from_cstr_no_limit(desc->name_cstr);
if (!resource_exists(name)) {
sys_panic(string_format(scratch.arena, LIT("Could not find shader \"%F\""), FMT_STR(name)));
}
struct resource src_res = resource_open(name);
{
struct dx11_shader *old_shader = &G.shaders[i];
b32 should_load = !old_shader->valid;
#if RESOURCE_RELOADING
struct sys_datetime last_modified = resource_get_time(&src_res).modified;
if (!MEMEQ_STRUCT(&last_modified, &old_shader->last_modified_load_attempt)) {
should_load = true;
}
old_shader->last_modified_load_attempt = last_modified;
#endif
if (should_load) {
struct dx11_shader new_shader = ZI;
struct string error = shader_alloc(scratch.arena, &new_shader, desc, &src_res);
#if RESOURCE_RELOADING
new_shader.last_modified_load_attempt = last_modified;
#endif
if (error.len == 0) {
if (old_shader->valid) {
shader_release(old_shader);
}
*old_shader = new_shader;
} else {
struct string error_msg = string_format(scratch.arena,
LIT("Failed to compile shader \"%F\":\n\n%F"),
FMT_STR(name),
FMT_STR(error));
if (old_shader->valid) {
log_error(error_msg);
} else {
sys_panic(error_msg);
}
shader_release(&new_shader);
}
}
}
resource_close(&src_res);
}
scratch_end(scratch); scratch_end(scratch);
} }
@ -315,12 +383,6 @@ struct renderer_startup_receipt renderer_startup(struct sys_window *window)
G.textures_mutex = sys_mutex_alloc(); G.textures_mutex = sys_mutex_alloc();
G.textures_arena = arena_alloc(GIGABYTE(64)); G.textures_arena = arena_alloc(GIGABYTE(64));
/* Load shader archive */
struct string embedded_data = inc_shaders_tar();
if (embedded_data.len > 0) {
G.shaders_archive = tar_parse(&G.arena, embedded_data, LIT("shaders/"));
}
/* Initialize shader table */ /* Initialize shader table */
init_shader_table(); init_shader_table();
@ -518,10 +580,7 @@ struct renderer_startup_receipt renderer_startup(struct sys_window *window)
/* Init shaders */ /* Init shaders */
logf_info("Compiling shaders"); logf_info("Compiling shaders");
for (u32 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) { reload_shaders();
/* Create shader */
shader_init(&G.shaders[i], i);
}
logf_info("Finished compiling shaders"); logf_info("Finished compiling shaders");
return (struct renderer_startup_receipt) { 0 }; return (struct renderer_startup_receipt) { 0 };
@ -849,6 +908,18 @@ void renderer_backbuffer_present(i32 vsync)
IDXGISwapChain1_Present(G.swapchain, vsync, 0); IDXGISwapChain1_Present(G.swapchain, vsync, 0);
__profframe(0); __profframe(0);
} }
#if RESOURCE_RELOADING
{
i64 now_ns = sys_time_ns();
const i64 reload_interval_ns = NS_FROM_SECONDS(0.1);
static f32 last_reload_ns = 0;
if (now_ns >= (last_reload_ns + reload_interval_ns)) {
reload_shaders();
last_reload_ns = now_ns;
}
}
#endif
} }
/* ========================== * /* ========================== *

View File

@ -34,6 +34,49 @@ struct resource_startup_receipt resource_startup(void)
return (struct resource_startup_receipt) { 0 }; return (struct resource_startup_receipt) { 0 };
} }
struct resource resource_open(struct string path)
{
__prof;
#if RESOURCES_EMBEDDED
struct resource res = ZI;
struct tar_entry *entry = tar_get(&G.archive, path);
if (entry) {
res._data = entry->data;
res._file_name = entry->file_name;
}
return res;
#else
struct resource res = ZI;
if (path.len < ARRAY_COUNT(res._file_name_text)) {
struct sys_file file = sys_file_open_read_wait(path);
struct sys_file_map file_map = sys_file_map_open_read(file);
struct string data = sys_file_map_data(file_map);
res._data = data;
res._file = file;
res._file_map = file_map;
res._file_name_len = path.len;
MEMCPY(res._file_name_text, path.text, path.len);
} else {
ASSERT(false);
}
return res;
#endif
}
#if !RESOURCES_EMBEDDED
void resource_close(struct resource *res_ptr)
{
sys_file_map_close(res_ptr->_file_map);
sys_file_close(res_ptr->_file);
}
struct sys_file_time resource_get_time(struct resource *res_ptr)
{
return sys_file_get_time(res_ptr->_file);
}
#endif
b32 resource_exists(struct string path) b32 resource_exists(struct string path)
{ {
__prof; __prof;
@ -44,31 +87,3 @@ b32 resource_exists(struct string path)
return sys_is_file(path); return sys_is_file(path);
#endif #endif
} }
struct resource resource_open(struct string path)
{
__prof;
#if RESOURCES_EMBEDDED
struct tar_entry *entry = tar_get(&G.archive, path);
return (struct resource) {
.data = entry ? entry->data : STRING(0, 0)
};
#else
struct sys_file file = sys_file_open_read_wait(path);
struct sys_file_map file_map = sys_file_map_open_read(file);
struct string data = sys_file_map_data(file_map);
return (struct resource) {
.data = data,
.file = file,
.file_map = file_map
};
#endif
}
#if !RESOURCES_EMBEDDED
void resource_close(struct resource res)
{
sys_file_map_close(res.file_map);
sys_file_close(res.file);
}
#endif

View File

@ -3,28 +3,38 @@
#include "sys.h" #include "sys.h"
/* Represents raw bytes that can back a resource. This can be file data or embedded /* A resource contains data that can be retrieved globally by name.
* data in the executable. */ * If enabled during compilation, resource data is embedded in the
* executable. Otherwise each resource represents a file on disk. */
struct resource { struct resource {
struct string data; struct string _data;
#if RESOURCES_EMBEDDED
/* Internal */ struct string _file_name;
#if !RESOURCES_EMBEDDED #else
struct sys_file file; struct sys_file _file;
struct sys_file_map file_map; struct sys_file_map _file_map;
u8 _file_name_text[256];
u8 _file_name_len;
#endif #endif
}; };
struct resource_startup_receipt { i32 _; }; struct resource_startup_receipt { i32 _; };
struct resource_startup_receipt resource_startup(void); struct resource_startup_receipt resource_startup(void);
b32 resource_exists(struct string path);
struct resource resource_open(struct string path); struct resource resource_open(struct string path);
#define resource_get_data(res_ptr) (res_ptr)->_data
#if RESOURCES_EMBEDDED #if RESOURCES_EMBEDDED
/* Embedded resources don't need to be closed */ #define resource_close(res_ptr) (UNUSED)res_ptr
#define resource_close(res) (UNUSED)res #define resource_get_time(res_ptr) ((UNUSED)res_ptr, (struct sys_file_time) ZI)
#define resource_get_name(res_ptr) (res_ptr)->_file_name
#else #else
void resource_close(struct resource res); void resource_close(struct resource *res_ptr);
struct sys_file_time resource_get_time(struct resource *res_ptr);
#define resource_get_name(res_ptr) STRING((res_ptr)->_file_name_len, (res_ptr)->_file_name_text)
#endif #endif
b32 resource_exists(struct string path);
#endif #endif

View File

@ -42,14 +42,15 @@ INLINE void scratch_dbg_push(struct scratch_ctx *ctx, struct temp_arena *temp)
#endif #endif
} }
/* Any arena parameters in the calling function's context should be passed into this /* Any arena parameters in the calling function's scope should be passed into this
* function as a potential `conflict`. This is to prevent friction when the * function as a potential "conflict". This is to prevent friction in case the
* context's arena is itself a scratch arena (since parameterized arenas are * passed arena is itself a scratch arena from another scope (since
* often used to allocate persistent results for the caller). * parameterized arenas are often used to allocate persistent results for the
* caller).
* *
* Use `scratch_begin_no_conflict` instead if there is no arena in the current * Use `scratch_begin_no_conflict` instead if there is no arena in the current
* context that could potentially be a scratch arena. */ * scope that could potentially be a scratch arena from another scope. */
#define scratch_begin(c) _scratch_begin(c) #define scratch_begin(potential_conflict) _scratch_begin(potential_conflict)
INLINE struct temp_arena _scratch_begin(struct arena *potential_conflict) INLINE struct temp_arena _scratch_begin(struct arena *potential_conflict)
{ {
@ -72,7 +73,7 @@ INLINE struct temp_arena _scratch_begin(struct arena *potential_conflict)
/* This macro declares an unused "arena" variable that will error if an existing "arena" /* This macro declares an unused "arena" variable that will error if an existing "arena"
* variable is present (due to shadowing). This is for catching obvious cases of * variable is present (due to shadowing). This is for catching obvious cases of
* `scratch_begin_no_conflict` getting called when an `arena` variable already * `scratch_begin_no_conflict` getting called when an `arena` variable already
* exists in the caller's context (`scratch_begin(arena)` should be called * exists in the caller's scope (`scratch_begin(arena)` should be called
* instead). */ * instead). */
#define scratch_begin_no_conflict() \ #define scratch_begin_no_conflict() \
_scratch_begin_no_conflict(); \ _scratch_begin_no_conflict(); \

View File

@ -533,7 +533,6 @@ void sim_ent_apply_torque(struct sim_ent *ent, f32 torque)
struct string sim_ent_get_tile_chunk_data(struct sim_ent *ent) struct string sim_ent_get_tile_chunk_data(struct sim_ent *ent)
{ {
DEBUGBREAKABLE;
(UNUSED)ent; (UNUSED)ent;
struct string res = ZI; struct string res = ZI;
return res; return res;
@ -541,7 +540,6 @@ struct string sim_ent_get_tile_chunk_data(struct sim_ent *ent)
void sim_ent_set_tile_chunk_data(struct sim_ent *ent, struct string data) void sim_ent_set_tile_chunk_data(struct sim_ent *ent, struct string data)
{ {
DEBUGBREAKABLE;
(UNUSED)ent; (UNUSED)ent;
(UNUSED)data; (UNUSED)data;
} }

View File

@ -105,8 +105,8 @@ INTERNAL WORK_TASK_FUNC_DEF(sound_load_asset_task, vparams)
/* Decode */ /* Decode */
struct resource sound_rs = resource_open(path); struct resource sound_rs = resource_open(path);
struct mp3_decode_result decoded = mp3_decode(scratch.arena, sound_rs.data, decode_flags); struct mp3_decode_result decoded = mp3_decode(scratch.arena, resource_get_data(&sound_rs), decode_flags);
resource_close(sound_rs); resource_close(&sound_rs);
if (!decoded.success) { if (!decoded.success) {
success = false; success = false;

View File

@ -356,11 +356,11 @@ INTERNAL void cache_node_load_texture(struct cache_node *n, struct sprite_tag ta
struct ase_decode_image_result decoded = ZI; struct ase_decode_image_result decoded = ZI;
if (resource_exists(path)) { if (resource_exists(path)) {
struct resource texture_rs = resource_open(path); struct resource texture_rs = resource_open(path);
decoded = ase_decode_image(scratch.arena, texture_rs.data); decoded = ase_decode_image(scratch.arena, resource_get_data(&texture_rs));
#if RESOURCE_RELOADING #if RESOURCE_RELOADING
n->initial_resource_file_modified_time = sys_file_get_time(texture_rs.file).modified; n->initial_resource_file_modified_time = resource_get_time(&texture_rs).modified;
#endif #endif
resource_close(texture_rs); resource_close(&texture_rs);
/* Initialize */ /* Initialize */
n->texture = arena_push(&n->arena, struct sprite_texture); n->texture = arena_push(&n->arena, struct sprite_texture);
@ -660,11 +660,11 @@ INTERNAL void cache_node_load_sheet(struct cache_node *n, struct sprite_tag tag)
struct ase_decode_sheet_result decoded = ZI; struct ase_decode_sheet_result decoded = ZI;
if (resource_exists(path)) { if (resource_exists(path)) {
struct resource sheet_rs = resource_open(path); struct resource sheet_rs = resource_open(path);
decoded = ase_decode_sheet(scratch.arena, sheet_rs.data); decoded = ase_decode_sheet(scratch.arena, resource_get_data(&sheet_rs));
#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 = resource_get_time(&sheet_rs).modified;
#endif #endif
resource_close(sheet_rs); resource_close(&sheet_rs);
/* Initialize */ /* Initialize */
n->sheet = arena_push(&n->arena, struct sprite_sheet); n->sheet = arena_push(&n->arena, struct sprite_sheet);
@ -904,6 +904,8 @@ INTERNAL void *data_from_tag_internal(struct sprite_scope *scope, struct sprite_
} }
} }
/* FIXME: Spinlock until resource loaded if async */
return res; return res;
} }
@ -1085,7 +1087,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(sprite_evictor_thread_entry_point, arg)
current_file_time = sys_file_get_time(file).modified; current_file_time = sys_file_get_time(file).modified;
sys_file_close(file); sys_file_close(file);
} }
file_changed = MEMCMP_STRUCT(&n->initial_resource_file_modified_time, &current_file_time) != 0; file_changed = !MEMEQ_STRUCT(&n->initial_resource_file_modified_time, &current_file_time);
if (file_changed) { if (file_changed) {
switch (n->kind) { switch (n->kind) {
case CACHE_NODE_KIND_TEXTURE: { case CACHE_NODE_KIND_TEXTURE: {

View File

@ -1882,8 +1882,8 @@ void sys_panic(struct string msg)
{ {
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);
ASSERT(false);
/* FIXME: Atomic panic str */
wchar_t *wstr = G.panic_wstr; wchar_t *wstr = G.panic_wstr;
u64 wstr_len = 0; u64 wstr_len = 0;
@ -1911,6 +1911,11 @@ void sys_panic(struct string msg)
wstr[wstr_len] = 0; wstr[wstr_len] = 0;
#if RTC
MessageBoxExW(NULL, wstr, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND | MB_TOPMOST, 0);
ASSERT(false);
#endif
WRITE_BARRIER(); WRITE_BARRIER();
SetEvent(G.panic_event); SetEvent(G.panic_event);