Day 12 complete

This commit is contained in:
Edward R. Gonzalez 2023-09-16 18:41:07 -04:00
parent 9831c46739
commit ad5288f9e8
7 changed files with 226 additions and 103 deletions

View File

@ -2,3 +2,11 @@
Architecture discussion was great. Did some diagramming of it. 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.

13
docs/Day 012.md Normal file
View File

@ -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.

View File

@ -291,7 +291,8 @@ int JslConnectDevices()
cur_dev->product_id == DS4_USB_V2 || cur_dev->product_id == DS4_USB_V2 ||
cur_dev->product_id == DS4_USB_DONGLE || cur_dev->product_id == DS4_USB_DONGLE ||
cur_dev->product_id == DS4_BT || 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; break;
case BROOK_DS4_VENDOR: case BROOK_DS4_VENDOR:
isSupported = cur_dev->product_id == BROOK_DS4_USB; isSupported = cur_dev->product_id == BROOK_DS4_USB;

View File

@ -2,6 +2,49 @@
NS_ENGINE_BEGIN 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 internal void
render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) 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 internal
void update_and_render( OffscreenBuffer* back_buffer void update_and_render( OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer
// Temp (for feature parity) // Temp (for feature parity)
, u32 x_offset, u32 y_offset , 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 ); render_weird_graident( back_buffer, x_offset, y_offset );
} }

View File

@ -20,9 +20,20 @@ struct OffscreenBuffer
u32 BytesPerPixel; 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: // Needs a contextual reference to four things:
// Timing, Input, Bitmap Buffer, Sound Buffer // 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) // Temp (for feature parity)
, u32 x_offset, u32 y_offset , u32 x_offset, u32 y_offset
); );

View File

