upgrade from fxc to dxc

This commit is contained in:
jacob 2025-07-25 13:43:47 -05:00
parent a7b6b501a2
commit a8651f7aa7
38 changed files with 913 additions and 772 deletions

180
build.c
View File

@ -201,15 +201,19 @@ String CleanResultOutput(Arena *arena, String output)
typedef struct BuildStepSimpleCommandArg BuildStepSimpleCommandArg; typedef struct BuildStepSimpleCommandArg BuildStepSimpleCommandArg;
struct BuildStepSimpleCommandArg { struct BuildStepSimpleCommandArg {
String cmd; String cmd;
AtomicI32 *skip_flag; Atomic32 *skip_flag;
AtomicI32 *failure_flag; Atomic32 *failure_flag;
D_Tag delete_file_on_failure;
D_Tag dxc_depfile_dependent;
D_Tag dxc_depfile;
}; };
typedef struct BuildStepMsvcCompileCommandArg BuildStepMsvcCompileCommandArg; typedef struct BuildStepMsvcCompileCommandArg BuildStepMsvcCompileCommandArg;
struct BuildStepMsvcCompileCommandArg { struct BuildStepMsvcCompileCommandArg {
String cmd; String cmd;
AtomicI32 *skip_flag; Atomic32 *skip_flag;
AtomicI32 *failure_flag; Atomic32 *failure_flag;
D_Tag delete_file_on_failure;
D_Tag depfile_dependent; D_Tag depfile_dependent;
D_Tag output_depfile; D_Tag output_depfile;
D_TagList depfile_force_includes; D_TagList depfile_force_includes;
@ -221,9 +225,9 @@ void BuildStepSimpleCommand(void *arg_raw)
Step *s = arg_raw; Step *s = arg_raw;
BuildStepSimpleCommandArg *arg = s->arg; BuildStepSimpleCommandArg *arg = s->arg;
AtomicI32 *skip_flag = arg->skip_flag; Atomic32 *skip_flag = arg->skip_flag;
AtomicI32 *failure_flag = arg->failure_flag; Atomic32 *failure_flag = arg->failure_flag;
if (!skip_flag || !AtomicI32Eval(skip_flag)) { if (!skip_flag || !Atomic32Fetch(skip_flag)) {
SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); SH_CommandResult result = RunCommand(scratch.arena, arg->cmd);
String result_output_cleaned = CleanResultOutput(scratch.arena, result.output); String result_output_cleaned = CleanResultOutput(scratch.arena, result.output);
{ {
@ -238,8 +242,17 @@ void BuildStepSimpleCommand(void *arg_raw)
MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len);
} }
s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; s->res_status = result.error ? StepStatus_Failure : StepStatus_Success;
if (result.error && failure_flag) { if (!D_IsNil(arg->dxc_depfile)) {
AtomicI32EvalExchange(failure_flag, 1); String depfile_data = D_DepfileTextFromDxcOutput(scratch.arena, arg->dxc_depfile_dependent, result.output);
D_ClearWrite(arg->dxc_depfile, depfile_data);
}
if (result.error) {
if (failure_flag) {
Atomic32FetchSet(failure_flag, 1);
}
if (!D_IsNil(arg->delete_file_on_failure)) {
D_Delete(arg->delete_file_on_failure);
}
} }
OS_ConditionVariableBroadcast(&s->res_cv); OS_ConditionVariableBroadcast(&s->res_cv);
OS_MutexUnlock(&res_lock); OS_MutexUnlock(&res_lock);
@ -248,7 +261,7 @@ void BuildStepSimpleCommand(void *arg_raw)
OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); OS_Lock res_lock = OS_MutexLockE(&s->res_mutex);
s->res_status = StepStatus_Skipped; s->res_status = StepStatus_Skipped;
if (failure_flag) { if (failure_flag) {
AtomicI32EvalExchange(failure_flag, 1); Atomic32FetchSet(failure_flag, 1);
} }
OS_ConditionVariableBroadcast(&s->res_cv); OS_ConditionVariableBroadcast(&s->res_cv);
OS_MutexUnlock(&res_lock); OS_MutexUnlock(&res_lock);
@ -264,9 +277,9 @@ void BuildStepMsvcCompileCommand(void *arg_raw)
Step *s = arg_raw; Step *s = arg_raw;
BuildStepMsvcCompileCommandArg *arg = s->arg; BuildStepMsvcCompileCommandArg *arg = s->arg;
AtomicI32 *skip_flag = arg->skip_flag; Atomic32 *skip_flag = arg->skip_flag;
AtomicI32 *failure_flag = arg->failure_flag; Atomic32 *failure_flag = arg->failure_flag;
if (!skip_flag || !AtomicI32Eval(skip_flag)) { if (!skip_flag || !Atomic32Fetch(skip_flag)) {
SH_CommandResult result = RunCommand(scratch.arena, arg->cmd); SH_CommandResult result = RunCommand(scratch.arena, arg->cmd);
if (!result.error && !D_IsNil(arg->depfile_dependent) && !D_IsNil(arg->output_depfile)) { if (!result.error && !D_IsNil(arg->depfile_dependent) && !D_IsNil(arg->output_depfile)) {
String depfile_data = D_DepfileTextFromMsvcOutput(scratch.arena, arg->depfile_dependent, arg->depfile_force_includes, result.output); String depfile_data = D_DepfileTextFromMsvcOutput(scratch.arena, arg->depfile_dependent, arg->depfile_force_includes, result.output);
@ -285,8 +298,13 @@ void BuildStepMsvcCompileCommand(void *arg_raw)
MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len); MemoryCopy(s->res_output.text, result_output_cleaned.text, result_output_cleaned.len);
} }
s->res_status = result.error ? StepStatus_Failure : StepStatus_Success; s->res_status = result.error ? StepStatus_Failure : StepStatus_Success;
if (result.error && failure_flag) { if (result.error) {
AtomicI32EvalExchange(failure_flag, 1); if (failure_flag) {
Atomic32FetchSet(failure_flag, 1);
}
if (!D_IsNil(arg->delete_file_on_failure)) {
D_Delete(arg->delete_file_on_failure);
}
} }
OS_ConditionVariableBroadcast(&s->res_cv); OS_ConditionVariableBroadcast(&s->res_cv);
OS_MutexUnlock(&res_lock); OS_MutexUnlock(&res_lock);
@ -295,7 +313,7 @@ void BuildStepMsvcCompileCommand(void *arg_raw)
OS_Lock res_lock = OS_MutexLockE(&s->res_mutex); OS_Lock res_lock = OS_MutexLockE(&s->res_mutex);
s->res_status = StepStatus_Skipped; s->res_status = StepStatus_Skipped;
if (failure_flag) { if (failure_flag) {
AtomicI32EvalExchange(failure_flag, 1); Atomic32FetchSet(failure_flag, 1);
} }
OS_ConditionVariableBroadcast(&s->res_cv); OS_ConditionVariableBroadcast(&s->res_cv);
OS_MutexUnlock(&res_lock); OS_MutexUnlock(&res_lock);
@ -369,12 +387,16 @@ void OnBuild(StringList cli_args)
String build_hash_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/.pp_build_hash"), FmtStr(arg_outdir))); String build_hash_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/.pp_build_hash"), FmtStr(arg_outdir)));
String out_dep_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/dep/"), FmtStr(arg_outdir))); String out_dep_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/dep/"), FmtStr(arg_outdir)));
String out_obj_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/obj/"), FmtStr(arg_outdir))); String out_obj_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/obj/"), FmtStr(arg_outdir)));
String out_dxc_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/dxc/"), FmtStr(arg_outdir)));
String out_inc_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/inc/"), FmtStr(arg_outdir))); String out_inc_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/inc/"), FmtStr(arg_outdir)));
String out_bin_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/bin/"), FmtStr(arg_outdir))); String out_bin_dir_path = OS_GetAbsPath(&perm, StringF(&perm, Lit("%F/bin/"), FmtStr(arg_outdir)));
if (!OS_DirExists(out_obj_dir_path)) { if (!OS_DirExists(out_obj_dir_path)) {
OS_CreateDirAtAbsPath(out_obj_dir_path); OS_CreateDirAtAbsPath(out_obj_dir_path);
} }
if (!OS_DirExists(out_dxc_dir_path)) {
OS_CreateDirAtAbsPath(out_dxc_dir_path);
}
if (!OS_DirExists(out_inc_dir_path)) { if (!OS_DirExists(out_inc_dir_path)) {
OS_CreateDirAtAbsPath(out_inc_dir_path); OS_CreateDirAtAbsPath(out_inc_dir_path);
} }
@ -416,6 +438,7 @@ void OnBuild(StringList cli_args)
Bool should_embed_in_rc = !!arg_msvc; Bool should_embed_in_rc = !!arg_msvc;
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 dxc_dir = D_TagFromPath(&perm, out_dxc_dir_path, D_TagKind_Dir);
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 icon_file = D_TagFromPath(&perm, Lit("icon.ico"), D_TagKind_File); D_Tag icon_file = D_TagFromPath(&perm, Lit("icon.ico"), D_TagKind_File);
@ -436,6 +459,7 @@ void OnBuild(StringList cli_args)
StringList link_warnings = { 0 }; StringList link_warnings = { 0 };
StringList link_args = { 0 }; StringList link_args = { 0 };
StringList rc_compile_args = { 0 }; StringList rc_compile_args = { 0 };
StringList dxc_compile_args = { 0 };
{ {
if (arg_msvc) { if (arg_msvc) {
/* Msvc */ /* Msvc */
@ -449,7 +473,7 @@ void OnBuild(StringList cli_args)
String warnings = Lit("/WX /Wall " String warnings = Lit("/WX /Wall "
"/options:strict " "/options:strict "
"/wd4820 /wd4201 /wd5220 /wd4514 /wd4244 /wd5045 /wd4242 /wd4061 /wd4189 /wd4723 /wd5246"); "/wd4820 /wd4201 /wd5220 /wd4514 /wd4244 /wd5045 /wd4242 /wd4061 /wd4189 /wd4723 /wd5246 /wd4324");
StringListAppend(&perm, &compile_warnings, warnings); StringListAppend(&perm, &compile_warnings, warnings);
StringListAppend(&perm, &link_warnings, Lit("/WX")); StringListAppend(&perm, &link_warnings, Lit("/WX"));
@ -495,6 +519,12 @@ void OnBuild(StringList cli_args)
StringListAppend(&perm, &link_warnings, warnings); StringListAppend(&perm, &link_warnings, warnings);
} }
/* DXC */
{
StringListAppend(&perm, &dxc_compile_args, Lit("dxc %F -Fo %F -E %F -T %F -H"));
}
/* RTC */ /* RTC */
if (arg_rtc) { if (arg_rtc) {
if (!arg_crtlib) { if (!arg_crtlib) {
@ -535,6 +565,7 @@ void OnBuild(StringList cli_args)
} else { } else {
StringListAppend(&perm, &compile_and_link_args, Lit("-O0")); StringListAppend(&perm, &compile_and_link_args, Lit("-O0"));
} }
StringListAppend(&perm, &dxc_compile_args, Lit("-Od"));
} else { } else {
if (arg_msvc) { if (arg_msvc) {
StringListAppend(&perm, &compile_args, Lit("/O2")); StringListAppend(&perm, &compile_args, Lit("/O2"));
@ -542,6 +573,7 @@ void OnBuild(StringList cli_args)
} else { } else {
StringListAppend(&perm, &compile_and_link_args, Lit("-O3 -flto")); StringListAppend(&perm, &compile_and_link_args, Lit("-O3 -flto"));
} }
StringListAppend(&perm, &dxc_compile_args, Lit("-O3"));
} }
/* Debug info */ /* Debug info */
@ -552,6 +584,7 @@ void OnBuild(StringList cli_args)
} else { } else {
StringListAppend(&perm, &compile_and_link_args, Lit("-g")); StringListAppend(&perm, &compile_and_link_args, Lit("-g"));
} }
StringListAppend(&perm, &dxc_compile_args, Lit("-Zi -Qembed_debug"));
} }
/* Address sanitizer */ /* Address sanitizer */
@ -638,16 +671,106 @@ void OnBuild(StringList cli_args)
hist = D_HistFromPath(&perm, hist_path); hist = D_HistFromPath(&perm, hist_path);
D_TagList link_files = { 0 }; D_TagList link_files = { 0 };
/* ========================== *
* Build step: Compile shaders
* ========================== */
Atomic32 shader_success_flag = { 0 };
{
D_TagList src_input_files = { 0 };
{
D_Tag src_dir = D_TagFromPath(&perm, Lit("src/sh"), D_TagKind_Dir);
D_TagList src_files = D_GetDirContents(&perm, src_dir, 0);
for (D_TagListNode *n = src_files.first; n; n = n->next) {
D_Tag file = n->tag;
String path = file.full_path;
String name = D_GetName(file);
String extension = StringPathExtension(name);
Bool is_dir = file.kind == D_TagKind_Dir;
Bool is_rs = !is_dir && StringEqual(extension, Lit("hlsl_rs"));
Bool is_cs = !is_dir && StringEqual(extension, Lit("hlsl_cs"));
if (is_rs || is_cs) {
D_TagListAppend(&perm, &src_input_files, file);
}
}
}
{
AddSyncPoint();
String dxc_compile_args_fmt = StringFromStringLists(&perm, Lit(" "), dxc_compile_args);
for (D_TagListNode *n = src_input_files.first; n; n = n->next) {
D_Tag file = n->tag;
String path = file.full_path;
String name = D_GetName(file);
String name_no_extension = StringPathNoExtension(name);
String extension = StringPathExtension(name);
Bool is_rs = StringEqual(extension, Lit("hlsl_rs"));
Bool is_cs = !is_rs && StringEqual(extension, Lit("hlsl_cs"));
for (I32 kind = 0; kind < 3; ++kind) {
String out_file_extension = { 0 };
String entry = { 0 };
String profile = { 0 };
if (kind == 0 && is_rs) {
/* Vertex shader */
out_file_extension = Lit("dxc_vs");
entry = Lit("vs");
profile = Lit("vs_6_6");
} else if (kind == 1 && is_rs) {
/* Pixel shader */
out_file_extension = Lit("dxc_ps");
entry = Lit("ps");
profile = Lit("ps_6_6");
} else if (kind == 2 && is_cs) {
/* Compute shader */
out_file_extension = Lit("dxc_cs");
entry = Lit("cs");
profile = Lit("cs_6_6");
}
if (entry.len > 0) {
D_Tag dep_file;
{
String dep_file_path = StringF(&perm, Lit("%F/%F.%F"), FmtStr(out_dxc_dir_path), FmtStr(name_no_extension), FmtStr(dep_file_extension));
dep_file = D_TagFromPath(&perm, dep_file_path, D_TagKind_DepFile);
}
D_Tag dxc_file;
{
String dxc_file_path = StringF(&perm, Lit("%F/%F.%F"), FmtStr(out_dxc_dir_path), FmtStr(name_no_extension), FmtStr(out_file_extension));
dxc_file = D_TagFromPath(&perm, dxc_file_path, D_TagKind_File);
}
D_AddDependency(&store, dxc_file, file);
D_AddDependency(&store, dxc_file, dep_file);
if (IsDirty(dxc_file)) {
String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(name), FmtStr(D_GetName(dxc_file)));
{
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg);
bs_arg->cmd = StringF(&perm, dxc_compile_args_fmt, FmtStr(file.full_path), FmtStr(dxc_file.full_path), FmtStr(entry), FmtStr(profile));
bs_arg->failure_flag = &shader_success_flag;
bs_arg->delete_file_on_failure = dxc_file;
bs_arg->dxc_depfile_dependent = dxc_file;
bs_arg->dxc_depfile = dep_file;
AddStep(step_name, &BuildStepSimpleCommand, bs_arg);
}
}
}
}
}
}
}
/* ========================== * /* ========================== *
* Build step: Tar archives * Build step: Tar archives
* ========================== */ * ========================== */
AtomicI32 tar_success_flag = { 0 }; Atomic32 tar_success_flag = { 0 };
{ {
AddSyncPoint(); AddSyncPoint();
D_TagList tar_input_dirs = { 0 }; D_TagList tar_input_dirs = { 0 };
D_TagListAppend(&perm, &tar_input_dirs, dxc_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);
} }
@ -665,6 +788,7 @@ void OnBuild(StringList cli_args)
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg);
bs_arg->cmd = StringF(&perm, Lit("cd %F && tar cvf %F ."), FmtStr(input_dir.full_path), FmtStr(tar_file.full_path)); bs_arg->cmd = StringF(&perm, Lit("cd %F && tar cvf %F ."), FmtStr(input_dir.full_path), FmtStr(tar_file.full_path));
bs_arg->failure_flag = &tar_success_flag; bs_arg->failure_flag = &tar_success_flag;
bs_arg->skip_flag = &shader_success_flag;
String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(input_dir)), FmtStr(D_GetName(tar_file))); String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(input_dir)), FmtStr(D_GetName(tar_file)));
AddStep(step_name, &BuildStepSimpleCommand, bs_arg); AddStep(step_name, &BuildStepSimpleCommand, bs_arg);
} }
@ -675,20 +799,23 @@ void OnBuild(StringList cli_args)
* Build step: Compile RC files * Build step: Compile RC files
* ========================== */ * ========================== */
AtomicI32 rc_success_flag = { 0 }; Atomic32 rc_success_flag = { 0 };
if (PlatformWindows) { if (PlatformWindows) {
AddSyncPoint(); AddSyncPoint();
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 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_Tag dxc_tar_file = D_TagFromPath(&perm, StringF(&perm, Lit("%F/%F.tar"), FmtStr(out_inc_dir_path), FmtStr(D_GetName(dxc_dir))), D_TagKind_File);
D_AddDependency(&store, rc_input_file, icon_file); D_AddDependency(&store, rc_input_file, icon_file);
D_AddDependency(&store, rc_input_file, dxc_tar_file);
if (should_embed_in_rc && should_embed_res_dir) { if (should_embed_in_rc && should_embed_res_dir) {
D_AddDependency(&store, rc_input_file, res_tar_file); 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))));
D_AppendWrite(rc_input_file, StringF(&perm, Lit("%F %F DISCARDABLE %F\n"), FmtStr(D_GetName(dxc_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(dxc_tar_file))));
if (should_embed_in_rc && should_embed_res_dir) { 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(res_tar_file)), FmtStr(Lit("RCDATA")), FmtStr(D_GetName(res_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))));
} }
@ -716,7 +843,7 @@ void OnBuild(StringList cli_args)
* Build step: Compile pch files * Build step: Compile pch files
* ========================== */ * ========================== */
AtomicI32 pch_success_flag = { 0 }; Atomic32 pch_success_flag = { 0 };
D_TagList depfile_force_includes = { 0 }; D_TagList depfile_force_includes = { 0 };
{ {
AddSyncPoint(); AddSyncPoint();
@ -767,6 +894,7 @@ void OnBuild(StringList cli_args)
bs_arg->output_depfile = dep_file; bs_arg->output_depfile = dep_file;
bs_arg->skip_flag = &rc_success_flag; bs_arg->skip_flag = &rc_success_flag;
bs_arg->failure_flag = &pch_success_flag; bs_arg->failure_flag = &pch_success_flag;
bs_arg->delete_file_on_failure = pch_c_file;
String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_c_file))); String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_c_file)));
AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg); AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg);
} }
@ -782,6 +910,7 @@ void OnBuild(StringList cli_args)
bs_arg->output_depfile = dep_file; bs_arg->output_depfile = dep_file;
bs_arg->skip_flag = &rc_success_flag; bs_arg->skip_flag = &rc_success_flag;
bs_arg->failure_flag = &pch_success_flag; bs_arg->failure_flag = &pch_success_flag;
bs_arg->delete_file_on_failure = pch_cpp_file;
String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_cpp_file))); String step_name = StringF(&perm, Lit("%F -> %F"), FmtStr(D_GetName(pch_header_file)), FmtStr(D_GetName(pch_cpp_file)));
AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg); AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg);
} }
@ -818,7 +947,7 @@ void OnBuild(StringList cli_args)
* Build step: Compile src files * Build step: Compile src files
* ========================== */ * ========================== */
AtomicI32 src_success_flag = { 0 }; Atomic32 src_success_flag = { 0 };
{ {
D_TagList src_input_files = { 0 }; D_TagList src_input_files = { 0 };
{ {
@ -840,13 +969,15 @@ void OnBuild(StringList cli_args)
StringBeginsWith(name, Lit("gp_")) || StringBeginsWith(name, Lit("gp_")) ||
StringBeginsWith(name, Lit("playback_")) || StringBeginsWith(name, Lit("playback_")) ||
StringBeginsWith(name, Lit("mp3_")) || StringBeginsWith(name, Lit("mp3_")) ||
StringBeginsWith(name, Lit("ttf_"))) { StringBeginsWith(name, Lit("ttf_")) ||
StringBeginsWith(name, Lit("dxc"))) {
if (PlatformWindows) { if (PlatformWindows) {
ignore = !(StringEqual(name, Lit("sys_win32.c")) || ignore = !(StringEqual(name, Lit("sys_win32.c")) ||
StringEqual(name, Lit("gp_dx12.c")) || StringEqual(name, Lit("gp_dx12.c")) ||
StringEqual(name, Lit("playback_wasapi.c")) || StringEqual(name, Lit("playback_wasapi.c")) ||
StringEqual(name, Lit("mp3_mmf.c")) || StringEqual(name, Lit("mp3_mmf.c")) ||
StringEqual(name, Lit("ttf_dwrite.cpp"))); StringEqual(name, Lit("ttf_dwrite.cpp")) ||
StringEqual(name, Lit("dxc.cpp")));
} }
} else { } else {
ignore = 0; ignore = 0;
@ -899,6 +1030,7 @@ void OnBuild(StringList cli_args)
bs_arg->depfile_force_includes = depfile_force_includes; bs_arg->depfile_force_includes = depfile_force_includes;
bs_arg->skip_flag = &pch_success_flag; bs_arg->skip_flag = &pch_success_flag;
bs_arg->failure_flag = &src_success_flag; bs_arg->failure_flag = &src_success_flag;
bs_arg->delete_file_on_failure = obj_file;
AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg); AddStep(step_name, &BuildStepMsvcCompileCommand, bs_arg);
} else { } else {
BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg); BuildStepSimpleCommandArg *bs_arg = ArenaPush(&perm, BuildStepSimpleCommandArg);

View File

@ -21,6 +21,7 @@
#include "phys.h" #include "phys.h"
#include "host.h" #include "host.h"
#include "bitbuff.h" #include "bitbuff.h"
#include "watch.h"
GLOBAL struct { GLOBAL struct {
struct arena *arena; struct arena *arena;
@ -278,6 +279,7 @@ void sys_app_startup(struct string args_str)
/* Global systems */ /* Global systems */
resource_startup(); resource_startup();
watch_startup();
gp_startup(); gp_startup();
/* Subsystems */ /* Subsystems */

View File

@ -54,8 +54,8 @@ void arena_release(struct arena *arena)
ASAN_UNPOISON(arena, arena->committed + ARENA_HEADER_SIZE); ASAN_UNPOISON(arena, arena->committed + ARENA_HEADER_SIZE);
__prof; __prof;
__proffree(arena); __proffree(arena);
gstat_add(GSTAT_MEMORY_COMMITTED, -arena->committed - ARENA_HEADER_SIZE); gstat_add(GSTAT_MEMORY_COMMITTED, -(i64)(arena->committed - ARENA_HEADER_SIZE));
gstat_add(GSTAT_MEMORY_RESERVED, -arena->reserved); gstat_add(GSTAT_MEMORY_RESERVED, -(i64)(arena->reserved));
gstat_add(GSTAT_NUM_ARENAS, -1); gstat_add(GSTAT_NUM_ARENAS, -1);
sys_memory_release(arena); sys_memory_release(arena);
} }

View File

@ -119,7 +119,7 @@ extern "C" {
* ========================== */ * ========================== */
/* Compile time assert */ /* Compile time assert */
#if LANGUAGE_C && (__STDC_VERSION__ < 202311L) #if COMPILER_MSVC || (LANGUAGE_C && __STDC_VERSION__ < 202311L)
# if COMPILER_MSVC # if COMPILER_MSVC
# define STATIC_ASSERT3(cond, line) struct STATIC_ASSERT_____##line {int foo[(cond) ? 1 : -1];} # define STATIC_ASSERT3(cond, line) struct STATIC_ASSERT_____##line {int foo[(cond) ? 1 : -1];}
# define STATIC_ASSERT2(cond, line) STATIC_ASSERT3(cond, line) # define STATIC_ASSERT2(cond, line) STATIC_ASSERT3(cond, line)
@ -265,7 +265,7 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
#endif #endif
/* alignas */ /* alignas */
#if (LANGUAGE_C && (__STDC_VERSION__ < 202311L)) #if COMPILER_MSVC || (LANGUAGE_C && __STDC_VERSION__ < 202311L)
# if COMPILER_MSVC # if COMPILER_MSVC
# define alignas(n) __declspec(align(n)) # define alignas(n) __declspec(align(n))
# else # else
@ -276,10 +276,14 @@ void __asan_unpoison_memory_region(void const volatile *add, size_t);
/* Field macros */ /* Field macros */
#define FIELD_SIZEOF(type, field) sizeof(((type *)0)->field) #define FIELD_SIZEOF(type, field) sizeof(((type *)0)->field)
#if COMPILER_MSVC && !defined _CRT_USE_BUILTIN_OFFSETOF #if 0
# define offsetof(type, field) ((u64)&(((type *)0)->field)) #if !COMPILER_MSVC
#else # if !defined _CRT_USE_BUILTIN_OFFSETOF
# define offsetof(type, field) __builtin_offsetof(type, field) # define offsetof(type, field) ((u64)&(((type *)0)->field))
# else
# define offsetof(type, field) __builtin_offsetof(type, field)
# endif
#endif
#endif #endif
/* Array */ /* Array */
@ -682,10 +686,10 @@ INLINE f64 clamp_f64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ?
#include "prof_tracy.h" #include "prof_tracy.h"
#define PROF_THREAD_GROUP_FIBERS -GIBI(1) #define PROF_THREAD_GROUP_FIBERS -(i64)GIBI(1)
#define PROF_THREAD_GROUP_SCHEDULER -MEBI(3) #define PROF_THREAD_GROUP_SCHEDULER -(i64)MEBI(3)
#define PROF_THREAD_GROUP_WINDOW -MEBI(2) #define PROF_THREAD_GROUP_WINDOW -(i64)MEBI(2)
#define PROF_THREAD_GROUP_MAIN -MEBI(1) #define PROF_THREAD_GROUP_MAIN -(i64)MEBI(1)
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -73,7 +73,7 @@
#define COLLIDER_DEBUG_DETAILED 1 #define COLLIDER_DEBUG_DETAILED 1
#define COLLIDER_DEBUG_DETAILED_DRAW_MENKOWSKI 1 #define COLLIDER_DEBUG_DETAILED_DRAW_MENKOWSKI 1
#define FLOOD_DEBUG 1 #define FLOOD_DEBUG 0
/* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */ /* If enabled, bitbuffs will insert/verify magic numbers & length for each read & write */
#define BITBUFF_DEBUG 0 #define BITBUFF_DEBUG 0

69
src/dxc.cpp Normal file
View File

@ -0,0 +1,69 @@
extern "C"
{
#include "dxc.h"
#include "arena.h"
#include "string.h"
}
#pragma clang diagnostic ignored "-Wlanguage-extension-token"
#include <Windows.h>
#include <atlbase.h>
#include <dxcapi.h>
#include <d3d12shader.h>
#pragma comment(lib, "d3dcompiler")
#pragma comment(lib, "dxcompiler")
/* https://github.com/microsoft/DirectXShaderCompiler/wiki/Using-dxc.exe-and-dxcompiler.dll */
struct dxc_compile_result dxc_compile(struct arena *arena, string shader_source, i32 num_args, struct string *args)
{
__prof;
struct arena_temp scratch = scratch_begin(arena);
struct dxc_compile_result res = ZI;
wchar_t **wstr_args = arena_push_array(scratch.arena, wchar_t *, num_args);
for (i32 i = 0; i < num_args; ++i) {
wstr_args[i] = wstr_from_string(scratch.arena, args[i]);
}
DxcBuffer dxc_src_buffer = ZI;
dxc_src_buffer.Ptr = shader_source.text;
dxc_src_buffer.Size = shader_source.len;
dxc_src_buffer.Encoding = DXC_CP_UTF8;
/* Init compiler */
CComPtr<IDxcUtils> dxc_utils;
CComPtr<IDxcCompiler3> dxc_compiler;
CComPtr<IDxcIncludeHandler> dxc_include_handler;
DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&dxc_utils));
DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&dxc_compiler));
dxc_utils->CreateDefaultIncludeHandler(&dxc_include_handler);
/* Compile */
CComPtr<IDxcResult> compile_results = 0;
dxc_compiler->Compile(&dxc_src_buffer, (LPCWSTR *)wstr_args, num_args, dxc_include_handler, IID_PPV_ARGS(&compile_results));
/* Copy errors */
CComPtr<IDxcBlobUtf8> dxc_errors = 0;
compile_results->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&dxc_errors), 0);
if (dxc_errors != 0) {
res.errors = string_copy(arena, STRING(dxc_errors->GetStringLength(), (u8 *)dxc_errors->GetBufferPointer()));
}
/* Get status */
HRESULT dxc_hr = 0;
compile_results->GetStatus(&dxc_hr);
res.success = SUCCEEDED(dxc_hr);
/* Copy shader output */
if (res.success) {
CComPtr<IDxcBlob> dxc_shader = 0;
compile_results->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&dxc_shader), 0);
if (dxc_shader != 0) {
res.dxc = string_copy(arena, STRING(dxc_shader->GetBufferSize(), (u8 *)dxc_shader->GetBufferPointer()));
}
}
scratch_end(scratch);
return res;
}

