diff --git a/.gitignore b/.gitignore index c46cc39..cdf0a29 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,10 @@ vc140.pdb build **/*.dll +**/*.exe +**/*.pdb + +**/*.hmi data/test.out data/handmade_engine.symbols data/handmade_win32.exe diff --git a/.vscode/bookmarks.json b/.vscode/bookmarks.json new file mode 100644 index 0000000..1ecd313 --- /dev/null +++ b/.vscode/bookmarks.json @@ -0,0 +1,39 @@ +{ + "files": [ + { + "path": "project/platform/win32_platform.cpp", + "bookmarks": [ + { + "line": 57, + "column": 0, + "label": "Struct Defs" + }, + { + "line": 96, + "column": 0, + "label": "Static Data" + }, + { + "line": 629, + "column": 0, + "label": "Timing" + }, + { + "line": 1479, + "column": 4, + "label": "Main Loop : Audio Processing" + }, + { + "line": 1598, + "column": 2, + "label": "Main Loop : Timing Update" + }, + { + "line": 1682, + "column": 0, + "label": "Main Loop : End" + } + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index a1c9885..29e0d72 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,29 @@ The build is done in two stages: 1. ~~Build and run metaprogram to scan and generate dependent code.~~ (Not needed yet) 2. Build the handmade hero runtime. +## Milestones + +## Win32 Platform Layer + +- [x] Day 001 +- [x] Day 002 +- [x] Day 003 +- [x] Day 004 +- [x] Day 005 +- [x] Day 006 +- [x] Day 007 +- [x] Day 008 +- [x] Day 009 +- [x] Day 010 +- [x] Day 011 +- [x] Day 012 +- [x] Day 013 +- [x] Day 014 +- [x] Day 015 + + ## Gallery +![img](https://files.catbox.moe/ruv97s.gif) ![img](https://files.catbox.moe/9zau4s.png) ![img](https://files.catbox.moe/b7ifa8.png) diff --git a/data/binaries/handmade_engine.symbols b/data/binaries/handmade_engine.symbols new file mode 100644 index 0000000..fb9320c --- /dev/null +++ b/data/binaries/handmade_engine.symbols @@ -0,0 +1,5 @@ +?on_module_reload@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z +?startup@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z +?shutdown@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z +?update_and_render@engine@@YAXPEAUInputState@1@PEAUOffscreenBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@@Z +?update_audio@engine@@YAXPEAUAudioBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@@Z diff --git a/data/test_input.hmi b/data/test_input.hmi new file mode 100644 index 0000000..84406a2 Binary files /dev/null and b/data/test_input.hmi differ diff --git a/docs/Day 022.md b/docs/Day 022.md index d8aff3a..fe11d5b 100644 --- a/docs/Day 022.md +++ b/docs/Day 022.md @@ -1,2 +1,4 @@ # Day 22 +There is an issue with the hot-reload on vscode, does not show up in vs2022 debugger nor remedybg so I won't bother with it. +(Debugging hot-reloaded module doesn't work with vscode's debugger) diff --git a/docs/Day 023.md b/docs/Day 023.md new file mode 100644 index 0000000..72537ad --- /dev/null +++ b/docs/Day 023.md @@ -0,0 +1,11 @@ +# Day 23 + +Getting state saving was nice. + +I decided to expose the file inteface through the platform API permanently, so that I can use it for other things later on. + +I'm changing my varaible naming convention in structus from UpperCamel to lower_snake_case. +I realized that semantic highlighting covers any contextual issues along with editor intellisense. +Even at worst case with public/protected/private specifers intellisense covers it. + +So I'm going with the option that keeps my naming convention consistent and more flexible to use with Casey's exploratory programming style. diff --git a/project/engine/engine.cpp b/project/engine/engine.cpp index 64c0387..1c0f3f4 100644 --- a/project/engine/engine.cpp +++ b/project/engine/engine.cpp @@ -5,6 +5,40 @@ NS_ENGINE_BEGIN +#define pressed( btn ) (btn.EndedDown && btn.HalfTransitions == 1) + +// Used to determine if analog input is at move threshold +constexpr f32 analog__move_threshold = 0.5f; + +struct EngineActions +{ + b32 move_up = false; + b32 move_down = false; + b32 move_left = false; + b32 move_right = false; + + b32 loop_mode = false; + + b32 raise_volume = false; + b32 lower_volume = false; + b32 raise_tone_hz = false; + b32 lower_tone_hz = false; + + b32 toggle_wave_tone = false; + + b32 pause_renderer = false; +}; + +struct PlayerActions +{ + s32 player_x_move_digital = 0; + s32 player_y_move_digital = 0; + f32 player_x_move_analog = 0; + f32 player_y_move_analog = 0; + + b32 jump = false; +}; + struct EngineState { s32 WaveToneHz; @@ -16,8 +50,17 @@ struct EngineState f32 SampleWaveSineTime; b32 SampleWaveSwitch; + + s32 InputRecordingIndex; + s32 InputPlayingIndex; + + platform::File ActiveInputRecordingFile; + platform::File ActivePlaybackFile; + + hh::Memory game_memory; }; + using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer ); internal s16 @@ -117,6 +160,209 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) } } +internal void +render_player( OffscreenBuffer* buffer, s32 pos_x, s32 pos_y ) +{ + u8* end_of_buffer = rcast(u8*, buffer->Memory) + - buffer->BytesPerPixel * buffer->Width + + buffer->Pitch * buffer->Height; + + s32 top = pos_y; + s32 bottom = pos_y + 10; + + u32 color = 0xFFFFFFFF; + + for ( s32 coord_x = pos_x; coord_x < (pos_x+ 10); ++ coord_x ) + { + u8* + pixel_byte = rcast(u8*, buffer->Memory); + pixel_byte += coord_x * buffer->BytesPerPixel; + pixel_byte += top * buffer->Pitch; + + for ( s32 coord_y = top; coord_y < bottom; ++ coord_y ) + { + if ( pixel_byte < buffer->Memory || pixel_byte >= end_of_buffer ) + continue; + + s32* pixel = rcast(s32*, pixel_byte); + *pixel = color; + + pixel_byte += buffer->Pitch; + } + } +} + +internal +void begin_recording_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api ) +{ + state->ActiveInputRecordingFile.Path = str_ascii("test_input.hmi"); + state->InputRecordingIndex = 1; + + // TODO(Ed) : If game persist memory is larger than 4 gb, this will need to be done in chunks... + platform_api->file_write_content( & state->ActiveInputRecordingFile, scast(u32, state->game_memory.PersistentSize), state->game_memory.Persistent ); +} + +internal +void end_recording_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api ) +{ + platform_api->file_close( & state->ActiveInputRecordingFile ); + state->InputRecordingIndex = 0; +} + +internal +void begin_playback_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api ) +{ + Str file_name = str_ascii("test_input.hmi"); + if ( platform_api->file_check_exists( file_name ) ) + { + state->ActivePlaybackFile.Path = str_ascii("test_input.hmi"); + state->InputPlayingIndex = 1; + } + + if ( state->ActiveInputRecordingFile.OpaqueHandle == nullptr ) + { + platform_api->file_read_stream( & state->ActivePlaybackFile, scast(u32, state->game_memory.PersistentSize), state->game_memory.Persistent ); + } +} + +internal +void end_playback_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api ) +{ + platform_api->file_rewind( & state->ActivePlaybackFile ); + platform_api->file_read_stream( & state->ActivePlaybackFile, scast(u32, state->game_memory.PersistentSize), state->game_memory.Persistent ); + platform_api->file_close( & state->ActivePlaybackFile ); + + state->InputPlayingIndex = 0; +} + +InputStateSnapshot input_state_snapshot( InputState* input ) +{ + InputStateSnapshot snapshot = {}; + for ( s32 idx = 0; idx < array_count( snapshot.Controllers ); ++ idx ) + { + ControllerState* controller = & input->Controllers[idx]; + if ( controller == nullptr ) + continue; + + if ( controller->DSPad ) + snapshot.Controllers[idx].DSPad = *controller->DSPad; + + if ( controller->XPad ) + snapshot.Controllers[idx].XPad = *controller->XPad; + + if ( controller->Keyboard ) + { + snapshot.Controllers[idx].Keyboard = *controller->Keyboard; + } + + if ( controller->Mouse ) + snapshot.Controllers[idx].Mouse = *controller->Mouse; + } + return snapshot; +} + +internal +void record_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api ) +{ + InputStateSnapshot snapshot = input_state_snapshot( input ); + if ( platform_api->file_write_stream( & state->ActiveInputRecordingFile, sizeof(snapshot), &snapshot ) == 0 ) + { + // TODO(Ed) : Logging + } +} + +internal +void play_input( EngineState* state, InputState* input, platform::ModuleAPI* platform_api ) +{ + InputStateSnapshot new_input; + if ( platform_api->file_read_stream( & state->ActivePlaybackFile, sizeof(InputStateSnapshot), & new_input ) == 0 ) + { + end_playback_input( state, input, platform_api ); + begin_playback_input( state, input, platform_api ); + return; + } + + for ( s32 idx = 0; idx < array_count( new_input.Controllers ); ++ idx ) + { + ControllerState* controller = & input->Controllers[idx]; + if ( controller == nullptr ) + continue; + + if ( controller->DSPad ) + *controller->DSPad = new_input.Controllers[idx].DSPad; + + if ( controller->XPad ) + *controller->XPad = new_input.Controllers[idx].XPad; + + if ( controller->Keyboard ) + { + *controller->Keyboard = new_input.Controllers[idx].Keyboard; + printf("keyboard D key: %d\n", controller->Keyboard->D.EndedDown ); + } + + if ( controller->Mouse ) + *controller->Mouse = new_input.Controllers[idx].Mouse; + } +} + +internal +void input_poll_engine_actions( InputState* input, EngineActions* actions ) +{ + ControllerState* controller = & input->Controllers[0]; + KeyboardState* keyboard = controller->Keyboard; + + actions->move_right |= keyboard->D.EndedDown; + actions->move_left |= keyboard->A.EndedDown; + actions->move_up |= keyboard->W.EndedDown; + actions->move_down |= keyboard->S.EndedDown; + + actions->raise_volume |= keyboard->Up.EndedDown; + actions->lower_volume |= keyboard->Down.EndedDown; + + actions->raise_tone_hz |= keyboard->Right.EndedDown; + actions->lower_tone_hz |= keyboard->Left.EndedDown; + + actions->pause_renderer |= pressed( keyboard->Pause ); + + actions->toggle_wave_tone |= pressed( keyboard->Q ); + + actions->loop_mode |= pressed( keyboard->L ); +} + +internal +void input_poll_player_actions( InputState* input, PlayerActions* actions ) +{ + ControllerState* controller = & input->Controllers[0]; + + if ( controller->DSPad ) + { + DualsensePadState* pad = controller->DSPad; + + actions->jump |= pressed( pad->X ); + + actions->player_x_move_analog += pad->Stick.Left.X.End; + actions->player_y_move_analog += pad->Stick.Left.Y.End; + } + if ( controller->XPad ) + { + XInputPadState* pad = controller->XPad; + + actions->jump |= pressed( pad->A ); + + actions->player_x_move_analog += pad->Stick.Left.X.End; + actions->player_y_move_analog += pad->Stick.Left.Y.End; + } + + if ( controller->Keyboard ) + { + KeyboardState* keyboard = controller->Keyboard; + actions->jump |= pressed( keyboard->Space ); + + actions->player_x_move_digital += keyboard->D.EndedDown - keyboard->A.EndedDown; + actions->player_y_move_digital += keyboard->W.EndedDown - keyboard->S.EndedDown; + } +} + Engine_API void on_module_reload( Memory* memory, platform::ModuleAPI* platfom_api ) { @@ -135,24 +381,28 @@ void startup( Memory* memory, platform::ModuleAPI* platform_api ) state->YOffset = 0; state->SampleWaveSwitch = false; - state->WaveToneHz = 120; + state->WaveToneHz = 60; state->SampleWaveSineTime = 0.f; state->RendererPaused = false; - #if Build_Debug && 0 - { - using namespace platform; + state->InputRecordingIndex = 0; + state->InputPlayingIndex = 0; - char const* file_path = __FILE__; - Debug_FileContent file_content = platform_api->debug_file_read_content( file_path ); - if ( file_content.Size ) - { - platform_api->debug_file_write_content( "test.out", file_content.Size, file_content.Data ); - platform_api->debug_file_free_content( & file_content ); - } - } - #endif + state->ActiveInputRecordingFile = {}; + state->ActivePlaybackFile = {}; + + state->game_memory.PersistentSize = memory->PersistentSize / 2; + state->game_memory.Persistent = rcast(Byte*, memory->Persistent) + state->game_memory.PersistentSize; + state->game_memory.TransientSize = memory->TransientSize / 2; + state->game_memory.Transient = rcast(Byte*, memory->Transient) + state->game_memory.TransientSize; + + hh::PlayerState* player = rcast( hh::PlayerState*, state->game_memory.Persistent ); + assert( sizeof(hh::PlayerState) <= state->game_memory.PersistentSize ); + + player->Pos_X = 100; + player->Pos_Y = 100; + player->MidJump = false; } Engine_API @@ -169,131 +419,116 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* ControllerState* controller = & input->Controllers[0]; - // Abstracting the actionables as booleans and processing within this scope - // for now until proper callbacks for input bindings are setup. - b32 move_up = false; - b32 move_down = false; - b32 move_left = false; - b32 move_right = false; + EngineActions engine_actions {}; + PlayerActions player_actions {}; - b32 action_up = false; - b32 action_down = false; - b32 action_left = false; - b32 action_right = false; - - b32 raise_volume = false; - b32 lower_volume = false; - b32 raise_tone_hz = false; - b32 lower_tone_hz = false; - - b32 toggle_wave_tone = false; - - b32 pause_renderer = false; - - f32 analog_threshold = 0.5f; - - #define pressed( btn ) (btn.EndedDown && btn.HalfTransitions < 1) - - if ( controller->DSPad ) + input_poll_engine_actions( input, & engine_actions ); { - DualsensePadState* pad = controller->DSPad; + state->XOffset += 3 * engine_actions.move_right; + state->XOffset -= 3 * engine_actions.move_left; + state->YOffset += 3 * engine_actions.move_down; + state->YOffset -= 3 * engine_actions.move_up; - move_right |= pad->DPad.Right.EndedDown || pad->Stick.Left.X.End > analog_threshold; - move_left |= pad->DPad.Left.EndedDown || pad->Stick.Left.X.End < -analog_threshold; - move_up |= pad->DPad.Up.EndedDown || pad->Stick.Left.Y.End > analog_threshold; - move_down |= pad->DPad.Down.EndedDown || pad->Stick.Left.Y.End < -analog_threshold; - - raise_volume |= pad->Triangle.EndedDown; - lower_volume |= pad->Circle.EndedDown; - - raise_tone_hz |= pad->Square.EndedDown; - lower_tone_hz |= pad->X.EndedDown; - - toggle_wave_tone |= pressed( pad->Options ); - } - if ( controller->XPad ) - { - XInputPadState* pad = controller->XPad; - - move_right |= pad->DPad.Right.EndedDown || pad->Stick.Left.X.End > analog_threshold; - move_left |= pad->DPad.Left.EndedDown || pad->Stick.Left.X.End < -analog_threshold; - move_up |= pad->DPad.Up.EndedDown || pad->Stick.Left.Y.End > analog_threshold; - move_down |= pad->DPad.Down.EndedDown || pad->Stick.Left.Y.End < -analog_threshold; - - raise_volume |= pad->Y.EndedDown; - lower_volume |= pad->B.EndedDown; - - raise_tone_hz |= pad->X.EndedDown; - lower_tone_hz |= pad->A.EndedDown; - - toggle_wave_tone |= pressed( pad->Start ); - } - if ( controller->Keyboard ) - { - KeyboardState* keyboard = controller->Keyboard; - - move_right |= keyboard->D.EndedDown; - move_left |= keyboard->A.EndedDown; - move_up |= keyboard->W.EndedDown; - move_down |= keyboard->S.EndedDown; - - raise_volume |= keyboard->Up.EndedDown; - lower_volume |= keyboard->Down.EndedDown; - - raise_tone_hz |= keyboard->Right.EndedDown; - lower_tone_hz |= keyboard->Left.EndedDown; - - pause_renderer |= pressed( keyboard->Pause ); - - toggle_wave_tone |= pressed( keyboard->Space ); - } - - state->XOffset += 3 * move_right; - state->XOffset -= 3 * move_left; - state->YOffset += 3 * move_down; - state->YOffset -= 3 * move_up; - - if ( raise_volume ) - { - state->ToneVolume += 10; - } - if ( lower_volume ) - { - state->ToneVolume -= 10; - if ( state->ToneVolume <= 0 ) - state->ToneVolume = 0; - } - - if ( raise_tone_hz ) - { - state->WaveToneHz += 1; - } - if ( lower_tone_hz ) - { - state->WaveToneHz -= 1; - if ( state->WaveToneHz <= 0 ) - state->WaveToneHz = 1; - } - - if ( toggle_wave_tone ) - { - state->SampleWaveSwitch ^= true; - } - render_weird_graident( back_buffer, state->XOffset, state->YOffset ); - - if ( pause_renderer ) - { - if ( state->RendererPaused ) + if ( engine_actions.raise_volume ) { - platform_api->debug_set_pause_rendering(false); - state->RendererPaused = false; + state->ToneVolume += 10; + } + if ( engine_actions.lower_volume ) + { + state->ToneVolume -= 10; + if ( state->ToneVolume <= 0 ) + state->ToneVolume = 0; + } + + if ( engine_actions.raise_tone_hz ) + { + state->WaveToneHz += 1; + } + if ( engine_actions.lower_tone_hz ) + { + state->WaveToneHz -= 1; + if ( state->WaveToneHz <= 0 ) + state->WaveToneHz = 1; + } + + if ( engine_actions.toggle_wave_tone ) + { + state->SampleWaveSwitch ^= true; + } + + if ( engine_actions.loop_mode ) + { + if ( state->InputRecordingIndex == 0 && state->InputPlayingIndex == 0 ) + { + begin_recording_input( state, input, platform_api ); + } + else if ( state->InputPlayingIndex ) + { + end_playback_input( state, input, platform_api ); + } + else if ( state->InputRecordingIndex ) + { + end_recording_input( state, input, platform_api ); + begin_playback_input( state, input, platform_api ); + } + } + + if ( engine_actions.pause_renderer ) + { + if ( state->RendererPaused ) + { + platform_api->debug_set_pause_rendering(false); + state->RendererPaused = false; + } + else + { + platform_api->debug_set_pause_rendering(true); + state->RendererPaused = true; + } + } + } + + if ( state->InputRecordingIndex ) + { + record_input( state, input, platform_api ); + } + if ( state->InputPlayingIndex ) + { + play_input( state, input, platform_api ); + } + + hh::PlayerState* player = rcast( hh::PlayerState*, state->game_memory.Persistent ); + assert( sizeof(hh::PlayerState) <= state->game_memory.PersistentSize ); + + 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->JumpTime * TAU ) * 10); + + if ( player->JumpTime > 0.f ) + { + player->JumpTime -= 0.025f; } else { - platform_api->debug_set_pause_rendering(true); - state->RendererPaused = true; + player->JumpTime = 0.f; + player->MidJump = false; + } + + if ( ! player->MidJump && player_actions.jump ) + { + player->JumpTime = 1.f; + player->MidJump = true; } } + + render_weird_graident( back_buffer, 0, 0 ); + render_player( back_buffer, player->Pos_X, player->Pos_Y ); + + if ( state->InputRecordingIndex ) + render_player( back_buffer, player->Pos_X + 20, player->Pos_Y - 20 ); } Engine_API @@ -312,3 +547,5 @@ void update_audio( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAP } NS_ENGINE_END + +#undef pressed diff --git a/project/engine/engine.hpp b/project/engine/engine.hpp index 7538d58..4b5363c 100644 --- a/project/engine/engine.hpp +++ b/project/engine/engine.hpp @@ -79,12 +79,14 @@ union KeyboardState { DigitalBtn Keys[12]; struct { + DigitalBtn Row_1; DigitalBtn Q; DigitalBtn E; DigitalBtn W; DigitalBtn A; DigitalBtn S; DigitalBtn D; + DigitalBtn L; DigitalBtn Escape; DigitalBtn Backspace; DigitalBtn Up; @@ -175,11 +177,24 @@ struct ControllerState DualsensePadState* DSPad; }; +struct ControllerStateSnapshot +{ + KeyboardState Keyboard; + MousesState Mouse; + XInputPadState XPad; + DualsensePadState DSPad; +}; + struct InputState { ControllerState Controllers[4]; }; +struct InputStateSnapshot +{ + ControllerStateSnapshot Controllers[4]; +}; + using InputBindCallback = void( void* ); using InputBindCallback_DigitalBtn = void( engine::DigitalBtn* Button ); using InputBindCallback_AnalogAxis = void( engine::AnalogAxis* Axis ); @@ -194,4 +209,12 @@ struct InputMode void input_mode_pop( InputMode* mode ); void input_mode_pop( InputMode* mode ); +#if 0 +struct RecordedInput +{ + s32 Num; + InputState* Stream; +}; +#endif + NS_ENGINE_END diff --git a/project/handmade.hpp b/project/handmade.hpp index f84775b..d5ab433 100644 --- a/project/handmade.hpp +++ b/project/handmade.hpp @@ -2,7 +2,7 @@ #include "engine/engine.hpp" -#define NS_HANDMADE_BEGIN namespace handmade { +#define NS_HANDMADE_BEGIN namespace hh { #define NS_HANDMADE_END } NS_HANDMADE_BEGIN @@ -63,4 +63,13 @@ struct Player // Possilby some other stuff in the future. }; +struct PlayerState +{ + s32 Pos_X; + s32 Pos_Y; + + b32 MidJump; + f32 JumpTime; +}; + NS_HANDMADE_END diff --git a/project/platform/platform.hpp b/project/platform/platform.hpp index 50eac50..7df0a86 100644 --- a/project/platform/platform.hpp +++ b/project/platform/platform.hpp @@ -44,33 +44,24 @@ NS_PLATFORM_BEGIN IMPORTANT : These are not for shipping code - they are blocking and the write isn't protected. */ -struct Debug_FileContent -{ - void* Data; - u32 Size; - Byte _PAD_[4]; -}; +using DebugSetPauseRenderingFn = void (b32 value); -struct BinaryModule +struct File { void* OpaqueHandle; + Str Path; + void* Data; + u32 Size; }; -using DebugFileFreeContentFn = void ( Debug_FileContent* file_content ); -using DebugFileReadContentFn = Debug_FileContent ( char const* file_path ); -using DebugFileWriteContentFn = b32 ( char const* file_path, u32 content_size, void* content_memory ); - -using DebugSetPauseRenderingFn = void (b32 value); - // TODO(Ed): This also assumes the symbol name is always within size of the provided buffer, needs to fail if not. // Note: This is a temporary solution until there is more infrastructure for the engine to use. -void get_symbol_from_module_table( Debug_FileContent symbol_table, u32 symbol_ID, char* symbol_name ) +void get_symbol_from_module_table( File symbol_table, u32 symbol_ID, char* symbol_name ) { struct Token { char const* Ptr; u32 Len; - char _PAD_[4]; }; Token tokens[256] = {}; @@ -124,17 +115,34 @@ using SetEngineFrameTargetFn = void ( u32 rate_in_hz ); // This module api will be used to manage the editor and game modules from the engine side, // without the platform layer needing to know about it. +struct BinaryModule +{ + void* OpaqueHandle; +}; + using LoadBinaryModuleFn = BinaryModule ( char const* module_path ); using UnloadBinaryModuleFn = void ( BinaryModule* module ); using GetModuleProcedureFn = void* ( BinaryModule module, char const* symbol ); +// The file interface is really just made for the engine to use. +// It will allow for only reading or writting to a file at a time. +// Note: If anything more robust is needed, I'll grab it from the zpl-c library. + +using FileCheckExistsFn = b32 ( Str const file_path ); +using FileCloseFn = void ( File* file ); +using FileDelete = b32 ( Str const file_path ); +using FileReadContentFn = b32 ( File* file ); +using FileReadStreamFn = b32 ( File* file, u32 content_size, void* content_memory ); +using FileWriteContentFn = u32 ( File* file, u32 content_size, void* content_memory ); +using FileWriteStreamFn = u32 ( File* file, u32 content_size, void* content_memory ); +using FileRewindFn = void ( File* file ); + struct ModuleAPI { -#if Build_Development - DebugFileFreeContentFn* debug_file_free_content; - DebugFileReadContentFn* debug_file_read_content; - DebugFileWriteContentFn* debug_file_write_content; + Str PathRoot; + Str PathBinaries; +#if Build_Development DebugSetPauseRenderingFn* debug_set_pause_rendering; #endif @@ -147,6 +155,15 @@ struct ModuleAPI LoadBinaryModuleFn* load_binary_module; UnloadBinaryModuleFn* unload_binary_module; GetModuleProcedureFn* get_module_procedure; + + FileCheckExistsFn* file_check_exists; // Checks if a file exists + FileCloseFn* file_close; // Files successfuly provided to the user are not automatically closed, use this to close them. + FileDelete* file_delete; // Deletes a file from the file system + FileReadContentFn* file_read_content; // Read all content within file + FileReadStreamFn* file_read_stream; // Read next chunk of content within file + FileRewindFn* file_rewind; // Rewinds the file stream to the beginning + FileWriteContentFn* file_write_content; // Writes content to file (overwrites) + FileWriteStreamFn* file_write_stream; // Appends content to file }; #pragma endregion Settings Exposure diff --git a/project/platform/win32.hpp b/project/platform/win32.hpp index 813be72..3618837 100644 --- a/project/platform/win32.hpp +++ b/project/platform/win32.hpp @@ -44,6 +44,12 @@ NS_WIN32_BEGIN +enum LWA : DWORD +{ + LWA_Alpha = 0x00000002, + LWA_ColorKey = 0x00000001, +}; + enum BI : DWORD { BI_RGB_Uncompressed = 0L, diff --git a/project/platform/win32_platform.cpp b/project/platform/win32_platform.cpp index 562c818..a848e5c 100644 --- a/project/platform/win32_platform.cpp +++ b/project/platform/win32_platform.cpp @@ -26,11 +26,6 @@ #include "engine/engine.hpp" #include "platform_engine_api.hpp" -// Standard-Library stand-ins -// #include -// TODO(Ed) : Remove these ^^ - - #if 1 // TODO(Ed): Redo these macros properly later. @@ -99,25 +94,14 @@ struct DirectSoundBuffer u32 LatencySampleCount; }; - #pragma region Static Data - global StrFixed< S16_MAX > Path_Root; global StrFixed< S16_MAX > Path_Binaries; -constexpr Str FName_Engine_DLL = str_ascii("handmade_engine.dll"); -constexpr Str FName_Engine_DLL_InUse = str_ascii("handmade_engine_in_use.dll"); - -global StrFixed< S16_MAX > Path_Engine_DLL; -global StrFixed< S16_MAX > Path_Engine_DLL_InUse; - // TODO(Ed) : This is a global for now. global b32 Running = false; global b32 Pause_Rendering = false; -// Max controllers for the platform layer and thus for all other layers is 4. (Sanity and xinput limit) -constexpr u32 Max_Controllers = 4; - global WinDimensions Window_Dimensions; global OffscreenBuffer Surface_Back_Buffer; @@ -136,11 +120,24 @@ constexpr u32 Monitor_Refresh_Max_Supported = 500; constexpr f32 High_Perf_Frametime_MS = 1000.f / 240.f; global u32 Monitor_Refresh_Hz = 60; -global u32 Engine_Refresh_Hz = Monitor_Refresh_Hz / 2; +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_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; +} + #if Build_Debug struct DebugTimeMarker { @@ -264,89 +261,7 @@ debug_sync_display( DirectSoundBuffer* sound_buffer } #endif -inline u64 -timing_get_wall_clock() -{ - u64 clock; - QueryPerformanceCounter( rcast( LARGE_INTEGER*, & clock) ); - return clock; -} - -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_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_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; -} - -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->EndedDown == new_state->EndedDown ) - new_state->EndedDown = (raw_btns & btn_flag) > 0; - new_state->HalfTransitions = had_transition() ? 1 : 0; -#undef had_transition -} - -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 f32 -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 void -poll_input( engine::InputState* input ) -{ - -} - +#pragma region Direct Sound internal void init_sound(HWND window_handle, DirectSoundBuffer* sound_buffer ) { @@ -511,6 +426,240 @@ ds_fill_sound_buffer( DirectSoundBuffer* sound_buffer, DWORD byte_to_lock, DWORD 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->EndedDown != new_state->EndedDown ) + new_state->EndedDown = (raw_btns & btn_flag) > 0; + if ( had_transition() ) + new_state->HalfTransitions += 1; + else + new_state->HalfTransitions = 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( engine::InputState* input, u32 jsl_num_devices, JSL_DeviceHandle* jsl_device_handles + , engine::KeyboardState* old_keyboard, engine::KeyboardState* new_keyboard + , EngineXInputPadStates* old_xpads, EngineXInputPadStates* new_xpads + , EngineDSPadStates* old_ds_pads, EngineDSPadStates* new_ds_pads ) +{ + // TODO(Ed) : Setup user definable deadzones for triggers and sticks. + + // Swapping at the beginning of the input frame instead of the end. + swap( old_keyboard, new_keyboard ); + swap( old_xpads, new_xpads ); + swap( old_ds_pads, new_ds_pads ); + + // Keyboard Polling + // Keyboards are unified for now. + { + constexpr u32 is_down = 0x80000000; + 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->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->Controllers[0].Keyboard = new_keyboard; + } + + // 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->LeftShoulder, & new_xpad->LeftShoulder, xpad->wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ); + input_process_digital_btn( & old_xpad->RightShoulder, & new_xpad->RightShoulder, 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->X, & new_ds_pad->X, 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 ].DSPad = 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 ) @@ -523,6 +672,23 @@ get_window_dimensions( HWND window_handle ) 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, 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 ) { @@ -561,23 +727,6 @@ resize_dib_section( OffscreenBuffer* buffer, u32 width, u32 height ) // TODO(Ed) : Clear to black } -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, window_width, window_height - , 0, 0, buffer->Width, buffer->Height - , buffer->Memory, & buffer->Info - , DIB_ColorTable_RGB, RO_Source_To_Dest ); -} - internal LRESULT CALLBACK main_window_callback( HWND handle , UINT system_messages @@ -590,7 +739,14 @@ main_window_callback( HWND handle { case WM_ACTIVATEAPP: { - OutputDebugStringA( "WM_ACTIVATEAPP\n" ); + 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; @@ -682,68 +838,156 @@ process_pending_window_messages( engine::KeyboardState* keyboard ) } } } + #pragma endregion Internal #pragma region Platfom API #if Build_Development -void debug_file_free_content( Debug_FileContent* content ) +b32 file_check_exists( Str path ) { - if ( content->Data) + HANDLE file_handle = CreateFileA( path + , GENERIC_READ, FILE_SHARE_READ, 0 + , OPEN_EXISTING, 0, 0 + ); + if ( file_handle != INVALID_HANDLE_VALUE ) { - VirtualFree( content->Data, 0, MEM_Release); - *content = {}; + CloseHandle( file_handle ); + return true; } + return false; } -Debug_FileContent debug_file_read_content( char const* file_path ) +void file_close( File* file ) { - Debug_FileContent result {}; + HANDLE handle = pcast(HANDLE, file->OpaqueHandle); - HANDLE file_handle = CreateFileA( file_path + 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->OpaqueHandle == 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->OpaqueHandle = file_handle; + } + else + { + file_handle = pcast(HANDLE, file->OpaqueHandle); + } + + 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 result; + return {}; } - GetFileSizeEx( file_handle, rcast(LARGE_INTEGER*, &result.Size) ); - if ( result.Size == 0 ) + u32 size; + GetFileSizeEx( file_handle, rcast(LARGE_INTEGER*, &size) ); + if ( size == 0 ) { // TODO(Ed) : Logging - return result; + CloseHandle( file_handle ); + return {}; } - result.Data = VirtualAlloc( 0, result.Size, MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write ); + + // 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->OpaqueHandle = file_handle; u32 bytes_read; - if ( ReadFile( file_handle, result.Data, result.Size, rcast(LPDWORD, &bytes_read), 0 ) == false ) + if ( ReadFile( file_handle, file->Data, file->Size, rcast(LPDWORD, &bytes_read), 0 ) == false ) { // TODO(Ed) : Logging + CloseHandle( file_handle ); return {}; } - if ( bytes_read != result.Size ) + if ( bytes_read != file->Size ) { // TODO : Logging + CloseHandle( file_handle ); return {}; } - - CloseHandle( file_handle ); - return result; + return bytes_read; } -b32 debug_file_write_content( char const* file_path, u32 content_size, void* content_memory ) +void file_rewind( File* file ) { - HANDLE file_handle = CreateFileA( file_path - , GENERIC_WRITE, 0, 0 - , CREATE_ALWAYS, 0, 0 - ); + HANDLE file_handle = pcast(HANDLE, file->OpaqueHandle); 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->OpaqueHandle == nullptr ) { - // TODO : Logging - return false; + file_handle = CreateFileA( file->Path + ,GENERIC_WRITE, 0, 0 + , OPEN_ALWAYS, 0, 0 + ); + if ( file_handle == INVALID_HANDLE_VALUE ) + { + // TODO(Ed) : Logging + return {}; + } + + file->OpaqueHandle = file_handle; + } + else + { + file_handle = pcast(HANDLE, file->OpaqueHandle); } DWORD bytes_written; @@ -753,8 +997,29 @@ b32 debug_file_write_content( char const* file_path, u32 content_size, void* con return false; } - CloseHandle( file_handle ); - return true; + 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->OpaqueHandle = file_handle; + + DWORD bytes_written; + if ( WriteFile( file_handle, content_memory, content_size, & bytes_written, 0 ) == false ) + { + // TODO : Logging + return false; + } + return bytes_written; } void debug_set_pause_rendering( b32 value ) @@ -763,7 +1028,21 @@ void debug_set_pause_rendering( b32 value ) } #endif -u32 get_monitor_refresh_rate(); +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 ) { @@ -783,21 +1062,17 @@ void* get_binary_module_symbol( BinaryModule module, char const* symbol_name ) } #pragma endregion Platform API -FILETIME file_get_last_write_time( char const* path ) -{ - 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; -} - #pragma region Engine Module API -global HMODULE Lib_Handmade_Engine = nullptr; +constexpr Str FName_Engine_DLL = str_ascii("handmade_engine.dll"); +constexpr Str FName_Engine_DLL_InUse = str_ascii("handmade_engine_in_use.dll"); +constexpr 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; @@ -816,8 +1091,9 @@ engine::ModuleAPI load_engine_module_api() StrFixed< S16_MAX > path_handmade_engine_symbols { 0, {} }; path_handmade_engine_symbols.concat( Path_Binaries, fname_handmade_engine_symbols ); - Debug_FileContent symbol_table = debug_file_read_content( path_handmade_engine_symbols ); - if ( symbol_table.Size == 0 ) + File symbol_table {}; + symbol_table.Path = path_handmade_engine_symbols; + if ( ! file_read_content( & symbol_table ) ) { fatal( "Failed to load symbol table for handmade engine module!" ); return {}; @@ -835,7 +1111,7 @@ engine::ModuleAPI load_engine_module_api() get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_UpdateAndRender, symboL_update_and_render ); get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_UpdateAudio, symbol_update_audio ); - debug_file_free_content( & symbol_table ); + file_close( & symbol_table ); engine::ModuleAPI engine_api {}; engine_api.on_module_reload = get_procedure_from_library< engine::OnModuleRelaodFn > ( Lib_Handmade_Engine, symbol_on_module_reload ); @@ -857,6 +1133,7 @@ engine::ModuleAPI load_engine_module_api() return engine_api; } +internal void unload_engine_module_api( engine::ModuleAPI* engine_api ) { if ( engine_api->IsValid ) @@ -876,6 +1153,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho using namespace win32; using namespace platform; +#pragma region Startup // Timing #if Build_Development u64 launch_clock = timing_get_wall_clock(); @@ -891,30 +1169,10 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & Performance_Counter_Frequency) ); - // Prepare platform API - ModuleAPI platform_api {}; - { - #if Build_Development - platform_api.debug_file_free_content = & debug_file_free_content; - platform_api.debug_file_read_content = & debug_file_read_content; - platform_api.debug_file_write_content = & debug_file_write_content; - - platform_api.debug_set_pause_rendering = & debug_set_pause_rendering; - #endif - 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; - } - // Memory engine::Memory engine_memory {}; { - engine_memory.PersistentSize = megabytes( 64 ); + engine_memory.PersistentSize = megabytes( 128 ); // engine_memory.FrameSize = megabytes( 64 ); engine_memory.TransientSize = gigabytes( 2 ); @@ -960,7 +1218,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho } window_handle = CreateWindowExW( - 0, + WS_EX_LAYERED | WS_EX_TOPMOST, window_class.lpszClassName, L"Handmade Hero", WS_Overlapped_Window | WS_Initially_Visible, @@ -970,6 +1228,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho instance, 0 // instance, param ); + if ( ! window_handle ) { // TODO : Diagnostic Logging @@ -980,8 +1239,10 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho resize_dib_section( &Surface_Back_Buffer, 1280, 720 ); // Setup pathing - // TODO(Ed): This will not support long paths, NEEDS to be changed to support long paths. + 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) ); @@ -1008,6 +1269,34 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho 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 ); + } + + // Prepare platform API + ModuleAPI platform_api {}; + { + #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; } // Load engine module @@ -1054,7 +1343,6 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho 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] {}; @@ -1067,17 +1355,14 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho engine::KeyboardState* new_keyboard = & keyboard_states[1]; // Important: Assuming keyboard always connected for now, and assigning to first controller. - using EngineXInputPadStates = engine::XInputPadState[ Max_Controllers ]; EngineXInputPadStates xpad_states[2] {}; EngineXInputPadStates* old_xpads = & xpad_states[0]; EngineXInputPadStates* new_xpads = & xpad_states[1]; - using EngineDSPadStates = engine::DualsensePadState[Max_Controllers]; EngineDSPadStates ds_pad_states[2] {}; EngineDSPadStates* old_ds_pads = & ds_pad_states[0]; EngineDSPadStates* new_ds_pads = & ds_pad_states[1]; - using JSL_DeviceHandle = int; u32 jsl_num_devices = JslConnectDevices(); JSL_DeviceHandle jsl_device_handles[4] {}; { @@ -1103,7 +1388,8 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho 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 ); + MessageBoxA( window_handle, "More than 4 JSL devices found, this engine will only support the first four found." + , "Warning", MB_ICONEXCLAMATION ); } } @@ -1121,6 +1407,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho sprintf_s( text_buffer, sizeof(text_buffer), "Startup MS: %f\n", startup_ms ); OutputDebugStringA( text_buffer ); #endif +#pragma endregion Startup Running = true; #if 0 @@ -1138,159 +1425,36 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho #endif while( Running ) { - FILETIME engine_api_current_time = file_get_last_write_time( Path_Engine_DLL ); - if ( CompareFileTime( & engine_api_load_time, & engine_api_current_time ) != 0 ) - { + // 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); process_pending_window_messages( new_keyboard ); - // TODO(Ed): Offload polling to these functions later. - // poll_xinput( & input, old_xpads, new_xpads ); - // poll_jsl( & input, old_jsl_pads, new_jsl_pads ); - - // or - // poll_input(); - - // Input - // void poll_input(); - { - // TODO(Ed) : Setup user definable deadzones for triggers and sticks. - - // Swapping at the beginning of the input frame instead of the end. - swap( old_keyboard, new_keyboard ); - swap( old_xpads, new_xpads ); - swap( old_ds_pads, new_ds_pads ); - - // Keyboard Polling - // Keyboards are unified for now. - { - constexpr u32 is_down = 0x80000000; - 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->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.Controllers[0].Keyboard = new_keyboard; - } - - // 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->LeftShoulder, & new_xpad->LeftShoulder, xpad->wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ); - input_process_digital_btn( & old_xpad->RightShoulder, & new_xpad->RightShoulder, 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->X, & new_ds_pad->X, 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 = input_process_axis_value( state.stickLX, 0.1f ); - f32 left_y = 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 ].DSPad = new_ds_pad; - } - } + poll_input( & input, jsl_num_devices, jsl_device_handles + , old_keyboard, new_keyboard + , old_xpads, new_xpads + , old_ds_pads, new_ds_pads ); // Engine's logical iteration and rendering process engine_api.update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & engine_memory, & platform_api ); @@ -1302,7 +1466,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho DWORD ds_write_cursor; do { /* - Sound computation: + 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). @@ -1485,6 +1649,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho 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(); diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 5af17d3..198f7ad 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -479,8 +479,11 @@ else { if ( $engine ) { - # Delete old PDBs - $pdb_files = Get-ChildItem -Path $path_binaries -Filter "handmade_engine_*.pdb" + $path_pdb_lock = Join-Path $path_binaries 'handmade_engine.pdb.lock' + New-Item $path_pdb_lock -ItemType File -Force -Verbose + + # 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 @@ -564,6 +567,8 @@ if ( $engine ) $path_engine_symbols = Join-Path $path_binaries 'handmade_engine.symbols' $engine_symbol_list | Out-File -Path $path_engine_symbols } + + Remove-Item $path_pdb_lock -Force -Verbose } if ( $platform ) diff --git a/scripts/handmade.rdbg b/scripts/handmade.rdbg index 89979ac..46d0323 100644 Binary files a/scripts/handmade.rdbg and b/scripts/handmade.rdbg differ