simple in-game debug console

This commit is contained in:
jacob 2025-05-24 13:49:22 -05:00
parent 716a26e42c
commit d48706d1f7
11 changed files with 301 additions and 139 deletions

View File

@ -268,8 +268,7 @@ void app_entry_point(struct string args_str)
struct string logfile_path = string_cat(temp.arena, logfile_dir, logfile_name);
sys_mkdir(logfile_dir);
struct log_startup_receipt log_sr = log_startup(logfile_path);
(UNUSED)log_sr;
log_startup(logfile_path);
logf_info("Start of logs");
arena_temp_end(temp);
}

View File

@ -372,14 +372,12 @@ struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct draw_text_params pa
u64 num_line_glyphs = 0;
struct drawable_glyph *line_glyphs = arena_dry_push(scratch.arena, struct drawable_glyph);
b32 submit_line = false;
b32 line_done = false;
while (!line_done) {
string_done = !string_codepoint_iter_next(&iter);
if (string_done) {
line_done = true;
} else {
submit_line = true;
u32 codepoint = iter.codepoint;
if (codepoint == '\n') {
line_done = true;
@ -411,7 +409,6 @@ struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct draw_text_params pa
}
/* Line ended */
if (submit_line) {
/* TODO: Only create nodes for non-empty lines. Embed line number in the node. */
struct drawable_line *node = arena_push(scratch.arena, struct drawable_line);
node->line_width = line_width;
@ -428,7 +425,6 @@ struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct draw_text_params pa
widest_line = max_f32(widest_line, line_width);
++num_lines;
}
}
string_codepoint_iter_end(&iter);
}
@ -453,12 +449,7 @@ struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct draw_text_params pa
/* Offset bounds Y */
switch (params.offset_y) {
case DRAW_TEXT_OFFSET_Y_TOP:
{
if (first_line) {
bounds.y -= first_line_top_offset;
}
} break;
case DRAW_TEXT_OFFSET_Y_TOP: break;
case DRAW_TEXT_OFFSET_Y_CENTER:
{
bounds.y -= bounds.height / 2.f;
@ -466,7 +457,7 @@ struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct draw_text_params pa
case DRAW_TEXT_OFFSET_Y_BOTTOM:
{
if (last_line) {
bounds.y -= bounds.height - first_line_top_offset - last_line_bottom_offset;
bounds.y -= bounds.height + last_line_bottom_offset;
}
} break;
}
@ -475,7 +466,7 @@ struct rect draw_text(struct gpu_cmd_buffer *cmdbuff, struct draw_text_params pa
u64 line_number = 0;
for (struct drawable_line *line = first_line; line; line = line->next) {
struct v2 draw_pos = bounds.pos;
draw_pos.y += line_number * line_spacing;
draw_pos.y += line_number * line_spacing - first_line_top_offset;
/* Alignment */
switch (params.alignment) {

View File

@ -156,8 +156,7 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
font_task_params_release(params);
f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns);
logf_info("Finished loading font \"%F\" (point size %F) in %F seconds", FMT_STR(path), FMT_FLOAT((f64)point_size), FMT_FLOAT(elapsed));
logf_success("Finished loading font \"%F\" (point size %F) in %F seconds", FMT_STR(path), FMT_FLOAT((f64)point_size), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
asset_cache_mark_ready(asset, font);
scratch_end(scratch);

View File

@ -638,6 +638,7 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha
__prof;
struct temp_arena scratch = scratch_begin(arena);
struct string error_str = ZI;
i64 start_ns = sys_time_ns();
struct string shader_name = string_from_cstr_no_limit(shader_desc->name_cstr);
@ -666,10 +667,12 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha
logf_info("Compiling shader \"%F\"", FMT_STR(shader_name));
/* Compile shader */
/* TODO: pre-compile shaders w/ FXC? */
HRESULT hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&include_handler, "vs_main", "vs_5_0", flags, 0, &vs_blob, &error_blob);
struct string friendly_name = string_cat(scratch.arena, LIT("res/"), shader_name);
char *friendly_name_cstr = cstr_from_string(scratch.arena, friendly_name);
HRESULT hr = D3DCompile(shader_src.text, shader_src.len, friendly_name_cstr, NULL, (ID3DInclude *)&include_handler, "vs_main", "vs_5_0", flags, 0, &vs_blob, &error_blob);
if (SUCCEEDED(hr)) {
ID3D11Device_CreateVertexShader(G.dev, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), NULL, &shader->vs);
hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&include_handler, "ps_main", "ps_5_0", flags, 0, &ps_blob, &error_blob);
hr = D3DCompile(shader_src.text, shader_src.len, friendly_name_cstr, NULL, (ID3DInclude *)&include_handler, "ps_main", "ps_5_0", flags, 0, &ps_blob, &error_blob);
if (SUCCEEDED(hr)) {
ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps);
success = true;
@ -688,9 +691,19 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha
}
/* 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);
HRESULT hr = ID3D11Device_CreateInputLayout(G.dev, shader_desc->input_layout_desc, elem_count, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &shader->input_layout);
if (!SUCCEEDED(hr)) {
success = false;
error_str = LIT("Failed to create input layout");
}
} else {
success = false;
}
if (!success || error_blob) {
if (error_str.len <= 0) {
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);
@ -709,6 +722,10 @@ INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *sha
}
}
if (success) {
logf_success("Finished compiling shader \"%F\" in %F seconds", FMT_STR(shader_name), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
}
if (vs_blob) {
ID3D10Blob_Release(vs_blob);
}
@ -759,7 +776,7 @@ INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_d
*old_shader = new_shader;
} else {
error_msg = string_format(scratch.arena,
LIT("Failed to compile shader \"%F\":\n\n%F"),
LIT("Failed to compile shader \"%F\": %F"),
FMT_STR(name),
FMT_STR(comp_error));
shader_release(&new_shader);
@ -785,7 +802,7 @@ INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_d
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name)
{
if (shader_set_dirty(name)) {
logf_info("Shader source file \"%F\" has changed", FMT_STR(name));
logf_debug("Shader source file \"%F\" has changed", FMT_STR(name));
}
}
#endif

View File

@ -1,12 +1,12 @@
#include "log.h"
#include "scratch.h"
#include "log.h"
#include "string.h"
#include "atomic.h"
struct log_event_callback {
log_event_callback_func *func;
i32 level;
struct log_event_callback *next;
i32 level;
};
/* ========================== *
@ -15,9 +15,12 @@ struct log_event_callback {
GLOBAL struct {
struct atomic_i32 initialized;
struct sys_mutex mutex;
struct arena arena;
log_event_callback_func *callbacks_head;
struct sys_mutex callbacks_mutex;
struct arena callbacks_arena;
struct log_event_callback *first_callback;
struct log_event_callback *last_callback;
struct sys_file file;
b32 file_valid;
} G = ZI, DEBUG_ALIAS(G, G_log);
@ -25,27 +28,32 @@ GLOBAL struct {
GLOBAL READONLY struct log_level_settings g_log_level_settings[LOG_LEVEL_COUNT] = {
[LOG_LEVEL_CRITICAL] = {
LIT_NOCAST("CRITICAL"),
0xFFFF00FF
COLOR_PURPLE
},
[LOG_LEVEL_ERROR] = {
LIT_NOCAST("ERROR"),
0xFFFF0000
COLOR_RED
},
[LOG_LEVEL_WARNING] = {
LIT_NOCAST("WARNING"),
0xFFFFFF00
COLOR_YELLOW
},
[LOG_LEVEL_SUCCESS] = {
LIT_NOCAST("SUCCESS"),
COLOR_GREEN
},
[LOG_LEVEL_INFO] = {
LIT_NOCAST("INFO"),
0xFFFFFFFF
COLOR_WHITE
},
[LOG_LEVEL_DEBUG] = {
LIT_NOCAST("DEBUG"),
0xFF30D5C8
COLOR_BLUE
}
};
@ -53,10 +61,10 @@ GLOBAL READONLY struct log_level_settings g_log_level_settings[LOG_LEVEL_COUNT]
* Startup
* ========================== */
struct log_startup_receipt log_startup(struct string logfile_path)
void log_startup(struct string logfile_path)
{
G.mutex = sys_mutex_alloc();
G.arena = arena_alloc(GIGABYTE(64));
G.callbacks_arena = arena_alloc(MEGABYTE(8));
G.callbacks_mutex = sys_mutex_alloc();
if (logfile_path.len > 0) {
/* Create / wipe log file */
sys_file_close(sys_file_open_write(logfile_path));
@ -67,22 +75,28 @@ struct log_startup_receipt log_startup(struct string logfile_path)
}
}
atomic_i32_eval_exchange(&G.initialized, 1);
return (struct log_startup_receipt) { 0 };
}
/* ========================== *
* Callback
* ========================== */
void log_register_callback(log_event_callback_func *func)
void log_register_callback(log_event_callback_func *func, i32 level)
{
/* TODO */
(UNUSED)func;
#if 0
if (!atomic_i32_eval(&G.initialized)) { return; }
struct sys_lock lock = sys_mutex_lock_e(&G.mutex);
struct sys_lock lock = sys_mutex_lock_e(&G.callbacks_mutex);
{
struct log_event_callback *callback = arena_push(&G.callbacks_arena, struct log_event_callback);
callback->func = func;
callback->level = level;
if (G.last_callback) {
G.last_callback->next = callback;
} else {
G.first_callback = callback;
}
G.last_callback = callback;
}
sys_mutex_unlock(&lock);
#endif
}
/* ========================== *
@ -130,7 +144,9 @@ void _log(i32 level, struct string msg)
struct temp_arena scratch = scratch_begin_no_conflict();
struct sys_datetime lt = sys_local_time();
struct sys_datetime datetime = sys_local_time();
i64 time_ns = sys_time_ns();
u32 tid = sys_thread_id();
struct log_level_settings settings = g_log_level_settings[level];
@ -142,10 +158,10 @@ void _log(i32 level, struct string msg)
LIT("[%F:%F:%F.%F] |%F| [%F] <%F:%F> %F"),
/* Time */
FMT_UINT_Z(lt.hour, 2),
FMT_UINT_Z(lt.minute, 2),
FMT_UINT_Z(lt.second, 2),
FMT_UINT_Z(lt.milliseconds, 3),
FMT_UINT_Z(datetime.hour, 2),
FMT_UINT_Z(datetime.minute, 2),
FMT_UINT_Z(datetime.second, 2),
FMT_UINT_Z(datetime.milliseconds, 3),
/* TID */
FMT_UINT_Z(tid, 5),
@ -166,10 +182,10 @@ void _log(i32 level, struct string msg)
LIT("[%F:%F:%F.%F] |%F| [%F] %F"),
/* Time */
FMT_UINT_Z(lt.hour, 2),
FMT_UINT_Z(lt.minute, 2),
FMT_UINT_Z(lt.second, 2),
FMT_UINT_Z(lt.milliseconds, 3),
FMT_UINT_Z(datetime.hour, 2),
FMT_UINT_Z(datetime.minute, 2),
FMT_UINT_Z(datetime.second, 2),
FMT_UINT_Z(datetime.milliseconds, 3),
/* TID */
FMT_UINT_Z(tid, 5),
@ -185,6 +201,27 @@ void _log(i32 level, struct string msg)
__profmsg((char *)msg.text, msg.len, settings.color);
append_to_logfile(msg_formatted);
/* Run callbacks */
struct log_event event = ZI;
event.level = level;
event.msg = msg;
event.datetime = datetime;
event.time_ns = time_ns;
#if LOG_INCLUDE_SOURCE_LOCATION
event.file = file;
event.line = line;
#endif
{
struct sys_lock lock = sys_mutex_lock_s(&G.callbacks_mutex);
for (struct log_event_callback *callback = G.first_callback; callback; callback = callback->next) {
if (level <= callback->level) {
callback->func(event);
}
}
sys_mutex_unlock(&lock);
}
scratch_end(scratch);
}