12
src/dxc.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef DXC_H
#define DXC_H
struct dxc_compile_result {
struct string dxc;
struct string errors;
b32 success;
};
struct dxc_compile_result dxc_compile(struct arena *arena, struct string shader_source, i32 num_args, struct string *args);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -13,3 +13,10 @@ struct string inc_res_tar(void)
return INCBIN_GET(res_tar); return INCBIN_GET(res_tar);
} }
#endif #endif
INCBIN_INCLUDE(dxc_tar, INCBIN_DIR "dxc.tar");
struct string inc_dxc_tar(void)
{
return INCBIN_GET(dxc_tar);
}

View File

@ -5,4 +5,6 @@
struct string inc_res_tar(void); struct string inc_res_tar(void);
#endif #endif
struct string inc_dxc_tar(void);
#endif #endif

View File

@ -5,7 +5,7 @@
* ========================== */ * ========================== */
#include "incbin.h" #include "incbin.h"
#include "scratch.h" #include "arena.h"
#include "string.h" #include "string.h"
#include "atomic.h" #include "atomic.h"
#include "intrinsics.h" #include "intrinsics.h"

View File

@ -16,6 +16,10 @@
#define COBJMACROS #define COBJMACROS
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define UNICODE #define UNICODE
#define WINVER 0x0A00
#define _WIN32_WINNT 0x0A00
#define NTDDI_WIN11_DT 0x0C0A0000
#define NTDDI_VERSION 0x0A000000
#include <Windows.h> #include <Windows.h>
#include <initguid.h> #include <initguid.h>
#include <objbase.h> #include <objbase.h>

