diff --git a/res/shaders/common.hlsl b/res/shaders/common.hlsl new file mode 100644 index 00000000..147c380a --- /dev/null +++ b/res/shaders/common.hlsl @@ -0,0 +1,4 @@ +float4 linear_from_srgb(float4 srgb) +{ + return float4(pow(srgb.rgb, 2.2), srgb.a); +} diff --git a/res/shaders/grid.hlsl b/res/shaders/grid.hlsl index 151dc738..6ffeb4dc 100644 --- a/res/shaders/grid.hlsl +++ b/res/shaders/grid.hlsl @@ -1,3 +1,5 @@ +#include "res/shaders/common.hlsl" + struct vs_input { float4 pos : POSITION; float line_thickness : THICKNESS; @@ -27,11 +29,6 @@ cbuffer vs_constants : register(b0) float4x4 projection; }; -float4 linear_from_srgb(float4 srgb) -{ - return float4(pow(srgb.rgb, 2.2), srgb.a); -} - ps_input vs_main(vs_input input) { ps_input output; diff --git a/res/shaders/triangle.hlsl b/res/shaders/triangle.hlsl index 0bf39989..1b684dab 100644 --- a/res/shaders/triangle.hlsl +++ b/res/shaders/triangle.hlsl @@ -1,3 +1,5 @@ +#include "res/shaders/common.hlsl" + struct { SamplerState sampler0; Texture2D texture0; @@ -20,11 +22,6 @@ struct ps_input { float4 tint_lin : COLOR; }; -float4 linear_from_srgb(float4 srgb) -{ - return float4(pow(srgb.rgb, 2.2), srgb.a); -} - ps_input vs_main(vs_input input) { ps_input output; diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index e1bce9ce..de00b8a0 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -32,12 +32,12 @@ /* FIXME: Enable this and resolve unreleased references */ #if RTC # define D3D11_DEBUG 0 +# define D3D11_SHADER_DEBUG 1 #else # define D3D11_DEBUG 0 +# define D3D11_SHADER_DEBUG 0 #endif -#define D3D11_SHADER_DEBUG RTC - #define D3D11_SWAPCHAIN_FLAGS (DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) #define D3D11_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM) @@ -209,206 +209,14 @@ INTERNAL void send_constant_buffer_data(ID3D11Buffer *buffer, struct mat4x4 vp) ID3D11DeviceContext_Unmap(G.devcon, (ID3D11Resource *)buffer, 0); } -/* ========================== * - * Shader - * ========================== */ - -INTERNAL void init_shader_table(void) -{ - MEMZERO_ARRAY(G.shader_info); - - /* Triangle shader layout */ - G.shader_info[SHADER_TRIANGLE] = (struct dx11_shader_desc) { - .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 } - } - }; - - /* Grid shader layout */ - G.shader_info[SHADER_GRID] = (struct dx11_shader_desc) { - .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 }, - { "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 }, - { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 2, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 3, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 4, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } - } - }; - - 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) { - 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); - } -} - -/* 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 string error_str = ZI; - - shader->kind = shader_desc->kind; - shader->vertex_size = shader_desc->vertex_size; - - u32 flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; -#if D3D11_SHADER_DEBUG - flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS; -#else - flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; -#endif - - /* Compile shader */ - ID3DBlob *vs_blob = NULL; - ID3DBlob *ps_blob = NULL; - ID3DBlob *error_blob = NULL; - b32 success = false; - { - struct string shader_src = resource_get_data(src_res); - logf_info("Compiling shader \"%F\"", FMT_STR(resource_get_name(src_res))); - /* Compile shader */ - /* TODO: pre-compile shaders w/ FXC? */ - HRESULT hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, NULL, "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, 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; - } - } - } - - if (success && !error_blob) { - /* Get number of device layout elements from NULL terminated array */ - u32 elem_count = 0; - for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) { - const D3D11_INPUT_ELEMENT_DESC *d = &shader_desc->input_layout_desc[elem_count]; - if (d->SemanticName == NULL) { - break; - } - } - - /* 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); - } else { - error_str = LIT("Unknown error"); - if (error_blob) { - u64 error_blob_cstr_len = ID3D10Blob_GetBufferSize(error_blob); - char *error_blob_cstr = (char *)ID3D10Blob_GetBufferPointer(error_blob); - struct string error_blob_str = string_copy(arena, string_from_cstr(error_blob_cstr, error_blob_cstr_len)); - if (string_ends_with(error_blob_str, LIT("\n"))) { - /* Remove trailing newline */ - error_blob_str.len -= 1; - } - if (error_blob_str.len > 0) { - error_str = error_blob_str; - } - } - } - - if (vs_blob) { - ID3D10Blob_Release(vs_blob); - } - if (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_shader(struct dx11_shader *old_shader, struct dx11_shader_desc *desc) -{ - __prof; - struct temp_arena scratch = scratch_begin_no_conflict(); - { - struct string name = string_from_cstr_no_limit(desc->name_cstr); - struct string error_msg = ZI; - if (resource_exists(name)) { - struct resource src_res = resource_open(name); - { - struct dx11_shader new_shader = ZI; - struct string comp_error = shader_alloc(scratch.arena, &new_shader, desc, &src_res); - if (comp_error.len == 0) { - if (old_shader->valid) { - shader_release(old_shader); - } - *old_shader = new_shader; - } else { - error_msg = string_format(scratch.arena, - LIT("Failed to compile shader \"%F\":\n%F"), - FMT_STR(name), - FMT_STR(comp_error)); - shader_release(&new_shader); - } - } - resource_close(&src_res); - } else { - error_msg = string_format(scratch.arena, LIT("Could not find shader \"%F\""), FMT_STR(name)); - } - if (error_msg.len != 0) { - if (old_shader->valid) { - /* If shader failed to load but a working shader already exists, just error rather than panicking */ - log_error(error_msg); - } else { - sys_panic(error_msg); - } - } - } - scratch_end(scratch); -} - -#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) { - logf_info("Shader source file \"%F\" has changed", FMT_STR(name)); - atomic_i32_eval_exchange(&desc->should_reload, 1); - } -} -#endif - /* ========================== * * Startup * ========================== */ +INTERNAL void init_shader_table(void); +INTERNAL void reload_shader(struct dx11_shader *shader, struct dx11_shader_desc *desc); +INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name); + struct renderer_startup_receipt renderer_startup(struct sys_window *window) { __profscope(initializing_d3d11); @@ -632,6 +440,284 @@ struct renderer_startup_receipt renderer_startup(struct sys_window *window) return (struct renderer_startup_receipt) { 0 }; } +/* ========================== * + * Shader table + * ========================== */ + +INTERNAL void init_shader_table(void) +{ + MEMZERO_ARRAY(G.shader_info); + + /* Triangle shader layout */ + G.shader_info[SHADER_TRIANGLE] = (struct dx11_shader_desc) { + .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 } + } + }; + + /* Grid shader layout */ + G.shader_info[SHADER_GRID] = (struct dx11_shader_desc) { + .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 }, + { "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 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 2, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 3, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 4, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 } + } + }; + + 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) { + 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); + } +} + +/* ========================== * + * Shader compilation + * ========================== */ + +/* TODO: Multithread shader compilation */ + +#define DX11_MAX_INCLUDES 64 + +struct dx11_include_handler { + ID3DInclude d3d_handler; + ID3DIncludeVtbl vtbl; + 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) +{ + __prof; + (UNUSED)include_type; + (UNUSED)parent_data; + struct dx11_include_handler *handler = (struct dx11_include_handler *)d3d_handler; + + 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)) { + handler->has_open_resource = true; + struct resource *res = &handler->res; + *res = resource_open(path); + 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; + } + + return S_OK; +} + +INTERNAL HRESULT dx11_include_close(ID3DInclude *d3d_handler, LPCVOID data) +{ + __prof; + (UNUSED)data; + struct dx11_include_handler *handler = (struct dx11_include_handler *)d3d_handler; + if (handler->has_open_resource) { + resource_close(&handler->res); + handler->has_open_resource = false; + } + return S_OK; +} + +INTERNAL struct dx11_include_handler dx11_include_handler_alloc(void) +{ + struct dx11_include_handler handler = ZI; + handler.d3d_handler.lpVtbl = &handler.vtbl; + handler.vtbl.Open = dx11_include_open; + handler.vtbl.Close = dx11_include_close; + return handler; +} + +INTERNAL void dx11_include_handler_release(struct dx11_include_handler *handler) +{ + if (handler->has_open_resource) { + ASSERT(false); /* Resource should have been closed by handler by now */ + resource_close(&handler->res); + } +} + +/* 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; + + shader->kind = shader_desc->kind; + shader->vertex_size = shader_desc->vertex_size; + + u32 flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; +#if D3D11_SHADER_DEBUG + flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS; +#else + flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; +#endif + + /* Compile shader */ + ID3DBlob *vs_blob = NULL; + ID3DBlob *ps_blob = NULL; + ID3DBlob *error_blob = NULL; + b32 success = false; + { + struct string shader_src = resource_get_data(src_res); + logf_info("Compiling shader \"%F\"", FMT_STR(resource_get_name(src_res))); + /* 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); + 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); + if (SUCCEEDED(hr)) { + ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps); + success = true; + } + } + } + + if (success && !error_blob) { + /* Get number of device layout elements from NULL terminated array */ + u32 elem_count = 0; + for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) { + const D3D11_INPUT_ELEMENT_DESC *d = &shader_desc->input_layout_desc[elem_count]; + if (d->SemanticName == NULL) { + break; + } + } + + /* 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); + } else { + error_str = LIT("Unknown error"); + if (error_blob) { + u64 error_blob_cstr_len = ID3D10Blob_GetBufferSize(error_blob); + char *error_blob_cstr = (char *)ID3D10Blob_GetBufferPointer(error_blob); + struct string error_blob_str = string_copy(scratch.arena, string_from_cstr(error_blob_cstr, error_blob_cstr_len)); + if (string_ends_with(error_blob_str, LIT("\n"))) { + /* Remove trailing newline */ + 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)); + } else { + error_str = string_copy(arena, error_blob_str); + } + } + } + } + + if (vs_blob) { + ID3D10Blob_Release(vs_blob); + } + if (ps_blob) { + ID3D10Blob_Release(ps_blob); + } + if (error_blob) { + ID3D10Blob_Release(error_blob); + } + + shader->valid = true; + + dx11_include_handler_release(&ps_include_handler); + dx11_include_handler_release(&vs_include_handler); + 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_shader(struct dx11_shader *old_shader, struct dx11_shader_desc *desc) +{ + __prof; + struct temp_arena scratch = scratch_begin_no_conflict(); + { + struct string name = string_from_cstr_no_limit(desc->name_cstr); + struct string error_msg = ZI; + if (resource_exists(name)) { + struct resource src_res = resource_open(name); + { + struct dx11_shader new_shader = ZI; + struct string comp_error = shader_alloc(scratch.arena, &new_shader, desc, &src_res); + if (comp_error.len == 0) { + if (old_shader->valid) { + shader_release(old_shader); + } + *old_shader = new_shader; + } else { + error_msg = string_format(scratch.arena, + LIT("Failed to compile shader \"%F\":\n\n%F"), + FMT_STR(name), + FMT_STR(comp_error)); + shader_release(&new_shader); + } + } + resource_close(&src_res); + } else { + error_msg = string_format(scratch.arena, LIT("Could not find shader \"%F\""), FMT_STR(name)); + } + if (error_msg.len != 0) { + if (old_shader->valid) { + /* If shader failed to load but a working shader already exists, just error rather than panicking */ + log_error(error_msg); + } else { + sys_panic(error_msg); + } + } + } + scratch_end(scratch); +} + +#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) { + logf_info("Shader source file \"%F\" has changed", FMT_STR(name)); + atomic_i32_eval_exchange(&desc->should_reload, 1); + } +} +#endif + /* ========================== * * Render * ========================== */