View File

@ -2,6 +2,7 @@
#define LOG_H
#include "string.h"
#include "sys.h"
#define LOG_LEVEL(l) (l <= LOG_LEVEL_COMPTIME)
@ -23,9 +24,10 @@
#define LOG_LEVEL_CRITICAL 0
#define LOG_LEVEL_ERROR 1
#define LOG_LEVEL_WARNING 2
#define LOG_LEVEL_INFO 3
#define LOG_LEVEL_DEBUG 4
#define LOG_LEVEL_COUNT 5
#define LOG_LEVEL_SUCCESS 3
#define LOG_LEVEL_INFO 4
#define LOG_LEVEL_DEBUG 5
#define LOG_LEVEL_COUNT 6
/* ========================== *
* Callback interface
@ -37,18 +39,22 @@ struct log_level_settings {
};
struct log_event {
/* Msg lifetime is only as long as callback duration */
struct string msg;
i32 level;
struct string msg; /* Lifetime is only as long as the callback function call receiving the event */
struct log_level_settings settings;
struct sys_datetime datetime;
i64 time_ns;
/* These will be nulled if LOG_INCLUDE_SOURCE_LOCATION is disabled */
struct string file;
i32 line;
};
typedef void (log_event_callback_func)(struct log_event);
#define LOG_EVENT_CALLBACK_FUNC_DEF(name, log_event_arg) void name(struct log_event log_event_arg)
typedef LOG_EVENT_CALLBACK_FUNC_DEF(log_event_callback_func, log_event);
void log_register_callback(log_event_callback_func *func);
void log_register_callback(log_event_callback_func *func, i32 level);
/* ========================== *
* Logging macros
@ -95,20 +101,19 @@ void log_register_callback(log_event_callback_func *func);
# define logf_warning(fmt_lit, ...)
#endif
#if LOG_LEVEL(LOG_LEVEL_DEBUG)
#if LOG_LEVEL(LOG_LEVEL_SUCCESS)
# if LOG_INCLUDE_SOURCE_LOCATION
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, msg)
# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
# define log_success(msg) _log(LOG_LEVEL_SUCCESS, LIT(__FILE__), __LINE__, msg)
# define logf_success(fmt_lit, ...) _logf(LOG_LEVEL_SUCCESS, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
# else
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, msg)
# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
# define log_success(msg) _log(LOG_LEVEL_SUCCESS, msg)
# define logf_success(fmt_lit, ...) _logf(LOG_LEVEL_SUCCESS, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
# endif
#else
# define log_debug(msg)
# define logf_debug(fmt_lit, ...)
# define log_success(msg)
# define logf_success(fmt_lit, ...)
#endif
#if LOG_LEVEL(LOG_LEVEL_INFO)
# if LOG_INCLUDE_SOURCE_LOCATION
# define log_info(msg) _log(LOG_LEVEL_INFO, LIT(__FILE__), __LINE__, msg)
@ -122,12 +127,24 @@ void log_register_callback(log_event_callback_func *func);
# define logf_info(fmt_lit, ...)
#endif
#if LOG_LEVEL(LOG_LEVEL_DEBUG)
# if LOG_INCLUDE_SOURCE_LOCATION
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, msg)
# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(__FILE__), __LINE__, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
# else
# define log_debug(msg) _log(LOG_LEVEL_DEBUG, msg)
# define logf_debug(fmt_lit, ...) _logf(LOG_LEVEL_DEBUG, LIT(fmt_lit) , ## __VA_ARGS__, FMT_END)
# endif
#else
# define log_debug(msg)
# define logf_debug(fmt_lit, ...)
#endif
/* ========================== *
* Function declarations
* ========================== */
struct log_startup_receipt { i32 _; };
struct log_startup_receipt log_startup(struct string logfile_path);
void log_startup(struct string logfile_path);
void _log_panic(struct string msg);