View File

@ -1,12 +1,12 @@
#ifndef PROF_H #ifndef PROF_H
#define PROF_H #define PROF_H
#if PROFILING
#if COMPILER_MSVC #if COMPILER_MSVC
# error "MSVC not supported for profiling (cleanup attributes are required for profiling markup)" # error "MSVC not supported for profiling (cleanup attributes are required for profiling markup)"
#endif #endif
#if PROFILING
#define PROFILING_SYSTEM_TRACE 0 #define PROFILING_SYSTEM_TRACE 0
#define PROFILING_CAPTURE_FRAME_IMAGE 0 #define PROFILING_CAPTURE_FRAME_IMAGE 0
#define PROFILING_LOCKS 0 #define PROFILING_LOCKS 0

View File

@ -21,33 +21,12 @@ GLOBAL struct {
#if RESOURCES_EMBEDDED #if RESOURCES_EMBEDDED
struct tar_archive archive; struct tar_archive archive;
#endif #endif
#if RESOURCE_RELOADING
struct sys_watch *watch;
struct atomic32 watch_shutdown;
struct snc_counter watch_jobs_counter;
struct snc_mutex watch_dispatcher_mutex;
struct arena *watch_dispatcher_info_arena;
struct sys_watch_info_list watch_dispatcher_info_list;
struct snc_cv watch_dispatcher_cv;
struct snc_mutex watch_callbacks_mutex;
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 * Startup
* ========================== */ * ========================== */
#if RESOURCE_RELOADING
INTERNAL SYS_JOB_DEF(resource_watch_monitor_job, _);
INTERNAL SYS_JOB_DEF(resource_watch_dispatcher_job, _);
INTERNAL SYS_EXIT_FUNC(resource_shutdown);
#endif
struct resource_startup_receipt resource_startup(void) struct resource_startup_receipt resource_startup(void)
{ {
__prof; __prof;
@ -66,17 +45,6 @@ struct resource_startup_receipt resource_startup(void)
} }
#endif #endif
#if RESOURCE_RELOADING
G.watch = sys_watch_alloc(LIT("res"));
G.watch_dispatcher_info_arena = arena_alloc(GIBI(64));
sys_run(1, resource_watch_monitor_job, 0, SYS_POOL_FLOATING, SYS_PRIORITY_LOW, &G.watch_jobs_counter);
sys_run(1, resource_watch_dispatcher_job, 0, SYS_POOL_BACKGROUND, SYS_PRIORITY_LOW, &G.watch_jobs_counter);
sys_on_exit(&resource_shutdown);
#endif
return (struct resource_startup_receipt) { 0 }; return (struct resource_startup_receipt) { 0 };
} }
@ -90,11 +58,9 @@ struct resource resource_open(struct string name)
#if RESOURCES_EMBEDDED #if RESOURCES_EMBEDDED
struct resource res = ZI; struct resource res = ZI;
struct tar_entry *entry = tar_get(&G.archive, name); struct tar_entry *entry = tar_get(&G.archive, name);
if (entry) { res._data = entry->data;
res._data = entry->data; res._name = entry->file_name;
res._name = entry->file_name; res._exists = entry->valid;
res._exists = 1;
}
return res; return res;
#else #else
struct resource res = ZI; struct resource res = ZI;
@ -146,160 +112,3 @@ void resource_close(struct resource *res_ptr)
sys_file_close(res_ptr->_file); sys_file_close(res_ptr->_file);
} }
#endif #endif
/* ========================== *
* Watch
* ========================== */
#if RESOURCE_RELOADING
INTERNAL SYS_EXIT_FUNC(resource_shutdown)
{
__prof;
atomic32_fetch_set(&G.watch_shutdown, 1);
{
struct snc_lock lock = snc_lock_e(&G.watch_dispatcher_mutex);
snc_cv_signal(&G.watch_dispatcher_cv, I32_MAX);
sys_watch_wake(G.watch);
snc_unlock(&lock);
}
snc_counter_wait(&G.watch_jobs_counter);
}
void resource_register_watch_callback(resource_watch_callback *callback)
{
struct snc_lock lock = snc_lock_e(&G.watch_callbacks_mutex);
{
if (G.num_watch_callbacks < countof(G.watch_callbacks)) {
G.watch_callbacks[G.num_watch_callbacks++] = callback;
} else {
sys_panic(LIT("Max resource watch callbacks reached"));
}
}
snc_unlock(&lock);
}
INTERNAL SYS_JOB_DEF(resource_watch_monitor_job, _)
{
(UNUSED)_;
struct arena_temp scratch = scratch_begin_no_conflict();
while (!atomic32_fetch(&G.watch_shutdown)) {
struct arena_temp temp = arena_temp_begin(scratch.arena);
struct sys_watch_info_list res = sys_watch_read_wait(temp.arena, G.watch);
if (res.first && !atomic32_fetch(&G.watch_shutdown)) {
struct snc_lock lock = snc_lock_e(&G.watch_dispatcher_mutex);
{
struct sys_watch_info_list list_part = sys_watch_info_copy(G.watch_dispatcher_info_arena, res);
if (G.watch_dispatcher_info_list.last) {
G.watch_dispatcher_info_list.last->next = list_part.first;
list_part.first->prev = G.watch_dispatcher_info_list.last;
G.watch_dispatcher_info_list.last = list_part.last;
} else {
G.watch_dispatcher_info_list = list_part;
}
}
snc_cv_signal(&G.watch_dispatcher_cv, I32_MAX);
snc_unlock(&lock);
}
arena_temp_end(temp);
}
scratch_end(scratch);
}
/* NOTE: We separate the responsibilities of monitoring directory changes
* & dispatching watch callbacks into two separate jobs so that we can delay
* the dispatch, allowing for deduplication of file modification notifications. */
#define WATCH_DISPATCHER_DELAY_SECONDS 0.050
#define WATCH_DISPATCHER_DEDUP_DICT_BINS 128
struct resource_watch_callback_job_sig {
struct string name;
resource_watch_callback **callbacks;
};
INTERNAL SYS_JOB_DEF(resource_watch_callback_job, job)
{
__prof;
struct resource_watch_callback_job_sig *sig = job.sig;
struct string name = sig->name;
resource_watch_callback *callback = sig->callbacks[job.id];
callback(name);
}
INTERNAL SYS_JOB_DEF(resource_watch_dispatcher_job, _)
{
(UNUSED)_;
struct arena_temp scratch = scratch_begin_no_conflict();
struct snc_lock watch_dispatcher_lock = snc_lock_e(&G.watch_dispatcher_mutex);
while (!atomic32_fetch(&G.watch_shutdown)) {
snc_cv_wait(&G.watch_dispatcher_cv, &watch_dispatcher_lock);
if (!atomic32_fetch(&G.watch_shutdown) && G.watch_dispatcher_info_arena->pos > 0) {
__profn("Dispatch resource watch callbacks");
/* Unlock and sleep a bit so duplicate events pile up */
{
__profn("Delay");
snc_unlock(&watch_dispatcher_lock);
sys_wait(0, 0, 0, NS_FROM_SECONDS(WATCH_DISPATCHER_DELAY_SECONDS));
watch_dispatcher_lock = snc_lock_e(&G.watch_dispatcher_mutex);
}
if (!atomic32_fetch(&G.watch_shutdown)) {
struct arena_temp temp = arena_temp_begin(scratch.arena);
/* Pull watch info from queue */
struct sys_watch_info_list watch_info_list = sys_watch_info_copy(temp.arena, G.watch_dispatcher_info_list);
MEMZERO_STRUCT(&G.watch_dispatcher_info_list);
arena_reset(G.watch_dispatcher_info_arena);
/* Build callbacks array */
u64 num_callbacks = 0;
resource_watch_callback **callbacks = 0;
struct snc_lock callbacks_lock = snc_lock_s(&G.watch_callbacks_mutex);
{
num_callbacks = G.num_watch_callbacks;
callbacks = arena_push_array_no_zero(temp.arena, resource_watch_callback *, num_callbacks);
for (u64 i = 0; i < num_callbacks; ++i) {
callbacks[i] = G.watch_callbacks[i];
}
}
snc_unlock(&callbacks_lock);
/* Unlock and run callbacks */
snc_unlock(&watch_dispatcher_lock);
{
struct dict *dedup_dict = dict_init(temp.arena, WATCH_DISPATCHER_DEDUP_DICT_BINS);
for (struct sys_watch_info *info = watch_info_list.first; info; info = info->next) {
__profn("Dispatch");
/* Do not run callbacks for the same file more than once */
b32 skip = 0;
u64 hash = hash_fnv64(HASH_FNV64_BASIS, info->name);
if (dict_get(dedup_dict, hash) == 1) {
skip = 1;
} else {
dict_set(temp.arena, dedup_dict, hash, 1);
}
if (!skip) {
struct resource_watch_callback_job_sig sig = ZI;
sig.name = info->name;
sig.callbacks = callbacks;
struct snc_counter counter = ZI;
sys_run(num_callbacks, resource_watch_callback_job, &sig, SYS_POOL_BACKGROUND, SYS_PRIORITY_LOW, &counter);
snc_counter_wait(&counter);
}
}
}
watch_dispatcher_lock = snc_lock_e(&G.watch_dispatcher_mutex);
arena_temp_end(temp);
}
}
}
scratch_end(scratch);
}
#endif

