diff --git a/.vscode/bookmarks.json b/.vscode/bookmarks.json
index a5935bf..30e7b89 100644
--- a/.vscode/bookmarks.json
+++ b/.vscode/bookmarks.json
@@ -1,35 +1,20 @@
{
"files": [
{
- "path": "project/platform/win32_platform.cpp",
+ "path": "project/platform/win32/win32_platform.cpp",
"bookmarks": [
{
- "line": 59,
+ "line": 39,
"column": 0,
"label": "Struct Defs"
},
{
- "line": 98,
+ "line": 58,
"column": 0,
"label": "Static Data"
},
{
- "line": 661,
- "column": 0,
- "label": "Timing"
- },
- {
- "line": 1592,
- "column": 4,
- "label": "Main Loop : Audio Processing"
- },
- {
- "line": 1713,
- "column": 2,
- "label": "Main Loop : Timing Update"
- },
- {
- "line": 1797,
+ "line": 980,
"column": 0,
"label": "Main Loop : End"
}
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index 71da193..c3b93be 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -12,6 +12,7 @@
"UNICODE",
"_UNICODE",
"GEN_TIME",
+ "INTELLISENSE_DIRECTIVES",
"Build_Debug",
"Build_Development",
],
@@ -31,6 +32,7 @@
"UNICODE",
"_UNICODE",
"GEN_TIME",
+ "INTELLISENSE_DIRECTIVES",
],
"windowsSdkVersion": "10.0.22621.0",
"compilerPath": "clang.exe",
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 1c9f279..85c72b9 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -16,11 +16,20 @@
{
"type":"cppvsdbg",
"request": "launch",
- "name" : "Debug handmade platform gen msvc",
+ "name" : "Debug handmade engine (post_build) gen msvc",
"program": "${workspaceFolder}/build/engine_postbuild_gen.exe",
"args": [],
"cwd": "${workspaceFolder}/build",
- "visualizerFile": "${workspaceFolder}/scripts/handmade.natvis"
+ "visualizerFile": "${workspaceFolder}/scripts/gencpp.natvis"
+ },
+ {
+ "type":"cppvsdbg",
+ "request": "launch",
+ "name" : "Debug handmade platform gen msvc",
+ "program": "${workspaceFolder}/build/platform_gen.exe",
+ "args": [],
+ "cwd": "${workspaceFolder}/project/platform",
+ "visualizerFile": "${workspaceFolder}/scripts/gencpp.natvis"
}
]
}
diff --git a/HandmadeHero.vcxproj b/HandmadeHero.vcxproj
index c35545b..716b78a 100644
--- a/HandmadeHero.vcxproj
+++ b/HandmadeHero.vcxproj
@@ -28,9 +28,13 @@
+
+
+
+
@@ -39,14 +43,20 @@
+
+
+
+
+
+
diff --git a/data/binaries/handmade_engine.exp b/data/binaries/handmade_engine.exp
new file mode 100644
index 0000000..f83e992
Binary files /dev/null and b/data/binaries/handmade_engine.exp differ
diff --git a/data/binaries/handmade_engine.lib b/data/binaries/handmade_engine.lib
new file mode 100644
index 0000000..02ae2b7
Binary files /dev/null and b/data/binaries/handmade_engine.lib differ
diff --git a/data/binaries/handmade_win32.exp b/data/binaries/handmade_win32.exp
new file mode 100644
index 0000000..f12565c
Binary files /dev/null and b/data/binaries/handmade_win32.exp differ
diff --git a/data/binaries/handmade_win32.lib b/data/binaries/handmade_win32.lib
new file mode 100644
index 0000000..74d01df
Binary files /dev/null and b/data/binaries/handmade_win32.lib differ
diff --git a/project/codegen/engine_postbuild_gen.cpp b/project/codegen/engine_postbuild_gen.cpp
index a8d63cf..3b66ebf 100644
--- a/project/codegen/engine_postbuild_gen.cpp
+++ b/project/codegen/engine_postbuild_gen.cpp
@@ -93,7 +93,7 @@ int gen_main()
builder.write();
#pragma pop_macro("str_ascii")
- log_fmt("Generaton finished for Handmade Hero: Engine Module\n");
+ log_fmt("Generaton finished for Handmade Hero: Engine Module\n\n");
// gen::deinit();
return 0;
}
diff --git a/project/codegen/platform_gen.cpp b/project/codegen/platform_gen.cpp
index 22469db..9b49bf5 100644
--- a/project/codegen/platform_gen.cpp
+++ b/project/codegen/platform_gen.cpp
@@ -6,11 +6,72 @@
#include "dependencies/gen.hpp"
using namespace gen;
+#define path_gen "./gen/"
+
+
int gen_main()
{
gen::init();
log_fmt("Generating code for Handmade Hero: Platform Module\n");
- log_fmt("Generaton finished for Handmade Hero: Platform Module\n");
+
+ CodeComment generation_notice = def_comment( txt("This was generated by project/codegen/platform_gen.cpp") );
+
+ Builder builder = Builder::open( path_gen "context.gen.hpp");
+ builder.print( generation_notice );
+ builder.print( fmt_newline );
+
+ FileContents h_context = file_read_contents(GlobalAllocator, true, "context.hpp");
+ CodeBody code_context = parse_global_body( { h_context.size, rcast( char const*, h_context.data ) } );
+
+ CodeStruct context_struct = {};
+ for ( Code code : code_context )
+ {
+ if ( code->Type == ECode::Struct )
+ {
+ if ( str_compare( code->Name, txt("Context") ) == 0 )
+ {
+ context_struct = code;
+ break;
+ }
+ break;
+ }
+ }
+
+ Array context_data_members = Array::init_reserve( GlobalAllocator, kilobytes( 1 ) );
+ for ( Code code : context_struct->Body )
+ {
+ if ( code->Type == ECode::Variable )
+ {
+ context_data_members.append( code );
+ }
+ }
+
+ CodeDefine using_context;
+ {
+ String using_context_content = String::make_reserve( GlobalAllocator, kilobytes( 1 ) );
+ {
+ String
+ content = using_context_content;
+ content.append( "\\\n" );
+
+ for ( s32 id = 0; id < context_data_members.num() - 1; ++id )
+ {
+ Code code = context_data_members[ id ];
+ content.append( code.to_string() );
+
+ // Default serializer inserts a newline at the end of the string, we need to insert a line continuation
+ content[ content.length() - 1 ] = '\\';
+ content.append( "\n" );
+ }
+ content.append( context_data_members[ context_data_members.num() - 1 ]->to_string() );
+ }
+ using_context = def_define( txt("using_context()"), using_context_content );
+ }
+
+ builder.print( using_context );
+ builder.write();
+
+ log_fmt("Generaton finished for Handmade Hero: Platform Module\n\n");
// gen::deinit();
return 0;
}
diff --git a/project/dependencies/gen.hpp b/project/dependencies/gen.hpp
index b31cb04..0171dc4 100644
--- a/project/dependencies/gen.hpp
+++ b/project/dependencies/gen.hpp
@@ -18050,6 +18050,8 @@ namespace Parser
);
return { { nullptr }, 0 };
}
+ move_forward();
+ content.Length++;
while ( left && current != '"' && current != '>' )
{
diff --git a/project/engine/engine.cpp b/project/engine/engine.cpp
index 217bd12..b9e84c5 100644
--- a/project/engine/engine.cpp
+++ b/project/engine/engine.cpp
@@ -30,15 +30,21 @@ struct EngineActions
#if Build_Development
b32 pause_renderer;
+ b32 load_auto_snapshot;
b32 set_snapshot_slot_1;
b32 set_snapshot_slot_2;
b32 set_snapshot_slot_3;
b32 set_snapshot_slot_4;
+
+ b32 force_null_access_violation;
#endif
};
struct EngineState
{
+ f32 auto_snapshot_interval;
+ f32 auto_snapshot_timer;
+
s32 wave_tone_hz;
s32 tone_volume;
s32 x_offset;
@@ -115,6 +121,7 @@ internal
void take_engine_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
{
platform_api->memory_copy( memory->snapshots[ memory->active_snapshot_slot ].memory, memory->total_size(), memory->persistent );
+ memory->snapshots[ memory->active_snapshot_slot ].age = platform_api->get_wall_clock();
}
internal
@@ -128,6 +135,7 @@ void take_game_snapshot( Memory* memory, platform::ModuleAPI* platform_api )
platform_api->memory_copy( persistent_slot, state->game_memory.persistent_size, state->game_memory.persistent );
platform_api->memory_copy( transient_slot, state->game_memory.transient_size, state->game_memory.transient );
+ memory->snapshots[ memory->active_snapshot_slot ].age = platform_api->get_wall_clock();
}
internal
@@ -300,7 +308,7 @@ void input_poll_engine_actions( InputState* input, EngineActions* actions )
actions->toggle_wave_tone |= pressed( keyboard->Q );
- actions->loop_mode_game |= pressed( keyboard->L ) && ! keyboard->right_shift.ended_down;
+ actions->loop_mode_game |= pressed( keyboard->L ) && ! keyboard->right_shift.ended_down && ! keyboard->right_alt.ended_down;
actions->loop_mode_engine |= pressed( keyboard->L ) && keyboard->right_shift.ended_down;
MousesState* mouse = controller->mouse;
@@ -311,6 +319,7 @@ void input_poll_engine_actions( InputState* input, EngineActions* actions )
actions->move_up = (mouse->vertical_wheel.end > 0.f) * 10;
actions->move_down = (mouse->vertical_wheel.end < 0.f) * 10;
+ actions->load_auto_snapshot |= pressed( keyboard->L ) && keyboard->right_alt.ended_down;
}
internal
@@ -402,7 +411,14 @@ void draw_rectangle( OffscreenBuffer* buffer
if ( max_y_32 > buffer_height )
max_y_32 = buffer_height;
- s32 color = (scast(s32, red) << 16) | (scast(s32, green) << 8) | (scast(s32, blue) << 0);
+ s32 red_32 = round_f32_to_s32( 255.f * red );
+ s32 green_32 = round_f32_to_s32( 255.f * green );
+ s32 blue_32 = round_f32_to_s32( 255.f * blue );
+
+ s32 color =
+ (scast(s32, red_32) << 16)
+ | (scast(s32, green_32) << 8)
+ | (scast(s32, blue_32) << 0);
// Start with the pixel on the top left corner of the rectangle
u8* row = rcast(u8*, buffer->memory )
@@ -439,14 +455,18 @@ void startup( Memory* memory, platform::ModuleAPI* platform_api )
memory->game_loop_active = false;
#endif
+#if 0
for ( s32 slot = 0; slot < memory->Num_Snapshot_Slots; ++ slot )
{
// TODO(Ed) : Specify default file paths for saving slots ?
}
+#endif
EngineState* state = rcast( EngineState*, memory->persistent );
assert( sizeof(EngineState) <= memory->persistent_size );
+ state->auto_snapshot_interval = 10.f;
+
state->tone_volume = 1000;
state->x_offset = 0;
@@ -465,8 +485,8 @@ void startup( Memory* memory, platform::ModuleAPI* platform_api )
hh::PlayerState* player = rcast( hh::PlayerState*, state->game_memory.persistent );
assert( sizeof(hh::PlayerState) <= state->game_memory.persistent_size );
- player->pos_x = 100;
- player->pos_y = 100;
+ player->pos_x = 920;
+ player->pos_y = 466;
player->mid_jump = false;
player->jump_time = 0.f;
}
@@ -483,6 +503,22 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
EngineState* state = rcast( EngineState*, memory->persistent );
assert( sizeof(EngineState) <= memory->persistent_size );
+ // Engine auto_snapshot
+ {
+ state->auto_snapshot_timer += delta_time;
+ if ( state->auto_snapshot_timer >= state->auto_snapshot_interval )
+ {
+ state->auto_snapshot_timer = 0.f;
+
+ s32 current_slot = memory->active_snapshot_slot;
+ memory->active_snapshot_slot = 0;
+
+ take_engine_snapshot( memory, platform_api );
+ memory->active_snapshot_slot = current_slot;
+ state->auto_snapshot_timer = 0.f;
+ }
+ }
+
ControllerState* controller = & input->controllers[0];
EngineActions engine_actions {};
@@ -490,6 +526,14 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
input_poll_engine_actions( input, & engine_actions );
+ if ( engine_actions.load_auto_snapshot )
+ {
+ s32 current_slot = memory->active_snapshot_slot;
+ memory->active_snapshot_slot = 0;
+ load_engine_snapshot( memory, platform_api );
+ memory->active_snapshot_slot = current_slot;
+ }
+
#if Build_Development
// Ease of use: Allow user to press L key without shift if engine loop recording is active.
engine_actions.loop_mode_engine |= engine_actions.loop_mode_game && memory->engine_loop_active;
@@ -571,10 +615,8 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
if ( ! memory->game_loop_active )
{
- if ( engine_actions.set_snapshot_slot_1 ) memory->active_snapshot_slot = 0;
- if ( engine_actions.set_snapshot_slot_2 ) memory->active_snapshot_slot = 1;
- if ( engine_actions.set_snapshot_slot_3 ) memory->active_snapshot_slot = 2;
- if ( engine_actions.set_snapshot_slot_4 ) memory->active_snapshot_slot = 3;
+ if ( engine_actions.set_snapshot_slot_1 ) memory->active_snapshot_slot = 1;
+ if ( engine_actions.set_snapshot_slot_2 ) memory->active_snapshot_slot = 2;
}
#endif
}
@@ -599,14 +641,18 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
input_poll_player_actions( input, & player_actions );
{
- player->pos_x += player_actions.player_x_move_digital * 5;
- player->pos_y -= player_actions.player_y_move_digital * 5;
- player->pos_x += scast(u32, player_actions.player_x_move_analog * 5);
- player->pos_y -= scast(u32, player_actions.player_y_move_analog * 5) - scast(u32, sinf( player->jump_time * TAU ) * 10);
+ f32 move_speed = 200.f;
+
+ player->pos_x += scast(f32, player_actions.player_x_move_digital) * delta_time * move_speed;
+ player->pos_y -= scast(f32, player_actions.player_y_move_digital) * delta_time * move_speed;
+
+ player->pos_x += scast(f32, player_actions.player_x_move_analog * delta_time * move_speed);
+ player->pos_y -= scast(f32, player_actions.player_y_move_analog * delta_time * move_speed);
+ player->pos_y += sinf( player->jump_time * TAU ) * 200.f * delta_time;
if ( player->jump_time > 0.f )
{
- player->jump_time -= 0.025f;
+ player->jump_time -= delta_time;
}
else
{
@@ -624,21 +670,97 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
f32 x_offset_f = scast(f32, state->x_offset);
f32 y_offset_f = scast(f32, state->y_offset);
- // render_weird_graident( back_buffer, state->x_offset, state->y_offset );
draw_rectangle( back_buffer
, 0.f, 0.f
, scast(f32, back_buffer->width), scast(f32, back_buffer->height)
- , 0x22, 0x22, 0x22 );
- render_player( back_buffer, player->pos_x, player->pos_y );
+ , 1.f, 0.24f, 0.24f );
+
+ // Draw tilemap
+ u32 tilemap[9][16] = {
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
+ { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 },
+ { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ };
+
+ f32 upper_left_x = 0;
+ f32 upper_left_y = 0;
+
+ f32 tile_width = 119;
+ f32 tile_height = 116;
+
+ for ( s32 row = 0; row < 9; ++ row )
+ {
+ for ( s32 col = 0; col < 16; ++ col )
+ {
+ u32 tileID = tilemap[row][col];
+ f32 grey[3] = { 0.15f, 0.15f, 0.15f };
+
+ if ( tileID == 1 )
+ {
+ grey[0] = 0.22f;
+ grey[1] = 0.22f;
+ grey[2] = 0.22f;
+ }
+
+ f32 min_x = upper_left_x + scast(f32, col) * tile_width;
+ f32 min_y = upper_left_y + scast(f32, row) * tile_height;
+ f32 max_x = min_x + tile_width;
+ f32 max_y = min_y + tile_height;
+
+ draw_rectangle( back_buffer
+ , min_x, min_y
+ , max_x, max_y
+ , grey[0], grey[1], grey[2] );
+ }
+ }
+
+ // Player
+ f32 player_width = 50.f;
+ f32 player_height = 100.f;
+
+ f32 player_red = 0.3f;
+ f32 player_green = 0.3f;
+ f32 player_blue = 0.3f;
+
+ draw_rectangle( back_buffer
+ , player->pos_x, player->pos_y
+ , player->pos_x + player_width, player->pos_y + player_height
+ , player_red, player_green, player_blue );
+
+ // Auto-Snapshot percent bar
+ if (1)
+ {
+ f32 snapshot_percent_x = ((state->auto_snapshot_timer / state->auto_snapshot_interval)) * (f32)back_buffer->width / 4.f;
+ draw_rectangle( back_buffer
+ , 0.f, 0.f
+ , snapshot_percent_x, 10.f
+ , 0x00, 0.15f, 0.35f );
+ }
#if Build_Development
if ( memory->replay_mode == ReplayMode_Record )
- render_player( back_buffer, player->pos_x + 20, player->pos_y - 20 );
+ {
+ draw_rectangle( back_buffer
+ , scast(f32, player->pos_x) + 50.f, scast(f32, player->pos_y) - 50.f
+ , scast(f32, player->pos_x) + 10.f, scast(f32, player->pos_y) + 40.f
+ , 1.f, 1.f, 1.f );
+ }
#endif
- render_player( back_buffer, (s32)input->controllers[0].mouse->X.end, (s32)input->controllers[0].mouse->Y.end );
+ // Change above to use draw rectangle
+ draw_rectangle( back_buffer
+ , (f32)input->controllers[0].mouse->X.end, (f32)input->controllers[0].mouse->Y.end
+ , (f32)input->controllers[0].mouse->X.end + 10.f, (f32)input->controllers[0].mouse->Y.end + 10.f
+ , 1.f, 1.f, 0.f );
// Mouse buttons test
+ #if 0
{
if ( input->controllers[0].mouse->left.ended_down == true )
render_player( back_buffer, 5, 5 );
@@ -650,6 +772,7 @@ void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back
if ( input->controllers[0].mouse->right.ended_down == true )
render_player( back_buffer, 5, 35 );
}
+ #endif
}
Engine_API
diff --git a/project/engine/engine.hpp b/project/engine/engine.hpp
index 1caf252..b76c281 100644
--- a/project/engine/engine.hpp
+++ b/project/engine/engine.hpp
@@ -35,6 +35,7 @@ struct MemorySnapshot
void* opaque_handle;
void* opaque_handle_2;
void* memory;
+ u64 age;
};
struct Memory
@@ -56,7 +57,7 @@ struct Memory
// TODO(Ed) : Move this crap to state & replay archive definitions?
#if Build_Development
static constexpr
- s32 Num_Snapshot_Slots = 2;
+ s32 Num_Snapshot_Slots = 3;
// Abuse RAM to store snapshots of the Engine or Game state.
MemorySnapshot snapshots[ Num_Snapshot_Slots ];
s32 active_snapshot_slot;
diff --git a/project/gen/engine_symbol_table.hpp b/project/gen/engine_symbol_table.hpp
index f22d898..37d90be 100644
--- a/project/gen/engine_symbol_table.hpp
+++ b/project/gen/engine_symbol_table.hpp
@@ -3,15 +3,12 @@
NS_ENGINE_BEGIN
-constexpr
-Str const symbol_on_module_load = str_ascii("?on_module_reload@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z");
-constexpr
-Str const symbol_startup = str_ascii("?startup@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z");
-constexpr
-Str const symbol_shutdown = str_ascii("?shutdown@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z");
-constexpr
-Str const symbol_update_and_render = str_ascii("?update_and_render@engine@@YAXMPEAUInputState@1@PEAUOffscreenBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z");
-constexpr
-Str const symbol_update_audio = str_ascii("?update_audio@engine@@YAXMPEAUAudioBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z");
+constexpr Str const symbol_on_module_load = str_ascii( "?on_module_reload@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z" );
+constexpr Str const symbol_startup = str_ascii( "?startup@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z" );
+constexpr Str const symbol_shutdown = str_ascii( "?shutdown@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z" );
+constexpr Str const symbol_update_and_render =
+ str_ascii( "?update_and_render@engine@@YAXMPEAUInputState@1@PEAUOffscreenBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z" );
+constexpr Str const symbol_update_audio =
+ str_ascii( "?update_audio@engine@@YAXMPEAUAudioBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z" );
NS_ENGINE_END
diff --git a/project/handmade.hpp b/project/handmade.hpp
index 8f1fe3c..88ab10f 100644
--- a/project/handmade.hpp
+++ b/project/handmade.hpp
@@ -70,8 +70,8 @@ struct Player
struct PlayerState
{
- s32 pos_x;
- s32 pos_y;
+ f32 pos_x;
+ f32 pos_y;
b32 mid_jump;
f32 jump_time;
diff --git a/project/handmade_win32.cpp b/project/handmade_win32.cpp
index d7e0e21..efaf60b 100644
--- a/project/handmade_win32.cpp
+++ b/project/handmade_win32.cpp
@@ -1,5 +1,5 @@
#include "platform/compiler_ignores.hpp"
#if Build_Unity
-#include "platform/win32_platform.cpp"
+#include "platform/win32/win32_platform.cpp"
#endif
diff --git a/project/platform/context.hpp b/project/platform/context.hpp
new file mode 100644
index 0000000..a99ec13
--- /dev/null
+++ b/project/platform/context.hpp
@@ -0,0 +1,15 @@
+#include "platform.hpp"
+
+
+struct Context
+{
+ Context* parent;
+ // AllocatorInfo allocator;
+ // Logger logger;
+};
+
+
+Context* make_context();
+
+
+#include "gen/context.gen.hpp"
diff --git a/project/platform/gen/context.gen.hpp b/project/platform/gen/context.gen.hpp
new file mode 100644
index 0000000..096e975
--- /dev/null
+++ b/project/platform/gen/context.gen.hpp
@@ -0,0 +1,3 @@
+// This was generated by project/codegen/platform_gen.cpp
+
+#define using_context() Context* parent;
diff --git a/project/platform/macros.hpp b/project/platform/macros.hpp
index 6781700..005c593 100644
--- a/project/platform/macros.hpp
+++ b/project/platform/macros.hpp
@@ -67,47 +67,12 @@
# define compiler_decorated_func_name __FUNCDNAME__
#endif
-// TODO(Ed) : Add this sauce later
-#if 0
-#define congrats( message )
-#define ensure( condition, expression )
-#define fatal( message )
-#endif
-
-// Just experimenting with a way to check for global variables being accessed from the wrong place.
-// (Could also be done with gencpp technically)
-#if 0
-enum class EGlobalVarsAllowFuncs
-{
- ProcessPendingWindowMessages,
- Num,
- Invalid,
-};
-EGlobalVarsAllowFuncs to_type( char const* proc_name )
-{
- char const* global_vars_allowed_funcs[] {
- "process_pending_window_messages"
- };
-
- if ( proc_name )
- {
- for ( u32 idx = 0; idx < array_count( global_vars_allowed_funcs ); ++idx )
- {
- if ( strcmp( proc_name, global_vars_allowed_funcs[idx] ) == 0 )
- {
- return scast( EGlobalVarsAllowFuncs, idx );
- }
- }
- }
-
- return EGlobalVarsAllowFuncs::Invalid;
-}
-
#if Build_Development
-# define checked_global_getter( global_var, procedure ) \
- ( ensure( to_type(procedure) != EGlobalVarsAllowFuncs::Invalid), global_var )
+# define congrats( message ) platform::impl_congrats( message )
+# define ensure( condition, message ) platform::impl_ensure( condition, message )
+# define fatal( message ) platform::impl_fatal( message )
#else
-# define checked_global_getter( global_var, procedure ) global_var
-#endif
-
+# define congrats( message )
+# define ensure( condition, message )
+# define fatal( message )
#endif
diff --git a/project/platform/platform.hpp b/project/platform/platform.hpp
index 98341cc..94da947 100644
--- a/project/platform/platform.hpp
+++ b/project/platform/platform.hpp
@@ -18,6 +18,7 @@
#include "math_constants.hpp"
#include "types.hpp"
#include "strings.hpp"
+#include "context.hpp"
#define NS_PLATFORM_BEGIN namespace platform {
#define NS_PLATFORM_END }
@@ -80,6 +81,8 @@ using FileRewindFn = void ( File* file );
using MemoryCopyFn = void( void* dest, u64 src_size, void* src );
+using GetWallClockFn = u64();
+
struct ModuleAPI
{
Str path_root;
@@ -90,6 +93,8 @@ struct ModuleAPI
DebugSetPauseRenderingFn* debug_set_pause_rendering;
#endif
+ GetWallClockFn* get_wall_clock;
+
GetMonitorRefreshRateFn* get_monitor_refresh_rate;
SetMonitorRefreshRateFn* set_monitor_refresh_rate;
@@ -112,6 +117,12 @@ struct ModuleAPI
MemoryCopyFn* memory_copy;
};
+#if Build_Development
+void impl_congrats( char const* message );
+bool impl_ensure( bool condition, char const* message );
+void impl_fatal( char const* message );
+#endif
+
#pragma endregion Settings Exposure
NS_PLATFORM_END
diff --git a/project/platform/win32.hpp b/project/platform/win32.hpp
deleted file mode 100644
index 1182e03..0000000
--- a/project/platform/win32.hpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- Windows dependency header
-*/
-#pragma once
-
-#pragma warning( push )
-#pragma warning( disable: 5105 )
-#pragma warning( disable: 4820 )
-#define WIN32_LEAN_AND_MEAN
-#include
-#include
-#include
-#include
-#include
-#pragma warning( pop )
-
-// #include "windows/windows_base.h"
-// #include "windows/window.h"
-
-// #include "windows/file.h"
-// #include "windows/io.h"
-
-// #if Build_Debug
-// # include "windows/dbghelp.h"
-// #endif
-
-#if Build_DLL
-# define WIN_LIB_API extern "C" __declspec(dllexport)
-#else
-# define WIN_LIB_API extern "C"
-#endif
-
-// #ifndef CONST
-// # define CONST const
-// #endif
-
-// SAL BS
-#ifndef _In_
-# define _In_
-#endif
-
-#define NS_WIN32_BEGIN namespace win32 {
-#define NS_WIN32_END }
-
-NS_WIN32_BEGIN
-
-enum LWA : DWORD
-{
- LWA_Alpha = 0x00000002,
- LWA_ColorKey = 0x00000001,
-};
-
-enum BI : DWORD
-{
- BI_RGB_Uncompressed = 0L,
- BI_RunLength_Encoded_8bpp = 1L,
- BI_RunLength_Encoded_4bpp = 2L,
-};
-
-enum CS : UINT
-{
- CS_Own_Device_Context = CS_OWNDC,
- CS_Horizontal_Redraw = CS_HREDRAW,
- CS_Vertical_Redraw = CS_VREDRAW,
-};
-
-enum CW : s32
-{
- CW_Use_Default = CW_USEDEFAULT,
-};
-
-enum DIB : UINT
-{
- DIB_ColorTable_RGB = 0,
- DIB_ColorTable_Palette = 1
-
-};
-
-enum MB : UINT
-{
- MB_Ok_Btn = MB_OK,
- MB_Icon_Information = MB_ICONINFORMATION,
-};
-
-enum Mem : DWORD
-{
- MEM_Commit_Zeroed = MEM_COMMIT,
- MEM_Reserve = MEM_RESERVE,
- MEM_Release = MEM_RELEASE,
- MEM_Use_Large_pages = MEM_LARGE_PAGES,
-};
-
-enum Page : DWORD
-{
- Page_Read_Write = PAGE_READWRITE,
-};
-
-enum PM : UINT
-{
- PM_Remove_Messages_From_Queue = PM_REMOVE,
-};
-
-enum RasterOps : DWORD
-{
- RO_Source_To_Dest = (DWORD)0x00CC0020,
- RO_Blackness = (DWORD)0x00000042,
- RO_Whiteness = (DWORD)0x00FF0062,
-};
-
-#define WM_ACTIVATEAPP 0x001C
-
-enum WS : UINT
-{
- WS_Overlapped_Window = WS_OVERLAPPEDWINDOW,
- WS_Initially_Visible = WS_VISIBLE,
-};
-
-enum XI_State : DWORD
-{
- XI_PluggedIn = ERROR_SUCCESS,
-};
-
-
-template< typename ProcSignature >
-ProcSignature* get_procedure_from_library( HMODULE library_module, char const* symbol )
-{
- void* address = rcast( void*, GetProcAddress( library_module, symbol ) );
- return rcast( ProcSignature*, address );
-}
-
-#pragma region XInput
-WIN_LIB_API DWORD WINAPI XInputGetState
-(
- DWORD dwUserIndex, // Index of the gamer associated with the device
- XINPUT_STATE* pState // Receives the current state
-);
-
-WIN_LIB_API DWORD WINAPI XInputSetState
-(
- DWORD dwUserIndex, // Index of the gamer associated with the device
- XINPUT_VIBRATION* pVibration // The vibration information to send to the controller
-);
-
-DWORD WINAPI xinput_get_state_stub( DWORD dwUserIndex, XINPUT_STATE* pVibration ) {
- do_once_start
- OutputDebugStringA( "xinput_get_state stubbed!\n");
- do_once_end
- return ERROR_DEVICE_NOT_CONNECTED;
-}
-
-DWORD WINAPI xinput_set_state_stub( DWORD dwUserIndex, XINPUT_VIBRATION* pVibration ) {
- do_once_start
- OutputDebugStringA( "xinput_set_state stubbed!\n");
- do_once_end
- return ERROR_DEVICE_NOT_CONNECTED;
-}
-
-using XInputGetStateFn = DWORD WINAPI( DWORD dwUserIndex, XINPUT_STATE* pVibration );
-using XInputSetStateFn = DWORD WINAPI( DWORD dwUserIndex, XINPUT_VIBRATION* pVibration );
-
-global XInputGetStateFn* xinput_get_state = xinput_get_state_stub;
-global XInputSetStateFn* xinput_set_state = xinput_set_state_stub;
-
-internal void
-xinput_load_library_bindings()
-{
- HMODULE xinput_lib = LoadLibraryA( XINPUT_DLL_A );
-
- XInputGetStateFn* get_state = get_procedure_from_library< XInputGetStateFn >( xinput_lib, "XInputGetState" );
- XInputSetStateFn* set_state = get_procedure_from_library< XInputSetStateFn >( xinput_lib, "XInputSetState" );
-
- if ( get_state )
- xinput_get_state = get_state;
-
- if ( set_state )
- xinput_set_state = set_state;
-}
-#pragma endregion XInput
-
-NS_WIN32_END
-#undef _SAL_nop_impl_
-#undef _SAL2_Source_
-#undef _Deref_post2_impl_
-#undef _Outptr_result_bytebuffer_
-#undef _At_
-#undef _When_
-#undef GDI_DIBSIZE
diff --git a/project/platform/win32_platform.cpp b/project/platform/win32_platform.cpp
deleted file mode 100644
index 08db01e..0000000
--- a/project/platform/win32_platform.cpp
+++ /dev/null
@@ -1,1814 +0,0 @@
-/*
- TODO : This is not a final platform layer
-
- - Saved game locations
- - Getting a handle to our own executable file
- - Asset loading path
- - Threading (launch a thread)
- - Raw Input (support for multiple keyboards)
- - Sleep / timeBeginPeriod
- - ClipCursor() (for multimonitor support)
- - Fullscreen support
- - WM_SETCURSOR (control cursor visibility)
- - QueryCancelAutoplay
- - WM_ACTIVATEAPP (for when not active)
- - Blit speed improvemnts (BitBlt)
- - Hardware acceleration ( OpenGL or Direct3D or both )
- - GetKeyboardLayout (for French keyboards, international WASD support)
-*/
-
-// Platform Layer headers
-#include "platform.hpp"
-#include "jsl.hpp" // Using this to get dualsense controllers
-#include "win32.hpp"
-
-// Engine layer headers
-#include "engine/engine.hpp"
-#include "engine/engine_to_platform_api.hpp"
-
-#include "gen/engine_symbol_table.hpp"
-
-#if 1
-// TODO(Ed): Redo these macros properly later.
-
-#define congrats( message ) do { \
- JslSetLightColour( 0, (255 << 16) | (215 << 8) ); \
- MessageBoxA( 0, message, "Congratulations!", MB_OK | MB_ICONEXCLAMATION ); \
- JslSetLightColour( 0, (255 << 8 ) ); \
-} while (0)
-
-#define ensure( condition, message ) ensure_impl( condition, message )
-inline bool
-ensure_impl( bool condition, char const* message ) {
- if ( ! condition ) {
- JslSetLightColour( 0, (255 << 16) );
- MessageBoxA( 0, message, "Ensure Failure", MB_OK | MB_ICONASTERISK );
- JslSetLightColour( 0, ( 255 << 8 ) );
- }
- return condition;
-}
-
-#define fatal(message) do { \
- JslSetLightColour( 0, (255 << 16) ); \
- MessageBoxA( 0, message, "Fatal Error", MB_OK | MB_ICONERROR ); \
- JslSetLightColour( 0, (255 << 8 ) ); \
-} while (0)
-#endif
-
-NS_PLATFORM_BEGIN
-using namespace win32;
-
-// This is the "backbuffer" data related to the windowing surface provided by the operating system.
-struct OffscreenBuffer
-{
- BITMAPINFO info;
- char _PAD_[4];
- void* memory; // Lets use directly mess with the "pixel's memory buffer"
- s32 width;
- s32 height;
- s32 pitch;
- s32 bytes_per_pixel;
-};
-
-struct WinDimensions
-{
- u32 width;
- u32 height;
-};
-
-// TODO : This will def need to be looked over.
-struct DirectSoundBuffer
-{
- LPDIRECTSOUNDBUFFER secondary_buffer;
- s16* samples;
- u32 secondary_buffer_size;
- u32 samples_per_second;
- u32 bytes_per_sample;
-
- // TODO(Ed) : Makes math easier...
- u32 bytes_per_second;
- u32 guard_sample_bytes;
-
- DWORD is_playing;
- u32 running_sample_index;
-
- // TODO(Ed) : Should this be in bytes?
- u32 latency_sample_count;
-};
-
-#pragma region Static Data
-global StrPath Path_Root;
-global StrPath Path_Binaries;
-global StrPath Path_Scratch;
-
-// TODO(Ed) : This is a global for now.
-global b32 Running = false;
-global b32 Pause_Rendering = false;
-
-global WinDimensions Window_Dimensions;
-global OffscreenBuffer Surface_Back_Buffer;
-
-using DirectSoundCreateFn = HRESULT WINAPI (LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN pUnkOuter );
-global DirectSoundCreateFn* direct_sound_create;
-
-constexpr u64 Tick_To_Millisecond = 1000;
-constexpr u64 Tick_To_Microsecond = 1000 * 1000;
-
-global u64 Performance_Counter_Frequency;
-
-// As of 2023 the highest refreshrate on the market is 500 hz. I'll just make this higher if something comes out beyond that...
-constexpr u32 Monitor_Refresh_Max_Supported = 500;
-
-// Anything at or below the high performance frame-time is too low latency to sleep against the window's scheduler.
-constexpr f32 High_Perf_Frametime_MS = 1000.f / 240.f;
-
-global u32 Monitor_Refresh_Hz = 60;
-global u32 Engine_Refresh_Hz = Monitor_Refresh_Hz;
-global f32 Engine_Frame_Target_MS = 1000.f / scast(f32, Engine_Refresh_Hz);
-#pragma endregion Static Data
-
-#pragma region Internal
-internal
-FILETIME file_get_last_write_time( char const* path )
-{
- WIN32_FILE_ATTRIBUTE_DATA engine_dll_file_attributes = {};
- GetFileAttributesExA( path, GetFileExInfoStandard, & engine_dll_file_attributes );
-
- return engine_dll_file_attributes.ftLastWriteTime;
-#if 0
- WIN32_FIND_DATAA dll_file_info = {};
- HANDLE dll_file_handle = FindFirstFileA( path, & dll_file_info );
- if ( dll_file_handle == INVALID_HANDLE_VALUE )
- {
- FindClose( dll_file_handle );
- }
- return dll_file_info.ftLastWriteTime;
-#endif
-}
-
-struct AudioTimeMarker
-{
- DWORD output_play_cursor;
- DWORD output_write_cursor;
- DWORD output_location;
- DWORD output_byte_count;
-
- DWORD flip_play_curosr;
- DWORD flip_write_cursor;
-
- DWORD expected_flip_cursor;
-};
-
-#if Build_Debug
-internal void
-debug_draw_vertical( s32 x_pos, s32 top, s32 bottom, s32 color )
-{
- if ( top <= 0 )
- {
- top = 0;
- }
-
- if ( bottom > Surface_Back_Buffer.height )
- {
- bottom = Surface_Back_Buffer.height;
- }
-
- if ( x_pos >= 0 && x_pos < Surface_Back_Buffer.width )
- {
- u8*
- pixel_byte = rcast(u8*, Surface_Back_Buffer.memory );
- pixel_byte += x_pos * Surface_Back_Buffer.bytes_per_pixel;
- pixel_byte += top * Surface_Back_Buffer.pitch;
-
- for ( s32 y = top; y < bottom; ++ y )
- {
- s32* pixel = rcast(s32*, pixel_byte);
- *pixel = color;
-
- pixel_byte += Surface_Back_Buffer.pitch;
- }
- }
-}
-
-inline void
-debug_draw_sound_buffer_marker( DirectSoundBuffer* sound_buffer, f32 ratio
- , u32 pad_x, u32 pad_y
- , u32 top, u32 bottom
- , DWORD value, u32 color )
-{
- // assert( value < sound_buffer->SecondaryBufferSize );
- u32 x = pad_x + scast(u32, ratio * scast(f32, value ));
- debug_draw_vertical( x, top, bottom, color );
-}
-
-internal void
-debug_sync_display( DirectSoundBuffer* sound_buffer
- , u32 num_markers, AudioTimeMarker* markers
- , u32 current_marker
- , f32 ms_per_frame )
-{
- u32 pad_x = 32;
- u32 pad_y = 16;
- f32 buffers_ratio = scast(f32, Surface_Back_Buffer.width) / (scast(f32, sound_buffer->secondary_buffer_size) * 1);
-
- u32 line_height = 64;
- for ( u32 marker_index = 0; marker_index < num_markers; ++ marker_index )
- {
- AudioTimeMarker* marker = & markers[marker_index];
- assert( marker->output_play_cursor < sound_buffer->secondary_buffer_size );
- assert( marker->output_write_cursor < sound_buffer->secondary_buffer_size );
- assert( marker->output_location < sound_buffer->secondary_buffer_size );
- assert( marker->output_byte_count < sound_buffer->secondary_buffer_size );
- assert( marker->flip_play_curosr < sound_buffer->secondary_buffer_size );
- assert( marker->flip_write_cursor < sound_buffer->secondary_buffer_size );
-
- DWORD play_color = 0x88888888;
- DWORD write_color = 0x88800000;
- DWORD expected_flip_color = 0xFFFFF000;
- DWORD play_window_color = 0xFFFF00FF;
-
- u32 top = pad_y;
- u32 bottom = pad_y + line_height;
- if ( marker_index == current_marker )
- {
- play_color = 0xFFFFFFFF;
- write_color = 0xFFFF0000;
-
- top += pad_y + line_height;
- bottom += pad_y + line_height;
-
- u32 row_2_top = top;
-
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_play_cursor, play_color );
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_write_cursor, write_color );
-
- play_color = 0xFFFFFFFF;
- write_color = 0xFFFF0000;
-
- top += pad_y + line_height;
- bottom += pad_y + line_height;
-
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_location, play_color );
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->output_location + marker->output_byte_count, write_color );
-
- play_color = 0xFFFFFFFF;
- write_color = 0xFFFF0000;
-
- top += pad_y + line_height;
- bottom += pad_y + line_height;
-
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, row_2_top, bottom, marker->expected_flip_cursor, expected_flip_color );
- }
-
- DWORD play_window = marker->flip_play_curosr + 480 * sound_buffer->bytes_per_sample;
-
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->flip_play_curosr, play_color );
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, play_window, play_window_color );
- debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->flip_write_cursor, write_color );
- }
-}
-#endif
-
-#pragma region Direct Sound
-internal void
-init_sound(HWND window_handle, DirectSoundBuffer* sound_buffer )
-{
- // Load library
- HMODULE sound_library = LoadLibraryA( "dsound.dll" );
- if ( ! ensure(sound_library, "Failed to load direct sound library" ) )
- {
- // TOOD : Diagnostic
- return;
- }
-
- // Get direct sound object
- direct_sound_create = get_procedure_from_library< DirectSoundCreateFn >( sound_library, "DirectSoundCreate" );
- if ( ! ensure( direct_sound_create, "Failed to get direct_sound_create_procedure" ) )
- {
- // TOOD : Diagnostic
- return;
- }
-
- LPDIRECTSOUND direct_sound;
- if ( ! SUCCEEDED(direct_sound_create( 0, & direct_sound, 0 )) )
- {
- // TODO : Diagnostic
- }
- if ( ! SUCCEEDED( direct_sound->SetCooperativeLevel(window_handle, DSSCL_PRIORITY) ) )
- {
- // TODO : Diagnostic
- }
-
- WAVEFORMATEX
- wave_format {};
- wave_format.wFormatTag = WAVE_FORMAT_PCM; /* format type */
- wave_format.nChannels = 2; /* number of channels (i.e. mono, stereo...) */
- wave_format.nSamplesPerSec = scast(u32, sound_buffer->samples_per_second); /* sample rate */
- wave_format.wBitsPerSample = 16; /* number of bits per sample of mono data */
- wave_format.nBlockAlign = wave_format.nChannels * wave_format.wBitsPerSample / 8 ; /* block size of data */
- wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign; /* for buffer estimation */
- wave_format.cbSize = 0; /* the count in bytes of the size of */
-
- LPDIRECTSOUNDBUFFER primary_buffer;
- {
- DSBUFFERDESC
- buffer_description { sizeof(buffer_description) };
- buffer_description.dwFlags = DSBCAPS_PRIMARYBUFFER;
- buffer_description.dwBufferBytes = 0;
-
- if ( ! SUCCEEDED( direct_sound->CreateSoundBuffer( & buffer_description, & primary_buffer, 0 ) ))
- {
- // TODO : Diagnostic
- }
- if ( ! SUCCEEDED( primary_buffer->SetFormat( & wave_format ) ) )
- {
- // TODO : Diagnostic
- }
- }
-
- DSBUFFERDESC
- buffer_description { sizeof(buffer_description) };
- buffer_description.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
- buffer_description.dwBufferBytes = sound_buffer->secondary_buffer_size;
- buffer_description.lpwfxFormat = & wave_format;
-
- if ( ! SUCCEEDED( direct_sound->CreateSoundBuffer( & buffer_description, & sound_buffer->secondary_buffer, 0 ) ))
- {
- // TODO : Diagnostic
- }
- if ( ! SUCCEEDED( sound_buffer->secondary_buffer->SetFormat( & wave_format ) ) )
- {
- // TODO : Diagnostic
- }
-}
-
-internal void
-ds_clear_sound_buffer( DirectSoundBuffer* sound_buffer )
-{
- LPVOID region_1;
- DWORD region_1_size;
- LPVOID region_2;
- DWORD region_2_size;
-
- HRESULT ds_lock_result = sound_buffer->secondary_buffer->Lock( 0, sound_buffer->secondary_buffer_size
- , & region_1, & region_1_size
- , & region_2, & region_2_size
- , 0 );
- if ( ! SUCCEEDED( ds_lock_result ) )
- {
- return;
- }
-
- u8* sample_out = rcast( u8*, region_1 );
- for ( DWORD byte_index = 0; byte_index < region_1_size; ++ byte_index )
- {
- *sample_out = 0;
- ++ sample_out;
- }
-
- sample_out = rcast( u8*, region_2 );
- for ( DWORD byte_index = 0; byte_index < region_2_size; ++ byte_index )
- {
- *sample_out = 0;
- ++ sample_out;
- }
-
- if ( ! SUCCEEDED( sound_buffer->secondary_buffer->Unlock( region_1, region_1_size, region_2, region_2_size ) ))
- {
- return;
- }
-}
-
-internal void
-ds_fill_sound_buffer( DirectSoundBuffer* sound_buffer, DWORD byte_to_lock, DWORD bytes_to_write )
-{
- LPVOID region_1;
- DWORD region_1_size;
- LPVOID region_2;
- DWORD region_2_size;
-
- HRESULT ds_lock_result = sound_buffer->secondary_buffer->Lock( byte_to_lock, bytes_to_write
- , & region_1, & region_1_size
- , & region_2, & region_2_size
- , 0 );
- if ( ! SUCCEEDED( ds_lock_result ) )
- {
- return;
- }
-
- // TODO : Assert that region sizes are valid
-
- DWORD region_1_sample_count = region_1_size / sound_buffer->bytes_per_sample;
- s16* sample_out = rcast( s16*, region_1 );
- s16* sample_in = sound_buffer->samples;
- for ( DWORD sample_index = 0; sample_index < region_1_sample_count; ++ sample_index )
- {
- *sample_out = *sample_in;
- ++ sample_out;
- ++ sample_in;
-
- *sample_out = *sample_in;
- ++ sample_out;
- ++ sample_in;
-
- ++ sound_buffer->running_sample_index;
- }
-
- DWORD region_2_sample_count = region_2_size / sound_buffer->bytes_per_sample;
- sample_out = rcast( s16*, region_2 );
- for ( DWORD sample_index = 0; sample_index < region_2_sample_count; ++ sample_index )
- {
- *sample_out = *sample_in;
- ++ sample_out;
- ++ sample_in;
-
- *sample_out = *sample_in;
- ++ sample_out;
- ++ sample_in;
-
- ++ sound_buffer->running_sample_index;
- }
-
- if ( ! SUCCEEDED( sound_buffer->secondary_buffer->Unlock( region_1, region_1_size, region_2, region_2_size ) ))
- {
- return;
- }
-}
-#pragma endregion Direct Sound
-
-#pragma region Input
-
-// Max controllers for the platform layer and thus for all other layers is 4. (Sanity and xinput limit)
-constexpr u32 Max_Controllers = 4;
-
-using JSL_DeviceHandle = int;
-using EngineXInputPadStates = engine::XInputPadState[ Max_Controllers ];
-using EngineDSPadStates = engine::DualsensePadState[Max_Controllers];
-
-internal void
-input_process_digital_btn( engine::DigitalBtn* old_state, engine::DigitalBtn* new_state, u32 raw_btns, u32 btn_flag )
-{
-#define had_transition() ( old_state->ended_down != new_state->ended_down )
- new_state->ended_down = (raw_btns & btn_flag) > 0;
- if ( had_transition() )
- new_state->half_transitions += 1;
- else
- new_state->half_transitions = 0;
-#undef had_transition
-}
-
-internal f32
-jsl_input_process_axis_value( f32 value, f32 deadzone_threshold )
-{
- f32 result = 0;
- if ( value < -deadzone_threshold )
- {
- result = (value + deadzone_threshold ) / (1.0f - deadzone_threshold );
-
- if (result < -1.0f)
- result = -1.0f; // Clamp to ensure it doesn't go below -1
- }
- else if ( value > deadzone_threshold )
- {
- result = (value - deadzone_threshold ) / (1.0f - deadzone_threshold );
-
- if (result > 1.0f)
- result = 1.0f; // Clamp to ensure it doesn't exceed 1
- }
- return result;
-}
-
-internal f32
-xinput_process_axis_value( s16 value, s16 deadzone_threshold )
-{
- f32 result = 0;
- if ( value < -deadzone_threshold )
- {
- result = scast(f32, value + deadzone_threshold) / (32768.0f - scast(f32, deadzone_threshold));
- }
- else if ( value > deadzone_threshold )
- {
- result = scast(f32, value + deadzone_threshold) / (32767.0f - scast(f32, deadzone_threshold));
- }
- return result;
-}
-
-internal void
-poll_input( HWND window_handle, engine::InputState* input, u32 jsl_num_devices, JSL_DeviceHandle* jsl_device_handles
- , engine::KeyboardState* old_keyboard, engine::KeyboardState* new_keyboard
- , engine::MousesState* old_mouse, engine::MousesState* new_mouse
- , EngineXInputPadStates* old_xpads, EngineXInputPadStates* new_xpads
- , EngineDSPadStates* old_ds_pads, EngineDSPadStates* new_ds_pads )
-{
- // Keyboard Polling
- // Keyboards are unified for now.
- {
- constexpr u32 is_down = 0x80000000;
- input_process_digital_btn( & old_keyboard->_1, & new_keyboard->_1, GetAsyncKeyState( '1' ), is_down );
- input_process_digital_btn( & old_keyboard->_2, & new_keyboard->_2, GetAsyncKeyState( '2' ), is_down );
- input_process_digital_btn( & old_keyboard->_3, & new_keyboard->_3, GetAsyncKeyState( '3' ), is_down );
- input_process_digital_btn( & old_keyboard->_4, & new_keyboard->_4, GetAsyncKeyState( '4' ), is_down );
- input_process_digital_btn( & old_keyboard->Q, & new_keyboard->Q, GetAsyncKeyState( 'Q' ), is_down );
- input_process_digital_btn( & old_keyboard->E, & new_keyboard->E, GetAsyncKeyState( 'E' ), is_down );
- input_process_digital_btn( & old_keyboard->W, & new_keyboard->W, GetAsyncKeyState( 'W' ), is_down );
- input_process_digital_btn( & old_keyboard->A, & new_keyboard->A, GetAsyncKeyState( 'A' ), is_down );
- input_process_digital_btn( & old_keyboard->S, & new_keyboard->S, GetAsyncKeyState( 'S' ), is_down );
- input_process_digital_btn( & old_keyboard->D, & new_keyboard->D, GetAsyncKeyState( 'D' ), is_down );
- input_process_digital_btn( & old_keyboard->K, & new_keyboard->K, GetAsyncKeyState( 'K' ), is_down );
- input_process_digital_btn( & old_keyboard->L, & new_keyboard->L, GetAsyncKeyState( 'L' ), is_down );
- input_process_digital_btn( & old_keyboard->escape, & new_keyboard->escape, GetAsyncKeyState( VK_ESCAPE ), is_down );
- input_process_digital_btn( & old_keyboard->backspace, & new_keyboard->backspace, GetAsyncKeyState( VK_BACK ), is_down );
- input_process_digital_btn( & old_keyboard->up, & new_keyboard->up, GetAsyncKeyState( VK_UP ), is_down );
- input_process_digital_btn( & old_keyboard->down, & new_keyboard->down, GetAsyncKeyState( VK_DOWN ), is_down );
- input_process_digital_btn( & old_keyboard->left, & new_keyboard->left, GetAsyncKeyState( VK_LEFT ), is_down );
- input_process_digital_btn( & old_keyboard->right, & new_keyboard->right, GetAsyncKeyState( VK_RIGHT ), is_down );
- input_process_digital_btn( & old_keyboard->space, & new_keyboard->space, GetAsyncKeyState( VK_SPACE ), is_down );
- input_process_digital_btn( & old_keyboard->pause, & new_keyboard->pause, GetAsyncKeyState( VK_PAUSE ), is_down );
- input_process_digital_btn( & old_keyboard->left_alt, & new_keyboard->left_alt, GetAsyncKeyState( VK_LMENU ), is_down );
- input_process_digital_btn( & old_keyboard->right_alt, & new_keyboard->right_alt, GetAsyncKeyState( VK_RMENU ), is_down );
- input_process_digital_btn( & old_keyboard->left_shift, & new_keyboard->left_shift, GetAsyncKeyState( VK_LSHIFT ), is_down );
- input_process_digital_btn( & old_keyboard->right_shift, & new_keyboard->right_shift, GetAsyncKeyState( VK_RSHIFT ), is_down );
-
- input->controllers[0].keyboard = new_keyboard;
- }
-
- // Mouse polling
- {
- // input->Controllers[0].Mouse = {};
-
- constexpr u32 is_down = 0x80000000;
- input_process_digital_btn( & old_mouse->left, & new_mouse->left, GetAsyncKeyState( VK_LBUTTON ), is_down );
- input_process_digital_btn( & old_mouse->middle, & new_mouse->middle, GetAsyncKeyState( VK_MBUTTON ), is_down );
- input_process_digital_btn( & old_mouse->right, & new_mouse->right, GetAsyncKeyState( VK_RBUTTON ), is_down );
-
- POINT mouse_pos;
- GetCursorPos( & mouse_pos );
- ScreenToClient( window_handle, & mouse_pos );
-
- new_mouse->vertical_wheel = {};
- new_mouse->horizontal_wheel = {};
-
- new_mouse->X.end = (f32)mouse_pos.x;
- new_mouse->Y.end = (f32)mouse_pos.y;
-
- input->controllers[0].mouse = new_mouse;
- }
-
- // XInput Polling
- // TODO(Ed) : Should we poll this more frequently?
- for ( DWORD controller_index = 0; controller_index < Max_Controllers; ++ controller_index )
- {
- XINPUT_STATE controller_state;
- b32 xinput_detected = xinput_get_state( controller_index, & controller_state ) == XI_PluggedIn;
- if ( xinput_detected )
- {
- XINPUT_GAMEPAD* xpad = & controller_state.Gamepad;
- engine::XInputPadState* old_xpad = old_xpads[ controller_index ];
- engine::XInputPadState* new_xpad = new_xpads[ controller_index ];
- input_process_digital_btn( & old_xpad->dpad.up, & new_xpad->dpad.up, xpad->wButtons, XINPUT_GAMEPAD_DPAD_UP );
- input_process_digital_btn( & old_xpad->dpad.down, & new_xpad->dpad.down, xpad->wButtons, XINPUT_GAMEPAD_DPAD_DOWN );
- input_process_digital_btn( & old_xpad->dpad.left, & new_xpad->dpad.left, xpad->wButtons, XINPUT_GAMEPAD_DPAD_LEFT );
- input_process_digital_btn( & old_xpad->dpad.right, & new_xpad->dpad.right, xpad->wButtons, XINPUT_GAMEPAD_DPAD_RIGHT );
-
- input_process_digital_btn( & old_xpad->Y, & new_xpad->Y, xpad->wButtons, XINPUT_GAMEPAD_Y );
- input_process_digital_btn( & old_xpad->A, & new_xpad->A, xpad->wButtons, XINPUT_GAMEPAD_A );
- input_process_digital_btn( & old_xpad->B, & new_xpad->B, xpad->wButtons, XINPUT_GAMEPAD_B );
- input_process_digital_btn( & old_xpad->X, & new_xpad->X, xpad->wButtons, XINPUT_GAMEPAD_X );
-
- input_process_digital_btn( & old_xpad->back, & new_xpad->back, xpad->wButtons, XINPUT_GAMEPAD_BACK );
- input_process_digital_btn( & old_xpad->start, & new_xpad->start, xpad->wButtons, XINPUT_GAMEPAD_START );
-
- input_process_digital_btn( & old_xpad->left_shoulder, & new_xpad->left_shoulder, xpad->wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER );
- input_process_digital_btn( & old_xpad->right_shoulder, & new_xpad->right_shoulder, xpad->wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER );
-
- new_xpad->stick.left.X.start = old_xpad->stick.left.X.end;
- new_xpad->stick.left.Y.start = old_xpad->stick.left.Y.end;
-
- f32 left_x = xinput_process_axis_value( xpad->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE );
- f32 left_y = xinput_process_axis_value( xpad->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE );
-
- // TODO(Ed) : Min/Max macros!!!
- new_xpad->stick.left.X.min = new_xpad->stick.left.X.max = new_xpad->stick.left.X.end = left_x;
- new_xpad->stick.left.Y.min = new_xpad->stick.left.Y.max = new_xpad->stick.left.Y.end = left_y;
-
- // TODO(Ed): Make this actually an average for later
- new_xpad->stick.left.X.average = left_x;
- new_xpad->stick.left.Y.average = left_y;
-
- input->controllers[ controller_index ].xpad = new_xpad;
- }
- else
- {
- input->controllers[ controller_index ].xpad = nullptr;
- }
- }
-
- // JSL Input Polling
- for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index )
- {
- if ( ! JslStillConnected( jsl_device_handles[ jsl_device_index ] ) )
- {
- OutputDebugStringA( "Error: JSLStillConnected returned false\n" );
- continue;
- }
-
- // TODO : Won't support more than 4 for now... (or prob ever)
- if ( jsl_device_index > 4 )
- break;
-
- JOY_SHOCK_STATE state = JslGetSimpleState( jsl_device_handles[ jsl_device_index ] );
-
- // For now we're assuming anything that is detected via JSL is a dualsense pad.
- // We'll eventually add support possibly for the nintendo pro controller.
- engine::DualsensePadState* old_ds_pad = old_ds_pads[ jsl_device_index ];
- engine::DualsensePadState* new_ds_pad = new_ds_pads[ jsl_device_index ];
-
- input_process_digital_btn( & old_ds_pad->dpad.up, & new_ds_pad->dpad.up, state.buttons, JSMASK_UP );
- input_process_digital_btn( & old_ds_pad->dpad.down, & new_ds_pad->dpad.down, state.buttons, JSMASK_DOWN );
- input_process_digital_btn( & old_ds_pad->dpad.left, & new_ds_pad->dpad.left, state.buttons, JSMASK_LEFT );
- input_process_digital_btn( & old_ds_pad->dpad.right, & new_ds_pad->dpad.right, state.buttons, JSMASK_RIGHT );
-
- input_process_digital_btn( & old_ds_pad->triangle, & new_ds_pad->triangle, state.buttons, JSMASK_N );
- input_process_digital_btn( & old_ds_pad->cross, & new_ds_pad->cross, state.buttons, JSMASK_S );
- input_process_digital_btn( & old_ds_pad->square, & new_ds_pad->square, state.buttons, JSMASK_W );
- input_process_digital_btn( & old_ds_pad->circle, & new_ds_pad->circle, state.buttons, JSMASK_E );
-
- input_process_digital_btn( & old_ds_pad->share, & new_ds_pad->share, state.buttons, JSMASK_SHARE );
- input_process_digital_btn( & old_ds_pad->options, & new_ds_pad->options, state.buttons, JSMASK_OPTIONS );
-
- input_process_digital_btn( & old_ds_pad->L1, & new_ds_pad->L1, state.buttons, JSMASK_L );
- input_process_digital_btn( & old_ds_pad->R1, & new_ds_pad->R1, state.buttons, JSMASK_R );
-
- new_ds_pad->stick.left.X.start = old_ds_pad->stick.left.X.end;
- new_ds_pad->stick.left.Y.start = old_ds_pad->stick.left.Y.end;
-
- // Joyshock abstracts the sticks to a float value already for us of -1.f to 1.f.
- // We'll assume a deadzone of 10% for now.
- f32 left_x = jsl_input_process_axis_value( state.stickLX, 0.1f );
- f32 left_y = jsl_input_process_axis_value( state.stickLY, 0.1f );
-
- new_ds_pad->stick.left.X.min = new_ds_pad->stick.left.X.max = new_ds_pad->stick.left.X.end = left_x;
- new_ds_pad->stick.left.Y.min = new_ds_pad->stick.left.Y.max = new_ds_pad->stick.left.Y.end = left_y;
-
- // TODO(Ed): Make this actually an average for later
- new_ds_pad->stick.left.X.average = left_x;
- new_ds_pad->stick.left.Y.average = left_y;
-
- input->controllers[ jsl_device_index ].ds_pad = new_ds_pad;
- }
-}
-#pragma endregion Input
-
-#pragma region Timing
-inline f32
-timing_get_ms_elapsed( u64 start, u64 end )
-{
- u64 delta = (end - start) * Tick_To_Millisecond;
- f32 result = scast(f32, delta) / scast(f32, Performance_Counter_Frequency);
- return result;
-}
-
-inline f32
-timing_get_seconds_elapsed( u64 start, u64 end )
-{
- u64 delta = end - start;
- f32 result = scast(f32, delta) / scast(f32, Performance_Counter_Frequency);
- return result;
-}
-
-inline f32
-timing_get_us_elapsed( u64 start, u64 end )
-{
- u64 delta = (end - start) * Tick_To_Microsecond;
- f32 result = scast(f32, delta) / scast(f32, Performance_Counter_Frequency);
- return result;
-}
-
-inline u64
-timing_get_wall_clock()
-{
- u64 clock;
- QueryPerformanceCounter( rcast( LARGE_INTEGER*, & clock) );
- return clock;
-}
-#pragma endregion Timing
-
-internal WinDimensions
-get_window_dimensions( HWND window_handle )
-{
- RECT client_rect;
- GetClientRect( window_handle, & client_rect );
- WinDimensions result;
- result.width = client_rect.right - client_rect.left;
- result.height = client_rect.bottom - client_rect.top;
- return result;
-}
-
-internal void
-display_buffer_in_window( HDC device_context, u32 window_width, u32 window_height, OffscreenBuffer* buffer
- , u32 x, u32 y
- , u32 width, u32 height )
-{
- // TODO(Ed) : Aspect ratio correction
- StretchDIBits( device_context
- #if 0
- , x, y, width, height
- , x, y, width, height
- #endif
- , 0, 0, buffer->width, buffer->height
- // , 0, 0, window_width, window_height
- , 0, 0, buffer->width, buffer->height
- , buffer->memory, & buffer->info
- , DIB_ColorTable_RGB, RO_Source_To_Dest );
-}
-
-internal void
-resize_dib_section( OffscreenBuffer* buffer, u32 width, u32 height )
-{
- // TODO(Ed) : Bulletproof memory handling here for the bitmap memory
- if ( buffer->memory )
- {
- VirtualFree( buffer->memory, 0, MEM_RELEASE );
- }
-
- buffer->width = width;
- buffer->height = height;
- buffer->bytes_per_pixel = 4;
- buffer->pitch = buffer->width * buffer->bytes_per_pixel;
-
- // Negative means top-down in the context of the biHeight
-# define Top_Down -
- BITMAPINFOHEADER&
- header = buffer->info.bmiHeader;
- header.biSize = sizeof( buffer->info.bmiHeader );
- header.biWidth = buffer->width;
- header.biHeight = Top_Down buffer->height;
- header.biPlanes = 1;
- header.biBitCount = 32; // Need 24, but want 32 ( alignment )
- header.biCompression = BI_RGB_Uncompressed;
- // header.biSizeImage = 0;
- // header.biXPelsPerMeter = 0;
- // header.biYPelsPerMeter = 0;
- // header.biClrUsed = 0;
- // header.biClrImportant = 0;
-# undef Top_Down
-
- // We want to "touch" a pixel on every 4-byte boundary
- u32 BitmapMemorySize = (buffer->width * buffer->height) * buffer->bytes_per_pixel;
- buffer->memory = VirtualAlloc( NULL, BitmapMemorySize, MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write );
-
- // TODO(Ed) : Clear to black
-}
-
-internal LRESULT CALLBACK
-main_window_callback( HWND handle
- , UINT system_messages
- , WPARAM w_param
- , LPARAM l_param )
-{
- LRESULT result = 0;
-
- switch ( system_messages )
- {
- case WM_ACTIVATEAPP:
- {
- if ( scast( bool, w_param ) == true )
- {
- SetLayeredWindowAttributes( handle, RGB(0, 0, 0), 255, LWA_Alpha );
- }
- else
- {
- SetLayeredWindowAttributes( handle, RGB(0, 0, 0), 100, LWA_Alpha );
- }
- }
- break;
-
- case WM_CLOSE:
- {
- // TODO(Ed) : Handle with a message to the user
- Running = false;
- }
- break;
-
- case WM_DESTROY:
- {
- // TODO(Ed) : Handle with as an error and recreate the window
- Running = false;
- }
- break;
-
- case WM_PAINT:
- {
- PAINTSTRUCT info;
- HDC device_context = BeginPaint( handle, & info );
- u32 x = info.rcPaint.left;
- u32 y = info.rcPaint.top;
- u32 width = info.rcPaint.right - info.rcPaint.left;
- u32 height = info.rcPaint.bottom - info.rcPaint.top;
-
- WinDimensions dimensions = get_window_dimensions( handle );
-
- display_buffer_in_window( device_context, dimensions.width, dimensions.height, &Surface_Back_Buffer
- , x, y
- , width, height );
- EndPaint( handle, & info );
- }
- break;
-
- case WM_MOUSEMOVE:
- {
- RECT rect;
- POINT pt = { LOWORD(l_param), HIWORD(l_param) };
-
- GetClientRect(handle, &rect);
- if (PtInRect(&rect, pt))
- {
- // Hide the cursor when it's inside the window
- while (ShowCursor(FALSE) >= 0);
- }
- else
- {
- // Show the cursor when it's outside the window
- while (ShowCursor(TRUE) < 0);
- }
- }
- break;
-
- case WM_SIZE:
- {
- }
- break;
-
- default:
- {
- result = DefWindowProc( handle, system_messages, w_param, l_param );
- }
- }
-
- return result;
-}
-
-internal void
-process_pending_window_messages( engine::KeyboardState* keyboard, engine::MousesState* mouse )
-{
- MSG window_msg_info;
- while ( PeekMessageA( & window_msg_info, 0, 0, 0, PM_Remove_Messages_From_Queue ) )
- {
- if ( window_msg_info.message == WM_QUIT )
- {
- OutputDebugStringA("WM_QUIT\n");
- Running = false;
- }
-
- // Keyboard input handling
- switch (window_msg_info.message)
- {
- // I rather do this with GetAsyncKeyState...
- case WM_SYSKEYDOWN:
- case WM_SYSKEYUP:
- {
- WPARAM vk_code = window_msg_info.wParam;
- b32 is_down = scast(b32, (window_msg_info.lParam >> 31) == 0 );
- b32 was_down = scast(b32, (window_msg_info.lParam >> 30) );
- b32 alt_down = scast(b32, (window_msg_info.lParam & (1 << 29)) );
-
- switch ( vk_code )
- {
- case VK_F4:
- {
- if ( alt_down )
- Running = false;
- }
- break;
- }
- }
- break;
-
- case WM_MOUSEWHEEL:
- {
- // This captures the vertical scroll value
- int verticalScroll = GET_WHEEL_DELTA_WPARAM(window_msg_info.wParam);
- mouse->vertical_wheel.end += scast(f32, verticalScroll);
- }
- break;
-
- case WM_MOUSEHWHEEL:
- {
- // This captures the horizontal scroll value
- int horizontalScroll = GET_WHEEL_DELTA_WPARAM(window_msg_info.wParam);
- mouse->horizontal_wheel.end += scast( f32, horizontalScroll);
- }
- break;
-
- default:
- TranslateMessage( & window_msg_info );
- DispatchMessageW( & window_msg_info );
- }
- }
-}
-
-#pragma endregion Internal
-
-#pragma region Platfom API
-#if Build_Development
-void debug_set_pause_rendering( b32 value )
-{
- Pause_Rendering = value;
-}
-#endif
-
-b32 file_check_exists( Str path )
-{
- HANDLE file_handle = CreateFileA( path
- , GENERIC_READ, FILE_SHARE_READ, 0
- , OPEN_EXISTING, 0, 0
- );
- if ( file_handle != INVALID_HANDLE_VALUE )
- {
- CloseHandle( file_handle );
- return true;
- }
- return false;
-}
-
-void file_close( File* file )
-{
- HANDLE handle = pcast(HANDLE, file->opaque_handle);
-
- if ( handle == INVALID_HANDLE_VALUE )
- return;
-
- CloseHandle( handle );
-
- if ( file->data )
- {
- // TODO(Ed): This should use our persistent memory block.
- VirtualFree( file->data, 0, MEM_Release);
- }
- *file = {};
-}
-
-b32 file_delete( Str path )
-{
- return DeleteFileA( path );
-}
-
-b32 file_read_stream( File* file, u32 content_size, void* content_memory )
-{
- HANDLE file_handle;
- if ( file->opaque_handle == nullptr )
- {
- file_handle = CreateFileA( file->path
- , GENERIC_READ, FILE_SHARE_READ, 0
- , OPEN_EXISTING, 0, 0
- );
- if ( file_handle == INVALID_HANDLE_VALUE )
- {
- // TODO : Logging
- return {};
- }
-
- file->opaque_handle = file_handle;
- }
- else
- {
- file_handle = pcast(HANDLE, file->opaque_handle );
- }
-
- u32 bytes_read;
- if ( ReadFile( file_handle, content_memory, content_size, rcast(LPDWORD, &bytes_read), 0 ) == false )
- {
- // TODO : Logging
- return {};
- }
-
- if ( bytes_read != content_size )
- {
- // TODO : Logging
- return {};
- }
- return bytes_read;
-}
-
-b32 file_read_content( File* file )
-{
- HANDLE file_handle = CreateFileA( file->path
- , GENERIC_READ, FILE_SHARE_READ, 0
- , OPEN_EXISTING, 0, 0
- );
- if ( file_handle == INVALID_HANDLE_VALUE )
- {
- // TODO(Ed) : Logging
- return {};
- }
-
- u32 size;
- GetFileSizeEx( file_handle, rcast(LARGE_INTEGER*, &size) );
- if ( size == 0 )
- {
- // TODO(Ed) : Logging
- CloseHandle( file_handle );
- return {};
- }
-
- // TODO(Ed) : This should use our memory block.
- file->data = rcast(HANDLE*, VirtualAlloc( 0, sizeof(HANDLE) + size, MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write ));
- file->size = size;
- file->opaque_handle = file_handle;
-
- u32 bytes_read;
- if ( ReadFile( file_handle, file->data, file->size, rcast(LPDWORD, &bytes_read), 0 ) == false )
- {
- // TODO(Ed) : Logging
- CloseHandle( file_handle );
- return {};
- }
-
- if ( bytes_read != file->size )
- {
- // TODO : Logging
- CloseHandle( file_handle );
- return {};
- }
- return bytes_read;
-}
-
-void file_rewind( File* file )
-{
- HANDLE file_handle = pcast(HANDLE, file->opaque_handle );
- if ( file_handle == INVALID_HANDLE_VALUE )
- return;
-
- SetFilePointer(file_handle, 0, NULL, FILE_BEGIN);
-}
-
-u32 file_write_stream( File* file, u32 content_size, void* content_memory )
-{
- HANDLE file_handle;
- if ( file->opaque_handle == nullptr )
- {
- file_handle = CreateFileA( file->path
- ,GENERIC_WRITE, 0, 0
- , OPEN_ALWAYS, 0, 0
- );
- if ( file_handle == INVALID_HANDLE_VALUE )
- {
- // TODO(Ed) : Logging
- return {};
- }
-
- file->opaque_handle = file_handle;
- }
- else
- {
- file_handle = pcast(HANDLE, file->opaque_handle );
- }
-
- DWORD bytes_written;
- if ( WriteFile( file_handle, content_memory, content_size, & bytes_written, 0 ) == false )
- {
- // TODO : Logging
- return false;
- }
-
- return bytes_written;
-}
-
-u32 file_write_content( File* file, u32 content_size, void* content_memory )
-{
- HANDLE file_handle = CreateFileA( file->path
- , GENERIC_WRITE, 0, 0
- , CREATE_ALWAYS, 0, 0
- );
- if ( file_handle == INVALID_HANDLE_VALUE )
- {
- // TODO : Logging
- return false;
- }
- file->opaque_handle = file_handle;
-
- DWORD bytes_written;
- if ( WriteFile( file_handle, content_memory, content_size, & bytes_written, 0 ) == false )
- {
- // TODO : Logging
- return false;
- }
- return bytes_written;
-}
-
-u32 get_monitor_refresh_rate()
-{
- return 0;
-}
-void set_monitor_refresh_rate( u32 refresh_rate )
-{
-}
-u32 get_engine_refresh_rate()
-{
- return 0;
-}
-void set_engine_refresh_rate( u32 refresh_rate )
-{
-
-}
-
-BinaryModule load_binary_module( char const* module_path )
-{
- HMODULE lib = LoadLibraryA( module_path );
- return BinaryModule { scast(void*, lib) };
-}
-
-void unload_binary_module( BinaryModule* module )
-{
- FreeLibrary( scast(HMODULE, module->opaque_handle) );
- *module = {};
-}
-
-void* get_binary_module_symbol( BinaryModule module, char const* symbol_name )
-{
- return rcast(void*, GetProcAddress( scast(HMODULE, module.opaque_handle), symbol_name ));
-}
-
-void memory_copy( void* dest, u64 src_size, void* src )
-{
- CopyMemory( dest, src, src_size );
-}
-#pragma endregion Platform API
-
-#pragma region Engine Module API
-
-constexpr const Str FName_Engine_DLL = str_ascii("handmade_engine.dll");
-constexpr const Str FName_Engine_DLL_InUse = str_ascii("handmade_engine_in_use.dll");
-constexpr const Str FName_Engine_PDB_Lock = str_ascii("handmade_engine.pdb.lock");
-
-global HMODULE Lib_Handmade_Engine = nullptr;
-global StrFixed< S16_MAX > Path_Engine_DLL;
-global StrFixed< S16_MAX > Path_Engine_DLL_InUse;
-
-internal
-engine::ModuleAPI load_engine_module_api()
-{
- using ModuleAPI = engine::ModuleAPI;
-
- CopyFileA( Path_Engine_DLL, Path_Engine_DLL_InUse, FALSE );
-
- // Engine
- Lib_Handmade_Engine = LoadLibraryA( Path_Engine_DLL_InUse );
- if ( ! Lib_Handmade_Engine )
- {
- return {};
- }
-
- engine::ModuleAPI engine_api {};
- engine_api.on_module_reload = get_procedure_from_library< engine::OnModuleRelaodFn > ( Lib_Handmade_Engine, engine::symbol_on_module_load );
- engine_api.startup = get_procedure_from_library< engine::StartupFn > ( Lib_Handmade_Engine, engine::symbol_startup );
- engine_api.shutdown = get_procedure_from_library< engine::ShutdownFn > ( Lib_Handmade_Engine, engine::symbol_shutdown );
- engine_api.update_and_render = get_procedure_from_library< engine::UpdateAndRenderFn >( Lib_Handmade_Engine, engine::symbol_update_and_render );
- engine_api.update_audio = get_procedure_from_library< engine::UpdateAudioFn > ( Lib_Handmade_Engine, engine::symbol_update_audio );
-
- engine_api.IsValid =
- engine_api.on_module_reload
- && engine_api.startup
- && engine_api.shutdown
- && engine_api.update_and_render
- && engine_api.update_audio;
- if ( engine_api.IsValid )
- {
- OutputDebugStringA( "Loaded engine module API\n" );
- }
- return engine_api;
-}
-
-internal
-void unload_engine_module_api( engine::ModuleAPI* engine_api )
-{
- if ( engine_api->IsValid )
- {
- FreeLibrary( Lib_Handmade_Engine );
- *engine_api = {};
- OutputDebugStringA( "Unloaded engine module API\n" );
- }
-}
-#pragma endregion Engine Module API
-
-NS_PLATFORM_END
-
-int CALLBACK
-WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int show_command )
-{
- using namespace win32;
- using namespace platform;
-
-#pragma region Startup
- // Timing
-#if Build_Development
- u64 launch_clock = timing_get_wall_clock();
- u64 launch_cycle = __rdtsc();
-#endif
-
- // Sets the windows scheduler granulaity for this process to 1 ms
- constexpr u32 desired_scheduler_ms = 1;
- b32 sleep_is_granular = ( timeBeginPeriod( desired_scheduler_ms ) == TIMERR_NOERROR );
-
- // If its a high-perofmrance frame-time (a refresh rate that produces a target frametime at or below 4.16~ ms, we cannot allow the scheduler to mess things up)
- b32 sub_ms_granularity_required = scast(f32, Engine_Refresh_Hz) <= High_Perf_Frametime_MS;
-
- QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & Performance_Counter_Frequency) );
-
- // Setup pathing
- StrFixed< S16_MAX > path_pdb_lock {};
- {
- // TODO(Ed): This will not support long paths, NEEDS to be changed to support long paths.
-
- char path_buffer[S16_MAX];
- GetModuleFileNameA( 0, path_buffer, sizeof(path_buffer) );
-
- if ( GetCurrentDirectoryA( S16_MAX, Path_Binaries ) == 0 )
- {
- fatal( "Failed to get the root directory!" );
- }
- Path_Binaries.len = str_length( Path_Binaries );
- Path_Binaries[ Path_Binaries.len ] = '\\';
- ++ Path_Binaries.len;
-
- if ( SetCurrentDirectoryA( ".." ) == 0 )
- {
- fatal( "Failed to set current directory to root!");
- }
-
- if ( GetCurrentDirectoryA( S16_MAX, Path_Root.ptr ) == 0 )
- {
- fatal( "Failed to get the root directory!" );
- }
- Path_Root.len = str_length(Path_Root.ptr);
- Path_Root.ptr[ Path_Root.len ] = '\\';
- ++ Path_Root.len;
-
- Path_Engine_DLL. concat( Path_Binaries, FName_Engine_DLL );
- Path_Engine_DLL_InUse.concat( Path_Binaries, FName_Engine_DLL_InUse );
-
- path_pdb_lock.concat( Path_Binaries, FName_Engine_PDB_Lock );
-
- Path_Scratch.concat( Path_Root, str_ascii("scratch") );
- Path_Scratch.ptr[ Path_Scratch.len ] = '\\';
- ++ Path_Scratch.len;
-
- CreateDirectoryA( Path_Scratch, 0 );
- }
-
- // Memory
- engine::Memory engine_memory {};
- {
- engine_memory.persistent_size = megabytes( 128 );
- // engine_memory.FrameSize = megabytes( 64 );
- engine_memory.transient_size = gigabytes( 2 );
-
- u64 total_size = engine_memory.persistent_size
- // + engine_memory.FrameSize
- + engine_memory.transient_size;
-
- #if Build_Debug
- void* base_address = rcast(void*, terabytes( 1 ));
- #else
- void* base_address = 0;
- #endif
-
- engine_memory.persistent = VirtualAlloc( base_address, total_size , MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write );
- engine_memory.transient = rcast( u8*, engine_memory.persistent ) + engine_memory.persistent_size;
-
- #if Build_Development
- for (u32 slot = 0; slot < engine_memory.Num_Snapshot_Slots; ++slot)
- {
- engine::MemorySnapshot& snapshot = engine_memory.snapshots[ slot ];
-
- snapshot.file_path.concat( Path_Scratch, str_ascii("snapshot_") );
- wsprintfA( snapshot.file_path.ptr, "%s%d.hm_snapshot", snapshot.file_path.ptr, slot );
-
- HANDLE snapshot_file = CreateFileA( snapshot.file_path
- , GENERIC_READ | GENERIC_WRITE, 0, 0
- , CREATE_ALWAYS, 0, 0 );
-
- LARGE_INTEGER file_size {};
- file_size.QuadPart = total_size;
-
- HANDLE snapshot_mapping = CreateFileMappingA( snapshot_file, 0
- , Page_Read_Write
- , file_size.HighPart, file_size.LowPart
- , 0 );
-
- snapshot.memory = MapViewOfFile( snapshot_mapping, FILE_MAP_ALL_ACCESS, 0, 0, total_size );
- snapshot.opaque_handle = snapshot_file;
- snapshot.opaque_handle_2 = snapshot_mapping;
- }
- #endif
-
- if ( engine_memory.persistent == nullptr
- || engine_memory.transient == nullptr )
- {
- // TODO : Diagnostic Logging
- return -1;
- }
- }
-
- WNDCLASSW window_class {};
- HWND window_handle = nullptr;
- {
- window_class.style = CS_Horizontal_Redraw | CS_Vertical_Redraw;
- window_class.lpfnWndProc = main_window_callback;
- // window_class.cbClsExtra = ;
- // window_class.cbWndExtra = ;
- window_class.hInstance = instance;
- // window_class.hIcon = ;
- // window_class.hCursor = ;
- // window_class.hbrBackground = ;
- window_class.lpszMenuName = L"Handmade Hero!";
- window_class.lpszClassName = L"HandmadeHeroWindowClass";
-
- if ( ! RegisterClassW( & window_class ) )
- {
- // TODO : Diagnostic Logging
- return 0;
- }
-
- window_handle = CreateWindowExW(
- // WS_EX_LAYERED | WS_EX_TOPMOST,
- WS_EX_LAYERED,
- window_class.lpszClassName,
- L"Handmade Hero",
- WS_Overlapped_Window | WS_Initially_Visible,
- CW_Use_Default, CW_Use_Default, // x, y
- 1920, 1080, // width, height
- 0, 0, // parent, menu
- instance, 0 // instance, param
- );
-
- if ( ! window_handle )
- {
- // TODO : Diagnostic Logging
- return 0;
- }
-
- // WinDimensions dimensions = get_window_dimensions( window_handle );
- resize_dib_section( &Surface_Back_Buffer, 1920, 1080 );
-
- // Setup monitor refresh and associated timers
- HDC refresh_dc = GetDC( window_handle );
- u32 monitor_refresh_hz = GetDeviceCaps( refresh_dc, VREFRESH );
- if ( monitor_refresh_hz > 1 )
- {
- Monitor_Refresh_Hz = monitor_refresh_hz;
- }
- ReleaseDC( window_handle, refresh_dc );
-
- Engine_Refresh_Hz = 60;
- Engine_Frame_Target_MS = 1000.f / scast(f32, Engine_Refresh_Hz);
- }
-
- // Prepare platform API
- ModuleAPI platform_api {};
- {
- platform_api.path_root = Path_Root;
- platform_api.path_binaries = Path_Binaries;
- platform_api.path_scratch = Path_Scratch;
-
- #if Build_Development
- platform_api.debug_set_pause_rendering = & debug_set_pause_rendering;
- #endif
- // Not implemented yet
- platform_api.get_monitor_refresh_rate = nullptr;
- platform_api.set_monitor_refresh_rate = nullptr;
- platform_api.get_engine_frame_target = nullptr;
- platform_api.set_engine_frame_target = nullptr;
-
- platform_api.load_binary_module = & load_binary_module;
- platform_api.unload_binary_module = & unload_binary_module;
- platform_api.get_module_procedure = & get_binary_module_symbol;
-
- platform_api.file_check_exists = & file_check_exists;
- platform_api.file_close = & file_close;
- platform_api.file_delete = & file_delete;
- platform_api.file_read_content = & file_read_content;
- platform_api.file_read_stream = & file_read_stream;
- platform_api.file_rewind = & file_rewind;
- platform_api.file_write_content = & file_write_content;
- platform_api.file_write_stream = & file_write_stream;
-
- platform_api.memory_copy = & memory_copy;
- }
-
- // Load engine module
- FILETIME engine_api_load_time = file_get_last_write_time( Path_Engine_DLL );
- engine::ModuleAPI engine_api = load_engine_module_api();
-
- b32 sound_is_valid = false;
- DWORD ds_cursor_byte_delta = 0;
- f32 ds_latency_ms = 0;
- DirectSoundBuffer ds_sound_buffer;
- u32 audio_marker_index = 0;
- AudioTimeMarker audio_time_markers[ Monitor_Refresh_Max_Supported ] {};
- u32 audio_time_markers_size = Engine_Refresh_Hz / 2;
- assert( audio_time_markers_size <= Monitor_Refresh_Max_Supported )
- {
- ds_sound_buffer.is_playing = 0;
- ds_sound_buffer.samples_per_second = 48000;
- ds_sound_buffer.bytes_per_sample = sizeof(s16) * 2;
-
- ds_sound_buffer.secondary_buffer_size = ds_sound_buffer.samples_per_second * ds_sound_buffer.bytes_per_sample;
- init_sound( window_handle, & ds_sound_buffer );
-
- ds_sound_buffer.samples = rcast( s16*, VirtualAlloc( 0, 48000 * 2 * sizeof(s16)
- , MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write ));
-
- assert( ds_sound_buffer.samples );
- ds_sound_buffer.running_sample_index = 0;
- // ds_clear_sound_buffer( & sound_output );
- ds_sound_buffer.secondary_buffer->Play( 0, 0, DSBPLAY_LOOPING );
-
- ds_sound_buffer.bytes_per_second = ds_sound_buffer.samples_per_second * ds_sound_buffer.bytes_per_sample;
- ds_sound_buffer.guard_sample_bytes = (ds_sound_buffer.bytes_per_second / Engine_Refresh_Hz) / 2;
-
- // TODO(Ed): When switching to core audio at minimum, this will be 1 ms of lag and guard samples wont really be needed.
- u32 min_guard_sample_bytes = 1540;
- if ( ds_sound_buffer.guard_sample_bytes < min_guard_sample_bytes )
- {
- ds_sound_buffer.guard_sample_bytes = min_guard_sample_bytes;
- }
- }
-
- engine::InputState input {};
-
- // There can be 4 of any of each input API type : KB & Mouse, XInput, JSL.
-#if 0
- using EngineKeyboardStates = engine::KeyboardState[ Max_Controllers ];
- EngineKeyboardStates keyboard_states[2] {};
- EngineKeyboardStates* old_keyboards = & keyboard_states[0];
- EngineKeyboardStates* new_keyboards = & keyboard_states[1];
-#endif
-
- engine::KeyboardState keyboard_states[2] {};
- engine::KeyboardState* old_keyboard = & keyboard_states[0];
- engine::KeyboardState* new_keyboard = & keyboard_states[1];
- // Important: Assuming keyboard always connected for now, and assigning to first controller.
-
- engine::MousesState mouse_states[2] {};
- engine::MousesState* old_mouse = & mouse_states[0];
- engine::MousesState* new_mouse = & mouse_states[1];
-
- EngineXInputPadStates xpad_states[2] {};
- EngineXInputPadStates* old_xpads = & xpad_states[0];
- EngineXInputPadStates* new_xpads = & xpad_states[1];
-
- EngineDSPadStates ds_pad_states[2] {};
- EngineDSPadStates* old_ds_pads = & ds_pad_states[0];
- EngineDSPadStates* new_ds_pads = & ds_pad_states[1];
-
- u32 jsl_num_devices = JslConnectDevices();
- JSL_DeviceHandle jsl_device_handles[4] {};
- {
- xinput_load_library_bindings();
-
- u32 jsl_getconnected_found = JslGetConnectedDeviceHandles( jsl_device_handles, jsl_num_devices );
- {
- if ( jsl_getconnected_found != jsl_num_devices )
- {
- OutputDebugStringA( "Error: JSLGetConnectedDeviceHandles didn't find as many as were stated with JslConnectDevices\n");
- }
-
- if ( jsl_num_devices > 0 )
- {
- OutputDebugStringA( "JSL Connected Devices:\n" );
- for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index )
- {
- JslSetLightColour( jsl_device_handles[ jsl_device_index ], (255 << 8) );
- }
- }
- }
-
- if ( jsl_num_devices > 4 )
- {
- jsl_num_devices = 4;
- MessageBoxA( window_handle, "More than 4 JSL devices found, this engine will only support the first four found."
- , "Warning", MB_ICONEXCLAMATION );
- }
- }
-
- engine_api.startup( & engine_memory, & platform_api );
-
- u64 last_frame_clock = timing_get_wall_clock();
- u64 last_frame_cycle = __rdtsc();
- u64 flip_wall_clock = last_frame_clock;
-
-#if Build_Development
- u64 startup_cycles = last_frame_cycle - launch_cycle;
- f32 startup_ms = timing_get_ms_elapsed( launch_clock, last_frame_clock );
-
- char text_buffer[256];
- sprintf_s( text_buffer, sizeof(text_buffer), "Startup MS: %f\n", startup_ms );
- OutputDebugStringA( text_buffer );
-#endif
-#pragma endregion Startup
-
- // Placeholder
- engine::ThreadContext thread_context_placeholder {};
-
- Running = true;
-#if 0
-// This tests the play & write cursor update frequency.
- while ( Running )
- {
- DWORD play_cursor;
- DWORD write_cursor;
-
- ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor );
- char text_buffer[256];
- sprintf_s( text_buffer, sizeof(text_buffer), "PC:%u WC:%u\n", (u32)play_cursor, (u32)write_cursor );
- OutputDebugStringA( text_buffer );
- }
-#endif
- while( Running )
- {
- // Engine Module Hot-Reload
- do {
- FILETIME engine_api_current_time = file_get_last_write_time( Path_Engine_DLL );
- if ( CompareFileTime( & engine_api_load_time, & engine_api_current_time ) == 0 )
- break;
-
- WIN32_FIND_DATAA lock_file_info = {};
- for(;;)
- {
- HANDLE lock_file = FindFirstFileA( path_pdb_lock, & lock_file_info );
- if ( lock_file != INVALID_HANDLE_VALUE )
- {
- FindClose( lock_file );
- Sleep( 1 );
- continue;
- }
- break;
- }
-
- engine_api_load_time = engine_api_current_time;
- unload_engine_module_api( & engine_api );
- engine_api = load_engine_module_api();
- } while (0);
-
-
- // Swapping at the beginning of the input frame instead of the end.
- swap( old_keyboard, new_keyboard );
- swap( old_mouse, new_mouse );
- swap( old_xpads, new_xpads );
- swap( old_ds_pads, new_ds_pads );
-
- poll_input( window_handle, & input, jsl_num_devices, jsl_device_handles
- , old_keyboard, new_keyboard
- , old_mouse, new_mouse
- , old_xpads, new_xpads
- , old_ds_pads, new_ds_pads );
-
- process_pending_window_messages( new_keyboard, new_mouse );
-
- f32 delta_time = timing_get_seconds_elapsed( last_frame_clock, timing_get_wall_clock() );
-
- // Engine's logical iteration and rendering process
- engine_api.update_and_render( delta_time, & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.memory )
- , & engine_memory, & platform_api, & thread_context_placeholder );
-
- u64 audio_frame_start = timing_get_wall_clock();
- f32 flip_to_audio_ms = timing_get_ms_elapsed( flip_wall_clock, audio_frame_start );
-
- DWORD ds_play_cursor;
- DWORD ds_write_cursor;
- do {
- /*
- Audio Processing:
- There is a sync boundary value, that is the number of samples that the engine's frame-time may vary by
- (ex: approx 2ms of variance between frame-times).
-
- On wakeup : Check play cursor position and forcast ahead where the cursor will be for the next sync boundary.
- Based on that, check the write cursor position, if its (at least) before the synch boundary, the target write position is
- the frame boundary plus one frame. (Low latency)
-
- If its after (sync boundary), we cannot sync audio.
- Write a frame's worth of audio plus some number of "guard" samples. (High Latency)
- */
- if ( ! SUCCEEDED( ds_sound_buffer.secondary_buffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) ))
- {
- sound_is_valid = false;
- break;
- }
-
- if ( ! sound_is_valid )
- {
- ds_sound_buffer.running_sample_index = ds_write_cursor / ds_sound_buffer.bytes_per_sample;
- sound_is_valid = true;
- }
-
- DWORD byte_to_lock = 0;
- DWORD target_cursor = 0;
- DWORD bytes_to_write = 0;
-
- byte_to_lock = (ds_sound_buffer.running_sample_index * ds_sound_buffer.bytes_per_sample) % ds_sound_buffer.secondary_buffer_size;
-
- DWORD bytes_per_second = ds_sound_buffer.bytes_per_sample * ds_sound_buffer.samples_per_second;
-
- DWORD expected_samplebytes_per_frame = bytes_per_second / Engine_Refresh_Hz;
-
- f32 left_until_flip_ms = Engine_Frame_Target_MS - flip_to_audio_ms;
- DWORD expected_bytes_until_flip = scast(DWORD, (left_until_flip_ms / Engine_Frame_Target_MS) * scast(f32, expected_samplebytes_per_frame));
-
- DWORD expected_sync_boundary_byte = ds_play_cursor + expected_bytes_until_flip;
-
- DWORD sync_write_cursor = ds_write_cursor;
- if ( sync_write_cursor < ds_play_cursor )
- {
- // unwrap the cursor so its ahead of the play curosr linearly.
- sync_write_cursor += ds_sound_buffer.secondary_buffer_size;
- }
- assert( sync_write_cursor >= ds_play_cursor );
-
- sync_write_cursor += ds_sound_buffer.guard_sample_bytes;
-
- b32 audio_interface_is_low_latency = sync_write_cursor < expected_sync_boundary_byte;
- if ( audio_interface_is_low_latency )
- {
- target_cursor = ( expected_sync_boundary_byte + expected_samplebytes_per_frame );
- }
- else
- {
- target_cursor = (ds_write_cursor + expected_samplebytes_per_frame + ds_sound_buffer.guard_sample_bytes);
- }
- target_cursor %= ds_sound_buffer.secondary_buffer_size;
-
- if ( byte_to_lock > target_cursor)
- {
- // Infront of play cursor |--play--byte_to_write-->--|
- bytes_to_write = ds_sound_buffer.secondary_buffer_size - byte_to_lock;
- bytes_to_write += target_cursor;
- }
- else
- {
- // Behind play cursor |--byte_to_write-->--play--|
- bytes_to_write = target_cursor - byte_to_lock;
- }
-
- // Engine Sound
- delta_time = timing_get_seconds_elapsed( last_frame_clock, timing_get_wall_clock() );
-
- // s16 samples[ 48000 * 2 ];
- engine::AudioBuffer sound_buffer {};
- sound_buffer.num_samples = bytes_to_write / ds_sound_buffer.bytes_per_sample;
- sound_buffer.running_sample_index = ds_sound_buffer.running_sample_index;
- sound_buffer.samples_per_second = ds_sound_buffer.samples_per_second;
- sound_buffer.samples = ds_sound_buffer.samples;
- engine_api.update_audio( delta_time, & sound_buffer, & engine_memory, & platform_api, & thread_context_placeholder );
-
- AudioTimeMarker* marker = & audio_time_markers[ audio_marker_index ];
- marker->output_play_cursor = ds_play_cursor;
- marker->output_write_cursor = ds_write_cursor;
- marker->output_location = byte_to_lock;
- marker->output_byte_count = bytes_to_write;
- marker->expected_flip_cursor = expected_sync_boundary_byte;
-
- // Update audio buffer
- if ( ! sound_is_valid )
- break;
-
- #if Build_Development && 0
- #if 0
- DWORD play_cursor;
- DWORD write_cursor;
- ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor );
- #endif
- DWORD unwrapped_write_cursor = ds_write_cursor;
- if ( unwrapped_write_cursor < ds_play_cursor )
- {
- unwrapped_write_cursor += ds_sound_buffer.SecondaryBufferSize;
- }
- ds_cursor_byte_delta = unwrapped_write_cursor - ds_play_cursor;
-
- constexpr f32 to_milliseconds = 1000.f;
- f32 sample_delta = scast(f32, ds_cursor_byte_delta) / scast(f32, ds_sound_buffer.BytesPerSample);
- f32 ds_latency_s = sample_delta / scast(f32, ds_sound_buffer.SamplesPerSecond);
- ds_latency_ms = ds_latency_s * to_milliseconds;
-
- char text_buffer[256];
- sprintf_s( text_buffer, sizeof(text_buffer), "BTL:%u TC:%u BTW:%u - PC:%u WC:%u DELTA:%u bytes %f ms\n"
- , (u32)byte_to_lock, (u32)target_cursor, (u32)bytes_to_write
- , (u32)play_cursor, (u32)write_cursor, (u32)ds_cursor_byte_delta, ds_latency_ms );
- OutputDebugStringA( text_buffer );
- #endif
- ds_fill_sound_buffer( & ds_sound_buffer, byte_to_lock, bytes_to_write );
-
- DWORD ds_status = 0;
- if ( SUCCEEDED( ds_sound_buffer.secondary_buffer->GetStatus( & ds_status ) ) )
- {
- ds_sound_buffer.is_playing = ds_status & DSBSTATUS_PLAYING;
- }
- if ( ds_sound_buffer.is_playing )
- break;
-
- ds_sound_buffer.secondary_buffer->Play( 0, 0, DSBPLAY_LOOPING );
- } while(0);
-
- // Timing Update
- {
- u64 work_frame_end_cycle = __rdtsc();
- u64 work_frame_end_clock = timing_get_wall_clock();
-
- f32 work_frame_ms = timing_get_ms_elapsed( last_frame_clock, work_frame_end_clock ); // WorkSecondsElapsed
- f32 work_cycles = timing_get_ms_elapsed( last_frame_cycle, work_frame_end_cycle );
-
- f32 frame_elapsed_ms = work_frame_ms; // SecondsElapsedForFrame
- if ( frame_elapsed_ms < Engine_Frame_Target_MS )
- {
- s32 sleep_ms = scast(DWORD, (Engine_Frame_Target_MS - frame_elapsed_ms)) - 1;
- if ( sleep_ms > 0 && ! sub_ms_granularity_required && sleep_is_granular )
- {
- Sleep( scast(DWORD, sleep_ms) );
- }
-
- u64 frame_clock = timing_get_wall_clock();
- frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock );
- if ( frame_elapsed_ms < Engine_Frame_Target_MS )
- {
- // TODO(Ed) : Log missed sleep here.
- }
-
- while ( frame_elapsed_ms < Engine_Frame_Target_MS )
- {
- frame_clock = timing_get_wall_clock();
- frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock );
- }
- }
- else
- {
- // TODO(Ed) : Missed the display sync window!
- }
-
- last_frame_clock = timing_get_wall_clock(); // LastCouner
- last_frame_cycle = __rdtsc();
- }
-
- // Update surface back buffer
- if ( ! Pause_Rendering )
- {
- WinDimensions dimensions = get_window_dimensions( window_handle );
- HDC device_context = GetDC( window_handle );
-
- #if Build_Development && 0
- // Note: debug_marker_index is wrong for the 0th index
- debug_sync_display( & ds_sound_buffer
- , audio_time_markers_size, audio_time_markers, audio_marker_index - 1
- , Engine_Frame_Target_MS );
- #endif
-
- display_buffer_in_window( device_context, dimensions.width, dimensions.height, &Surface_Back_Buffer
- , 0, 0
- , dimensions.width, dimensions.height );
- ReleaseDC( window_handle, device_context );
- }
-
- flip_wall_clock = timing_get_wall_clock();
- #if Build_Development
- {
- // Audio Debug
- DWORD play_cursor = 0;
- DWORD write_cursor = 0;
- if ( SUCCEEDED( ds_sound_buffer.secondary_buffer->GetCurrentPosition( & play_cursor, & write_cursor ) ) )
- {
- if ( ! sound_is_valid )
- {
- ds_sound_buffer.running_sample_index = write_cursor / ds_sound_buffer.bytes_per_sample;
- sound_is_valid = true;
- }
-
- assert( audio_marker_index < audio_time_markers_size )
- AudioTimeMarker* marker = & audio_time_markers[ audio_marker_index ];
-
- marker->flip_play_curosr = play_cursor;
- marker->flip_write_cursor = write_cursor;
- }
- }
- #endif
-
- #if Build_Development
- audio_marker_index++;
- if ( audio_marker_index >= audio_time_markers_size )
- audio_marker_index = 0;
- #endif
- }
-
- engine_api.shutdown( & engine_memory, & platform_api );
-
- unload_engine_module_api( & engine_api );
- DeleteFileA( Path_Engine_DLL_InUse );
-
- if ( jsl_num_devices > 0 )
- {
- for ( u32 jsl_device_index = 0; jsl_device_index < jsl_num_devices; ++ jsl_device_index )
- {
- JslSetLightColour( jsl_device_handles[ jsl_device_index ], 0 );
- }
- }
-
- return 0;
-}
diff --git a/scripts/.clang-format b/scripts/.clang-format
new file mode 100644
index 0000000..06805ad
--- /dev/null
+++ b/scripts/.clang-format
@@ -0,0 +1,171 @@
+# Format Style Options - Created with Clang Power Tools
+---
+AccessModifierOffset: -4
+
+AlignAfterOpenBracket: BlockIndent
+AlignArrayOfStructures: Left
+AlignConsecutiveAssignments:
+ Enabled: true
+ AcrossEmptyLines: true
+ AcrossComments: false
+ AlignCompound: true
+ PadOperators: true
+AlignConsecutiveBitFields:
+ Enabled: true
+ AcrossEmptyLines: true
+ AcrossComments: false
+AlignConsecutiveDeclarations:
+ Enabled: true
+ AcrossEmptyLines: false
+ AcrossComments: false
+AlignConsecutiveMacros:
+ Enabled: true
+ AcrossEmptyLines: true
+ AcrossComments: false
+AlignEscapedNewlines: Left
+AlignOperands: DontAlign
+
+AlignTrailingComments: true
+
+AllowAllArgumentsOnNextLine: false
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortLambdasOnASingleLine: None
+AllowShortEnumsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+
+BinPackArguments: false
+BinPackParameters: false
+
+BitFieldColonSpacing: Both
+
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+
+BreakAfterAttributes: Always
+BreakArrays: true
+# BreakBeforeInlineASMColon: OnlyMultiline
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: Allman
+BreakBeforeInheritanceComma: true
+BreakInheritanceList: BeforeComma
+BreakBeforeConceptDeclarations: true
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakStringLiterals: true
+
+ColumnLimit: 160
+
+CompactNamespaces: true
+
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth : 4
+
+ContinuationIndentWidth: 4
+
+Cpp11BracedListStyle: false
+
+DeriveLineEnding: true
+
+ExperimentalAutoDetectBinPacking: false
+
+FixNamespaceComments: true
+
+IncludeBlocks: Preserve
+
+IndentCaseBlocks: false
+IndentCaseLabels: true
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentRequires: true
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+
+# InsertNewlineAtEOF: true
+# InsertTrailingCommas: Wrapped
+
+LambdaBodyIndentation: OuterScope
+
+Language: Cpp
+
+MaxEmptyLinesToKeep: 4
+
+NamespaceIndentation: All
+
+PointerAlignment: Left
+
+QualifierAlignment: Leave
+
+ReferenceAlignment: Left
+
+ReflowComments: true
+
+# RequiresExpressionIndentation: OuterScope
+
+SeparateDefinitionBlocks: Always
+
+ShortNamespaceLines: 40
+
+SortIncludes: false
+SortUsingDeclarations: false
+
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: true
+SpaceAfterTemplateKeyword: false
+
+SpaceAroundPointerQualifiers: Default
+
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: true
+SpaceBeforeCpp11BracedList: true
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatementsExceptControlMacros
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpacesBeforeTrailingComments: 4
+
+SpaceInEmptyBlock: true
+SpaceInEmptyParentheses: false
+SpacesInAngles: true
+SpacesInCStyleCastParentheses: true
+SpacesInConditionalStatement: true
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: 20
+SpacesInParentheses: true
+SpacesInSquareBrackets: true
+
+Standard: c++17
+
+TabWidth: 4
+
+UseTab: ForIndentation
+...
diff --git a/scripts/build.ps1 b/scripts/build.ps1
index 21979b3..95e9d1b 100644
--- a/scripts/build.ps1
+++ b/scripts/build.ps1
@@ -1,11 +1,17 @@
-Clear-Host
+if ( $CursorPosition ) {
+ Clear-Host
+}
+
+$target_arch = Join-Path $PSScriptRoot 'helpers/target_arch.psm1'
+$devshell = Join-Path $PSScriptRoot 'helpers/devshell.ps1'
+$format_cpp = Join-Path $PSScriptRoot 'helpers/format_cpp.psm1'
+$config_toolchain = Join-Path $PSScriptRoot 'helpers/configure_toolchain.ps1'
-$target_arch = Join-Path $PSScriptRoot 'helpers/target_arch.psm1'
-$devshell = Join-Path $PSScriptRoot 'helpers/devshell.ps1'
$path_root = git rev-parse --show-toplevel
$path_build = Join-Path $path_root 'build'
Import-Module $target_arch
+Import-Module $format_cpp
Push-Location $path_root
@@ -18,6 +24,7 @@ Push-Location $path_root
$platform = $null
$engine = $null
$game = $null
+ $verbose = $null
[array] $vendors = @( "clang", "msvc" )
@@ -33,362 +40,13 @@ if ( $args ) { $args | ForEach-Object {
"platform" { $platform = $true }
"engine" { $engine = $true }
"game" { $game = $true }
+ "verbose" { $verbose = $true }
}
}}
#endregion Argument
-#region Toolchain Configuration
-if ($IsWindows) {
- # This HandmadeHero implementation is only designed for 64-bit systems
- & $devshell -arch amd64
-}
-
-if ( $vendor -eq $null ) {
- write-host "No vendor specified, assuming clang available"
- $compiler = "clang"
-}
-
-write-host "Building HandmadeHero with $vendor"
-
-if ( $dev ) {
- if ( $debug -eq $null ) {
- $debug = $true
- }
-
- if ( $optimize -eq $null ) {
- $optimize = $false
- }
-}
-
-function run-compiler
-{
- param( $compiler, $unit, $compiler_args )
-
- if ( $analysis ) {
- $compiler_args += $flag_syntax_only
- }
-
- write-host "`Compiling $unit"
- write-host "Compiler config:"
- $compiler_args | ForEach-Object {
- write-host $_ -ForegroundColor Cyan
- }
-
- $time_taken = Measure-Command {
- & $compiler $compiler_args 2>&1 | ForEach-Object {
- $color = 'White'
- switch ($_){
- { $_ -match "error" } { $color = 'Red' ; break }
- { $_ -match "warning" } { $color = 'Yellow'; break }
- }
- write-host `t $_ -ForegroundColor $color
- }
- }
-
- if ( Test-Path($unit) ) {
- write-host "$unit compile finished in $($time_taken.TotalMilliseconds) ms"
- }
- else {
- write-host "Compile failed for $unit" -ForegroundColor Red
- }
-}
-
-function run-linker
-{
- param( $linker, $binary, $linker_args )
-
- write-host "`Linking $binary"
- write-host "Linker config:"
- $linker_args | ForEach-Object {
- write-host $_ -ForegroundColor Cyan
- }
-
- $time_taken = Measure-Command {
- & $linker $linker_args 2>&1 | ForEach-Object {
- $color = 'White'
- switch ($_){
- { $_ -match "error" } { $color = 'Red' ; break }
- { $_ -match "warning" } { $color = 'Yellow'; break }
- }
- write-host `t $_ -ForegroundColor $color
- }
- }
-
- if ( Test-Path($binary) ) {
- write-host "$binary linking finished in $($time_taken.TotalMilliseconds) ms"
- }
- else {
- write-host "Linking failed for $binary" -ForegroundColor Red
- }
-}
-
-if ( $vendor -match "clang" )
-{
- # https://clang.llvm.org/docs/ClangCommandLineReference.html
- $flag_all_c = '/TC'
- $flag_all_cpp = '/TP'
- $flag_compile = '-c'
- $flag_color_diagnostics = '-fcolor-diagnostics'
- $flag_no_color_diagnostics = '-fno-color-diagnostics'
- $flag_debug = '-g'
- $flag_debug_codeview = '-gcodeview'
- $flag_define = '-D'
- $flag_exceptions_disabled = '-fno-exceptions'
- $flag_preprocess = '-E'
- $flag_include = '-I'
- $flag_section_data = '-fdata-sections'
- $flag_section_functions = '-ffunction-sections'
- $flag_library = '-l'
- $flag_library_path = '-L'
- $flag_linker = '-Wl,'
- if ( $IsWindows ) {
- $flag_link_dll = '/DLL'
- $flag_link_mapfile = '/MAP:'
- $flag_link_optimize_references = '/OPT:REF'
- }
- if ( $IsLinux ) {
- $flag_link_mapfile = '--Map='
- $flag_link_optimize_references = '--gc-sections'
- }
- $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
- $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
- $flag_link_win_machine_32 = '/MACHINE:X86'
- $flag_link_win_machine_64 = '/MACHINE:X64'
- $flag_link_win_debug = '/DEBUG'
- $flag_link_win_pdb = '/PDB:'
- $flag_link_win_path_output = '/OUT:'
- $flag_no_optimization = '-O0'
- $flag_optimize_fast = '-O2'
- $flag_optimize_size = '-O1'
- $flag_optimize_intrinsics = '-Oi'
- $flag_path_output = '-o'
- $flag_preprocess_non_intergrated = '-no-integrated-cpp'
- $flag_profiling_debug = '-fdebug-info-for-profiling'
- $flag_set_stack_size = '-stack='
- $flag_syntax_only = '-fsyntax-only'
- $flag_target_arch = '-target'
- $flag_wall = '-Wall'
- $flag_warning = '-W'
- $flag_warnings_as_errors = '-Werror'
- $flag_win_nologo = '/nologo'
-
- $ignore_warning_ms_include = 'no-microsoft-include'
- $ignore_warning_return_type_c_linkage = 'no-return-type-c-linkage'
-
- $target_arch = Get-TargetArchClang
-
- $warning_ignores = @(
- $ignore_warning_ms_include,
- $ignore_warning_return_type_c_linkage
- )
-
- # https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170
- $libraries = @(
- 'Kernel32' # For Windows API
- # 'msvcrt', # For the C Runtime (Dynamically Linked)
- # 'libucrt',
- 'libcmt' # For the C Runtime (Static Linkage)
- )
-
- function build-simple
- {
- param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
- Write-Host "build-simple: clang"
-
- $object = $unit -replace '\.cpp', '.obj'
- $map = $unit -replace '\.cpp', '.map'
- $object = join-path $path_build (split-path $object -Leaf)
- $map = join-path $path_build (split-path $map -Leaf)
-
- # The PDB file has to also be time-stamped so that we can reload the DLL at runtime
- $pdb = $binary -replace '\.(exe|dll)$', "_$(get-random).pdb"
-
- $compiler_args += @(
- $flag_no_color_diagnostics,
- $flag_exceptions_disabled,
- $flag_target_arch, $target_arch,
- $flag_wall,
- $flag_preprocess_on_intergrated,
- # $flag_section_data,
- # $flag_section_functions,
- ( $flag_path_output + $object )
- )
- if ( $optimize ) {
- $compiler_args += $flag_optimize_fast
- }
- else {
- $compiler_args += $flag_no_optimization
- }
- if ( $debug ) {
- $compiler_args += ( $flag_define + 'Build_Debug=1' )
- $compiler_args += $flag_debug, $flag_debug_codeview, $flag_profiling_debug
- }
- else {
- $compiler_args += ( $flag_define + 'Build_Debug=0' )
- }
-
- $warning_ignores | ForEach-Object {
- $compiler_args += $flag_warning + $_
- }
- $compiler_args += $includes | ForEach-Object { $flag_include + $_ }
-
- $compiler_args += $flag_compile, $unit
- run-compiler $compiler $unit $compiler_args
-
- $linker_args += @(
- $flag_link_win_machine_64,
- $( $flag_link_win_path_output + $binary )
- )
- if ( $debug ) {
- $linker_args += $flag_link_win_debug
- $linker_args += $flag_link_win_pdb + $pdb
- $linker_args += $flag_link_mapfile + $map
- }
-
- $libraries | ForEach-Object {
- $linker_args += $_ + '.lib'
- }
-
- $linker_args += $object
- run-linker $linker $binary $linker_args
-
- # $compiler_args += $unit
- # $linker_args | ForEach-Object {
- # $compiler_args += $flag_linker + $_
- # }
- # run-compiler $compiler $unit $compiler_args
- }
-
- $compiler = 'clang++'
- $linker = 'lld-link'
-}
-
-if ( $vendor -match "msvc" )
-{
- # https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170
- $flag_all_c = '/TC'
- $flag_all_cpp = '/TP'
- $flag_compile = '/c'
- $flag_debug = '/Zi'
- $flag_define = '/D'
- $flag_exceptions_disabled = '/EHsc-'
- $flag_RTTI_disabled = '/GR-'
- $flag_include = '/I'
- $flag_full_src_path = '/FC'
- $flag_nologo = '/nologo'
- $flag_dll = '/LD'
- $flag_dll_debug = '/LDd'
- $flag_linker = '/link'
- $flag_link_dll = '/DLL'
- $flag_link_no_incremental = '/INCREMENTAL:NO'
- $flag_link_mapfile = '/MAP:'
- $flag_link_optimize_references = '/OPT:REF'
- $flag_link_win_debug = '/DEBUG'
- $flag_link_win_pdb = '/PDB:'
- $flag_link_win_machine_32 = '/MACHINE:X86'
- $flag_link_win_machine_64 = '/MACHINE:X64'
- $flag_link_win_path_output = '/OUT:'
- $flag_link_win_rt_dll = '/MD'
- $flag_link_win_rt_dll_debug = '/MDd'
- $flag_link_win_rt_static = '/MT'
- $flag_link_win_rt_static_debug = '/MTd'
- $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
- $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
- $flag_no_optimization = '/Od'
- $flag_optimize_fast = '/O2'
- $flag_optimize_size = '/O1'
- $flag_optimize_intrinsics = '/Oi'
- $flag_optimized_debug = '/Zo'
- $flag_out_name = '/OUT:'
- $flag_path_interm = '/Fo'
- $flag_path_debug = '/Fd'
- $flag_path_output = '/Fe'
- $flag_preprocess_conform = '/Zc:preprocessor'
- $flag_set_stack_size = '/F'
- $flag_syntax_only = '/Zs'
- $flag_wall = '/Wall'
- $flag_warnings_as_errors = '/WX'
-
- # This works because this project uses a single unit to build
- function build-simple
- {
- param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
- Write-Host "build-simple: msvc"
-
- $object = $unit -replace '\.(cpp)$', '.obj'
- $map = $unit -replace '\.(cpp)$', '.map'
- $object = join-path $path_build (split-path $object -Leaf)
- $map = join-path $path_build (split-path $map -Leaf)
-
- # The PDB file has to also be time-stamped so that we can reload the DLL at runtime
- $pdb = $binary -replace '\.(exe|dll)$', "_$(get-random).pdb"
-
- $compiler_args += @(
- $flag_nologo,
- # $flag_all_cpp,
- $flag_exceptions_disabled,
- ( $flag_define + '_HAS_EXCEPTIONS=0' ),
- $flag_RTTI_disabled,
- $flag_preprocess_conform,
- $flag_full_src_path,
- ( $flag_path_interm + $path_build + '\' ),
- ( $flag_path_output + $path_build + '\' )
- )
-
- if ( $optimize ) {
- $compiler_args += $flag_optimize_fast
- }
- else {
- $compiler_args += $flag_no_optimization
- }
-
- if ( $debug )
- {
- $compiler_args += $flag_debug
- $compiler_args += ( $flag_define + 'Build_Debug=1' )
- $compiler_args += ( $flag_path_debug + $path_build + '\' )
- $compiler_args += $flag_link_win_rt_static_debug
-
- if ( $optimize ) {
- $compiler_args += $flag_optimized_debug
- }
- }
- else {
- $compiler_args += ( $flag_define + 'Build_Debug=0' )
- $compiler_args += $flag_link_win_rt_static
- }
- $compiler_args += $includes | ForEach-Object { $flag_include + $_ }
-
- $compiler_args += $flag_compile, $unit
- run-compiler $compiler $unit $compiler_args
-
- $linker_args += @(
- $flag_nologo,
- $flag_link_win_machine_64,
- $flag_link_no_incremental,
- ( $flag_link_win_path_output + $binary )
- )
- if ( $debug ) {
- $linker_args += $flag_link_win_debug
- $linker_args += $flag_link_win_pdb + $pdb
- $linker_args += $flag_link_mapfile + $map
- }
- else {
- }
-
- $linker_args += $object
- run-linker $linker $binary $linker_args
-
- # $compiler_args += $unit
- # $compiler_args += $flag_linker
- # $compiler_args += $linker_args
- # run-compiler $compiler $unit $compiler_args
- }
-
- $compiler = 'cl'
- $linker = 'link'
-}
-#endregion Configuration
+# Load up toolchain configuraion
+. $config_toolchain
#region Building
$path_project = Join-Path $path_root 'project'
@@ -450,31 +108,31 @@ else {
$compiler_args += ( $flag_define + 'Build_Development=0' )
}
-if ( $engine )
+function build-engine
{
$path_pdb_lock = Join-Path $path_binaries 'handmade_engine.pdb.lock'
- New-Item $path_pdb_lock -ItemType File -Force -Verbose
+ New-Item $path_pdb_lock -ItemType File -Force
# Delete old PDBs
[Array]$pdb_files = Get-ChildItem -Path $path_binaries -Filter "handmade_engine_*.pdb"
foreach ($file in $pdb_files) {
Remove-Item -Path $file.FullName -Force
- Write-Host "Deleted $file" -ForegroundColor Green
+ if ( $verbose ) { Write-Host "Deleted $file" -ForegroundColor Green }
}
- $engine_compiler_args = $compiler_args
- $engine_compiler_args += ($flag_define + 'Build_DLL=1' )
+ $local:compiler_args = $script:compiler_args
+ $compiler_args += ($flag_define + 'Build_DLL=1' )
if ( $vendor -eq 'msvc' )
{
- $engine_compiler_args += ($flag_define + 'Engine_API=__declspec(dllexport)')
+ $compiler_args += ($flag_define + 'Engine_API=__declspec(dllexport)')
}
if ( $vendor -eq 'clang' )
{
- $engine_compiler_args += ($flag_define + 'Engine_API=__attribute__((visibility("default")))')
+ $compiler_args += ($flag_define + 'Engine_API=__attribute__((visibility("default")))')
}
- $linker_args = @(
+ $local:linker_args = @(
$flag_link_dll
# $flag_link_optimize_references
)
@@ -482,124 +140,167 @@ if ( $engine )
$unit = Join-Path $path_project 'handmade_engine.cpp'
$dynamic_library = Join-Path $path_binaries 'handmade_engine.dll'
- build-simple $includes $engine_compiler_args $linker_args $unit $dynamic_library
+ build-simple $includes $compiler_args $linker_args $unit $dynamic_library
- if ( Test-Path $dynamic_library )
- {
- # $data_path = Join-Path $path_data 'handmade_engine.dll'
- # move-item $dynamic_library $data_path -Force
- $path_lib = $dynamic_library -replace '\.dll', '.lib'
- $path_exp = $dynamic_library -replace '\.dll', '.exp'
- Remove-Item $path_lib -Force
- if ( Test-Path $path_exp ) { Remove-Item $path_exp -Force }
+ Remove-Item $path_pdb_lock -Force
- # We need to generate the symbol table so that we can lookup the symbols we need when loading/reloading the library at runtime.
- # This is done by sifting through the emitter.map file from the linker for the base symbol names
- # and mapping them to their found decorated name
+ #region CodeGen Post-Build
+ if ( -not $handmade_process_active ) {
+ # Create the symbol table
+ if ( Test-Path $dynamic_library )
+ {
+ # $data_path = Join-Path $path_data 'handmade_engine.dll'
+ # move-item $dynamic_library $data_path -Force
+ $path_lib = $dynamic_library -replace '\.dll', '.lib'
+ $path_exp = $dynamic_library -replace '\.dll', '.exp'
+ Remove-Item $path_lib -Force
+ if ( Test-Path $path_exp ) { Remove-Item $path_exp -Force }
- # Initialize the hashtable with the desired order of symbols
- $engine_symbols = [ordered]@{
- 'on_module_reload' = $null
- 'startup' = $null
- 'shutdown' = $null
- 'update_and_render' = $null
- 'update_audio' = $null
- }
+ # We need to generate the symbol table so that we can lookup the symbols we need when loading/reloading the library at runtime.
+ # This is done by sifting through the emitter.map file from the linker for the base symbol names
+ # and mapping them to their found decorated name
- $path_engine_obj = Join-Path $path_build 'handmade_engine.obj'
- $path_engine_map = Join-Path $path_build 'handmade_engine.map'
- $maxNameLength = ($engine_symbols.Keys | Measure-Object -Property Length -Maximum).Maximum
-
- Get-Content -Path $path_engine_map | ForEach-Object {
- # If all symbols are found, exit the loop
- if ($engine_symbols.Values -notcontains $null) {
- return
- }
- # Split the line into tokens
- $tokens = $_ -split '\s+', 4
- # Extract only the decorated name using regex for both MSVC and Clang conventions
- $decoratedName = ($tokens[2] -match '(\?[\w@]+|_Z[\w@]+)') ? $matches[1] : $null
-
- # Check the origin of the symbol
- # If the origin matches 'handmade_engine.obj', then process the symbol
- $originParts = $tokens[3] -split '\s+'
- $origin = if ($originParts.Count -eq 3) { $originParts[2] } else { $originParts[1] }
-
- # Diagnostic output
- if ( $false -and $decoratedName) {
- write-host "Found decorated name: $decoratedName" -ForegroundColor Yellow
- write-host "Origin : $origin" -ForegroundColor Yellow
+ # Initialize the hashtable with the desired order of symbols
+ $engine_symbols = [ordered]@{
+ 'on_module_reload' = $null
+ 'startup' = $null
+ 'shutdown' = $null
+ 'update_and_render' = $null
+ 'update_audio' = $null
}
- if ($origin -like 'handmade_engine.obj') {
- # Check each regular name against the current line
- $engine_symbols.Keys | Where-Object { $engine_symbols[$_] -eq $null } | ForEach-Object {
- if ($decoratedName -like "*$_*") {
- $engine_symbols[$_] = $decoratedName
+ $path_engine_obj = Join-Path $path_build 'handmade_engine.obj'
+ $path_engine_map = Join-Path $path_build 'handmade_engine.map'
+ $maxNameLength = ($engine_symbols.Keys | Measure-Object -Property Length -Maximum).Maximum
+
+ Get-Content -Path $path_engine_map | ForEach-Object {
+ # If all symbols are found, exit the loop
+ if ($engine_symbols.Values -notcontains $null) {
+ return
+ }
+ # Split the line into tokens
+ $tokens = $_ -split '\s+', 4
+ # Extract only the decorated name using regex for both MSVC and Clang conventions
+ $decoratedName = ($tokens[2] -match '(\?[\w@]+|_Z[\w@]+)') ? $matches[1] : $null
+
+ # Check the origin of the symbol
+ # If the origin matches 'handmade_engine.obj', then process the symbol
+ $originParts = $tokens[3] -split '\s+'
+ $origin = if ($originParts.Count -eq 3) { $originParts[2] } else { $originParts[1] }
+
+ # Diagnostic output
+ if ( $false -and $decoratedName) {
+ write-host "Found decorated name: $decoratedName" -ForegroundColor Yellow
+ write-host "Origin : $origin" -ForegroundColor Yellow
+ }
+
+ if ($origin -like 'handmade_engine.obj') {
+ # Check each regular name against the current line
+ $engine_symbols.Keys | Where-Object { $engine_symbols[$_] -eq $null } | ForEach-Object {
+ if ($decoratedName -like "*$_*") {
+ $engine_symbols[$_] = $decoratedName
+ }
}
}
}
+
+ if ($verbose) { write-host "Engine Symbol Table:" -ForegroundColor Green }
+ $engine_symbols.GetEnumerator() | ForEach-Object {
+ $paddedName = $_.Key.PadRight($maxNameLength)
+ $decoratedName = $_.Value
+ if ($verbose ) { write-host "`t$paddedName, $decoratedName" -ForegroundColor Green }
+ }
+
+ # Write the symbol table to a file
+ $path_engine_symbols = Join-Path $path_build 'handmade_engine.symbols'
+ $engine_symbols.Values | Out-File -Path $path_engine_symbols
}
- write-host "Engine Symbol Table:" -ForegroundColor Green
- $engine_symbols.GetEnumerator() | ForEach-Object {
- $paddedName = $_.Key.PadRight($maxNameLength)
- $decoratedName = $_.Value
- write-host "`t$paddedName, $decoratedName" -ForegroundColor Green
- }
-
- # Write the symbol table to a file
- $path_engine_symbols = Join-Path $path_build 'handmade_engine.symbols'
- $engine_symbols.Values | Out-File -Path $path_engine_symbols
- }
-
- Remove-Item $path_pdb_lock -Force -Verbose
-
- #region CodeGen
- if ( $handmade_process_active -eq $null ) {
# Delete old PDBs
$pdb_files = Get-ChildItem -Path $path_build -Filter "engine_postbuild_gen_*.pdb"
foreach ($file in $pdb_files) {
Remove-Item -Path $file.FullName -Force
- Write-Host "Deleted $file" -ForegroundColor Green
+ if ($verbose) { Write-Host "Deleted $file" -ForegroundColor Green }
}
- $engine_codegen_compiler_args = @()
- $engine_codegen_compiler_args += ( $flag_define + 'GEN_TIME' )
+ $compiler_args = @()
+ $compiler_args += ( $flag_define + 'GEN_TIME' )
- $engine_codegen_linker_args = @(
+ $linker_args = @(
$flag_link_win_subsystem_console
)
$unit = Join-Path $path_codegen 'engine_postbuild_gen.cpp'
$executable = Join-Path $path_build 'engine_postbuild_gen.exe'
- build-simple $includes $engine_codegen_compiler_args $engine_codegen_linker_args $unit $executable
- write-host
+ build-simple $includes $compiler_args $linker_args $unit $executable
Push-Location $path_build
- & $executable
+ $time_taken = Measure-Command {
+ & $executable 2>&1 | ForEach-Object {
+ write-host `t $_ -ForegroundColor Green
+ }
+ }
Pop-Location
$path_generated_file = Join-Path $path_build 'engine_symbol_table.hpp'
move-item $path_generated_file (join-path $path_gen (split-path $path_generated_file -leaf)) -Force
}
- #endregion CodeGen
+}
+if ( $engine ) {
+ build-engine
}
-if ( $platform )
+function build-platform
{
+ # CodeGen Pre-Build
+ if ( $true )
+ {
+ # Delete old PDBs
+ $pdb_files = Get-ChildItem -Path $path_build -Filter "platform_gen_*.pdb"
+ foreach ($file in $pdb_files) {
+ Remove-Item -Path $file.FullName -Force
+ if ( $verbose ) { Write-Host "Deleted $file" -ForegroundColor Green }
+ }
+
+ $path_platform_gen = Join-Path $path_platform 'gen'
+
+ if ( -not (Test-Path $path_platform_gen) ) {
+ New-Item $path_platform_gen -ItemType Directory
+ }
+
+ $local:compiler_args = @()
+ $compiler_args += ( $flag_define + 'GEN_TIME' )
+
+ $local:linker_args = @(
+ $flag_link_win_subsystem_console
+ )
+
+ $unit = Join-Path $path_codegen 'platform_gen.cpp'
+ $executable = Join-Path $path_build 'platform_gen.exe'
+
+ build-simple $includes $compiler_args $linker_args $unit $executable
+
+ Push-Location $path_platform
+ $time_taken = Measure-Command {
+ & $executable 2>&1 | ForEach-Object {
+ write-host `t $_ -ForegroundColor Green
+ }
+ }
+ Pop-Location
+ }
+
# Delete old PDBs
$pdb_files = Get-ChildItem -Path $path_binaries -Filter "handmade_win32_*.pdb"
foreach ($file in $pdb_files) {
Remove-Item -Path $file.FullName -Force
- Write-Host "Deleted $file" -ForegroundColor Green
+ if ( $verbose ) { Write-Host "Deleted $file" -ForegroundColor Green }
}
- $platform_compiler_args = $compiler_args
- $platform_compiler_args += ($flag_define + 'Build_DLL=0' )
+ $local:compiler_args = $script:compiler_args
+ $compiler_args += ($flag_define + 'Build_DLL=0' )
- $linker_args = @(
+ $local:linker_args = @(
$lib_gdi32,
# $lib_xinput,
$lib_user32,
@@ -614,7 +315,7 @@ if ( $platform )
$unit = Join-Path $path_project 'handmade_win32.cpp'
$executable = Join-Path $path_binaries 'handmade_win32.exe'
- build-simple $includes $platform_compiler_args $linker_args $unit $executable
+ build-simple $includes $compiler_args $linker_args $unit $executable
# if ( Test-Path $executable )
# {
@@ -622,6 +323,9 @@ if ( $platform )
# move-item $executable $data_path -Force
# }
}
+if ( $platform ) {
+ build-platform
+}
$path_jsl_dll = Join-Path $path_binaries 'JoyShockLibrary.dll'
if ( (Test-Path $path_jsl_dll) -eq $false )
@@ -631,5 +335,12 @@ if ( (Test-Path $path_jsl_dll) -eq $false )
}
#endregion Handmade Runtime
+$include = @(
+ '*.cpp'
+ '*.hpp'
+)
+format-cpp $path_gen $include
+format-cpp (Join-Path $path_platform 'gen' ) $include
+
Pop-Location
#endregion Building
diff --git a/scripts/handmade.rdbg b/scripts/handmade.rdbg
index 7ddac53..14c6c47 100644
Binary files a/scripts/handmade.rdbg and b/scripts/handmade.rdbg differ
diff --git a/scripts/helpers/configure_toolchain.ps1 b/scripts/helpers/configure_toolchain.ps1
new file mode 100644
index 0000000..48e6de1
--- /dev/null
+++ b/scripts/helpers/configure_toolchain.ps1
@@ -0,0 +1,356 @@
+# This is meant to be used with build.ps1, and is not a standalone script.
+
+if ($IsWindows) {
+ # This HandmadeHero implementation is only designed for 64-bit systems
+ & $devshell -arch amd64
+}
+
+if ( $vendor -eq $null ) {
+ write-host "No vendor specified, assuming clang available"
+ $compiler = "clang"
+}
+
+write-host "Building HandmadeHero with $vendor"
+
+if ( $dev ) {
+ if ( $debug -eq $null ) {
+ $debug = $true
+ }
+
+ if ( $optimize -eq $null ) {
+ $optimize = $false
+ }
+}
+
+function run-compiler
+{
+ param( $compiler, $unit, $compiler_args )
+
+ if ( $analysis ) {
+ $compiler_args += $flag_syntax_only
+ }
+
+ write-host "`Compiling $unit"
+ if ( $verbose ) {
+ write-host "Compiler config:"
+ $compiler_args | ForEach-Object {
+ write-host $_ -ForegroundColor Cyan
+ }
+ }
+
+ $time_taken = Measure-Command {
+ & $compiler $compiler_args 2>&1 | ForEach-Object {
+ $color = 'White'
+ switch ($_){
+ { $_ -match "error" } { $color = 'Red' ; break }
+ { $_ -match "warning" } { $color = 'Yellow'; break }
+ }
+ write-host `t $_ -ForegroundColor $color
+ }
+ }
+
+ if ( Test-Path($unit) ) {
+ write-host "$unit compile finished in $($time_taken.TotalMilliseconds) ms`n"
+ }
+ else {
+ write-host "Compile failed for $unit`n" -ForegroundColor Red
+ }
+}
+
+function run-linker
+{
+ param( $linker, $binary, $linker_args )
+
+ write-host "`Linking $binary"
+ if ( $verbose ) {
+ write-host "Linker config:"
+ $linker_args | ForEach-Object {
+ write-host $_ -ForegroundColor Cyan
+ }
+ }
+
+ $time_taken = Measure-Command {
+ & $linker $linker_args 2>&1 | ForEach-Object {
+ $color = 'White'
+ switch ($_){
+ { $_ -match "error" } { $color = 'Red' ; break }
+ { $_ -match "warning" } { $color = 'Yellow'; break }
+ }
+ write-host `t $_ -ForegroundColor $color
+ }
+ }
+
+ if ( Test-Path($binary) ) {
+ write-host "$binary linking finished in $($time_taken.TotalMilliseconds) ms`n"
+ }
+ else {
+ write-host "Linking failed for $binary`n" -ForegroundColor Red
+ }
+}
+
+if ( $vendor -match "clang" )
+{
+ # https://clang.llvm.org/docs/ClangCommandLineReference.html
+ $flag_all_c = '/TC'
+ $flag_all_cpp = '/TP'
+ $flag_compile = '-c'
+ $flag_color_diagnostics = '-fcolor-diagnostics'
+ $flag_no_color_diagnostics = '-fno-color-diagnostics'
+ $flag_debug = '-g'
+ $flag_debug_codeview = '-gcodeview'
+ $flag_define = '-D'
+ $flag_exceptions_disabled = '-fno-exceptions'
+ $flag_preprocess = '-E'
+ $flag_include = '-I'
+ $flag_section_data = '-fdata-sections'
+ $flag_section_functions = '-ffunction-sections'
+ $flag_library = '-l'
+ $flag_library_path = '-L'
+ $flag_linker = '-Wl,'
+ if ( $IsWindows ) {
+ $flag_link_dll = '/DLL'
+ $flag_link_mapfile = '/MAP:'
+ $flag_link_optimize_references = '/OPT:REF'
+ }
+ if ( $IsLinux ) {
+ $flag_link_mapfile = '--Map='
+ $flag_link_optimize_references = '--gc-sections'
+ }
+ $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
+ $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
+ $flag_link_win_machine_32 = '/MACHINE:X86'
+ $flag_link_win_machine_64 = '/MACHINE:X64'
+ $flag_link_win_debug = '/DEBUG'
+ $flag_link_win_pdb = '/PDB:'
+ $flag_link_win_path_output = '/OUT:'
+ $flag_no_optimization = '-O0'
+ $flag_optimize_fast = '-O2'
+ $flag_optimize_size = '-O1'
+ $flag_optimize_intrinsics = '-Oi'
+ $flag_path_output = '-o'
+ $flag_preprocess_non_intergrated = '-no-integrated-cpp'
+ $flag_profiling_debug = '-fdebug-info-for-profiling'
+ $flag_set_stack_size = '-stack='
+ $flag_syntax_only = '-fsyntax-only'
+ $flag_target_arch = '-target'
+ $flag_wall = '-Wall'
+ $flag_warning = '-W'
+ $flag_warnings_as_errors = '-Werror'
+ $flag_win_nologo = '/nologo'
+
+ $ignore_warning_ms_include = 'no-microsoft-include'
+ $ignore_warning_return_type_c_linkage = 'no-return-type-c-linkage'
+
+ $target_arch = Get-TargetArchClang
+
+ $warning_ignores = @(
+ $ignore_warning_ms_include,
+ $ignore_warning_return_type_c_linkage
+ )
+
+ # https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-170
+ $libraries = @(
+ 'Kernel32' # For Windows API
+ # 'msvcrt', # For the C Runtime (Dynamically Linked)
+ # 'libucrt',
+ 'libcmt' # For the C Runtime (Static Linkage)
+ )
+
+ function build-simple
+ {
+ param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
+ #Write-Host "build-simple: clang"
+
+ $object = $unit -replace '\.cpp', '.obj'
+ $map = $unit -replace '\.cpp', '.map'
+ $object = join-path $path_build (split-path $object -Leaf)
+ $map = join-path $path_build (split-path $map -Leaf)
+
+ # The PDB file has to also be time-stamped so that we can reload the DLL at runtime
+ $pdb = $binary -replace '\.(exe|dll)$', "_$(get-random).pdb"
+
+ $compiler_args += @(
+ $flag_no_color_diagnostics,
+ $flag_exceptions_disabled,
+ $flag_target_arch, $target_arch,
+ $flag_wall,
+ $flag_preprocess_on_intergrated,
+ # $flag_section_data,
+ # $flag_section_functions,
+ ( $flag_path_output + $object )
+ )
+ if ( $optimize ) {
+ $compiler_args += $flag_optimize_fast
+ }
+ else {
+ $compiler_args += $flag_no_optimization
+ }
+ if ( $debug ) {
+ $compiler_args += ( $flag_define + 'Build_Debug=1' )
+ $compiler_args += $flag_debug, $flag_debug_codeview, $flag_profiling_debug
+ }
+ else {
+ $compiler_args += ( $flag_define + 'Build_Debug=0' )
+ }
+
+ $warning_ignores | ForEach-Object {
+ $compiler_args += $flag_warning + $_
+ }
+ $compiler_args += $includes | ForEach-Object { $flag_include + $_ }
+
+ $compiler_args += $flag_compile, $unit
+ run-compiler $compiler $unit $compiler_args
+
+ $linker_args += @(
+ $flag_link_win_machine_64,
+ $( $flag_link_win_path_output + $binary )
+ )
+ if ( $debug ) {
+ $linker_args += $flag_link_win_debug
+ $linker_args += $flag_link_win_pdb + $pdb
+ $linker_args += $flag_link_mapfile + $map
+ }
+
+ $libraries | ForEach-Object {
+ $linker_args += $_ + '.lib'
+ }
+
+ $linker_args += $object
+ run-linker $linker $binary $linker_args
+
+ # $compiler_args += $unit
+ # $linker_args | ForEach-Object {
+ # $compiler_args += $flag_linker + $_
+ # }
+ # run-compiler $compiler $unit $compiler_args
+ }
+
+ $compiler = 'clang++'
+ $linker = 'lld-link'
+}
+
+if ( $vendor -match "msvc" )
+{
+ # https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170
+ $flag_all_c = '/TC'
+ $flag_all_cpp = '/TP'
+ $flag_compile = '/c'
+ $flag_debug = '/Zi'
+ $flag_define = '/D'
+ $flag_exceptions_disabled = '/EHsc-'
+ $flag_RTTI_disabled = '/GR-'
+ $flag_include = '/I'
+ $flag_full_src_path = '/FC'
+ $flag_nologo = '/nologo'
+ $flag_dll = '/LD'
+ $flag_dll_debug = '/LDd'
+ $flag_linker = '/link'
+ $flag_link_dll = '/DLL'
+ $flag_link_no_incremental = '/INCREMENTAL:NO'
+ $flag_link_mapfile = '/MAP:'
+ $flag_link_optimize_references = '/OPT:REF'
+ $flag_link_win_debug = '/DEBUG'
+ $flag_link_win_pdb = '/PDB:'
+ $flag_link_win_machine_32 = '/MACHINE:X86'
+ $flag_link_win_machine_64 = '/MACHINE:X64'
+ $flag_link_win_path_output = '/OUT:'
+ $flag_link_win_rt_dll = '/MD'
+ $flag_link_win_rt_dll_debug = '/MDd'
+ $flag_link_win_rt_static = '/MT'
+ $flag_link_win_rt_static_debug = '/MTd'
+ $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
+ $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
+ $flag_no_optimization = '/Od'
+ $flag_optimize_fast = '/O2'
+ $flag_optimize_size = '/O1'
+ $flag_optimize_intrinsics = '/Oi'
+ $flag_optimized_debug = '/Zo'
+ $flag_out_name = '/OUT:'
+ $flag_path_interm = '/Fo'
+ $flag_path_debug = '/Fd'
+ $flag_path_output = '/Fe'
+ $flag_preprocess_conform = '/Zc:preprocessor'
+ $flag_set_stack_size = '/F'
+ $flag_syntax_only = '/Zs'
+ $flag_wall = '/Wall'
+ $flag_warnings_as_errors = '/WX'
+
+ # This works because this project uses a single unit to build
+ function build-simple
+ {
+ param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary )
+ #Write-Host "build-simple: msvc"
+
+ $object = $unit -replace '\.(cpp)$', '.obj'
+ $map = $unit -replace '\.(cpp)$', '.map'
+ $object = join-path $path_build (split-path $object -Leaf)
+ $map = join-path $path_build (split-path $map -Leaf)
+
+ # The PDB file has to also be time-stamped so that we can reload the DLL at runtime
+ $pdb = $binary -replace '\.(exe|dll)$', "_$(get-random).pdb"
+
+ $compiler_args += @(
+ $flag_nologo,
+ # $flag_all_cpp,
+ $flag_exceptions_disabled,
+ ( $flag_define + '_HAS_EXCEPTIONS=0' ),
+ $flag_RTTI_disabled,
+ $flag_preprocess_conform,
+ $flag_full_src_path,
+ ( $flag_path_interm + $path_build + '\' ),
+ ( $flag_path_output + $path_build + '\' )
+ )
+
+ if ( $optimize ) {
+ $compiler_args += $flag_optimize_fast
+ }
+ else {
+ $compiler_args += $flag_no_optimization
+ }
+
+ if ( $debug )
+ {
+ $compiler_args += $flag_debug
+ $compiler_args += ( $flag_define + 'Build_Debug=1' )
+ $compiler_args += ( $flag_path_debug + $path_build + '\' )
+ $compiler_args += $flag_link_win_rt_static_debug
+
+ if ( $optimize ) {
+ $compiler_args += $flag_optimized_debug
+ }
+ }
+ else {
+ $compiler_args += ( $flag_define + 'Build_Debug=0' )
+ $compiler_args += $flag_link_win_rt_static
+ }
+ $compiler_args += $includes | ForEach-Object { $flag_include + $_ }
+
+ $compiler_args += $flag_compile, $unit
+ run-compiler $compiler $unit $compiler_args
+
+ $linker_args += @(
+ $flag_nologo,
+ $flag_link_win_machine_64,
+ $flag_link_no_incremental,
+ ( $flag_link_win_path_output + $binary )
+ )
+ if ( $debug ) {
+ $linker_args += $flag_link_win_debug
+ $linker_args += $flag_link_win_pdb + $pdb
+ $linker_args += $flag_link_mapfile + $map
+ }
+ else {
+ }
+
+ $linker_args += $object
+ run-linker $linker $binary $linker_args
+
+ # $compiler_args += $unit
+ # $compiler_args += $flag_linker
+ # $compiler_args += $linker_args
+ # run-compiler $compiler $unit $compiler_args
+ }
+
+ $compiler = 'cl'
+ $linker = 'link'
+}
diff --git a/scripts/helpers/format_cpp.psm1 b/scripts/helpers/format_cpp.psm1
new file mode 100644
index 0000000..fdfc293
--- /dev/null
+++ b/scripts/helpers/format_cpp.psm1
@@ -0,0 +1,26 @@
+# format_cpp.psm1
+
+function format-cpp
+{
+ param( $path, $include, $exclude )
+
+ # Format generated gencpp
+ Write-Host "Beginning format"
+ $formatParams = @(
+ '-i' # In-place
+ '-style=file:./scripts/.clang-format'
+ '-verbose'
+ )
+
+ $targetFiles = @(
+ Get-ChildItem -Recurse -Path $path -Include $include -Exclude $exclude
+ | Select-Object -ExpandProperty FullName
+ )
+
+ $time_taken = Measure-Command {
+ clang-format $formatParams $targetFiles
+ }
+ Write-Host "Formatting complete in $($time_taken.TotalMilliseconds) ms`n"
+}
+
+Export-ModuleMember -Function format-cpp