View File

@ -997,7 +997,7 @@ void sim_step(struct sim_step_ctx *ctx)
test_clear_level(ctx);
}
if (flags & SIM_CONTROL_FLAG_SPAWN1_TEST) {
logf_info("Spawn test 1");
logf_debug("Spawn test 1");
u32 count = 1;
f32 spread = 0;
for (u32 j = 0; j < count; ++j) {
@ -1007,7 +1007,7 @@ void sim_step(struct sim_step_ctx *ctx)
}
}
if (flags & SIM_CONTROL_FLAG_SPAWN2_TEST) {
logf_info("Spawn test 2");
logf_debug("Spawn test 2");
u32 count = 1;
f32 spread = 0;
for (u32 j = 0; j < count; ++j) {
@ -1017,7 +1017,7 @@ void sim_step(struct sim_step_ctx *ctx)
}
}
if (flags & SIM_CONTROL_FLAG_SPAWN3_TEST) {
logf_info("Spawn test 3");
logf_debug("Spawn test 3");
u32 count = 1;
f32 spread = 0;
for (u32 j = 0; j < count; ++j) {
@ -1030,7 +1030,7 @@ void sim_step(struct sim_step_ctx *ctx)
test_generate_walls(world);
}
if (flags & SIM_CONTROL_FLAG_EXPLODE_TEST) {
logf_info("Explosion test");
logf_debug("Explosion test");
test_spawn_explosion(root, player->player_cursor_pos, 100, 2);
}
}