View File

@ -3,7 +3,7 @@
#include "sys.h" #include "sys.h"
#define RESOURCE_NAME_LEN_MAX 255 #define RESOURCE_NAME_LEN_MAX 256
/* A resource contains data that can be retrieved globally by name. /* A resource contains data that can be retrieved globally by name.
* If enabled during compilation, resource data is embedded in the * If enabled during compilation, resource data is embedded in the
@ -54,17 +54,4 @@ struct resource resource_open(struct string name);
#define resource_get_name(res_ptr) STRING((res_ptr)->_name_len, (res_ptr)->_name_text) #define resource_get_name(res_ptr) STRING((res_ptr)->_name_len, (res_ptr)->_name_text)
#endif #endif
/* ========================== *
* Watch
* ========================== */
#define RESOURCE_WATCH_CALLBACK_FUNC_DEF(func_name, arg_name) void func_name(struct string arg_name)
typedef RESOURCE_WATCH_CALLBACK_FUNC_DEF(resource_watch_callback, name);
#if RESOURCE_RELOADING
void resource_register_watch_callback(resource_watch_callback *callback);
#else
#define resource_register_watch_callback(callback)
#endif
#endif #endif

3
src/sh/blit.d Normal file
View File

@ -0,0 +1,3 @@
C:\Users\Jacob\Home\dev\repos\power_play\build\clang-developer-debug\dxc\blit.dxc_ps: C:\Users\Jacob\Home\dev\repos\power_play\src\sh\blit.hlsl_rs \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/common.hlsl \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/sh_common.h

