From faa0bacc070bc87758e0d816604787de4a475828 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 30 Sep 2023 20:26:00 -0400 Subject: [PATCH] Day 27 complete --- .vscode/bookmarks.json | 6 +- README.md | 2 +- docs/Day 027.md | 3 + project/engine/engine.cpp | 185 ++++++++++------------ project/engine/engine_to_platform_api.hpp | 4 +- project/engine/test_samples.cpp | 78 +++++++++ project/gen/engine_symbol_table.hpp | 4 +- project/platform/win32_platform.cpp | 15 +- 8 files changed, 180 insertions(+), 117 deletions(-) create mode 100644 docs/Day 027.md create mode 100644 project/engine/test_samples.cpp diff --git a/.vscode/bookmarks.json b/.vscode/bookmarks.json index 63127c5..a5935bf 100644 --- a/.vscode/bookmarks.json +++ b/.vscode/bookmarks.json @@ -19,17 +19,17 @@ "label": "Timing" }, { - "line": 1591, + "line": 1592, "column": 4, "label": "Main Loop : Audio Processing" }, { - "line": 1710, + "line": 1713, "column": 2, "label": "Main Loop : Timing Update" }, { - "line": 1794, + "line": 1797, "column": 0, "label": "Main Loop : End" } diff --git a/README.md b/README.md index 688b19e..cc8470a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The build is done in two stages: ## Milestone -Day 023: Looped Live Code Editing +Day 026: Introduction to Game Architecture ## Gallery diff --git a/docs/Day 027.md b/docs/Day 027.md new file mode 100644 index 0000000..88bc651 --- /dev/null +++ b/docs/Day 027.md @@ -0,0 +1,3 @@ +# Day 27 + +Definitely feels refreshing to not touch win32. diff --git a/project/engine/engine.cpp b/project/engine/engine.cpp index 9fbe9fd..217bd12 100644 --- a/project/engine/engine.cpp +++ b/project/engine/engine.cpp @@ -52,105 +52,7 @@ struct EngineState hh::Memory game_memory; }; -using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer ); - -internal s16 -square_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer ) -{ - s32 wave_period = sound_buffer->samples_per_second / state->wave_tone_hz; - - s32 sample_value = (sound_buffer->running_sample_index / (wave_period / 2) ) % 2 ? - state->tone_volume : - state->tone_volume; - - return scast(s16, sample_value); -} - -internal s16 -sine_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer ) -{ - f32& time = state->sample_wave_sine_time; - - s32 wave_period = sound_buffer->samples_per_second / state->wave_tone_hz; - - // time = TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod; - f32 sine_value = sinf( time ); - s16 sample_value = scast(s16, sine_value * scast(f32, state->tone_volume)); - - time += TAU * 1.0f / scast(f32, wave_period ); - if ( time > TAU ) - { - time -= TAU; - } - return sample_value; -} - -internal void -output_sound( EngineState* state, AudioBuffer* sound_buffer, GetSoundSampleValueFn* get_sample_value ) -{ - s16* sample_out = sound_buffer->samples; - for ( s32 sample_index = 0; sample_index < sound_buffer->num_samples; ++ sample_index ) - { - s16 sample_value = get_sample_value( state, sound_buffer ); - sound_buffer->running_sample_index++; - - // char ms_timing_debug[256] {}; - // wsprintfA( ms_timing_debug, "sample_value: %d\n", sample_value ); - // OutputDebugStringA( ms_timing_debug ); - - *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 ) -{ - // TODO(Ed): See if with optimizer if buffer should be passed by value. - - struct Pixel { - u8 Blue; - u8 Green; - u8 Red; - u8 Alpha; - }; - - u8* row = rcast( u8*, buffer->memory); - local_persist float wildcard = 0; - for ( u32 y = 0; y < buffer->height; ++ y ) - { - // u8* pixel = rcast(u8*, row); - // Pixel* pixel = rcast( Pixel*, row ); - u32* pixel = rcast(u32*, row); - for ( u32 x = 0; x < buffer->width; ++ x ) - { - /* Pixel in memory: - ----------------------------------------------- - Pixel + 0 Pixel + 1 Pixel + 2 Pixel + 3 - RR GG GG XX - ----------------------------------------------- - x86-64 : Little Endian Arch - 0x XX BB GG RR - */ - #if 0 - u8 blue = scast(u8, x + x_offset * u8(wildcard) % 256); - u8 green = scast(u8, y + y_offset - u8(wildcard) % 128); - u8 red = scast(u8, wildcard) % 256 - x * 0.4f; - #else - u8 red = scast(u8, y + y_offset); - u8 green = scast(u8, x + x_offset); - u8 blue = scast(u8, x + y_offset) - scast(u8, y + y_offset); - // blue *= 2; - #endif - - *pixel++ = u32(red/2 << 16) | u32(green/6 << 0) | blue/2 << 0; - } - wildcard += 0.5375f; - row += buffer->pitch; - } -} +#include "test_samples.cpp" internal void render_player( OffscreenBuffer* buffer, s32 pos_x, s32 pos_y ) @@ -236,8 +138,8 @@ void begin_recording_input( Memory* memory, InputState* input, platform::ModuleA file_path.concat( platform_api->path_scratch, file_name ); snprintf( file_path.ptr, file_path.len, "%s%d.hm_replay", file_name.ptr, memory->active_snapshot_slot ); - platform_api->file_delete( memory->active_input_replay_file.path ); memory->active_input_replay_file.path = file_path; + platform_api->file_delete( memory->active_input_replay_file.path ); memory->replay_mode = ReplayMode_Record; } @@ -450,6 +352,76 @@ void input_poll_player_actions( InputState* input, hh::PlayerActions* actions ) } } +internal void +output_sound( EngineState* state, AudioBuffer* sound_buffer, GetSoundSampleValueFn* get_sample_value ) +{ + s16* sample_out = sound_buffer->samples; + for ( s32 sample_index = 0; sample_index < sound_buffer->num_samples; ++ sample_index ) + { + s16 sample_value = get_sample_value( state, sound_buffer ); + sound_buffer->running_sample_index++; + + // char ms_timing_debug[256] {}; + // wsprintfA( ms_timing_debug, "sample_value: %d\n", sample_value ); + // d ms_timing_debug ); + + *sample_out = sample_value; + ++ sample_out; + + *sample_out = sample_value; + ++ sample_out; + } +} + +s32 round_f32_to_s32( f32 value ) +{ + // TODO(Ed) : Casey wants to use an intrinsic + return scast(s32, value + 0.5f); +} + +internal +void draw_rectangle( OffscreenBuffer* buffer + , f32 min_x, f32 min_y + , f32 max_x, f32 max_y + , f32 red, f32 green, f32 blue ) +{ + s32 min_x_32 = round_f32_to_s32( min_x ); + s32 min_y_32 = round_f32_to_s32( min_y ); + s32 max_x_32 = round_f32_to_s32( max_x ); + s32 max_y_32 = round_f32_to_s32( max_y ); + + s32 buffer_width = buffer->width; + s32 buffer_height = buffer->height; + + if ( min_x_32 < 0 ) + min_x_32 = 0; + if ( min_y_32 < 0 ) + min_y_32 = 0; + if ( max_x_32 > buffer_width ) + max_x_32 = buffer_width; + if ( max_y_32 > buffer_height ) + max_y_32 = buffer_height; + + s32 color = (scast(s32, red) << 16) | (scast(s32, green) << 8) | (scast(s32, blue) << 0); + + // Start with the pixel on the top left corner of the rectangle + u8* row = rcast(u8*, buffer->memory ) + + min_x_32 * buffer->bytes_per_pixel + + min_y_32 * buffer->pitch; + + for ( s32 y = min_y_32; y < max_y_32; ++ y ) + { + s32* pixel_32 = rcast(s32*, row); + + for ( s32 x = min_x_32; x < max_x_32; ++ x ) + { + *pixel_32 = color; + pixel_32++; + } + row += buffer->pitch; + } +} + Engine_API void on_module_reload( Memory* memory, platform::ModuleAPI* platfom_api ) { @@ -506,7 +478,7 @@ void shutdown( Memory* memory, platform::ModuleAPI* platform_api ) Engine_API // TODO : I rather expose the back_buffer and sound_buffer using getters for access in any function. -void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ) +void update_and_render( f32 delta_time, InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ) { EngineState* state = rcast( EngineState*, memory->persistent ); assert( sizeof(EngineState) <= memory->persistent_size ); @@ -649,7 +621,14 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* } } - render_weird_graident( back_buffer, state->x_offset, state->y_offset ); + f32 x_offset_f = scast(f32, state->x_offset); + f32 y_offset_f = scast(f32, state->y_offset); + + // render_weird_graident( back_buffer, state->x_offset, state->y_offset ); + draw_rectangle( back_buffer + , 0.f, 0.f + , scast(f32, back_buffer->width), scast(f32, back_buffer->height) + , 0x22, 0x22, 0x22 ); render_player( back_buffer, player->pos_x, player->pos_y ); #if Build_Development @@ -674,7 +653,7 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* } Engine_API -void update_audio( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ) +void update_audio( f32 delta_time, AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ) { EngineState* state = rcast( EngineState*, memory->persistent ); do_once_start diff --git a/project/engine/engine_to_platform_api.hpp b/project/engine/engine_to_platform_api.hpp index 18c2e85..d7676c2 100644 --- a/project/engine/engine_to_platform_api.hpp +++ b/project/engine/engine_to_platform_api.hpp @@ -16,12 +16,12 @@ using ShutdownFn = void( Memory* memory, platform::ModuleAPI* platform_api // Needs a contextual reference to four things: // Timing, Input, Bitmap Buffer -using UpdateAndRenderFn = void ( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ); +using UpdateAndRenderFn = void ( f32 delta_time, InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ); // Audio timing is complicated, processing samples must be done at a different period from the rest of the engine's usual update. // IMPORTANT: This has very tight timing, and cannot be more than a millisecond in execution. // TODO(Ed) : Reduce timing pressure on performance by measuring it or pinging its time. -using UpdateAudioFn = void ( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ); +using UpdateAudioFn = void ( f32 delta_time, AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api, ThreadContext* thread ); struct ModuleAPI { diff --git a/project/engine/test_samples.cpp b/project/engine/test_samples.cpp new file mode 100644 index 0000000..664ac95 --- /dev/null +++ b/project/engine/test_samples.cpp @@ -0,0 +1,78 @@ +using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer ); + +internal s16 +square_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer ) +{ + s32 wave_period = sound_buffer->samples_per_second / state->wave_tone_hz; + + s32 sample_value = (sound_buffer->running_sample_index / (wave_period / 2) ) % 2 ? + state->tone_volume : - state->tone_volume; + + return scast(s16, sample_value); +} + +internal s16 +sine_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer ) +{ + f32& time = state->sample_wave_sine_time; + + s32 wave_period = sound_buffer->samples_per_second / state->wave_tone_hz; + + // time = TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod; + f32 sine_value = sinf( time ); + s16 sample_value = scast(s16, sine_value * scast(f32, state->tone_volume)); + + time += TAU * 1.0f / scast(f32, wave_period ); + if ( time > TAU ) + { + time -= TAU; + } + return sample_value; +} + +internal void +render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) +{ + // TODO(Ed): See if with optimizer if buffer should be passed by value. + + struct Pixel { + u8 Blue; + u8 Green; + u8 Red; + u8 Alpha; + }; + + u8* row = rcast( u8*, buffer->memory); + local_persist float wildcard = 0; + for ( u32 y = 0; y < buffer->height; ++ y ) + { + // u8* pixel = rcast(u8*, row); + // Pixel* pixel = rcast( Pixel*, row ); + u32* pixel = rcast(u32*, row); + for ( u32 x = 0; x < buffer->width; ++ x ) + { + /* Pixel in memory: + ----------------------------------------------- + Pixel + 0 Pixel + 1 Pixel + 2 Pixel + 3 + RR GG GG XX + ----------------------------------------------- + x86-64 : Little Endian Arch + 0x XX BB GG RR + */ + #if 0 + u8 blue = scast(u8, x + x_offset * u8(wildcard) % 256); + u8 green = scast(u8, y + y_offset - u8(wildcard) % 128); + u8 red = scast(u8, wildcard) % 256 - x * 0.4f; + #else + u8 red = scast(u8, y + y_offset); + u8 green = scast(u8, x + x_offset); + u8 blue = scast(u8, x + y_offset) - scast(u8, y + y_offset); + // blue *= 2; + #endif + + *pixel++ = u32(red/2 << 16) | u32(green/6 << 0) | blue/2 << 0; + } + wildcard += 0.5375f; + row += buffer->pitch; + } +} diff --git a/project/gen/engine_symbol_table.hpp b/project/gen/engine_symbol_table.hpp index a043db0..f22d898 100644 --- a/project/gen/engine_symbol_table.hpp +++ b/project/gen/engine_symbol_table.hpp @@ -10,8 +10,8 @@ Str const symbol_startup = str_ascii("?startup@engine@@YAXPEAUMemory@1@PEAUModu constexpr Str const symbol_shutdown = str_ascii("?shutdown@engine@@YAXPEAUMemory@1@PEAUModuleAPI@platform@@@Z"); constexpr -Str const symbol_update_and_render = str_ascii("?update_and_render@engine@@YAXPEAUInputState@1@PEAUOffscreenBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z"); +Str const symbol_update_and_render = str_ascii("?update_and_render@engine@@YAXMPEAUInputState@1@PEAUOffscreenBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z"); constexpr -Str const symbol_update_audio = str_ascii("?update_audio@engine@@YAXPEAUAudioBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z"); +Str const symbol_update_audio = str_ascii("?update_audio@engine@@YAXMPEAUAudioBuffer@1@PEAUMemory@1@PEAUModuleAPI@platform@@PEAUThreadContext@1@@Z"); NS_ENGINE_END diff --git a/project/platform/win32_platform.cpp b/project/platform/win32_platform.cpp index 3c0bae8..08db01e 100644 --- a/project/platform/win32_platform.cpp +++ b/project/platform/win32_platform.cpp @@ -1331,18 +1331,17 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho } window_handle = CreateWindowExW( - WS_EX_LAYERED | WS_EX_TOPMOST, - // WS_EX_LAYERED, + // WS_EX_LAYERED | WS_EX_TOPMOST, + WS_EX_LAYERED, window_class.lpszClassName, L"Handmade Hero", WS_Overlapped_Window | WS_Initially_Visible, CW_Use_Default, CW_Use_Default, // x, y - CW_Use_Default, CW_Use_Default, // width, height + 1920, 1080, // width, height 0, 0, // parent, menu instance, 0 // instance, param ); - if ( ! window_handle ) { // TODO : Diagnostic Logging @@ -1567,8 +1566,10 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho process_pending_window_messages( new_keyboard, new_mouse ); + f32 delta_time = timing_get_seconds_elapsed( last_frame_clock, timing_get_wall_clock() ); + // Engine's logical iteration and rendering process - engine_api.update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.memory ) + engine_api.update_and_render( delta_time, & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.memory ) , & engine_memory, & platform_api, & thread_context_placeholder ); u64 audio_frame_start = timing_get_wall_clock(); @@ -1650,13 +1651,15 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho } // Engine Sound + delta_time = timing_get_seconds_elapsed( last_frame_clock, timing_get_wall_clock() ); + // s16 samples[ 48000 * 2 ]; engine::AudioBuffer sound_buffer {}; sound_buffer.num_samples = bytes_to_write / ds_sound_buffer.bytes_per_sample; sound_buffer.running_sample_index = ds_sound_buffer.running_sample_index; sound_buffer.samples_per_second = ds_sound_buffer.samples_per_second; sound_buffer.samples = ds_sound_buffer.samples; - engine_api.update_audio( & sound_buffer, & engine_memory, & platform_api, & thread_context_placeholder ); + engine_api.update_audio( delta_time, & sound_buffer, & engine_memory, & platform_api, & thread_context_placeholder ); AudioTimeMarker* marker = & audio_time_markers[ audio_marker_index ]; marker->output_play_cursor = ds_play_cursor;