View File

@ -134,8 +134,7 @@ INTERNAL WORK_TASK_FUNC_DEF(sound_load_asset_task, vparams)
sound->pcm.samples = samples;
MEMCPY(sound->pcm.samples, decoded.pcm.samples, decoded.pcm.count * sizeof(*decoded.pcm.samples));
f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns);
logf_info("Finished loading sound \"%F\" in %F seconds", FMT_STR(path), FMT_FLOAT(elapsed));
logf_success("Finished loading sound \"%F\" in %F seconds", FMT_STR(path), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
asset_cache_mark_ready(asset, sound);
} else {
logf_error("Error loading sound \"%F\": %F", FMT_STR(path), FMT_STR(error_msg));

View File

@ -351,6 +351,7 @@ INTERNAL void cache_entry_load_texture(struct cache_ref ref, struct sprite_tag t
struct string path = tag.path;
logf_info("Loading sprite texture [%F] \"%F\"", FMT_HEX(e->hash.v), FMT_STR(path));
b32 success = false;
i64 start_ns = sys_time_ns();
ASSERT(string_ends_with(path, LIT(".ase")));
@ -388,18 +389,20 @@ INTERNAL void cache_entry_load_texture(struct cache_ref ref, struct sprite_tag t
e->texture->loaded = true;
/* TODO: Query gpu for more accurate texture size in VRAM */
memory_size += (decoded.image.width * decoded.image.height) * sizeof(*decoded.image.pixels);
success = true;
}
}
arena_set_readonly(&e->arena);
e->memory_usage = e->arena.committed + memory_size;
atomic_u64_eval_add_u64(&G.cache.memory_usage, e->memory_usage);
f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns);
logf_info("Finished loading sprite texture [%F] \"%F\" in %F seconds (cache size: %F bytes).",
if (success) {
logf_success("Finished loading sprite texture [%F] \"%F\" in %F seconds (cache size: %F bytes).",
FMT_HEX(e->hash.v),
FMT_STR(path),
FMT_FLOAT(elapsed),
FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)),
FMT_UINT(e->memory_usage));
}
atomic_i32_eval_exchange(&e->state, CACHE_ENTRY_STATE_LOADED);
@ -676,6 +679,7 @@ INTERNAL void cache_entry_load_sheet(struct cache_ref ref, struct sprite_tag tag
struct string path = tag.path;
logf_info("Loading sprite sheet [%F] \"%F\"", FMT_HEX(e->hash.v), FMT_STR(path));
b32 success = false;
i64 start_ns = sys_time_ns();
ASSERT(e->kind == CACHE_ENTRY_KIND_SHEET);
@ -705,19 +709,21 @@ INTERNAL void cache_entry_load_sheet(struct cache_ref ref, struct sprite_tag tag
*e->sheet = init_sheet_from_ase_result(&e->arena, decoded);
e->sheet->loaded = true;
e->sheet->valid = true;
success = true;
}
}
arena_set_readonly(&e->arena);
e->memory_usage = e->arena.committed;
atomic_u64_eval_add_u64(&G.cache.memory_usage, e->memory_usage);
f64 elapsed = SECONDS_FROM_NS(sys_time_ns() - start_ns);
logf_info("Finished loading sprite sheet [%F] \"%F\" in %F seconds (cache size: %F bytes).",
if (success) {
logf_success("Finished loading sprite sheet [%F] \"%F\" in %F seconds (cache size: %F bytes).",
FMT_HEX(e->hash.v),
FMT_STR(path),
FMT_FLOAT(elapsed),
FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)),
FMT_UINT(e->memory_usage));
}
atomic_i32_eval_exchange(&e->state, CACHE_ENTRY_STATE_LOADED);