View File

@ -1,4 +1,4 @@
#include "sh/common.hlsl" #include "common.hlsl"
/* ========================== * /* ========================== *
* Root signature * Root signature

View File

@ -1,4 +1,4 @@
#include "sh/sh_common.h" #include "sh_common.h"
#define TAU 6.28318530718 #define TAU 6.28318530718
#define PI 3.14159265359 #define PI 3.14159265359

3
src/sh/flood.d Normal file
View File

@ -0,0 +1,3 @@
C:\Users\Jacob\Home\dev\repos\power_play\build\clang-developer-debug\dxc\flood.dxc_cs: C:\Users\Jacob\Home\dev\repos\power_play\src\sh\flood.hlsl_cs \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/common.hlsl \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/sh_common.h

View File

@ -1,4 +1,4 @@
#include "sh/common.hlsl" #include "common.hlsl"
/* ========================== * /* ========================== *
* Root signature * Root signature

3
src/sh/material.d Normal file
View File

@ -0,0 +1,3 @@
C:\Users\Jacob\Home\dev\repos\power_play\build\clang-developer-debug\dxc\material.dxc_ps: C:\Users\Jacob\Home\dev\repos\power_play\src\sh\material.hlsl_rs \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/common.hlsl \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/sh_common.h

View File

@ -1,4 +1,4 @@
#include "sh/common.hlsl" #include "common.hlsl"
/* ========================== * /* ========================== *
* Root signature * Root signature

View File

@ -4,9 +4,9 @@
#define SH_DECL(t, n) struct CAT(sh_, t) n #define SH_DECL(t, n) struct CAT(sh_, t) n
#define SH_DECLS(t, n) SH_DECL(t, n) #define SH_DECLS(t, n) SH_DECL(t, n)
#define SH_ENTRY(rootsig) static #define SH_ENTRY(rootsig) static
#define SH_ASSERT_ROOT_CONST(s, n) STATIC_ASSERT(sizeof(s) % 16 == 0); /* Root constant struct should pad to 16 byte alignment */ \ #define SH_ASSERT_ROOT_CONST(s, n) STATIC_ASSERT((sizeof(s) % 16 == 0) && /* Root constant struct should pad to 16 byte alignment */ \
STATIC_ASSERT((sizeof(s) / 4) == n); /* Root constant struct size should match the specified 32-bit-constant count */ \ ((sizeof(s) / 4) == n) && /* Root constant struct size should match the specified 32-bit-constant count */ \
STATIC_ASSERT((sizeof(s) <= 256)) /* Root constant struct can only fit 64 DWORDS */ (sizeof(s) <= 256)) /* Root constant struct can only fit 64 DWORDS */
struct sh_uint { u32 v; }; struct sh_uint { u32 v; };
INLINE struct sh_uint sh_uint_from_u32(u32 v) INLINE struct sh_uint sh_uint_from_u32(u32 v)

