resource reloading via file change notifications
This commit is contained in:
parent
abac85d324
commit
86a696a70a
@ -18,8 +18,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* If we are not compiling in developer mode, assume resources are embedded as
|
/* If we are not compiling in developer mode, assume resources are embedded as
|
||||||
* a tar archive in the executable. Otherwise, look for resources in the file
|
* a tar archive in the executable. Otherwise, assume resources are files on
|
||||||
* system. */
|
* disk. */
|
||||||
#define RESOURCES_EMBEDDED (!DEVELOPER)
|
#define RESOURCES_EMBEDDED (!DEVELOPER)
|
||||||
#define RESOURCE_RELOADING (DEVELOPER && !RESOURCES_EMBEDDED)
|
#define RESOURCE_RELOADING (DEVELOPER && !RESOURCES_EMBEDDED)
|
||||||
|
|
||||||
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
#define IMAGE_PIXELS_PER_UNIT 128.0
|
#define IMAGE_PIXELS_PER_UNIT 128.0
|
||||||
|
|
||||||
/* How many ticks back in time should the user blend between?
|
/* How many ticks back in time should the user thread blend between?
|
||||||
* <Delay> = <USER_INTERP_RATIO> * <Tick interval>
|
* <Delay> = <USER_INTERP_RATIO> * <Tick interval>
|
||||||
* E.g: At 1.5, the user thread will render 75ms back in time if the sim runs at 50tps
|
* E.g: At 1.5, the user thread will render 75ms back in time if the sim runs at 50tps
|
||||||
*/
|
*/
|
||||||
|
|||||||
30
src/log.h
30
src/log.h
@ -59,67 +59,67 @@ void log_register_callback(log_event_callback_func *func);
|
|||||||
#if LOG_LEVEL(LOG_LEVEL_CRITICAL)
|
#if LOG_LEVEL(LOG_LEVEL_CRITICAL)
|
||||||
# if LOG_INCLUDE_SOURCE_LOCATION
|
# if LOG_INCLUDE_SOURCE_LOCATION
|
||||||
# define log_critical(msg) _log(LOG_LEVEL_CRITICAL, LIT(__FILE__), __LINE__, msg)
|
# define log_critical(msg) _log(LOG_LEVEL_CRITICAL, LIT(__FILE__), __LINE__, msg)
|
||||||
# define logf_critical(fmt, ...) _logf(LOG_LEVEL_CRITICAL, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_critical(fmt_lit, ...) _logf(LOG_LEVEL_CRITICAL, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# else
|
# else
|
||||||
# define log_critical(msg) _log(LOG_LEVEL_CRITICAL, msg)
|
# define log_critical(msg) _log(LOG_LEVEL_CRITICAL, msg)
|
||||||
# define logf_critical(fmt, ...) _logf(LOG_LEVEL_CRITICAL, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_critical(fmt_lit, ...) _logf(LOG_LEVEL_CRITICAL, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# define log_critical(msg)
|
# define log_critical(msg)
|
||||||
# define logf_critical(fmt, ...)
|
# define logf_critical(fmt_lit, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LOG_LEVEL(LOG_LEVEL_ERROR)
|
#if LOG_LEVEL(LOG_LEVEL_ERROR)
|
||||||
# if LOG_INCLUDE_SOURCE_LOCATION
|
# if LOG_INCLUDE_SOURCE_LOCATION
|
||||||
# define log_error(msg) _log(LOG_LEVEL_ERROR, LIT(__FILE__), __LINE__, msg)
|
# define log_error(msg) _log(LOG_LEVEL_ERROR, LIT(__FILE__), __LINE__, msg)
|
||||||
# define logf_error(fmt, ...) _logf(LOG_LEVEL_ERROR, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_error(fmt_lit, ...) _logf(LOG_LEVEL_ERROR, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# else
|
# else
|
||||||
# define log_error(msg) _log(LOG_LEVEL_ERROR, msg)
|
# define log_error(msg) _log(LOG_LEVEL_ERROR, msg)
|
||||||
# define logf_error(fmt, ...) _logf(LOG_LEVEL_ERROR, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_error(fmt_lit, ...) _logf(LOG_LEVEL_ERROR, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# define log_error(msg)
|
# define log_error(msg)
|
||||||
# define logf_error(fmt, ...)
|
# define logf_error(fmt_lit, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LOG_LEVEL(LOG_LEVEL_WARNING)
|
#if LOG_LEVEL(LOG_LEVEL_WARNING)
|
||||||
# if LOG_INCLUDE_SOURCE_LOCATION
|
# if LOG_INCLUDE_SOURCE_LOCATION
|
||||||
# define log_warning(msg) _log(LOG_LEVEL_WARNING, LIT(__FILE__), __LINE__, msg)
|
# define log_warning(msg) _log(LOG_LEVEL_WARNING, LIT(__FILE__), __LINE__, msg)
|
||||||
# define logf_warning(fmt, ...) _logf(LOG_LEVEL_WARNING, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_warning(fmt_lit, ...) _logf(LOG_LEVEL_WARNING, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# else
|
# else
|
||||||
# define log_warning(msg) _log(LOG_LEVEL_WARNING, msg)
|
# define log_warning(msg) _log(LOG_LEVEL_WARNING, msg)
|
||||||
# define logf_warning(fmt, ...) _logf(LOG_LEVEL_WARNING, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_warning(fmt_lit, ...) _logf(LOG_LEVEL_WARNING, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# define log_warning(msg)
|
# define log_warning(msg)
|
||||||
# define logf_warning(fmt, ...)
|
# define logf_warning(fmt_lit, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LOG_LEVEL(LOG_LEVEL_DEBUG)
|
#if LOG_LEVEL(LOG_LEVEL_DEBUG)
|
||||||
# if LOG_INCLUDE_SOURCE_LOCATION
|
# if LOG_INCLUDE_SOURCE_LOCATION
|
||||||
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, msg)
|
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, msg)
|
||||||
# define logf_debug(fmt, ...) _logf(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# else
|
# else
|
||||||
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, msg)
|
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, msg)
|
||||||
# define logf_debug(fmt, ...) _logf(LOG_LEVEL_DEBUG, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# define log_debug(msg)
|
# define log_debug(msg)
|
||||||
# define logf_debug(fmt, ...)
|
# define logf_debug(fmt_lit, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if LOG_LEVEL(LOG_LEVEL_INFO)
|
#if LOG_LEVEL(LOG_LEVEL_INFO)
|
||||||
# if LOG_INCLUDE_SOURCE_LOCATION
|
# if LOG_INCLUDE_SOURCE_LOCATION
|
||||||
# define log_info(msg) _log(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, msg)
|
# define log_info(msg) _log(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, msg)
|
||||||
# define logf_info(fmt, ...) _logf(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_info(fmt_lit, ...) _logf(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# else
|
# else
|
||||||
# define log_info(msg) _log(LOG_LEVEL_INFO, msg)
|
# define log_info(msg) _log(LOG_LEVEL_INFO, msg)
|
||||||
# define logf_info(fmt, ...) _logf(LOG_LEVEL_INFO, LIT(fmt) , ## __VA_ARGS__, FMT_END)
|
# define logf_info(fmt_lit, ...) _logf(LOG_LEVEL_INFO, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# define log_info(msg)
|
# define log_info(msg)
|
||||||
# define logf_info(fmt, ...)
|
# define logf_info(fmt_lit, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
#pragma comment(lib, "d3dcompiler")
|
#pragma comment(lib, "d3dcompiler")
|
||||||
|
|
||||||
#define MAX_CMD_BUFFERS 1024
|
#define MAX_CMD_BUFFERS 1024
|
||||||
|
#define SHADER_INFO_LOOKUP_BINS 64
|
||||||
|
|
||||||
/* FIXME: Enable this and resolve unreleased references */
|
/* FIXME: Enable this and resolve unreleased references */
|
||||||
//#define D3D11_DEBUG RTC
|
//#define D3D11_DEBUG RTC
|
||||||
@ -40,10 +41,6 @@ struct dx11_shader {
|
|||||||
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 {
|
||||||
@ -136,6 +133,11 @@ struct dx11_shader_desc {
|
|||||||
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 */
|
||||||
|
|
||||||
|
/* Internal */
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
struct atomic_i32 should_reload;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
@ -166,6 +168,7 @@ 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 fixed_dict shader_info_lookup;
|
||||||
|
|
||||||
} G = ZI, DEBUG_ALIAS(G, G_renderer_d3d11);
|
} G = ZI, DEBUG_ALIAS(G, G_renderer_d3d11);
|
||||||
|
|
||||||
@ -210,7 +213,8 @@ INTERNAL void init_shader_table(void)
|
|||||||
{ "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 },
|
||||||
{ "TEXCOORD", 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 }
|
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
|
||||||
}
|
},
|
||||||
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Grid shader layout */
|
/* Grid shader layout */
|
||||||
@ -224,8 +228,16 @@ INTERNAL void init_shader_table(void)
|
|||||||
{ "THICKNESS", 0, DXGI_FORMAT_R32_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 },
|
{ "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 }
|
{ "OFFSET", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
|
||||||
}
|
},
|
||||||
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
G.shader_info_lookup = fixed_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);
|
||||||
|
fixed_dict_set(&G.arena, &G.shader_info_lookup, name, desc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If shader compilation fails, then error string is returned allocated on `arena` */
|
/* If shader compilation fails, then error string is returned allocated on `arena` */
|
||||||
@ -317,58 +329,59 @@ INTERNAL void shader_release(struct dx11_shader *shader)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL void reload_shaders(void)
|
INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_desc *desc)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
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);
|
struct string name = string_from_cstr_no_limit(desc->name_cstr);
|
||||||
if (!resource_exists(name)) {
|
struct string error_msg = ZI;
|
||||||
sys_panic(string_format(scratch.arena, LIT("Could not find shader \"%F\""), FMT_STR(name)));
|
if (resource_exists(name)) {
|
||||||
}
|
|
||||||
struct resource src_res = resource_open(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 dx11_shader new_shader = ZI;
|
||||||
struct string error = shader_alloc(scratch.arena, &new_shader, desc, &src_res);
|
struct string comp_error = shader_alloc(scratch.arena, &new_shader, desc, &src_res);
|
||||||
#if RESOURCE_RELOADING
|
if (comp_error.len == 0) {
|
||||||
new_shader.last_modified_load_attempt = last_modified;
|
|
||||||
#endif
|
|
||||||
if (error.len == 0) {
|
|
||||||
if (old_shader->valid) {
|
if (old_shader->valid) {
|
||||||
shader_release(old_shader);
|
shader_release(old_shader);
|
||||||
}
|
}
|
||||||
*old_shader = new_shader;
|
*old_shader = new_shader;
|
||||||
} else {
|
} else {
|
||||||
struct string error_msg = string_format(scratch.arena,
|
error_msg = string_format(scratch.arena,
|
||||||
LIT("Failed to compile shader \"%F\":\n\n%F"),
|
LIT("Failed to compile shader \"%F\":\n\n%F"),
|
||||||
FMT_STR(name),
|
FMT_STR(name),
|
||||||
FMT_STR(error));
|
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 (old_shader->valid) {
|
||||||
|
/* If shader failed to load but a working shader already exists, just error rather than panicking */
|
||||||
log_error(error_msg);
|
log_error(error_msg);
|
||||||
} else {
|
} else {
|
||||||
sys_panic(error_msg);
|
sys_panic(error_msg);
|
||||||
}
|
}
|
||||||
shader_release(&new_shader);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
resource_close(&src_res);
|
|
||||||
|
|
||||||
}
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
INTERNAL RESOURCE_WATCH_CALLBACK_DEF(shader_resource_watch_callback, info)
|
||||||
|
{
|
||||||
|
struct string name = info->name;
|
||||||
|
struct dx11_shader_desc *desc = (struct dx11_shader_desc *)fixed_dict_get(&G.shader_info_lookup, name);
|
||||||
|
if (desc) {
|
||||||
|
logf_info("Shader source file \"%F\" has changed", FMT_STR(name));
|
||||||
|
atomic_i32_eval_exchange(&desc->should_reload, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Startup
|
* Startup
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -580,9 +593,18 @@ struct renderer_startup_receipt renderer_startup(struct sys_window *window)
|
|||||||
|
|
||||||
/* Init shaders */
|
/* Init shaders */
|
||||||
logf_info("Compiling shaders");
|
logf_info("Compiling shaders");
|
||||||
reload_shaders();
|
for (u32 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) {
|
||||||
|
struct dx11_shader *shader = &G.shaders[i];
|
||||||
|
struct dx11_shader_desc *desc = &G.shader_info[i];
|
||||||
|
reload_shader(shader, desc);
|
||||||
|
}
|
||||||
logf_info("Finished compiling shaders");
|
logf_info("Finished compiling shaders");
|
||||||
|
|
||||||
|
/* Setup file change callbacks */
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
resource_register_watch_callback(shader_resource_watch_callback);
|
||||||
|
#endif
|
||||||
|
|
||||||
return (struct renderer_startup_receipt) { 0 };
|
return (struct renderer_startup_receipt) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -902,24 +924,21 @@ struct renderer_texture renderer_backbuffer_recreate(struct v2i32 size)
|
|||||||
void renderer_backbuffer_present(i32 vsync)
|
void renderer_backbuffer_present(i32 vsync)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
for (u64 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) {
|
||||||
|
struct dx11_shader_desc *desc = &G.shader_info[i];
|
||||||
|
if (atomic_i32_eval_compare_exchange(&desc->should_reload, 1, 0) == 1) {
|
||||||
|
reload_shader(&G.shaders[i], desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
renderer_capture_image_for_profiler();
|
renderer_capture_image_for_profiler();
|
||||||
{
|
{
|
||||||
__profscope(IDXGISwapchain_Present);
|
__profscope(IDXGISwapchain_Present);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
|
|||||||
119
src/resource.c
119
src/resource.c
@ -1,25 +1,50 @@
|
|||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
#include "app.h"
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
#include "tar.h"
|
#include "tar.h"
|
||||||
#include "incbin.h"
|
#include "incbin.h"
|
||||||
|
|
||||||
#if RESOURCES_EMBEDDED
|
/* ========================== *
|
||||||
|
* Global data
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
#if RESOURCES_EMBEDDED
|
||||||
# include "inc.h"
|
# include "inc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Add resource data to binary */
|
/* Add resource data to binary */
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
struct arena arena;
|
struct arena arena;
|
||||||
|
|
||||||
|
#if RESOURCES_EMBEDDED
|
||||||
struct tar_archive archive;
|
struct tar_archive archive;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
struct sys_thread resource_watch_thread;
|
||||||
|
struct sys_mutex watch_callbacks_mutex;
|
||||||
|
b32 watch_stop;
|
||||||
|
resource_watch_callback *watch_callbacks[64];
|
||||||
|
u64 num_watch_callbacks;
|
||||||
|
#endif
|
||||||
} G = ZI, DEBUG_ALIAS(G, G_resource);
|
} G = ZI, DEBUG_ALIAS(G, G_resource);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(resource_watch_thread_entry_point, _);
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(resource_shutdown);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct resource_startup_receipt resource_startup(void)
|
struct resource_startup_receipt resource_startup(void)
|
||||||
{
|
{
|
||||||
|
G.arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
#if RESOURCES_EMBEDDED
|
#if RESOURCES_EMBEDDED
|
||||||
struct string embedded_data = inc_res_tar();
|
struct string embedded_data = inc_res_tar();
|
||||||
G.arena = arena_alloc(GIGABYTE(64));
|
|
||||||
if (embedded_data.len <= 0) {
|
if (embedded_data.len <= 0) {
|
||||||
sys_panic(LIT("No embedded resources found"));
|
sys_panic(LIT("No embedded resources found"));
|
||||||
}
|
}
|
||||||
@ -31,9 +56,20 @@ struct resource_startup_receipt resource_startup(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
G.watch_callbacks_mutex = sys_mutex_alloc();
|
||||||
|
app_register_exit_callback(&resource_shutdown);
|
||||||
|
G.resource_watch_thread = sys_thread_alloc(resource_watch_thread_entry_point, NULL, LIT("[P2] Resource watcher"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
return (struct resource_startup_receipt) { 0 };
|
return (struct resource_startup_receipt) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Open / close
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
struct resource resource_open(struct string path)
|
struct resource resource_open(struct string path)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
@ -70,13 +106,12 @@ void resource_close(struct resource *res_ptr)
|
|||||||
sys_file_map_close(res_ptr->_file_map);
|
sys_file_map_close(res_ptr->_file_map);
|
||||||
sys_file_close(res_ptr->_file);
|
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
|
#endif
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Util
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
b32 resource_exists(struct string path)
|
b32 resource_exists(struct string path)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
@ -87,3 +122,73 @@ b32 resource_exists(struct string path)
|
|||||||
return sys_is_file(path);
|
return sys_is_file(path);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !RESOURCES_EMBEDDED
|
||||||
|
struct sys_file_time resource_get_time(struct resource *res_ptr)
|
||||||
|
{
|
||||||
|
return sys_file_get_time(res_ptr->_file);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Watch
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
|
||||||
|
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(resource_shutdown)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
/* Wait until any watch callbacks finish before shutting down */
|
||||||
|
struct sys_lock lock = sys_mutex_lock_e(&G.watch_callbacks_mutex);
|
||||||
|
{
|
||||||
|
G.watch_stop = true;
|
||||||
|
}
|
||||||
|
sys_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resource_register_watch_callback(resource_watch_callback *callback)
|
||||||
|
{
|
||||||
|
struct sys_lock lock = sys_mutex_lock_e(&G.watch_callbacks_mutex);
|
||||||
|
{
|
||||||
|
if (G.num_watch_callbacks < ARRAY_COUNT(G.watch_callbacks)) {
|
||||||
|
G.watch_callbacks[G.num_watch_callbacks++] = callback;
|
||||||
|
} else {
|
||||||
|
sys_panic(LIT("Max resource watch callbacks reached"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(resource_watch_thread_entry_point, _)
|
||||||
|
{
|
||||||
|
(UNUSED)_;
|
||||||
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
|
struct sys_watch dir_watch = sys_watch_alloc(LIT("res"));
|
||||||
|
|
||||||
|
/* NOTE: This thread is force-shutdown at the moment, however shutdown is guaranteed not to occur while the watch mutex is locked by the thread */
|
||||||
|
volatile i32 run = true;
|
||||||
|
while (run) {
|
||||||
|
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
||||||
|
struct sys_watch_info *info = sys_watch_wait(temp.arena, &dir_watch);
|
||||||
|
{
|
||||||
|
struct sys_lock lock = sys_mutex_lock_s(&G.watch_callbacks_mutex);
|
||||||
|
if (!G.watch_stop) {
|
||||||
|
while (info) {
|
||||||
|
for (u64 i = 0; i < G.num_watch_callbacks; ++i) {
|
||||||
|
resource_watch_callback *callback = G.watch_callbacks[i];
|
||||||
|
callback(info);
|
||||||
|
}
|
||||||
|
info = info->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
arena_temp_end(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_watch_release(&dir_watch);
|
||||||
|
scratch_end(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@ -23,18 +23,35 @@ struct resource_startup_receipt resource_startup(void);
|
|||||||
|
|
||||||
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
|
||||||
#define resource_close(res_ptr) (UNUSED)res_ptr
|
#define resource_close(res_ptr)
|
||||||
#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_ptr);
|
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);
|
b32 resource_exists(struct string path);
|
||||||
|
|
||||||
|
#define resource_get_data(res_ptr) (res_ptr)->_data
|
||||||
|
|
||||||
|
#if RESOURCES_EMBEDDED
|
||||||
|
#define resource_get_time(res_ptr) (struct sys_file_time) ZI
|
||||||
|
#else
|
||||||
|
struct sys_file_time resource_get_time(struct resource *res_ptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if RESOURCES_EMBEDDED
|
||||||
|
#define resource_get_name(res_ptr) (res_ptr)->_file_name
|
||||||
|
#else
|
||||||
|
#define resource_get_name(res_ptr) STRING((res_ptr)->_file_name_len, (res_ptr)->_file_name_text)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RESOURCE_WATCH_CALLBACK_DEF(func_name, arg_info) void func_name(struct sys_watch_info *arg_info)
|
||||||
|
typedef RESOURCE_WATCH_CALLBACK_DEF(resource_watch_callback, info);
|
||||||
|
|
||||||
|
#if RESOURCE_RELOADING
|
||||||
|
void resource_register_watch_callback(resource_watch_callback *callback);
|
||||||
|
#else
|
||||||
|
#define resource_register_watch_callback(callback)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
26
src/sys.h
26
src/sys.h
@ -256,6 +256,32 @@ struct sys_file_filter sys_file_filter_begin(struct arena *arena, struct string
|
|||||||
b32 sys_file_filter_next(struct arena *arena, struct sys_file_filter *iter);
|
b32 sys_file_filter_next(struct arena *arena, struct sys_file_filter *iter);
|
||||||
void sys_file_filter_end(struct sys_file_filter *iter);
|
void sys_file_filter_end(struct sys_file_filter *iter);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Watch
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
enum sys_watch_info_kind {
|
||||||
|
SYS_WATCH_INFO_KIND_UNKNOWN,
|
||||||
|
SYS_WATCH_INFO_KIND_ADDED,
|
||||||
|
SYS_WATCH_INFO_KIND_REMOVED,
|
||||||
|
SYS_WATCH_INFO_KIND_MODIFIED
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sys_watch {
|
||||||
|
u64 handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sys_watch_info {
|
||||||
|
enum sys_watch_info_kind kind;
|
||||||
|
struct string name;
|
||||||
|
struct sys_watch_info *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sys_watch sys_watch_alloc(struct string path);
|
||||||
|
void sys_watch_release(struct sys_watch *dw);
|
||||||
|
struct sys_watch_info *sys_watch_wait(struct arena *arena, struct sys_watch *dw);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Window
|
* Window
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|||||||
184
src/sys_win32.c
184
src/sys_win32.c
@ -128,6 +128,11 @@ GLOBAL struct {
|
|||||||
struct win32_thread *threads_last;
|
struct win32_thread *threads_last;
|
||||||
struct win32_thread *threads_first_free;
|
struct win32_thread *threads_first_free;
|
||||||
|
|
||||||
|
/* Watches */
|
||||||
|
struct sys_mutex watches_mutex;
|
||||||
|
struct arena watches_arena;
|
||||||
|
struct win32_watch *watches_first_free;
|
||||||
|
|
||||||
/* Windows */
|
/* Windows */
|
||||||
WNDCLASSEXW window_class;
|
WNDCLASSEXW window_class;
|
||||||
struct sys_mutex windows_mutex;
|
struct sys_mutex windows_mutex;
|
||||||
@ -663,6 +668,181 @@ void sys_file_filter_end(struct sys_file_filter *filter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Watch
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct win32_watch {
|
||||||
|
HANDLE dir_handle;
|
||||||
|
struct win32_watch *next_free;
|
||||||
|
|
||||||
|
u64 dir_path_len;
|
||||||
|
u8 dir_path_text[1024];
|
||||||
|
u8 results_buff[KILOBYTE(64)];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sys_watch sys_watch_alloc(struct string dir_path)
|
||||||
|
{
|
||||||
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
|
|
||||||
|
struct win32_watch *w32_watch = NULL;
|
||||||
|
{
|
||||||
|
struct sys_lock lock = sys_mutex_lock_e(&G.watches_mutex);
|
||||||
|
{
|
||||||
|
if (G.watches_first_free) {
|
||||||
|
w32_watch = G.watches_first_free;
|
||||||
|
G.watches_first_free = w32_watch->next_free;
|
||||||
|
} else {
|
||||||
|
w32_watch = arena_push(&G.watches_arena, struct win32_watch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
MEMZERO_STRUCT(w32_watch);
|
||||||
|
|
||||||
|
if (dir_path.len > 0 && dir_path.len < (ARRAY_COUNT(w32_watch->dir_path_text) - 1)) {
|
||||||
|
u64 dir_path_len = dir_path.len;
|
||||||
|
MEMCPY(w32_watch->dir_path_text, dir_path.text, dir_path_len);
|
||||||
|
for (u64 i = 0; i < dir_path_len; ++i) {
|
||||||
|
if (w32_watch->dir_path_text[i] == '\\') {
|
||||||
|
w32_watch->dir_path_text[i] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (w32_watch->dir_path_text[dir_path_len - 1] != '/') {
|
||||||
|
w32_watch->dir_path_text[dir_path_len] = '/';
|
||||||
|
++dir_path_len;
|
||||||
|
}
|
||||||
|
w32_watch->dir_path_len = dir_path_len;
|
||||||
|
} else {
|
||||||
|
sys_panic(string_format(scratch.arena, LIT("Directory path too \"%F\" has invalid length"), FMT_STR(dir_path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t *dir_path_wstr = wstr_from_string(scratch.arena, dir_path);
|
||||||
|
w32_watch->dir_handle = CreateFileW(
|
||||||
|
dir_path_wstr,
|
||||||
|
FILE_LIST_DIRECTORY,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
|
||||||
|
struct sys_watch watch = ZI;
|
||||||
|
watch.handle = (u64)w32_watch;
|
||||||
|
return watch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sys_watch_release(struct sys_watch *dw)
|
||||||
|
{
|
||||||
|
struct win32_watch *w32_watch = (struct win32_watch *)dw->handle;
|
||||||
|
CloseHandle(w32_watch->dir_handle);
|
||||||
|
|
||||||
|
struct sys_lock lock = sys_mutex_lock_e(&G.watches_mutex);
|
||||||
|
{
|
||||||
|
w32_watch->next_free = G.watches_first_free;
|
||||||
|
G.watches_first_free = w32_watch;
|
||||||
|
}
|
||||||
|
sys_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sys_watch_info *sys_watch_wait(struct arena *arena, struct sys_watch *dw)
|
||||||
|
{
|
||||||
|
__prof;
|
||||||
|
struct win32_watch *w32_watch = (struct win32_watch *)dw->handle;
|
||||||
|
struct sys_watch_info *first_info = NULL;
|
||||||
|
struct sys_watch_info *last_info = NULL;
|
||||||
|
|
||||||
|
DWORD filter = FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||||
|
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||||
|
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||||
|
FILE_NOTIFY_CHANGE_SIZE |
|
||||||
|
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||||
|
FILE_NOTIFY_CHANGE_CREATION |
|
||||||
|
FILE_NOTIFY_CHANGE_SECURITY;
|
||||||
|
|
||||||
|
b32 done = false;
|
||||||
|
while (!done) {
|
||||||
|
DWORD num_bytes_returned = 0;
|
||||||
|
BOOL success = ReadDirectoryChangesW(w32_watch->dir_handle,
|
||||||
|
w32_watch->results_buff,
|
||||||
|
ARRAY_COUNT(w32_watch->results_buff),
|
||||||
|
true,
|
||||||
|
filter,
|
||||||
|
&num_bytes_returned,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (success && num_bytes_returned > 0) {
|
||||||
|
i64 offset = 0;
|
||||||
|
while (!done) {
|
||||||
|
FILE_NOTIFY_INFORMATION *res = (FILE_NOTIFY_INFORMATION *)(w32_watch->results_buff + offset);
|
||||||
|
|
||||||
|
struct sys_watch_info *info = arena_push_zero(arena, struct sys_watch_info);
|
||||||
|
if (last_info) {
|
||||||
|
last_info->next = info;
|
||||||
|
} else {
|
||||||
|
first_info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct string16 name16 = ZI;
|
||||||
|
name16.text = res->FileName;
|
||||||
|
name16.len = res->FileNameLength / sizeof(wchar_t);
|
||||||
|
|
||||||
|
info->name = string_copy(arena, STRING(w32_watch->dir_path_len, w32_watch->dir_path_text));
|
||||||
|
info->name.len += string_from_string16(arena, name16).len;
|
||||||
|
for (u64 i = 0; i < info->name.len; ++i) {
|
||||||
|
if (info->name.text[i] == '\\') {
|
||||||
|
info->name.text[i] = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (res->Action) {
|
||||||
|
case FILE_ACTION_ADDED:
|
||||||
|
{
|
||||||
|
info->kind = SYS_WATCH_INFO_KIND_ADDED;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FILE_ACTION_REMOVED:
|
||||||
|
{
|
||||||
|
info->kind = SYS_WATCH_INFO_KIND_REMOVED;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FILE_ACTION_MODIFIED:
|
||||||
|
{
|
||||||
|
info->kind = SYS_WATCH_INFO_KIND_MODIFIED;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||||
|
{
|
||||||
|
info->kind = SYS_WATCH_INFO_KIND_REMOVED;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case FILE_ACTION_RENAMED_NEW_NAME:
|
||||||
|
{
|
||||||
|
info->kind = SYS_WATCH_INFO_KIND_ADDED;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
info->kind = SYS_WATCH_INFO_KIND_UNKNOWN;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res->NextEntryOffset == 0) {
|
||||||
|
done = true;
|
||||||
|
} else {
|
||||||
|
offset += res->NextEntryOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return first_info;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Window
|
* Window
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -2100,6 +2280,10 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
|
|||||||
G.threads_mutex = sys_mutex_alloc();
|
G.threads_mutex = sys_mutex_alloc();
|
||||||
G.threads_arena = arena_alloc(GIGABYTE(64));
|
G.threads_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
|
/* Set up watches */
|
||||||
|
G.watches_mutex = sys_mutex_alloc();
|
||||||
|
G.watches_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|
||||||
/* Set up windows */
|
/* Set up windows */
|
||||||
G.windows_mutex = sys_mutex_alloc();
|
G.windows_mutex = sys_mutex_alloc();
|
||||||
G.windows_arena = arena_alloc(GIGABYTE(64));
|
G.windows_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
|||||||
@ -61,9 +61,8 @@ INTERNAL u64 str_oct_to_u64(struct string str)
|
|||||||
|
|
||||||
/* `prefix` will be prepended to all file names in the archive
|
/* `prefix` will be prepended to all file names in the archive
|
||||||
*
|
*
|
||||||
* NOTE: The resulting archive merely points into the supplied tar data. No
|
* NOTE: The resulting archive merely points into the supplied tar data, no
|
||||||
* copying is done. Accessing the archive assumes that the data it's derived
|
* copying is done. Accessing the archive assumes that the data string is still valid.
|
||||||
* from is valid (AKA open if from a file / memory map).
|
|
||||||
*/
|
*/
|
||||||
struct tar_archive tar_parse(struct arena *arena, struct string data, struct string prefix)
|
struct tar_archive tar_parse(struct arena *arena, struct string data, struct string prefix)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user