hot reloading for included shaders

This commit is contained in:
jacob 2025-05-23 20:04:59 -05:00
parent a1fcf2ce08
commit b228ffe7b6
4 changed files with 130 additions and 37 deletions

View File

@ -2,3 +2,8 @@ float4 linear_from_srgb(float4 srgb)
{ {
return float4(pow(srgb.rgb, 2.2), srgb.a); return float4(pow(srgb.rgb, 2.2), srgb.a);
} }
float bla(float a)
{
return a;
}

View File

@ -35,5 +35,6 @@ ps_input vs_main(vs_input input)
float4 ps_main(ps_input input) : SV_TARGET 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;
} }

View File

@ -43,6 +43,8 @@
#define D3D11_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM) #define D3D11_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM)
#define D3D11_SWAPCHAIN_RTV_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) #define D3D11_SWAPCHAIN_RTV_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
#define SHADER_NAME_LEN 255
struct dx11_shader { struct dx11_shader {
enum shader_kind kind; enum shader_kind kind;
b32 valid; /* Is this shader allocated */ b32 valid; /* Is this shader allocated */
@ -50,6 +52,9 @@ struct dx11_shader {
ID3D11InputLayout *input_layout; ID3D11InputLayout *input_layout;
ID3D11VertexShader *vs; ID3D11VertexShader *vs;
ID3D11PixelShader *ps; ID3D11PixelShader *ps;
struct string name;
u8 name_text[SHADER_NAME_LEN];
}; };
struct dx11_constant_buffer_data { struct dx11_constant_buffer_data {
@ -145,6 +150,9 @@ struct dx11_shader_desc {
/* Internal */ /* Internal */
#if RESOURCE_RELOADING #if RESOURCE_RELOADING
struct arena includes_arena;
struct sys_mutex includes_mutex;
struct dict includes_dict;
struct atomic_i32 should_reload; struct atomic_i32 should_reload;
#endif #endif
}; };
@ -181,7 +189,6 @@ GLOBAL struct {
struct dx11_shader shaders[NUM_SHADERS]; struct dx11_shader shaders[NUM_SHADERS];
struct dx11_shader_desc shader_info[NUM_SHADERS]; struct dx11_shader_desc shader_info[NUM_SHADERS];
struct dict shader_info_lookup;
} G = ZI, DEBUG_ALIAS(G, G_renderer_d3d11); } 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 init_shader_table(void);
INTERNAL void reload_shader(struct dx11_shader *shader, struct dx11_shader_desc *desc); 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); INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name);
#endif
struct renderer_startup_receipt renderer_startup(struct sys_window *window) 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); #if RESOURCE_RELOADING
for (u64 i = SHADER_NONE + 1; i < ARRAY_COUNT(G.shader_info); ++i) { for (u64 i = 0; i < ARRAY_COUNT(G.shader_info); ++i) {
struct dx11_shader_desc *desc = &G.shader_info[i]; struct dx11_shader_desc *desc = &G.shader_info[i];
struct string name = string_from_cstr_no_limit(desc->name_cstr); desc->includes_arena = arena_alloc(MEGABYTE(8));
u64 hash = hash_fnv64(HASH_FNV64_BASIS, name); desc->includes_mutex = sys_mutex_alloc();
dict_set(&G.arena, &G.shader_info_lookup, hash, desc); 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 { struct dx11_include_handler {
ID3DInclude d3d_handler; ID3DInclude d3d_handler;
ID3DIncludeVtbl vtbl; ID3DIncludeVtbl vtbl;
struct dx11_shader *shader;
struct string error; struct string error;
b32 has_open_resource; b32 has_open_resource;
struct resource res; 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; __prof;
(UNUSED)include_type; (UNUSED)include_type;
(UNUSED)parent_data; (UNUSED)parent_data;
HRESULT result = E_FAIL;
struct dx11_include_handler *handler = (struct dx11_include_handler *)d3d_handler; 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) { if (handler->has_open_resource) {
sys_panic(LIT("D3d include handler somehow already has a resource open")); 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(name)) {
if (resource_exists(path)) {
handler->has_open_resource = true; handler->has_open_resource = true;
struct resource *res = &handler->res; struct resource *res = &handler->res;
*res = resource_open(path); *res = resource_open(name);
struct string data = resource_get_data(res); struct string data = resource_get_data(res);
*data_out = data.text; *data_out = data.text;
*data_len_out = data.len; *data_len_out = data.len;
return S_OK; result = S_OK;
} else {
handler->error = LIT("Resource not found.");
return E_FAIL;
} }
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) 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; 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; struct dx11_include_handler handler = ZI;
handler.d3d_handler.lpVtbl = &handler.vtbl; handler.d3d_handler.lpVtbl = &handler.vtbl;
handler.vtbl.Open = dx11_include_open; handler.vtbl.Open = dx11_include_open;
handler.vtbl.Close = dx11_include_close; handler.vtbl.Close = dx11_include_close;
handler.shader = shader;
return handler; 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` */ /* 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) INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *shader, struct dx11_shader_desc *shader_desc, struct resource *src_res)
{ {
__prof; __prof;
struct temp_arena scratch = scratch_begin(arena); 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 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->kind = shader_desc->kind;
shader->vertex_size = shader_desc->vertex_size; 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; u32 flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;
#if D3D11_SHADER_DEBUG #if D3D11_SHADER_DEBUG
@ -586,13 +669,13 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha
b32 success = false; b32 success = false;
{ {
struct string shader_src = resource_get_data(src_res); 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 */ /* Compile shader */
/* TODO: pre-compile shaders w/ FXC? */ /* 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)) { if (SUCCEEDED(hr)) {
ID3D11Device_CreateVertexShader(G.dev, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), NULL, &shader->vs); 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)) { if (SUCCEEDED(hr)) {
ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps); ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps);
success = true; success = true;
@ -623,9 +706,8 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha
error_blob_str.len -= 1; error_blob_str.len -= 1;
} }
if (error_blob_str.len > 0) { 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_handler.error.len > 0) {
if (include_error.len > 0) { error_str = string_format(arena, LIT("%F %F"), FMT_STR(error_blob_str), FMT_STR(include_handler.error));
error_str = string_format(arena, LIT("%F %F"), FMT_STR(error_blob_str), FMT_STR(include_error));
} else { } else {
error_str = string_copy(arena, error_blob_str); 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; shader->valid = true;
dx11_include_handler_release(&ps_include_handler); dx11_include_handler_release(&include_handler);
dx11_include_handler_release(&vs_include_handler);
scratch_end(scratch); scratch_end(scratch);
return error_str; return error_str;
} }
@ -709,11 +790,8 @@ INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_d
#if RESOURCE_RELOADING #if RESOURCE_RELOADING
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name) INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name)
{ {
u64 hash = hash_fnv64(HASH_FNV64_BASIS, name); if (shader_set_dirty(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)); logf_info("Shader source file \"%F\" has changed", FMT_STR(name));
atomic_i32_eval_exchange(&desc->should_reload, 1);
} }
} }
#endif #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_VSSetConstantBuffers(G.devcon, 0, 1, &G.vs_constant_buffer);
ID3D11DeviceContext_PSSetConstantBuffers(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; struct renderer_cmd *cmd = cmdbuff ? cmdbuff->gpu_cmd_store.cmd_first : NULL;
for (; cmd; cmd = cmd->next) { 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]; struct dx11_buffer *buffer = &cmdbuff->buffers[shader->kind];
/* Activate shader */ /* Activate shader */
if (shader != last_shader) { if (shader != prev_shader) {
ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0); ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0);
ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0); ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0);
ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout); ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout);
last_shader = shader; prev_shader = shader;
} }
ID3D11ShaderResourceView *null_srv[1] = { NULL }; ID3D11ShaderResourceView *null_srv[1] = { NULL };

View File

@ -136,6 +136,15 @@ INLINE struct dict dict_init(struct arena *arena, u64 bins_count)
return dict; 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) INLINE struct dict_entry *dict_ensure_entry(struct arena *arena, struct dict *dict, u64 hash)
{ {
__prof; __prof;