@ -29,11 +29,14 @@
#endif #endif
#include <math.h> // TODO : Implement math ourselves #include <math.h> // TODO : Implement math ourselves
#include "engine.cpp"
// Platform Layer headers // Platform Layer headers
#include "platform.h" #include "platform.h"
#include "jsl.h" // Using this to get dualsense controllers #include "jsl.h" // Using this to get dualsense controllers
#include "win32.h" #include "win32.h"
#include <malloc.h>
// Engine layer headers // Engine layer headers
#include "engine.h" #include "engine.h"
@ -100,6 +103,8 @@ global s32 DS_SecondaryBuffer_Size;
global s32 DS_SecondaryBuffer_SamplesPerSecond; global s32 DS_SecondaryBuffer_SamplesPerSecond;
global s32 DS_SecondaryBuffer_BytesPerSample; global s32 DS_SecondaryBuffer_BytesPerSample;
global s16* SoundBufferSamples;
internal void internal void
init_sound(HWND window_handle, s32 samples_per_second, s32 buffer_size ) 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 SoundOutput
struct DS_SoundOutputTest
{ {
DWORD IsPlaying; DWORD IsPlaying;
u32 RunningSampleIndex; u32 RunningSampleIndex;
@ -183,50 +187,45 @@ struct DS_SoundOutputTest
s32 LatencySampleCount; 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 internal void
ds_fill_soundbuffer_region( LPVOID region, DWORD region_size ds_clear_sound_buffer( SoundOutput* sound_output )
, DS_SoundOutputTest* sound_output, DS_FillSoundBuffer_GetSampleValueFn* get_sample_value )
{ {
DWORD region_sample_count = region_size / DS_SecondaryBuffer_BytesPerSample; LPVOID region_1;
s16* sample_out = rcast( s16*, region ); DWORD region_1_size;
for ( DWORD sample_index = 0; sample_index < region_sample_count; ++ sample_index ) 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 ); return;
++ sound_output->RunningSampleIndex; }
*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_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; ++ sample_out;
} }
if ( ! SUCCEEDED( DS_SecondaryBuffer->Unlock( region_1, region_1_size, region_2, region_2_size ) ))
{
return;
}
} }
internal void 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; LPVOID region_1;
DWORD region_1_size; 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 // TODO : Assert that region sizes are valid
ds_fill_soundbuffer_region( region_1, region_1_size, sound_output, get_sample_value ); DWORD region_1_sample_count = region_1_size / DS_SecondaryBuffer_BytesPerSample;
ds_fill_soundbuffer_region( region_2, region_2_size, sound_output, get_sample_value ); 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 ) )) 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; return result;
} }
internal void internal void
resize_dib_section( OffscreenBuffer* buffer, u32 width, u32 height ) resize_dib_section( OffscreenBuffer* buffer, u32 width, u32 height )
{ {
@ -465,6 +491,7 @@ main_window_callback(
return result; return result;
} }
NS_WIN32_END NS_WIN32_END
int CALLBACK int CALLBACK
@ -542,7 +569,7 @@ WinMain(
WinDimensions dimensions = get_window_dimensions( window_handle ); WinDimensions dimensions = get_window_dimensions( window_handle );
resize_dib_section( &BackBuffer, 1280, 720 ); resize_dib_section( &BackBuffer, 1280, 720 );
DS_SoundOutputTest sound_output; SoundOutput sound_output;
sound_output.IsPlaying = 0; sound_output.IsPlaying = 0;
DS_SecondaryBuffer_SamplesPerSecond = 48000; DS_SecondaryBuffer_SamplesPerSecond = 48000;
DS_SecondaryBuffer_BytesPerSample = sizeof(s16) * 2; DS_SecondaryBuffer_BytesPerSample = sizeof(s16) * 2;
@ -550,6 +577,9 @@ WinMain(
DS_SecondaryBuffer_Size = DS_SecondaryBuffer_SamplesPerSecond * DS_SecondaryBuffer_BytesPerSample; DS_SecondaryBuffer_Size = DS_SecondaryBuffer_SamplesPerSecond * DS_SecondaryBuffer_BytesPerSample;
init_sound( window_handle, DS_SecondaryBuffer_SamplesPerSecond, DS_SecondaryBuffer_Size ); 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 // Wave Sound Test
bool wave_switch = false; bool wave_switch = false;
sound_output.RunningSampleIndex = 0; sound_output.RunningSampleIndex = 0;
@ -557,7 +587,7 @@ WinMain(
sound_output.WavePeriod = DS_SecondaryBuffer_SamplesPerSecond / sound_output.WaveToneHz; sound_output.WavePeriod = DS_SecondaryBuffer_SamplesPerSecond / sound_output.WaveToneHz;
sound_output.ToneVolume = 3000; sound_output.ToneVolume = 3000;
sound_output.LatencySampleCount = DS_SecondaryBuffer_SamplesPerSecond / 15; 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 ); DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
// Graphics & Input Test // 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 // Rendering
{ {
@ -754,50 +828,14 @@ WinMain(
sound_output.IsPlaying = ds_status & DSBSTATUS_PLAYING; sound_output.IsPlaying = ds_status & DSBSTATUS_PLAYING;
} }
DWORD ds_play_cursor; if ( ! sound_is_valid )
DWORD ds_write_cursor;
if ( ! SUCCEEDED( DS_SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) ))
{
break; 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 ) if ( sound_output.IsPlaying )
{
break; break;
}
#endif
DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING ); DS_SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
} while(0); } while(0);
@ -837,4 +875,4 @@ WinMain(
} }
// Engine layer translation unit. // Engine layer translation unit.
#include "engine.cpp" // #include "engine.cpp"

View File

