diff --git a/project/engine.cpp b/project/engine.cpp index ffc48fe..b56f09c 100644 --- a/project/engine.cpp +++ b/project/engine.cpp @@ -168,84 +168,84 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu do_once_end ControllerState* controller = & input->Controllers[0]; - - // Abstracting the actionables as booleans and processing within this scope + + // 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; - + b32 action_up = false; b32 action_down = false; b32 action_left = false; b32 action_right = false; - - f32 analog_threshold = 0.5f; - + b32 raise_volume = false; b32 lower_volume = false; b32 raise_tone_hz = false; b32 lower_tone_hz = false; - + b32 toggle_wave_tone = false; + f32 analog_threshold = 0.5f; + if ( controller->DSPad ) { DualsensePadState* pad = controller->DSPad; - + 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 |= pad->Options.EndedDown; } 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 |= pad->Start.EndedDown; } 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; - + toggle_wave_tone |= keyboard->Space.EndedDown; } - - x_offset += move_right; - x_offset -= move_left; - y_offset += move_down; - y_offset -= move_up; + + x_offset += 3 * move_right; + x_offset -= 3 * move_left; + y_offset += 3 * move_down; + y_offset -= 3 * move_up; if ( raise_volume ) { diff --git a/project/platform/handmade_win32.cpp b/project/platform/handmade_win32.cpp index 1240238..89aeea0 100644 --- a/project/platform/handmade_win32.cpp +++ b/project/platform/handmade_win32.cpp @@ -29,7 +29,7 @@ #endif #include // TODO : Implement math ourselves -#include +#include // TODO : Implement output logging ourselves #include "engine.cpp" @@ -100,8 +100,7 @@ struct SoundOutput s32 LatencySampleCount; }; -HRESULT WINAPI DirectSoundCreate(LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN pUnkOuter ); - +// HRESULT WINAPI DirectSoundCreate(LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN pUnkOuter ); using DirectSoundCreateFn = HRESULT WINAPI (LPGUID lpGuid, LPDIRECTSOUND* ppDS, LPUNKNOWN pUnkOuter ); global DirectSoundCreateFn* direct_sound_create; @@ -115,7 +114,11 @@ global s32 DS_SecondaryBuffer_BytesPerSample; global s16* SoundBufferSamples; +constexpr u64 Tick_To_Millisecond = 1000; +constexpr u64 Tick_To_Microsecond = 1000 * 1000; +global u64 Performance_Counter_Frequency; + #if Build_Debug void debug_file_free_content( Debug_FileContent* content ) { @@ -189,6 +192,37 @@ b32 debug_file_write_content( char* file_path, u32 content_size, void* content_m } #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); +} + +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 ) { @@ -220,15 +254,15 @@ input_process_axis_value( f32 value, f32 deadzone_threshold ) if ( value < -deadzone_threshold ) { result = (value + deadzone_threshold ) / (1.0f - deadzone_threshold ); - - if (result < -1.0f) + + 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) + + if (result > 1.0f) result = 1.0f; // Clamp to ensure it doesn't exceed 1 } return result; @@ -559,10 +593,6 @@ process_pending_window_messages( engine::KeyboardState* keyboard ) // I rather do this with GetAsyncKeyState... case WM_SYSKEYDOWN: case WM_SYSKEYUP: - #if 0 - case WM_KEYDOWN: - case WM_KEYUP: - #endif { WPARAM vk_code = window_msg_info.wParam; b32 is_down = scast(b32, (window_msg_info.lParam >> 31) == 0 ); @@ -571,71 +601,6 @@ process_pending_window_messages( engine::KeyboardState* keyboard ) switch ( vk_code ) { - #if 0 - case 'Q': - { - input_process_keyboard_key( & keyboard->Q, is_down ); - } - break; - case 'E': - { - input_process_keyboard_key( & keyboard->E, is_down ); - } - break; - case 'W': - { - input_process_keyboard_key( & keyboard->W, is_down ); - } - break; - case 'A': - { - input_process_keyboard_key( & keyboard->A, is_down ); - } - break; - case 'S': - { - input_process_keyboard_key( & keyboard->S, is_down ); - } - break; - case 'D': - { - input_process_keyboard_key( & keyboard->D, is_down ); - } - break; - case VK_ESCAPE: - { - input_process_keyboard_key( & keyboard->Escape, is_down ); - } - break; - case VK_BACK: - input_process_keyboard_key( & keyboard->Backspace, is_down ); - break; - case VK_UP: - { - input_process_keyboard_key( & keyboard->Up, is_down ); - } - break; - case VK_DOWN: - { - input_process_keyboard_key( & keyboard->Down, is_down ); - } - break; - case VK_LEFT: - { - input_process_keyboard_key( & keyboard->Left, is_down ); - } - break; - case VK_RIGHT: - { - input_process_keyboard_key( & keyboard->Right, is_down ); - } - break; - case VK_SPACE: - { - input_process_keyboard_key( & keyboard->Space, is_down ); - } - break; - #endif case VK_F4: { if ( alt_down ) @@ -666,6 +631,29 @@ WinMain( using namespace win32; using namespace platform; + // Timing +#if Build_Development + u64 launch_clock = timing_get_wall_clock(); + u64 launch_cycle = __rdtsc(); +#endif + + // TODO(Ed): Make this more flexible later + f32 monitor_refresh_hz = 165.f; + f32 engine_update_hz + // = monitor_refresh_hz / 2.f; + = monitor_refresh_hz; + f32 engine_frame_target_ms = 1000.f / engine_update_hz; + + // Sets the windows scheduler granulaity for this process to 1 ms + u32 desired_scheduler_ms = 1; + b32 sleep_is_granular = ( timeBeginPeriod( desired_scheduler_ms ) == TIMERR_NOERROR ); + + // Anything at or below the high performance frame-time is too low latency to sleep against the window's scheduler. + f32 high_perf_frametime_ms = 1000.f / 120.f; + b32 sub_ms_granularity_required = engine_frame_target_ms <= high_perf_frametime_ms; + + QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & Performance_Counter_Frequency) ); + // Memory engine::Memory engine_memory {}; { @@ -678,38 +666,23 @@ WinMain( + engine_memory.TransientSize; #if Build_Debug - void* Base_Address = (void*) terabytes( 1 ); - // void* Frame_Address = (void*) terabytes( 2 ); - // void* Transient_Address = (void*) terabytes( 2 ); + void* Base_Address = rcast(void*, terabytes( 1 )); #else - void* Base_Address = 0; - // void* Frame_Address = 0; - // void* Transient_Address = 0; + 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.PersistentSize; - #if 0 - engine_memory.Frame = VirtualAlloc( 0, engine_memory.FrameSize - , MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write ); - - engine_memory.Transient = VirtualAlloc( 0, engine_memory.TransientSize - , MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write ); - #endif - - if ( engine_memory.Persistent == nullptr - // || ! engine_memory.Frame - || engine_memory.Transient == nullptr ) + if ( engine_memory.Persistent == nullptr + || engine_memory.Transient == nullptr ) { // TODO : Diagnostic Logging return -1; } } - // MessageBox( 0, L"First message!", L"Handmade Hero", MB_Ok_Btn | MB_Icon_Information ); - WNDCLASSW window_class {}; HWND window_handle = nullptr; { @@ -747,7 +720,6 @@ WinMain( return 0; } } - // WinDimensions dimensions = get_window_dimensions( window_handle ); resize_dib_section( &BackBuffer, 1280, 720 ); @@ -771,39 +743,28 @@ WinMain( DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); } - // Timing - u64 perf_counter_frequency; - u64 last_frame_time; - QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & perf_counter_frequency) ); - QueryPerformanceCounter( rcast(LARGE_INTEGER*, & last_frame_time) ); - - u64 last_cycle_time = __rdtsc(); - - // Input shitshow constexpr u32 Max_Controllers = 4; // Max controllers for the platform layer and thus for all other layers is 4. (Sanity and xinput limit) engine::InputState input {}; - engine::KeyboardState keyboard_states[2] {}; + 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. using EngineXInputPadStates = engine::XInputPadState[ Max_Controllers ]; - EngineXInputPadStates xpad_states[2] {}; + 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 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(); - // = 0; + u32 jsl_num_devices = JslConnectDevices(); JSL_DeviceHandle jsl_device_handles[4] {}; { xinput_load_library_bindings(); @@ -832,27 +793,18 @@ WinMain( } } + u64 last_frame_clock = timing_get_wall_clock(); + u64 last_frame_cycle = __rdtsc(); + +#if Build_Development + u64 startup_cycles = last_frame_cycle - launch_cycle; + f32 startup_ms = timing_get_ms_elapsed( launch_clock, last_frame_clock ); +#endif + Running = true; while( Running ) { - // Handeled properly in the input section. - #if 0 - swap( old_keyboard, new_keyboard ); - *new_keyboard = {}; - for ( u32 key_index = 0; key_index < array_count( new_keyboard->Keys ); ++ key_index ) - { - engine::DigitalBtn* old_key = & old_keyboard->Keys[ key_index ]; - engine::DigitalBtn* new_key = & new_keyboard->Keys[ key_index ]; - - new_key->EndedDown = old_key->EndedDown; - } - #endif process_pending_window_messages( new_keyboard ); - - - input.Controllers[0].Keyboard = new_keyboard; - // printf("Q- Old: %d, New: %d\n", old_keyboard->Q.EndedDown, new_keyboard->Q.EndedDown); - printf("Q- HTOld: %d, HTNew: %d\n", old_keyboard->Q.HalfTransitions, new_keyboard->Q.HalfTransitions); // Input // TODO(Ed) : Setup user definable deadzones for triggers and sticks. @@ -861,8 +813,9 @@ WinMain( 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 = 0x8000; input_process_digital_btn( & old_keyboard->Q, & new_keyboard->Q, GetAsyncKeyState( 'Q' ), is_down ); @@ -878,6 +831,8 @@ WinMain( 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.Controllers[0].Keyboard = new_keyboard; } // XInput Polling @@ -917,7 +872,7 @@ WinMain( // 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; @@ -944,6 +899,9 @@ WinMain( 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 ]; @@ -973,7 +931,7 @@ WinMain( 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; @@ -1018,16 +976,38 @@ WinMain( engine::update_and_render( & input, rcast(engine::OffscreenBuffer*, & BackBuffer.Memory), & sound_buffer, & engine_memory ); - // Rendering + 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 ); + f32 work_cycles = timing_get_ms_elapsed( last_frame_cycle, work_frame_end_cycle ); + + f32 frame_elapsed_ms = work_frame_ms; + if ( frame_elapsed_ms < engine_frame_target_ms ) { - WinDimensions dimensions = get_window_dimensions( window_handle ); - HDC device_context = GetDC( window_handle ); - display_buffer_in_window( device_context, dimensions.Width, dimensions.Height, &BackBuffer - , 0, 0 - , dimensions.Width, dimensions.Height ); + + DWORD sleep_ms = scast(DWORD, (engine_frame_target_ms - frame_elapsed_ms)) - 1; + if ( ! sub_ms_granularity_required && sleep_is_granular ) + { + Sleep( sleep_ms ); + } + + u64 frame_clock = timing_get_wall_clock(); + frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock ); + + assert( frame_elapsed_ms < engine_frame_target_ms ); + 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! } - // Audio + // Update audio buffer do { DWORD ds_status = 0; if ( SUCCEEDED( DS_SecondaryBuffer->GetStatus( & ds_status ) ) ) @@ -1045,28 +1025,20 @@ WinMain( DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); } while(0); + + // Update framebuffer + { + WinDimensions dimensions = get_window_dimensions( window_handle ); + HDC device_context = GetDC( window_handle ); + display_buffer_in_window( device_context, dimensions.Width, dimensions.Height, &BackBuffer + , 0, 0 + , dimensions.Width, dimensions.Height ); + } - u64 end_cycle_count = __rdtsc(); + printf("%f\n", frame_elapsed_ms ); - u64 frame_cycle_time_end; - QueryPerformanceCounter( rcast( LARGE_INTEGER*, & frame_cycle_time_end) ); - - // TODO : Display value here - - #define MS_PER_SECOND 1000 - #define MegaCycles_Per_Second (1000 * 1000) - u64 cycles_elapsed = end_cycle_count - last_cycle_time; - u64 mega_cycles_elapsed = cycles_elapsed / MegaCycles_Per_Second; - u64 frame_time_elapsed = frame_cycle_time_end - last_frame_time; - u32 ms_per_frame = scast(u32, MS_PER_SECOND * frame_time_elapsed / perf_counter_frequency); - u32 fps = scast(u32, perf_counter_frequency / frame_time_elapsed); - - // char ms_timing_debug[256] {}; - // wsprintfA( ms_timing_debug, "%d ms\n" "FPS: %d\n" "mega cycles: %d\n", ms_per_frame, fps, mega_cycles_elapsed ); - // OutputDebugStringA( ms_timing_debug ); - - last_cycle_time = end_cycle_count; - last_frame_time = frame_cycle_time_end; + last_frame_clock = timing_get_wall_clock(); + last_frame_cycle = __rdtsc(); } if ( jsl_num_devices > 0 ) diff --git a/project/platform/win32.h b/project/platform/win32.h index 5ce2223..197b3c7 100644 --- a/project/platform/win32.h +++ b/project/platform/win32.h @@ -11,6 +11,7 @@ #include #include #include +#include #pragma warning( pop ) // #include "windows/windows_base.h" diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 18f44bc..81dfd68 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -415,6 +415,7 @@ $includes = @( $lib_gdi32 = 'Gdi32.lib' $lib_xinput = 'Xinput.lib' $lib_user32 = 'User32.lib' +$lib_winmm = 'Winmm.lib' # Github $lib_jsl = Join-Path $path_deps 'JoyShockLibrary/x64/JoyShockLibrary.lib' @@ -446,6 +447,7 @@ $linker_args = @( $lib_gdi32, # $lib_xinput, $lib_user32, + $lib_winmm, $lib_jsl, diff --git a/scripts/handmade.rdbg b/scripts/handmade.rdbg index 6af95b1..fb5812d 100644 Binary files a/scripts/handmade.rdbg and b/scripts/handmade.rdbg differ