View File

@ -683,9 +683,6 @@ struct win32_watch {
HANDLE dir_handle;
HANDLE wake_handle;
struct win32_watch *next_free;
u64 dir_path_len;
u8 dir_path_text[1024];
u8 results_buff[KILOBYTE(64)];
};
@ -708,23 +705,6 @@ struct sys_watch sys_watch_alloc(struct string dir_path)
}
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,
@ -812,8 +792,7 @@ struct sys_watch_info_list sys_watch_wait(struct arena *arena, struct sys_watch
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;
info->name = string_from_string16(arena, name16);
for (u64 i = 0; i < info->name.len; ++i) {
if (info->name.text[i] == '\\') {
info->name.text[i] = '/';

View File

@ -37,6 +37,17 @@ struct second_stat {
u64 last_second;
};
struct console_log {
struct string msg;
i32 level;
i32 index;
struct sys_datetime datetime;
i64 time_ns;
struct rect bounds;
struct console_log *prev;
struct console_log *next;
};
GLOBAL struct {
struct atomic_i32 user_thread_shutdown;
struct sys_thread user_thread;
@ -74,13 +85,20 @@ GLOBAL struct {
struct bind_state bind_states[USER_BIND_KIND_COUNT];
/* Debug camera */
struct sim_ent_id debug_following;
b32 debug_camera;
b32 debug_camera_panning;
struct v2 debug_camera_pan_start;
b32 debug_draw;
/* Debug console */
struct sys_mutex console_logs_mutex;
struct arena console_logs_arena;
struct console_log *first_console_log;
struct console_log *last_console_log;
f32 console_logs_height;
/* Window -> user */
struct sys_mutex sys_events_mutex;
struct arena sys_events_arena;
@ -178,6 +196,7 @@ GLOBAL READONLY enum user_bind_kind g_binds[SYS_BTN_COUNT] = {
* ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown);
INTERNAL LOG_EVENT_CALLBACK_FUNC_DEF(debug_console_log_callback, log);
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg);
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg);
INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event);
@ -239,12 +258,17 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
G.final_cmd_buffer = gpu_cmd_buffer_alloc();
G.backbuffer_cmd_buffer = gpu_cmd_buffer_alloc();
//log_register_callback(debug_console_log_callback, LOG_LEVEL_SUCCESS);
log_register_callback(debug_console_log_callback, LOG_LEVEL_INFO);
G.console_logs_mutex = sys_mutex_alloc();
G.console_logs_arena = arena_alloc(GIGABYTE(64));
G.window = window;
sys_window_register_event_callback(G.window, &window_event_callback);
G.local_sim_thread = sys_thread_alloc(&user_local_sim_thread_entry_point, G.local_sim_ctx, LIT("[P8] Local sim thread"));
G.debug_draw = true;
//G.debug_draw = true;
G.user_thread = sys_thread_alloc(&user_thread_entry_point, NULL, LIT("[P9] User thread"));
app_register_exit_callback(&user_shutdown);
@ -435,6 +459,106 @@ INTERNAL struct string get_ent_debug_text(struct arena *arena, struct sim_ent *e
return res;
}
/* ========================== *
* Debug console
* ========================== */
INTERNAL LOG_EVENT_CALLBACK_FUNC_DEF(debug_console_log_callback, log)
{
struct sys_lock lock = sys_mutex_lock_e(&G.console_logs_mutex);
{
struct console_log *clog = arena_push(&G.console_logs_arena, struct console_log);
clog->level = log.level;
clog->msg = string_copy(&G.console_logs_arena, log.msg);
clog->datetime = log.datetime;
clog->time_ns = log.time_ns;
if (G.last_console_log) {
G.last_console_log->next = clog;
clog->prev = G.last_console_log;
clog->index = G.last_console_log->index + 1;
} else {
G.first_console_log = clog;
}
G.last_console_log = clog;
}
sys_mutex_unlock(&lock);
}
INTERNAL void draw_debug_console(b32 minimized)
{
struct temp_arena scratch = scratch_begin_no_conflict();
struct v2 desired_start_pos = V2(10, 400);
i64 fade_time_ns = NS_FROM_SECONDS(10);
f32 fade_curve = 0.5;
f32 spacing = 0;
f32 bg_margin = 5;
u32 info_colors[2] = { RGB_F(0.4, 0.4, 0.4), RGB_F(0.5, 0.5, 0.5) };
u32 success_colors[2] = { RGB_F(0.1, 0.3, 0.1), RGB_F(0.2, 0.4, 0.2) };
u32 warning_colors[2] = { RGB_F(0.4, 0.4, 0.1), RGB_F(0.5, 0.5, 0.2) };
u32 error_colors[2] = { RGB_F(0.4, 0.1, 0.1), RGB_F(0.5, 0.2, 0.2) };
struct v2 draw_pos = desired_start_pos;
f32 bounds_top = F32_INFINITY;
f32 bounds_bottom = -F32_INFINITY;
if (G.console_logs_height < desired_start_pos.y) {
draw_pos.y = G.console_logs_height;
}
G.console_logs_height = 0;
i64 now_ns = sys_time_ns();
struct font *font = font_load_async(LIT("fonts/fixedsys.ttf"), 12.0f);
if (font) {
struct sys_lock lock = sys_mutex_lock_e(&G.console_logs_mutex);
{
for (struct console_log *log = G.last_console_log; log; log = log->prev) {
f32 opacity = 0.75;
if (minimized) {
f32 lin = 1.0 - clamp_f64((f64)(now_ns - log->time_ns) / (f64)fade_time_ns, 0, 1);
opacity *= math_pow(lin, fade_curve);
}
if (draw_pos.y > -desired_start_pos.y && opacity > 0) {
/* Draw background */
u32 color_index = log->index & 1; /* Alternate colors for greater distinction between logs */
u32 color = info_colors[color_index];
switch (log->level) {
default: break;
case LOG_LEVEL_ERROR: color = error_colors[color_index]; break;
case LOG_LEVEL_WARNING: color = warning_colors[color_index]; break;
case LOG_LEVEL_SUCCESS: color = success_colors[color_index]; break;
}
draw_quad(G.ui_cmd_buffer, quad_from_rect(log->bounds), ALPHA_F(color, opacity));
/* Draw text */
struct string text = log->msg;
struct draw_text_params params = DRAW_TEXT_PARAMS(.font = font, .pos = draw_pos, .offset_y = DRAW_TEXT_OFFSET_Y_BOTTOM, .color = ALPHA_F(COLOR_WHITE, opacity), .str = text);
struct rect bounds = draw_text(G.ui_cmd_buffer, params);
struct rect draw_bounds = bounds;
draw_bounds.x -= bg_margin;
draw_bounds.y -= bg_margin;
draw_bounds.width += bg_margin * 2.f;
draw_bounds.height += bg_margin * 2.f;
draw_pos.y -= draw_bounds.height + spacing;
log->bounds = draw_bounds;
bounds_top = min_f32(bounds_top, draw_bounds.y);
bounds_bottom = max_f32(bounds_bottom, draw_bounds.y + draw_bounds.height);
} else {
break;
}
}
}
sys_mutex_unlock(&lock);
}
if (bounds_top < F32_INFINITY && bounds_bottom > -F32_INFINITY) {
G.console_logs_height = bounds_bottom - bounds_top;
}
scratch_end(scratch);
}
/* ========================== *
* Sort entities
* ========================== */
@ -1880,9 +2004,16 @@ INTERNAL void user_update(void)
draw_text(G.ui_cmd_buffer, DRAW_TEXT_PARAMS(.font = font, .pos = pos, .str = text, .offset_y = DRAW_TEXT_OFFSET_Y_BOTTOM, .color = COLOR_WHITE));
arena_temp_end(temp);
}
}
#if DEVELOPER
draw_debug_console(!G.debug_draw);
#else
if (G.debug_draw) {
draw_debug_console(false);
}
#endif
/* ========================== *
* Render
* ========================== */
@ -2601,19 +2732,6 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg)
}
}
/* Publish snapshot to remote clients */
for (u64 i = 0; i < store->num_clients_reserved; ++i) {
struct sim_client *client = &store->clients[i];