@ -38,11 +38,6 @@ if ( $vendor -eq $null ) {
$compiler = "clang" $compiler = "clang"
} }
if ( $release -eq $null ) {
write-host "No build type specified, assuming debug"
$release = $false
}
write-host "Building HandmadeHero with $vendor" write-host "Building HandmadeHero with $vendor"
write-host "Build Type: $(if ($release) {"Release"} else {"Debug"} )" write-host "Build Type: $(if ($release) {"Release"} else {"Debug"} )"
@ -124,7 +119,7 @@ if ( $vendor -match "clang" )
$flag_include = '-I' $flag_include = '-I'
$flag_library = '-l' $flag_library = '-l'
$flag_library_path = '-L' $flag_library_path = '-L'
$flag_link_win = '-Wl,' $flag_linker = '-Wl,'
$flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE' $flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE'
$flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS' $flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS'
$flag_link_win_machine_32 = '/MACHINE:X86' $flag_link_win_machine_32 = '/MACHINE:X86'
@ -138,6 +133,7 @@ if ( $vendor -match "clang" )
$flag_path_output = '-o' $flag_path_output = '-o'
$flag_preprocess_non_intergrated = '-no-integrated-cpp' $flag_preprocess_non_intergrated = '-no-integrated-cpp'
$flag_profiling_debug = '-fdebug-info-for-profiling' $flag_profiling_debug = '-fdebug-info-for-profiling'
$flag_set_stack_size = '-stack='
$flag_syntax_only = '-fsyntax-only' $flag_syntax_only = '-fsyntax-only'
$flag_target_arch = '-target' $flag_target_arch = '-target'
$flag_wall = '-Wall' $flag_wall = '-Wall'
@ -211,6 +207,12 @@ if ( $vendor -match "clang" )
$linker_args += $object $linker_args += $object
run-linker $linker $executable $linker_args 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++' $compiler = 'clang++'
@ -253,6 +255,7 @@ if ( $vendor -match "msvc" )
$flag_path_debug = '/Fd' $flag_path_debug = '/Fd'
$flag_path_output = '/Fe' $flag_path_output = '/Fe'
$flag_preprocess_conform = '/Zc:preprocessor' $flag_preprocess_conform = '/Zc:preprocessor'
$flag_set_stack_size = '/F'
$flag_syntax_only = '/Zs' $flag_syntax_only = '/Zs'
# This works because this project uses a single unit to build # This works because this project uses a single unit to build
@ -266,10 +269,10 @@ if ( $vendor -match "msvc" )
$compiler_args += @( $compiler_args += @(
$flag_nologo, $flag_nologo,
$flag_all_cpp, # $flag_all_cpp,
$flag_exceptions_disabled, # $flag_exceptions_disabled,
( $flag_define + '_HAS_EXCEPTIONS=0' ), # ( $flag_define + '_HAS_EXCEPTIONS=0' ),
$flag_RTTI_disabled, # $flag_RTTI_disabled,
$flag_preprocess_conform, $flag_preprocess_conform,
$flag_full_src_path, $flag_full_src_path,
( $flag_path_interm + $path_build + '\' ), ( $flag_path_interm + $path_build + '\' ),
@ -297,18 +300,17 @@ if ( $vendor -match "msvc" )
else { else {
$compiler_args += $flag_link_win_rt_static $compiler_args += $flag_link_win_rt_static
} }
$compiler_args += $includes | ForEach-Object { $flag_include + $_ } $compiler_args += $includes | ForEach-Object { $flag_include + $_ }
$compiler_args += $unit
# $compiler_args += $flag_compile, $unit $compiler_args += $flag_compile, $unit
# run-compiler $compiler $unit $compiler_args run-compiler $compiler $unit $compiler_args
$linker_args += @( $linker_args += @(
$flag_nologo, $flag_nologo,
$flag_link_win_machine_64, $flag_link_win_machine_64,
( $flag_link_win_path_output + $executable ) ( $flag_link_win_path_output + $executable )
) )
if ( $release -eq $false ) { if ( $debug ) {
$linker_args += $flag_link_win_debug $linker_args += $flag_link_win_debug
$linker_args += $flag_link_win_pdb + $pdb $linker_args += $flag_link_win_pdb + $pdb
} }
@ -316,11 +318,12 @@ if ( $vendor -match "msvc" )
} }
$linker_args += $object $linker_args += $object
# run-linker $linker $executable $linker_args run-linker $linker $executable $linker_args
$compiler_args += $flag_linker # $compiler_args += $unit
$compiler_args += $linker_args # $compiler_args += $flag_linker
run-compiler $compiler $unit $compiler_args # $compiler_args += $linker_args
# run-compiler $compiler $unit $compiler_args
} }
$compiler = 'cl' $compiler = 'cl'
@ -393,9 +396,12 @@ $lib_jsl = Join-Path $path_deps 'JoyShockLibrary/x64/JoyShockLibrary.lib'
$unit = Join-Path $path_platform 'handmade_win32.cpp' $unit = Join-Path $path_platform 'handmade_win32.cpp'
$executable = Join-Path $path_build 'handmade_win32.exe' $executable = Join-Path $path_build 'handmade_win32.exe'
$stack_size = 1024 * 1024 * 4
$compiler_args = @( $compiler_args = @(
($flag_define + 'UNICODE'), ($flag_define + 'UNICODE'),
($flag_define + '_UNICODE') ($flag_define + '_UNICODE')
# ($flag_set_stack_size + $stack_size)
) )
$linker_args = @( $linker_args = @(