shader hot reloading
This commit is contained in:
parent
58b31f9192
commit
abac85d324
24
build.c
24
build.c
@ -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,11 +657,9 @@ 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) {
|
||||||
@ -694,23 +691,16 @@ 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);
|
D_AddDependency(&store, rc_input_file, res_tar_file);
|
||||||
if (should_embed_res_dir) {
|
|
||||||
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))));
|
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))));
|
||||||
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))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
10
src/inc.c
10
src/inc.c
@ -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);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,50 +246,126 @@ 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)) {
|
||||||
HRESULT p_res = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, NULL, "ps_main", "ps_5_0", flags, 0, &ps_blob, &error_blob);
|
ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps);
|
||||||
if (FAILED(p_res)) {
|
success = true;
|
||||||
process_shader_compilation_error(error_blob);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get number of device layout elements from NULL terminated array */
|
if (success && !error_blob) {
|
||||||
u32 elem_count = 0;
|
/* Get number of device layout elements from NULL terminated array */
|
||||||
for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) {
|
u32 elem_count = 0;
|
||||||
const D3D11_INPUT_ELEMENT_DESC *d = &shader_desc->input_layout_desc[elem_count];
|
for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) {
|
||||||
if (d->SemanticName == NULL) {
|
const D3D11_INPUT_ELEMENT_DESC *d = &shader_desc->input_layout_desc[elem_count];
|
||||||
break;
|
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_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 device layout */
|
if (vs_blob) {
|
||||||
ID3D11Device_CreateInputLayout(G.dev, shader_desc->input_layout_desc, elem_count, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &shader->input_layout);
|
ID3D10Blob_Release(vs_blob);
|
||||||
|
}
|
||||||
|
if (ps_blob) {
|
||||||
|
ID3D10Blob_Release(ps_blob);
|
||||||
|
}
|
||||||
|
if (error_blob) {
|
||||||
|
ID3D10Blob_Release(error_blob);
|
||||||
|
}
|
||||||
|
|
||||||
/* Create shader */
|
shader->valid = true;
|
||||||
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);
|
scratch_end(scratch);
|
||||||
ID3D10Blob_Release(ps_blob);
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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(); \
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
16
src/sprite.c
16
src/sprite.c
@ -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, ¤t_file_time) != 0;
|
file_changed = !MEMEQ_STRUCT(&n->initial_resource_file_modified_time, ¤t_file_time);
|
||||||
if (file_changed) {
|
if (file_changed) {
|
||||||
switch (n->kind) {
|
switch (n->kind) {
|
||||||
case CACHE_NODE_KIND_TEXTURE: {
|
case CACHE_NODE_KIND_TEXTURE: {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user