From b228ffe7b6fd9053a52270e196a11308a3d62132 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 23 May 2025 20:04:59 -0500 Subject: [PATCH] hot reloading for included shaders --- res/shaders/common.hlsl | 5 ++ res/shaders/triangle.hlsl | 3 +- src/renderer_d3d11.c | 150 +++++++++++++++++++++++++++++--------- src/util.h | 9 +++ 4 files changed, 130 insertions(+), 37 deletions(-) diff --git a/res/shaders/common.hlsl b/res/shaders/common.hlsl index 147c380a..600817e7 100644 --- a/res/shaders/common.hlsl +++ b/res/shaders/common.hlsl @@ -2,3 +2,8 @@ float4 linear_from_srgb(float4 srgb) { return float4(pow(srgb.rgb, 2.2), srgb.a); } + +float bla(float a) +{ + return a; +} diff --git a/res/shaders/triangle.hlsl b/res/shaders/triangle.hlsl index 1b684dab..f4fa3236 100644 --- a/res/shaders/triangle.hlsl +++ b/res/shaders/triangle.hlsl @@ -35,5 +35,6 @@ ps_input vs_main(vs_input input) float4 ps_main(ps_input input) : SV_TARGET { - return globals.texture0.Sample(globals.sampler0, input.uv) * input.tint_lin; + float4 color = globals.texture0.Sample(globals.sampler0, input.uv) * input.tint_lin; + return color; } diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index de00b8a0..f51fab78 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -43,6 +43,8 @@ #define D3D11_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM) #define D3D11_SWAPCHAIN_RTV_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) +#define SHADER_NAME_LEN 255 + struct dx11_shader { enum shader_kind kind; b32 valid; /* Is this shader allocated */ @@ -50,6 +52,9 @@ struct dx11_shader { ID3D11InputLayout *input_layout; ID3D11VertexShader *vs; ID3D11PixelShader *ps; + + struct string name; + u8 name_text[SHADER_NAME_LEN]; }; struct dx11_constant_buffer_data { @@ -145,6 +150,9 @@ struct dx11_shader_desc { /* Internal */ #if RESOURCE_RELOADING + struct arena includes_arena; + struct sys_mutex includes_mutex; + struct dict includes_dict; struct atomic_i32 should_reload; #endif }; @@ -181,7 +189,6 @@ GLOBAL struct { struct dx11_shader shaders[NUM_SHADERS]; struct dx11_shader_desc shader_info[NUM_SHADERS]; - struct dict shader_info_lookup; } G = ZI, DEBUG_ALIAS(G, G_renderer_d3d11); @@ -215,7 +222,10 @@ INTERNAL void send_constant_buffer_data(ID3D11Buffer *buffer, struct mat4x4 vp) INTERNAL void init_shader_table(void); INTERNAL void reload_shader(struct dx11_shader *shader, struct dx11_shader_desc *desc); + +#if RESOURCE_RELOADING INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name); +#endif struct renderer_startup_receipt renderer_startup(struct sys_window *window) { @@ -478,57 +488,109 @@ INTERNAL void init_shader_table(void) } }; - G.shader_info_lookup = dict_init(&G.arena, SHADER_INFO_LOOKUP_BINS); - for (u64 i = SHADER_NONE + 1; i < ARRAY_COUNT(G.shader_info); ++i) { +#if RESOURCE_RELOADING + for (u64 i = 0; i < ARRAY_COUNT(G.shader_info); ++i) { struct dx11_shader_desc *desc = &G.shader_info[i]; - struct string name = string_from_cstr_no_limit(desc->name_cstr); - u64 hash = hash_fnv64(HASH_FNV64_BASIS, name); - dict_set(&G.arena, &G.shader_info_lookup, hash, desc); + desc->includes_arena = arena_alloc(MEGABYTE(8)); + desc->includes_mutex = sys_mutex_alloc(); + desc->includes_dict = dict_init(&desc->includes_arena, 64); } +#endif } /* ========================== * - * Shader compilation + * Shader dirty check * ========================== */ -/* TODO: Multithread shader compilation */ +#if RESOURCE_RELOADING +INTERNAL void shader_add_include(struct dx11_shader_desc *desc, struct string include_name_src) +{ + u64 hash = hash_fnv64(HASH_FNV64_BASIS, include_name_src); + struct dict *dict = &desc->includes_dict; + struct sys_lock lock = sys_mutex_lock_e(&desc->includes_mutex); + { + dict_set(&desc->includes_arena, dict, hash, 1); + } + sys_mutex_unlock(&lock); +} -#define DX11_MAX_INCLUDES 64 +INTERNAL void shader_reset_includes(struct dx11_shader_desc *desc) +{ + struct dict *dict = &desc->includes_dict; + struct sys_lock lock = sys_mutex_lock_e(&desc->includes_mutex); + { + dict_reset(dict); + } + sys_mutex_unlock(&lock); +} + +INTERNAL b32 shader_set_dirty(struct string name) +{ + b32 caused_dirty = false; + for (u64 i = 0; i < NUM_SHADERS; ++i) { + struct dx11_shader_desc *desc = &G.shader_info[i]; + struct string desc_name = string_from_cstr_no_limit(desc->name_cstr); + if (string_eq(desc_name, name)) { + atomic_i32_eval_exchange(&desc->should_reload, 1); + caused_dirty = true; + } else { + struct dict *includes_dict = &desc->includes_dict; + u64 hash = hash_fnv64(HASH_FNV64_BASIS, name); + struct sys_lock lock = sys_mutex_lock_e(&desc->includes_mutex); + { + if (dict_get(includes_dict, hash) != 0) { + atomic_i32_eval_exchange(&desc->should_reload, 1); + caused_dirty = true; + } + } + sys_mutex_unlock(&lock); + } + } + return caused_dirty; +} +#endif + +/* ========================== * + * Shader include handler + * ========================== */ struct dx11_include_handler { ID3DInclude d3d_handler; ID3DIncludeVtbl vtbl; + struct dx11_shader *shader; struct string error; b32 has_open_resource; struct resource res; }; -INTERNAL HRESULT dx11_include_open(ID3DInclude *d3d_handler, D3D_INCLUDE_TYPE include_type, LPCSTR path_cstr, LPCVOID parent_data, LPCVOID *data_out, UINT *data_len_out) +INTERNAL HRESULT dx11_include_open(ID3DInclude *d3d_handler, D3D_INCLUDE_TYPE include_type, LPCSTR name_cstr, LPCVOID parent_data, LPCVOID *data_out, UINT *data_len_out) { __prof; (UNUSED)include_type; (UNUSED)parent_data; + HRESULT result = E_FAIL; struct dx11_include_handler *handler = (struct dx11_include_handler *)d3d_handler; + struct string name = string_from_cstr_no_limit((char *)name_cstr); if (handler->has_open_resource) { sys_panic(LIT("D3d include handler somehow already has a resource open")); } - struct string path = string_from_cstr_no_limit((char *)path_cstr); - if (resource_exists(path)) { + if (resource_exists(name)) { handler->has_open_resource = true; struct resource *res = &handler->res; - *res = resource_open(path); + *res = resource_open(name); struct string data = resource_get_data(res); *data_out = data.text; *data_len_out = data.len; - return S_OK; - } else { - handler->error = LIT("Resource not found."); - return E_FAIL; + result = S_OK; } - return S_OK; +#if RESOURCE_RELOADING + shader_add_include(&G.shader_info[handler->shader->kind], name); +#endif + + return result; } INTERNAL HRESULT dx11_include_close(ID3DInclude *d3d_handler, LPCVOID data) @@ -543,12 +605,13 @@ INTERNAL HRESULT dx11_include_close(ID3DInclude *d3d_handler, LPCVOID data) return S_OK; } -INTERNAL struct dx11_include_handler dx11_include_handler_alloc(void) +INTERNAL struct dx11_include_handler dx11_include_handler_alloc(struct dx11_shader *shader) { struct dx11_include_handler handler = ZI; handler.d3d_handler.lpVtbl = &handler.vtbl; handler.vtbl.Open = dx11_include_open; handler.vtbl.Close = dx11_include_close; + handler.shader = shader; return handler; } @@ -560,17 +623,37 @@ INTERNAL void dx11_include_handler_release(struct dx11_include_handler *handler) } } +/* ========================== * + * Shader compilation + * ========================== */ + + /* TODO: Multithread shader compilation */ + /* 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; struct temp_arena scratch = scratch_begin(arena); - struct dx11_include_handler vs_include_handler = dx11_include_handler_alloc(); - struct dx11_include_handler ps_include_handler = dx11_include_handler_alloc(); struct string error_str = ZI; + struct string res_name = resource_get_name(src_res); + if (res_name.len > SHADER_NAME_LEN) { + sys_panic(string_format(scratch.arena, + LIT("Shader name \"%F\" too long (%F > %F)"), + FMT_STR(res_name), + FMT_UINT(res_name.len), + FMT_UINT(SHADER_NAME_LEN))); + } + + MEMCPY(shader->name_text, res_name.text, res_name.len); + shader->name = STRING(res_name.len, shader->name_text); shader->kind = shader_desc->kind; shader->vertex_size = shader_desc->vertex_size; +#if RESOURCE_RELOADING + shader_reset_includes(shader_desc); +#endif + + struct dx11_include_handler include_handler = dx11_include_handler_alloc(shader); u32 flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; #if D3D11_SHADER_DEBUG @@ -586,13 +669,13 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha b32 success = false; { struct string shader_src = resource_get_data(src_res); - logf_info("Compiling shader \"%F\"", FMT_STR(resource_get_name(src_res))); + logf_info("Compiling shader \"%F\"", FMT_STR(shader->name)); /* Compile shader */ /* TODO: pre-compile shaders w/ FXC? */ - HRESULT hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&vs_include_handler, "vs_main", "vs_5_0", flags, 0, &vs_blob, &error_blob); + HRESULT hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&include_handler, "vs_main", "vs_5_0", flags, 0, &vs_blob, &error_blob); if (SUCCEEDED(hr)) { ID3D11Device_CreateVertexShader(G.dev, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), NULL, &shader->vs); - hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&ps_include_handler, "ps_main", "ps_5_0", flags, 0, &ps_blob, &error_blob); + hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&include_handler, "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; @@ -623,9 +706,8 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha error_blob_str.len -= 1; } if (error_blob_str.len > 0) { - struct string include_error = vs_include_handler.error.len > 0 ? vs_include_handler.error : ps_include_handler.error; - if (include_error.len > 0) { - error_str = string_format(arena, LIT("%F %F"), FMT_STR(error_blob_str), FMT_STR(include_error)); + if (include_handler.error.len > 0) { + error_str = string_format(arena, LIT("%F %F"), FMT_STR(error_blob_str), FMT_STR(include_handler.error)); } else { error_str = string_copy(arena, error_blob_str); } @@ -645,8 +727,7 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha shader->valid = true; - dx11_include_handler_release(&ps_include_handler); - dx11_include_handler_release(&vs_include_handler); + dx11_include_handler_release(&include_handler); scratch_end(scratch); return error_str; } @@ -709,11 +790,8 @@ INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_d #if RESOURCE_RELOADING INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name) { - u64 hash = hash_fnv64(HASH_FNV64_BASIS, name); - struct dx11_shader_desc *desc = (struct dx11_shader_desc *)dict_get(&G.shader_info_lookup, hash); - if (desc) { + if (shader_set_dirty(name)) { logf_info("Shader source file \"%F\" has changed", FMT_STR(name)); - atomic_i32_eval_exchange(&desc->should_reload, 1); } } #endif @@ -751,7 +829,7 @@ INTERNAL void dx11_render(ID3D11RenderTargetView *target, struct renderer_cmd_bu ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &G.vs_constant_buffer); ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 1, &G.vs_constant_buffer); - struct dx11_shader *last_shader = NULL; + struct dx11_shader *prev_shader = NULL; struct renderer_cmd *cmd = cmdbuff ? cmdbuff->gpu_cmd_store.cmd_first : NULL; for (; cmd; cmd = cmd->next) { @@ -759,11 +837,11 @@ INTERNAL void dx11_render(ID3D11RenderTargetView *target, struct renderer_cmd_bu struct dx11_buffer *buffer = &cmdbuff->buffers[shader->kind]; /* Activate shader */ - if (shader != last_shader) { + if (shader != prev_shader) { ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0); ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0); ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout); - last_shader = shader; + prev_shader = shader; } ID3D11ShaderResourceView *null_srv[1] = { NULL }; diff --git a/src/util.h b/src/util.h index 7a96b6ae..0e86fe80 100644 --- a/src/util.h +++ b/src/util.h @@ -136,6 +136,15 @@ INLINE struct dict dict_init(struct arena *arena, u64 bins_count) return dict; } +INLINE void dict_reset(struct dict *dict) +{ + MEMZERO(dict->bins, sizeof(*dict->bins) * dict->bins_count); + if (dict->first) { + dict->last->next = dict->first_free; + dict->first_free = dict->first; + } +} + INLINE struct dict_entry *dict_ensure_entry(struct arena *arena, struct dict *dict, u64 hash) { __prof;