3
src/sh/shade.d Normal file
View File

@ -0,0 +1,3 @@
C:\Users\Jacob\Home\dev\repos\power_play\build\clang-developer-debug\dxc\shade.dxc_cs: C:\Users\Jacob\Home\dev\repos\power_play\src\sh\shade.hlsl_cs \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/common.hlsl \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/sh_common.h

View File

@ -1,4 +1,4 @@
#include "sh/common.hlsl" #include "common.hlsl"
/* ========================== * /* ========================== *
* Root signature * Root signature
@ -69,7 +69,6 @@ INLINE float3 get_light_in_dir(uint2 ray_start, float2 ray_dir)
} }
} }
} }
return result; return result;
} }

3
src/sh/shape.d Normal file
View File

@ -0,0 +1,3 @@
C:\Users\Jacob\Home\dev\repos\power_play\build\clang-developer-debug\dxc\shape.dxc_ps: C:\Users\Jacob\Home\dev\repos\power_play\src\sh\shape.hlsl_rs \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/common.hlsl \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/sh_common.h

View File

@ -1,4 +1,4 @@
#include "sh/common.hlsl" #include "common.hlsl"
/* ========================== * /* ========================== *
* Root signature * Root signature

3
src/sh/ui.d Normal file
View File

@ -0,0 +1,3 @@
C:\Users\Jacob\Home\dev\repos\power_play\build\clang-developer-debug\dxc\ui.dxc_ps: C:\Users\Jacob\Home\dev\repos\power_play\src\sh\ui.hlsl_rs \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/common.hlsl \
C:\Users\Jacob\Home\dev\repos\power_play\src\sh/sh_common.h

View File

@ -1,4 +1,4 @@
#include "sh/common.hlsl" #include "common.hlsl"
/* ========================== * /* ========================== *
* Root signature * Root signature

View File

@ -11,6 +11,7 @@
#include "math.h" #include "math.h"
#include "rand.h" #include "rand.h"
#include "snc.h" #include "snc.h"
#include "watch.h"
/* The evictor will begin evicting once cache usage is > threshold. /* The evictor will begin evicting once cache usage is > threshold.
* It will entries until the budget has shrunk < target. */ * It will entries until the budget has shrunk < target. */
@ -205,7 +206,7 @@ INTERNAL SYS_JOB_DEF(sprite_load_job, arg);
INTERNAL SYS_JOB_DEF(sprite_evictor_job, _); INTERNAL SYS_JOB_DEF(sprite_evictor_job, _);
#if RESOURCE_RELOADING #if RESOURCE_RELOADING
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(sprite_resource_watch_callback, info); INTERNAL WATCH_CALLBACK_FUNC_DEF(sprite_watch_callback, info);
#endif #endif
struct sprite_startup_receipt sprite_startup(void) struct sprite_startup_receipt sprite_startup(void)
@ -249,7 +250,7 @@ struct sprite_startup_receipt sprite_startup(void)
sys_run(1, sprite_evictor_job, 0, SYS_POOL_BACKGROUND, SYS_PRIORITY_LOW, &G.shutdown_counter); sys_run(1, sprite_evictor_job, 0, SYS_POOL_BACKGROUND, SYS_PRIORITY_LOW, &G.shutdown_counter);
sys_on_exit(&sprite_shutdown); sys_on_exit(&sprite_shutdown);
resource_register_watch_callback(&sprite_resource_watch_callback); watch_register_callback(&sprite_watch_callback);
return (struct sprite_startup_receipt) { 0 }; return (struct sprite_startup_receipt) { 0 };
} }
@ -1173,10 +1174,15 @@ INTERNAL void reload_if_exists(struct sprite_scope *scope, struct sprite_tag tag
} }
} }
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(sprite_resource_watch_callback, name) INTERNAL WATCH_CALLBACK_FUNC_DEF(sprite_watch_callback, name)
{ {
struct sprite_scope *scope = sprite_scope_begin(); struct sprite_scope *scope = sprite_scope_begin();
if (string_starts_with(name, LIT("res/"))) {
name.len -= LIT("res/").len;
name.text += LIT("res/").len;
}
struct sprite_tag tag = sprite_tag_from_path(name); struct sprite_tag tag = sprite_tag_from_path(name);
for (enum cache_entry_kind kind = 0; kind < NUM_CACHE_ENTRY_KINDS; ++kind) { for (enum cache_entry_kind kind = 0; kind < NUM_CACHE_ENTRY_KINDS; ++kind) {
reload_if_exists(scope, tag, kind); reload_if_exists(scope, tag, kind);

View File

@ -225,44 +225,38 @@ struct string string_cat(struct arena *arena, struct string str1, struct string
return new_str; return new_str;
} }
/* `arena` is where pieces items will be allocated. These strings point /* `arena` is where pieces will be allocated. These strings point
* into the existing supplied string and do not allocate any new text. */ * into the existing string and do not allocate any new text. */
struct string_array string_split(struct arena *arena, struct string str, struct string delim) struct string_array string_split(struct arena *arena, struct string str, struct string delim)
{ {
struct string_array pieces = { struct string_array pieces = ZI;
.count = 0, pieces.strings = arena_push_dry(arena, struct string);
.strings = arena_push_dry(arena, struct string) i64 piece_start = 0;
}; for (i64 i = 0; i < (i64)str.len - (i64)delim.len; ++i) {
struct string cmp = ZI;
cmp.text = &str.text[i];
cmp.len = min_i64(str.len - i, delim.len);
struct string piece = { b32 is_delimiter = string_eq(cmp, delim);
.len = 0, if (is_delimiter) {
.text = str.text struct string piece = ZI;
}; piece.text = &str.text[piece_start];
piece.len = i - piece_start;
for (u64 i = 0; i <= str.len - delim.len; ++i) { i += delim.len;
/* Clamp comparison string so we don't overflow. */ piece_start = i;
struct string comp_str = { if (piece.len > 0) {
.len = delim.len, *arena_push_no_zero(arena, struct string) = piece;
.text = &str.text[i] ++pieces.count;
}; }
b32 is_delimiter = string_eq(comp_str, delim);
b32 is_end = i == str.len - 1;
if (!is_delimiter || is_end) {
++piece.len;
}
if (is_delimiter || is_end) {
/* Delimiter found */
struct string *piece_pushed = arena_push_no_zero(arena, struct string);
*piece_pushed = piece;
++pieces.count;
piece.text = piece.text + piece.len + delim.len;
piece.len = 0;
} }
} }
if (piece_start < (i64)str.len) {
struct string piece = ZI;
piece.text = &str.text[piece_start];
piece.len = str.len - piece_start;
*arena_push_no_zero(arena, struct string) = piece;
++pieces.count;
}
return pieces; return pieces;
} }

View File

@ -8,8 +8,7 @@ struct snc_counter;
* ========================== */ * ========================== */
/* Futex-like wait & wake */ /* Futex-like wait & wake */
void sys_wait(volatile void *addr, void *cmp, u32 size, i64 timeout_ns);
void sys_wait(void *addr, void *cmp, u32 size, i64 timeout_ns);
void sys_wake(void *addr, i32 count); void sys_wake(void *addr, i32 count);
/* ========================== * /* ========================== *
@ -232,8 +231,6 @@ struct sys_watch_info_list sys_watch_read_wait(struct arena *arena, struct sys_w
void sys_watch_wake(struct sys_watch *dw); void sys_watch_wake(struct sys_watch *dw);
struct sys_watch_info_list sys_watch_info_copy(struct arena *arena, struct sys_watch_info_list src);
/* ========================== * /* ========================== *
* Window * Window
* ========================== */ * ========================== */

View File

@ -481,7 +481,7 @@ INTERNAL b32 thread_try_release(struct thread *thread, f32 timeout_seconds)
HANDLE handle = t->handle; HANDLE handle = t->handle;
if (handle) { if (handle) {
/* Wait for thread to stop */ /* Wait for thread to stop */
DWORD timeout_ms = (timeout_seconds == F32_INFINITY) ? INFINITE : math_round_to_int(timeout_seconds * 1000); DWORD timeout_ms = (timeout_seconds > 10000000) ? INFINITE : math_round_to_int(timeout_seconds * 1000);
DWORD wait_res = WaitForSingleObject(handle, timeout_ms); DWORD wait_res = WaitForSingleObject(handle, timeout_ms);
if (wait_res == WAIT_OBJECT_0) { if (wait_res == WAIT_OBJECT_0) {
/* Release thread */ /* Release thread */
@ -524,7 +524,7 @@ INTERNAL void thread_wait_release(struct thread *thread)
* Wait / wake * Wait / wake
* ========================== */ * ========================== */
void sys_wait(void *addr, void *cmp, u32 size, i64 timeout_ns) void sys_wait(volatile void *addr, void *cmp, u32 size, i64 timeout_ns)
{ {
struct fiber *fiber = fiber_from_id(sys_current_fiber_id()); struct fiber *fiber = fiber_from_id(sys_current_fiber_id());
i16 parent_id = fiber->parent_id; i16 parent_id = fiber->parent_id;
@ -541,7 +541,7 @@ void sys_wait(void *addr, void *cmp, u32 size, i64 timeout_ns)
job_fiber_yield(fiber, fiber_from_id(parent_id)); job_fiber_yield(fiber, fiber_from_id(parent_id));
} else { } else {
i32 timeout_ms = 0; i32 timeout_ms = 0;
if (timeout_ns == I64_MAX) { if (timeout_ns > 10000000000000000ll) {
timeout_ms = INFINITE; timeout_ms = INFINITE;
} else if (timeout_ns != 0) { } else if (timeout_ns != 0) {
timeout_ms = timeout_ns / 1000000; timeout_ms = timeout_ns / 1000000;
@ -1258,7 +1258,7 @@ INTERNAL THREAD_DEF(job_worker_entry, worker_ctx_arg)
tm_unlock(&G.wait_lists_arena_lock); tm_unlock(&G.wait_lists_arena_lock);
} }
MEMZERO_STRUCT(wait_addr_list); MEMZERO_STRUCT(wait_addr_list);
wait_addr_list->value = wait_addr; wait_addr_list->value = (u64)wait_addr;
if (wait_addr_bin->last_wait_list) { if (wait_addr_bin->last_wait_list) {
wait_addr_bin->last_wait_list->next_in_bin = wait_addr_list; wait_addr_bin->last_wait_list->next_in_bin = wait_addr_list;
wait_addr_list->prev_in_bin = wait_addr_bin->last_wait_list; wait_addr_list->prev_in_bin = wait_addr_bin->last_wait_list;
@ -1268,7 +1268,7 @@ INTERNAL THREAD_DEF(job_worker_entry, worker_ctx_arg)
wait_addr_bin->last_wait_list = wait_addr_list; wait_addr_bin->last_wait_list = wait_addr_list;
} }
/* Insert fiber into wait addr list */ /* Insert fiber into wait addr list */
job_fiber->wait_addr = wait_addr; job_fiber->wait_addr = (u64)wait_addr;
if (wait_addr_list->last_waiter) { if (wait_addr_list->last_waiter) {
fiber_from_id(wait_addr_list->last_waiter)->next_addr_waiter = job_fiber_id; fiber_from_id(wait_addr_list->last_waiter)->next_addr_waiter = job_fiber_id;
job_fiber->prev_addr_waiter = wait_addr_list->last_waiter; job_fiber->prev_addr_waiter = wait_addr_list->last_waiter;
@ -2107,26 +2107,6 @@ void sys_watch_wake(struct sys_watch *dw)
SetEvent(w32_watch->wake_handle); SetEvent(w32_watch->wake_handle);
} }
struct sys_watch_info_list sys_watch_info_copy(struct arena *arena, struct sys_watch_info_list src_list)
{
struct sys_watch_info_list dst_list = ZI;
for (struct sys_watch_info *src = src_list.first; src; src = src->next) {
struct sys_watch_info *dst = arena_push(arena, struct sys_watch_info);
dst->kind = src->kind;
dst->name = string_copy(arena, src->name);
if (dst_list.last) {
dst_list.last->next = dst;
dst->prev = dst_list.last;
dst_list.last = dst;
} else {
dst_list.first = dst;
dst_list.last = dst;
}
dst_list.count = src_list.count;
}
return dst_list;
}
/* ========================== * /* ========================== *
* Window * Window
* ========================== */ * ========================== */

View File

@ -120,6 +120,7 @@ struct tar_archive tar_parse(struct arena *arena, struct string data, struct str
struct string file_name = string_cat(arena, prefix, file_name_cstr); struct string file_name = string_cat(arena, prefix, file_name_cstr);
struct tar_entry *entry = arena_push(arena, struct tar_entry); struct tar_entry *entry = arena_push(arena, struct tar_entry);
entry->valid = 1;
entry->is_dir = is_dir; entry->is_dir = is_dir;
entry->file_name = file_name; entry->file_name = file_name;
entry->data = file_data; entry->data = file_data;
@ -162,8 +163,10 @@ struct tar_archive tar_parse(struct arena *arena, struct string data, struct str
return archive; return archive;
} }
READONLY GLOBAL struct tar_entry g_nil_tar_entry = ZI;
struct tar_entry *tar_get(struct tar_archive *archive, struct string name) struct tar_entry *tar_get(struct tar_archive *archive, struct string name)
{ {
u64 hash = hash_fnv64(HASH_FNV64_BASIS, name); u64 hash = hash_fnv64(HASH_FNV64_BASIS, name);
return (struct tar_entry *)dict_get(archive->lookup, hash); struct tar_entry *lookup = (struct tar_entry *)dict_get(archive->lookup, hash);
return lookup ? lookup : &g_nil_tar_entry;
} }

View File

@ -4,6 +4,7 @@
#include "util.h" #include "util.h"
struct tar_entry { struct tar_entry {
b32 valid;
struct string file_name; struct string file_name;
struct string data; struct string data;

View File

@ -266,7 +266,7 @@ INLINE void sleep_precise(i64 sleep_time_ns)
__prof; __prof;
i64 big_sleep = sys_current_scheduler_period_ns(); i64 big_sleep = sys_current_scheduler_period_ns();
i64 tolerance = big_sleep * 0.5; i64 tolerance = (f64)big_sleep * 0.5;
//i64 tolerance = 1000000000; //i64 tolerance = 1000000000;
i64 now_ns = sys_time_ns(); i64 now_ns = sys_time_ns();

245
src/watch.c Normal file
View File

@ -0,0 +1,245 @@
#include "watch.h"
#if RESOURCE_RELOADING
#include "sys.h"
#include "snc.h"
#include "arena.h"
#include "atomic.h"
#include "util.h"
#include "string.h"
struct watch_event {
struct string name;
struct watch_event *next;
};
GLOBAL struct {
struct sys_watch *watch;
struct atomic32 watch_shutdown;
struct snc_counter watch_jobs_counter;
struct snc_mutex watch_dispatcher_mutex;
struct arena *watch_events_arena;
struct watch_event *first_watch_event;
struct watch_event *last_watch_event;
struct snc_cv watch_dispatcher_cv;
struct snc_mutex watch_callbacks_mutex;
watch_callback *watch_callbacks[64];
u64 num_watch_callbacks;
} G = ZI, DEBUG_ALIAS(G, G_watch);
/* ========================== *
* Startup
* ========================== */
INTERNAL SYS_JOB_DEF(watch_monitor_job, _);
INTERNAL SYS_JOB_DEF(watch_dispatcher_job, _);
INTERNAL SYS_EXIT_FUNC(watch_shutdown);
void watch_startup(void)
{
G.watch = sys_watch_alloc(LIT("./"));
G.watch_events_arena = arena_alloc(GIBI(64));
sys_run(1, watch_monitor_job, 0, SYS_POOL_FLOATING, SYS_PRIORITY_LOW, &G.watch_jobs_counter);
sys_run(1, watch_dispatcher_job, 0, SYS_POOL_BACKGROUND, SYS_PRIORITY_LOW, &G.watch_jobs_counter);
sys_on_exit(&watch_shutdown);
}
/* ========================== *
* Watch
* ========================== */
INTERNAL SYS_EXIT_FUNC(watch_shutdown)
{
__prof;
atomic32_fetch_set(&G.watch_shutdown, 1);
{
struct snc_lock lock = snc_lock_e(&G.watch_dispatcher_mutex);
snc_cv_signal(&G.watch_dispatcher_cv, I32_MAX);
sys_watch_wake(G.watch);
snc_unlock(&lock);
}
snc_counter_wait(&G.watch_jobs_counter);
}
void watch_register_callback(watch_callback *callback)
{
struct snc_lock lock = snc_lock_e(&G.watch_callbacks_mutex);
{
if (G.num_watch_callbacks < countof(G.watch_callbacks)) {
G.watch_callbacks[G.num_watch_callbacks++] = callback;
} else {
sys_panic(LIT("Max resource watch callbacks reached"));
}
}
snc_unlock(&lock);
}
INTERNAL SYS_JOB_DEF(watch_monitor_job, _)
{
(UNUSED)_;
struct arena_temp scratch = scratch_begin_no_conflict();
struct string ignored[] = {
LIT(".vs"),
LIT(".git")
};
while (!atomic32_fetch(&G.watch_shutdown)) {
struct arena_temp temp = arena_temp_begin(scratch.arena);
struct sys_watch_info_list info_list = sys_watch_read_wait(temp.arena, G.watch);
if (info_list.first && !atomic32_fetch(&G.watch_shutdown)) {
struct snc_lock lock = snc_lock_e(&G.watch_dispatcher_mutex);
{
for (struct sys_watch_info *info = info_list.first; info; info = info->next) {
struct string name_src = info->name;
b32 ignore = 0;
for (u32 i = 0; i < countof(ignored); ++i) {
if (string_starts_with(name_src, ignored[i])) {
ignore = 1;
break;
}
}
if (!ignore) {
struct watch_event *e = arena_push(G.watch_events_arena, struct watch_event);
e->name = string_copy(G.watch_events_arena, name_src);
if (G.last_watch_event) {
G.last_watch_event->next = e;
} else {
G.first_watch_event = e;
}
G.last_watch_event = e;
}
}
}
snc_cv_signal(&G.watch_dispatcher_cv, I32_MAX);
snc_unlock(&lock);
}
arena_temp_end(temp);
}
scratch_end(scratch);
}
/* NOTE: We separate the responsibilities of monitoring directory changes
* & dispatching watch callbacks into two separate jobs so that we can delay
* the dispatch, allowing for deduplication of file modification notifications. */
#define WATCH_DISPATCHER_DELAY_SECONDS 0.050
#define WATCH_DISPATCHER_DEDUP_DICT_BINS 128
struct watch_callback_job_sig {
struct string name;
watch_callback **callbacks;
};
INTERNAL SYS_JOB_DEF(watch_callback_job, job)
{
__prof;
struct watch_callback_job_sig *sig = job.sig;
struct string name = sig->name;
watch_callback *callback = sig->callbacks[job.id];
callback(name);
}
INTERNAL SYS_JOB_DEF(watch_dispatcher_job, _)
{
(UNUSED)_;
b32 shutdown = 0;
while (!shutdown) {
{
struct arena_temp scratch = scratch_begin_no_conflict();
struct watch_event *first_watch_event = 0;
struct watch_event *last_watch_event = 0;
/* Delay so that duplicate events pile up */
{
__profn("Delay");
sys_wait(0, 0, 0, NS_FROM_SECONDS(WATCH_DISPATCHER_DELAY_SECONDS));
}
/* Pull watch events from queue */
{
struct snc_lock lock = snc_lock_e(&G.watch_dispatcher_mutex);
for (struct watch_event *src_event = G.first_watch_event; src_event; src_event = src_event->next) {
struct watch_event *e = arena_push(scratch.arena, struct watch_event);
e->name = string_copy(scratch.arena, src_event->name);
if (last_watch_event) {
last_watch_event->next = e;
} else {
first_watch_event = e;
}
last_watch_event = e;
}
G.first_watch_event = 0;
G.last_watch_event = 0;
arena_reset(G.watch_events_arena);
snc_unlock(&lock);
}
/* Build callbacks array */
u64 num_callbacks = 0;
watch_callback **callbacks = 0;
struct snc_lock callbacks_lock = snc_lock_s(&G.watch_callbacks_mutex);
{
num_callbacks = G.num_watch_callbacks;
callbacks = arena_push_array_no_zero(scratch.arena, watch_callback *, num_callbacks);
for (u64 i = 0; i < num_callbacks; ++i) {
callbacks[i] = G.watch_callbacks[i];
}
}
snc_unlock(&callbacks_lock);
/* Run callbacks */
{
struct dict *dedup_dict = dict_init(scratch.arena, WATCH_DISPATCHER_DEDUP_DICT_BINS);
for (struct watch_event *e = first_watch_event; e; e = e->next) {
__profn("Dispatch");
/* Do not run callbacks for the same file more than once */
b32 skip = 0;
u64 hash = hash_fnv64(HASH_FNV64_BASIS, e->name);
if (dict_get(dedup_dict, hash) == 1) {
skip = 1;
} else {
dict_set(scratch.arena, dedup_dict, hash, 1);
}
if (!skip) {
struct watch_callback_job_sig sig = ZI;
sig.name = e->name;
sig.callbacks = callbacks;
struct snc_counter counter = ZI;
sys_run(num_callbacks, watch_callback_job, &sig, SYS_POOL_BACKGROUND, SYS_PRIORITY_LOW, &counter);
snc_counter_wait(&counter);
}
}
}
scratch_end(scratch);
}
/* Wait for event */
struct snc_lock lock = snc_lock_s(&G.watch_dispatcher_mutex);
{
shutdown = atomic32_fetch(&G.watch_shutdown);
while (!shutdown && !G.first_watch_event) {
snc_cv_wait(&G.watch_dispatcher_cv, &lock);
shutdown = atomic32_fetch(&G.watch_shutdown);
}
}
snc_unlock(&lock);
}
}
#else /* RESOURCE_RELOADING */
void watch_startup(void)
{
}
#endif /* RESOURCE_RELOADING */

15
src/watch.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef WATCH_H
#define WATCH_H
#define WATCH_CALLBACK_FUNC_DEF(func_name, arg_name) void func_name(struct string arg_name)
typedef WATCH_CALLBACK_FUNC_DEF(watch_callback, name);
void watch_startup(void);
#if RESOURCE_RELOADING
void watch_register_callback(watch_callback *callback);
#else
#define watch_register_callback(callback)
#endif
#endif