diff --git a/docs/Day 011.md b/docs/Day 011.md index c1a72af..9479f32 100644 --- a/docs/Day 011.md +++ b/docs/Day 011.md @@ -2,3 +2,11 @@ Architecture discussion was great. Did some diagramming of it. +I'm going to use a 3 layer approach to the architecture: + +* Platform +* Engine +* Game (Handmade) + +It will be style 2 so they each will provide a service to the layer below it and platform will provide an API to both engine and game. + diff --git a/docs/Day 012.md b/docs/Day 012.md new file mode 100644 index 0000000..f77df5a --- /dev/null +++ b/docs/Day 012.md @@ -0,0 +1,13 @@ +# Day 12 + +This day was a mess to follow along. + +> Always write the usage code first if you can, when designing something like an API. + +Casey decided to do some heavy hits the audio (makes sense) due to the use of DirectSound. +I'm going to preserve his implmentation as is for the most part since I rather keep things simple so that this project actually gets done. + +> Only introduce added complexity when you need it. + +There are two reasons you allow for a more complex interface, you ***NEED*** the feature. It actually is required for the project to have for the UX to preserve the user-level features. Or, there is a performance bottleneck that you need to address. + diff --git a/project/dependencies/JoyShockLibrary/JoyShockLibrary.cpp b/project/dependencies/JoyShockLibrary/JoyShockLibrary.cpp index 306c327..cd92f84 100644 --- a/project/dependencies/JoyShockLibrary/JoyShockLibrary.cpp +++ b/project/dependencies/JoyShockLibrary/JoyShockLibrary.cpp @@ -291,7 +291,8 @@ int JslConnectDevices() cur_dev->product_id == DS4_USB_V2 || cur_dev->product_id == DS4_USB_DONGLE || cur_dev->product_id == DS4_BT || - cur_dev->product_id == DS_USB; + cur_dev->product_id == DS_USB || + cur_dev->product_id == DS_USB_V2; break; case BROOK_DS4_VENDOR: isSupported = cur_dev->product_id == BROOK_DS4_USB; diff --git a/project/engine.cpp b/project/engine.cpp index 4df59cb..dc63912 100644 --- a/project/engine.cpp +++ b/project/engine.cpp @@ -2,6 +2,49 @@ NS_ENGINE_BEGIN + +using GetSoundSampleValueFn = s16( SoundBuffer* sound_buffer ); + +global s32 SoundWavePeriod = 250; + +internal s16 +square_wave_sample_value( SoundBuffer* sound_buffer ) +{ + s16 sample_value = (sound_buffer->RunningSampleIndex / (sound_buffer->WavePeriod /2)) % 2 ? + sound_buffer->ToneVolume : - sound_buffer->ToneVolume; + + return sample_value; +} + +internal s16 +sine_wave_sample_value( SoundBuffer* sound_buffer ) +{ + local_persist f32 time = 0.f; + + f32 sine_value = sinf( time ); + s16 sample_value = scast(u16, sine_value * sound_buffer->ToneVolume); + + time += TAU * 1.0f / scast(f32, sound_buffer->WavePeriod ); + return sample_value; +} + +internal void +output_sound( SoundBuffer* sound_buffer, GetSoundSampleValueFn* get_sample_value ) +{ + s16* sample_out = sound_buffer->Samples; + for ( u32 sample_index = 0; sample_index < sound_buffer->NumSamples; ++ sample_index ) + { + s16 sample_value = get_sample_value( sound_buffer ); + ++ sound_buffer->RunningSampleIndex; + + *sample_out = sample_value; + ++ sample_out; + + *sample_out = sample_value; + ++ sample_out; + } +} + internal void render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) { @@ -50,11 +93,14 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) } internal -void update_and_render( OffscreenBuffer* back_buffer +void update_and_render( OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer // Temp (for feature parity) , u32 x_offset, u32 y_offset ) { + // TODO(Ed) : Allow sample offsets here for more robust platform options + output_sound( sound_buffer, square_wave_sample_value ); + render_weird_graident( back_buffer, x_offset, y_offset ); } diff --git a/project/engine.h b/project/engine.h index 8ba7b7d..f644daa 100644 --- a/project/engine.h +++ b/project/engine.h @@ -20,9 +20,20 @@ struct OffscreenBuffer u32 BytesPerPixel; }; +struct SoundBuffer +{ + s16* Samples; + u32 RunningSampleIndex; + s32 SamplesPerSecond; + s32 NumSamples; + s32 ToneVolume; + s32 WaveToneHz; + s32 WavePeriod; +}; + // Needs a contextual reference to four things: // Timing, Input, Bitmap Buffer, Sound Buffer -void update_and_render( OffscreenBuffer* back_buffer +void update_and_render( OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer // Temp (for feature parity) , u32 x_offset, u32 y_offset ); diff --git a/project/platform/handmade_win32.cpp b/project/platform/handmade_win32.cpp index 3d11f98..21e45e3 100644 --- a/project/platform/handmade_win32.cpp +++ b/project/platform/handmade_win32.cpp @@ -29,11 +29,14 @@ #endif #include // TODO : Implement math ourselves +#include "engine.cpp" + // Platform Layer headers #include "platform.h" #include "jsl.h" // Using this to get dualsense controllers #include "win32.h" +#include // Engine layer headers #include "engine.h" @@ -100,6 +103,8 @@ global s32 DS_SecondaryBuffer_Size; global s32 DS_SecondaryBuffer_SamplesPerSecond; global s32 DS_SecondaryBuffer_BytesPerSample; +global s16* SoundBufferSamples; + internal void init_sound(HWND window_handle, s32 samples_per_second, s32 buffer_size ) { @@ -172,8 +177,7 @@ init_sound(HWND window_handle, s32 samples_per_second, s32 buffer_size ) } } - -struct DS_SoundOutputTest +struct SoundOutput { DWORD IsPlaying; u32 RunningSampleIndex; @@ -183,50 +187,45 @@ struct DS_SoundOutputTest s32 LatencySampleCount; }; -using DS_FillSoundBuffer_GetSampleValueFn = s16( DS_SoundOutputTest* sound_output ); - -internal s16 -square_wave_sample_value( DS_SoundOutputTest* sound_output ) -{ - s16 sample_value = (sound_output->RunningSampleIndex / (sound_output->WavePeriod /2)) % 2 ? - sound_output->ToneVolume : - sound_output->ToneVolume; - - return sample_value; -} - -internal s16 -sine_wave_sample_value( DS_SoundOutputTest* sound_output ) -{ - local_persist f32 time = 0.f; - - f32 sine_value = sinf( time ); - s16 sample_value = scast(u16, sine_value * sound_output->ToneVolume); - - time += TAU * 1.0f / scast(f32, sound_output->WavePeriod ); - return sample_value; -} - internal void -ds_fill_soundbuffer_region( LPVOID region, DWORD region_size - , DS_SoundOutputTest* sound_output, DS_FillSoundBuffer_GetSampleValueFn* get_sample_value ) +ds_clear_sound_buffer( SoundOutput* sound_output ) { - DWORD region_sample_count = region_size / DS_SecondaryBuffer_BytesPerSample; - s16* sample_out = rcast( s16*, region ); - for ( DWORD sample_index = 0; sample_index < region_sample_count; ++ sample_index ) + LPVOID region_1; + DWORD region_1_size; + LPVOID region_2; + DWORD region_2_size; + + HRESULT ds_lock_result = DS_SecondaryBuffer->Lock( 0, DS_SecondaryBuffer_Size + , & region_1, & region_1_size + , & region_2, & region_2_size + , 0 ); + if ( ! SUCCEEDED( ds_lock_result ) ) { - s16 sample_value = get_sample_value( sound_output ); - ++ sound_output->RunningSampleIndex; + return; + } - *sample_out = sample_value; + 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 = sample_value; + 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( DS_SecondaryBuffer->Unlock( region_1, region_1_size, region_2, region_2_size ) )) + { + return; + } } internal void -ds_fill_sound_buffer_test( DS_SoundOutputTest* sound_output, DWORD byte_to_lock, DWORD bytes_to_write, DS_FillSoundBuffer_GetSampleValueFn* get_sample_value ) +ds_fill_sound_buffer( SoundOutput* sound_output, DWORD byte_to_lock, DWORD bytes_to_write, engine::SoundBuffer* sound_buffer ) { LPVOID region_1; DWORD region_1_size; @@ -244,8 +243,36 @@ ds_fill_sound_buffer_test( DS_SoundOutputTest* sound_output, DWORD byte_to_lock, // TODO : Assert that region sizes are valid - ds_fill_soundbuffer_region( region_1, region_1_size, sound_output, get_sample_value ); - ds_fill_soundbuffer_region( region_2, region_2_size, sound_output, get_sample_value ); + DWORD region_1_sample_count = region_1_size / DS_SecondaryBuffer_BytesPerSample; + 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_output->RunningSampleIndex; + } + + DWORD region_2_sample_count = region_2_size / DS_SecondaryBuffer_BytesPerSample; + 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_output->RunningSampleIndex; + } if ( ! SUCCEEDED( DS_SecondaryBuffer->Unlock( region_1, region_1_size, region_2, region_2_size ) )) { @@ -264,7 +291,6 @@ get_window_dimensions( HWND window_handle ) return result; } - internal void resize_dib_section( OffscreenBuffer* buffer, u32 width, u32 height ) { @@ -465,6 +491,7 @@ main_window_callback( return result; } + NS_WIN32_END int CALLBACK @@ -542,7 +569,7 @@ WinMain( WinDimensions dimensions = get_window_dimensions( window_handle ); resize_dib_section( &BackBuffer, 1280, 720 ); - DS_SoundOutputTest sound_output; + SoundOutput sound_output; sound_output.IsPlaying = 0; DS_SecondaryBuffer_SamplesPerSecond = 48000; DS_SecondaryBuffer_BytesPerSample = sizeof(s16) * 2; @@ -550,6 +577,9 @@ WinMain( DS_SecondaryBuffer_Size = DS_SecondaryBuffer_SamplesPerSecond * DS_SecondaryBuffer_BytesPerSample; init_sound( window_handle, DS_SecondaryBuffer_SamplesPerSecond, DS_SecondaryBuffer_Size ); + SoundBufferSamples = rcast( s16*, VirtualAlloc( 0, 48000 * 2 * sizeof(s16) + , MEM_Commit_Zeroed | MEM_Reserve, Page_Read_Write )); + // Wave Sound Test bool wave_switch = false; sound_output.RunningSampleIndex = 0; @@ -557,7 +587,7 @@ WinMain( sound_output.WavePeriod = DS_SecondaryBuffer_SamplesPerSecond / sound_output.WaveToneHz; sound_output.ToneVolume = 3000; sound_output.LatencySampleCount = DS_SecondaryBuffer_SamplesPerSecond / 15; - ds_fill_sound_buffer_test( & sound_output, 0, sound_output.LatencySampleCount * DS_SecondaryBuffer_BytesPerSample, & sine_wave_sample_value ); + ds_clear_sound_buffer( & sound_output ); DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); // Graphics & Input Test @@ -712,7 +742,51 @@ WinMain( } } - engine::update_and_render( rcast(engine::OffscreenBuffer*, & BackBuffer.Memory), x_offset, y_offset ); + + // Pain... + b32 sound_is_valid = false; + DWORD ds_play_cursor; + DWORD ds_write_cursor; + DWORD byte_to_lock; + DWORD bytes_to_write; + if ( SUCCEEDED( DS_SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) )) + { + DWORD target_cursor = (ds_play_cursor + sound_output.LatencySampleCount * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; + + byte_to_lock = (sound_output.RunningSampleIndex * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; + bytes_to_write; + + if ( byte_to_lock == target_cursor ) + { + // We are in the middle of playing. Wait for the write cursor to catch up. + bytes_to_write = 0; + } + else if ( byte_to_lock > target_cursor) + { + // Infront of play cursor |--play--byte_to_write-->--| + bytes_to_write = DS_SecondaryBuffer_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; + } + + sound_is_valid = true; + } + + // s16 samples[ 48000 * 2 ]; + engine::SoundBuffer sound_buffer {}; + sound_buffer.NumSamples = DS_SecondaryBuffer_SamplesPerSecond / 30; + sound_buffer.RunningSampleIndex = sound_output.RunningSampleIndex; + sound_buffer.SamplesPerSecond = DS_SecondaryBuffer_SamplesPerSecond; + sound_buffer.ToneVolume = sound_output.ToneVolume; + sound_buffer.Samples = SoundBufferSamples; + sound_buffer.WavePeriod = sound_output.WavePeriod; + sound_buffer.WaveToneHz = sound_output.WaveToneHz; + + engine::update_and_render( rcast(engine::OffscreenBuffer*, & BackBuffer.Memory), & sound_buffer, x_offset, y_offset ); // Rendering { @@ -754,50 +828,14 @@ WinMain( sound_output.IsPlaying = ds_status & DSBSTATUS_PLAYING; } - DWORD ds_play_cursor; - DWORD ds_write_cursor; - if ( ! SUCCEEDED( DS_SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) )) - { + if ( ! sound_is_valid ) break; - } - DWORD target_cursor = (ds_play_cursor + sound_output.LatencySampleCount * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; + ds_fill_sound_buffer( & sound_output, byte_to_lock, bytes_to_write, & sound_buffer ); - DWORD byte_to_lock = (sound_output.RunningSampleIndex * DS_SecondaryBuffer_BytesPerSample) % DS_SecondaryBuffer_Size; - DWORD bytes_to_write; - - if ( byte_to_lock == target_cursor ) - { - // We are in the middle of playing. Wait for the write cursor to catch up. - bytes_to_write = 0; - } - else if ( byte_to_lock > target_cursor) - { - // Infront of play cursor |--play--byte_to_write-->--| - bytes_to_write = DS_SecondaryBuffer_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; - } - - if ( wave_switch ) - { - ds_fill_sound_buffer_test( & sound_output, byte_to_lock, bytes_to_write, square_wave_sample_value ); - } - else - { - ds_fill_sound_buffer_test( & sound_output, byte_to_lock, bytes_to_write, sine_wave_sample_value ); - } - - #if 1 if ( sound_output.IsPlaying ) - { break; - } - #endif + DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); } while(0); @@ -837,4 +875,4 @@ WinMain( } // Engine layer translation unit. -#include "engine.cpp" +// #include "engine.cpp" diff --git a/scripts/build.ps1 b/scripts/build.ps1 index ea50587..2b8040d 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -38,11 +38,6 @@ if ( $vendor -eq $null ) { $compiler = "clang" } -if ( $release -eq $null ) { - write-host "No build type specified, assuming debug" - $release = $false -} - write-host "Building HandmadeHero with $vendor" write-host "Build Type: $(if ($release) {"Release"} else {"Debug"} )" @@ -124,7 +119,7 @@ if ( $vendor -match "clang" ) $flag_include = '-I' $flag_library = '-l' $flag_library_path = '-L' - $flag_link_win = '-Wl,' + $flag_linker = '-Wl,' $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE' $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS' $flag_link_win_machine_32 = '/MACHINE:X86' @@ -138,6 +133,7 @@ if ( $vendor -match "clang" ) $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' @@ -211,6 +207,12 @@ if ( $vendor -match "clang" ) $linker_args += $object run-linker $linker $executable $linker_args + + # $compiler_args += $unit + # $linker_args | ForEach-Object { + # $compiler_args += $flag_linker + $_ + # } + # run-compiler $compiler $unit $compiler_args } $compiler = 'clang++' @@ -253,6 +255,7 @@ if ( $vendor -match "msvc" ) $flag_path_debug = '/Fd' $flag_path_output = '/Fe' $flag_preprocess_conform = '/Zc:preprocessor' + $flag_set_stack_size = '/F' $flag_syntax_only = '/Zs' # This works because this project uses a single unit to build @@ -266,10 +269,10 @@ if ( $vendor -match "msvc" ) $compiler_args += @( $flag_nologo, - $flag_all_cpp, - $flag_exceptions_disabled, - ( $flag_define + '_HAS_EXCEPTIONS=0' ), - $flag_RTTI_disabled, + # $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 + '\' ), @@ -297,18 +300,17 @@ if ( $vendor -match "msvc" ) else { $compiler_args += $flag_link_win_rt_static } - $compiler_args += $includes | ForEach-Object { $flag_include + $_ } - $compiler_args += $unit - # $compiler_args += $flag_compile, $unit - # run-compiler $compiler $unit $compiler_args + + $compiler_args += $flag_compile, $unit + run-compiler $compiler $unit $compiler_args $linker_args += @( $flag_nologo, $flag_link_win_machine_64, ( $flag_link_win_path_output + $executable ) ) - if ( $release -eq $false ) { + if ( $debug ) { $linker_args += $flag_link_win_debug $linker_args += $flag_link_win_pdb + $pdb } @@ -316,11 +318,12 @@ if ( $vendor -match "msvc" ) } $linker_args += $object - # run-linker $linker $executable $linker_args + run-linker $linker $executable $linker_args - $compiler_args += $flag_linker - $compiler_args += $linker_args - run-compiler $compiler $unit $compiler_args + # $compiler_args += $unit + # $compiler_args += $flag_linker + # $compiler_args += $linker_args + # run-compiler $compiler $unit $compiler_args } $compiler = 'cl' @@ -393,9 +396,12 @@ $lib_jsl = Join-Path $path_deps 'JoyShockLibrary/x64/JoyShockLibrary.lib' $unit = Join-Path $path_platform 'handmade_win32.cpp' $executable = Join-Path $path_build 'handmade_win32.exe' +$stack_size = 1024 * 1024 * 4 + $compiler_args = @( ($flag_define + 'UNICODE'), ($flag_define + '_UNICODE') + # ($flag_set_stack_size + $stack_size) ) $linker_args = @(