mirror of
				https://github.com/Ed94/HandmadeHero.git
				synced 2025-11-03 15:26:12 -08:00 
			
		
		
		
	Day 20 complete
This commit is contained in:
		@@ -25,5 +25,5 @@ The build is done in two stages:
 | 
			
		||||
 | 
			
		||||
## Gallery
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								docs/Day 020.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								docs/Day 020.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
# Day 20
 | 
			
		||||
 | 
			
		||||
This stream was easier to follow than the last. I'm starting to gain some intuition on his style.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Archtecture wise
 | 
			
		||||
 | 
			
		||||
I'm deciding to cause some more complexity leakage to the API layers..  
 | 
			
		||||
 | 
			
		||||
I'll restrict it to on-demand but I want to have the engine have configurable options modern games tend to have, if I find it fullfilling todo so I may also expose some configuration for aspects of the engine, most games do not allow the user to mess with.
 | 
			
		||||
 | 
			
		||||
This was something I wanted to do with my previous attempt and constructing a "probeable" game engine, however, that attempt failed because I kept the API granularity far to high resolution.. and it was *Modern C++* which is the project I learned the hardway was not worth the mental effort to maintain or engage with.  
 | 
			
		||||
(I also dove straight into graphics programming with Vulkan... making my own vulkan wrapper library... At least I learned a good chunk of the driver's graphics pipeline..)
 | 
			
		||||
 | 
			
		||||
Debug visualization for the audio was great.
 | 
			
		||||
@@ -5,39 +5,47 @@ NS_ENGINE_BEGIN
 | 
			
		||||
 | 
			
		||||
struct EngineState
 | 
			
		||||
{
 | 
			
		||||
	s32 WavePeriod;
 | 
			
		||||
	s32 WaveSwitch;
 | 
			
		||||
	s32 WaveToneHz;
 | 
			
		||||
	s32 ToneVolume;
 | 
			
		||||
	s32 XOffset;
 | 
			
		||||
	s32 YOffset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using GetSoundSampleValueFn = s16( EngineState* state, SoundBuffer* sound_buffer );
 | 
			
		||||
using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer );
 | 
			
		||||
 | 
			
		||||
internal s16
 | 
			
		||||
square_wave_sample_value( EngineState* state, SoundBuffer* sound_buffer )
 | 
			
		||||
square_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
 | 
			
		||||
{
 | 
			
		||||
	s32 sample_value = (sound_buffer->RunningSampleIndex /  (state->WavePeriod / 2) ) % 2 ?
 | 
			
		||||
	s32 wave_period = sound_buffer->SamplesPerSecond / state->WaveToneHz;
 | 
			
		||||
 | 
			
		||||
	s32 sample_value = (sound_buffer->RunningSampleIndex /  (wave_period / 2) ) % 2 ?
 | 
			
		||||
		state->ToneVolume : - state->ToneVolume;
 | 
			
		||||
 | 
			
		||||
	return scast(s16, sample_value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal s16
 | 
			
		||||
sine_wave_sample_value( EngineState* state, SoundBuffer* sound_buffer )
 | 
			
		||||
sine_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer )
 | 
			
		||||
{
 | 
			
		||||
	local_persist f32 time = 0.f;
 | 
			
		||||
 | 
			
		||||
	s32 wave_period = sound_buffer->SamplesPerSecond / state->WaveToneHz;
 | 
			
		||||
 | 
			
		||||
	// time =  TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod;
 | 
			
		||||
	f32 sine_value   = sinf( time );
 | 
			
		||||
	s16 sample_value = scast(s16, sine_value * scast(f32, state->ToneVolume));
 | 
			
		||||
 | 
			
		||||
	time += TAU * 1.0f / scast(f32, state->WavePeriod );
 | 
			
		||||
	time += TAU * 1.0f / scast(f32, wave_period );
 | 
			
		||||
	if ( time > TAU )
 | 
			
		||||
	{
 | 
			
		||||
		time -= TAU;
 | 
			
		||||
	}
 | 
			
		||||
	return sample_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal void
 | 
			
		||||
output_sound( EngineState* state, SoundBuffer* sound_buffer, GetSoundSampleValueFn* get_sample_value )
 | 
			
		||||
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->NumSamples; ++ sample_index )
 | 
			
		||||
@@ -118,7 +126,7 @@ void shutdown()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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, SoundBuffer* sound_buffer, Memory* memory )
 | 
			
		||||
void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory )
 | 
			
		||||
{
 | 
			
		||||
	// Graphics & Input Test
 | 
			
		||||
	local_persist u32 x_offset = 0;
 | 
			
		||||
@@ -145,10 +153,10 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
 | 
			
		||||
 | 
			
		||||
		state->ToneVolume = 1000;
 | 
			
		||||
		state->WaveToneHz = 120;
 | 
			
		||||
		state->WavePeriod = sound_buffer->SamplesPerSecond / state->WaveToneHz;
 | 
			
		||||
 | 
			
		||||
		state->XOffset    = 0;
 | 
			
		||||
		state->YOffset    = 0;
 | 
			
		||||
		state->WaveSwitch = false;
 | 
			
		||||
 | 
			
		||||
		#if Build_Debug && 0
 | 
			
		||||
		{
 | 
			
		||||
@@ -186,8 +194,13 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
 | 
			
		||||
 | 
			
		||||
	b32 toggle_wave_tone = false;
 | 
			
		||||
 | 
			
		||||
	b32 pause_renderer  = false;
 | 
			
		||||
	local_persist b32 renderer_paused = false;
 | 
			
		||||
 | 
			
		||||
	f32 analog_threshold = 0.5f;
 | 
			
		||||
 | 
			
		||||
	#define pressed( btn ) (btn.EndedDown && btn.HalfTransitions < 1)
 | 
			
		||||
 | 
			
		||||
	if ( controller->DSPad )
 | 
			
		||||
	{
 | 
			
		||||
		DualsensePadState* pad = controller->DSPad;
 | 
			
		||||
@@ -203,7 +216,7 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
 | 
			
		||||
		raise_tone_hz |= pad->Square.EndedDown;
 | 
			
		||||
		lower_tone_hz |= pad->X.EndedDown;
 | 
			
		||||
 | 
			
		||||
		toggle_wave_tone |= pad->Options.EndedDown;
 | 
			
		||||
		toggle_wave_tone |= pressed( pad->Options );
 | 
			
		||||
	}
 | 
			
		||||
	if ( controller->XPad )
 | 
			
		||||
	{
 | 
			
		||||
@@ -220,7 +233,7 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
 | 
			
		||||
		raise_tone_hz |= pad->X.EndedDown;
 | 
			
		||||
		lower_tone_hz |= pad->A.EndedDown;
 | 
			
		||||
 | 
			
		||||
		toggle_wave_tone |= pad->Start.EndedDown;
 | 
			
		||||
		toggle_wave_tone |= pressed( pad->Start );
 | 
			
		||||
	}
 | 
			
		||||
	if ( controller->Keyboard )
 | 
			
		||||
	{
 | 
			
		||||
@@ -237,7 +250,9 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
 | 
			
		||||
		raise_tone_hz |= keyboard->Right.EndedDown;
 | 
			
		||||
		lower_tone_hz |= keyboard->Left.EndedDown;
 | 
			
		||||
 | 
			
		||||
		toggle_wave_tone |= keyboard->Space.EndedDown;
 | 
			
		||||
		pause_renderer |= pressed( keyboard->Pause );
 | 
			
		||||
 | 
			
		||||
		toggle_wave_tone |= pressed( keyboard->Space );
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	x_offset += 3 * move_right;
 | 
			
		||||
@@ -252,31 +267,54 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
 | 
			
		||||
	if ( lower_volume )
 | 
			
		||||
	{
 | 
			
		||||
		state->ToneVolume -= 10;
 | 
			
		||||
		if ( state->ToneVolume <= 0 )
 | 
			
		||||
			state->ToneVolume = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( raise_tone_hz )
 | 
			
		||||
	{
 | 
			
		||||
		state->WaveToneHz += 1;
 | 
			
		||||
		state->WavePeriod  = sound_buffer->SamplesPerSecond / state->WaveToneHz;
 | 
			
		||||
	}
 | 
			
		||||
	if ( lower_tone_hz )
 | 
			
		||||
	{
 | 
			
		||||
		state->WaveToneHz -= 1;
 | 
			
		||||
		state->WavePeriod  = sound_buffer->SamplesPerSecond / state->WaveToneHz;
 | 
			
		||||
		if ( state->WaveToneHz <= 0 )
 | 
			
		||||
			state->WaveToneHz = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( toggle_wave_tone )
 | 
			
		||||
	{
 | 
			
		||||
		wave_switch ^= true;
 | 
			
		||||
		state->WaveSwitch ^= true;
 | 
			
		||||
	}
 | 
			
		||||
	render_weird_graident( back_buffer, x_offset, y_offset );
 | 
			
		||||
 | 
			
		||||
	if ( pause_renderer )
 | 
			
		||||
	{
 | 
			
		||||
		if ( renderer_paused )
 | 
			
		||||
		{
 | 
			
		||||
			platform::set_pause_rendering(false);
 | 
			
		||||
			renderer_paused = false;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			platform::set_pause_rendering(true);
 | 
			
		||||
			renderer_paused = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void update_audio( AudioBuffer* audio_buffer, Memory* memory )
 | 
			
		||||
{
 | 
			
		||||
	EngineState* state = rcast( EngineState*, memory->Persistent );
 | 
			
		||||
	do_once_start
 | 
			
		||||
 | 
			
		||||
	do_once_end
 | 
			
		||||
 | 
			
		||||
	// TODO(Ed) : Allow sample offsets here for more robust platform options
 | 
			
		||||
	if ( ! wave_switch )
 | 
			
		||||
		output_sound( state, sound_buffer, sine_wave_sample_value );
 | 
			
		||||
	if ( ! state->WaveSwitch )
 | 
			
		||||
		output_sound( state, audio_buffer, sine_wave_sample_value );
 | 
			
		||||
	else
 | 
			
		||||
		output_sound( state, sound_buffer, square_wave_sample_value );
 | 
			
		||||
 | 
			
		||||
	render_weird_graident( back_buffer, x_offset, y_offset );
 | 
			
		||||
		output_sound( state, audio_buffer, square_wave_sample_value );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NS_ENGINE_END
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ struct OffscreenBuffer
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// TODO : Will be gutting this once we have other stuff lifted.
 | 
			
		||||
struct SoundBuffer
 | 
			
		||||
struct AudioBuffer
 | 
			
		||||
{
 | 
			
		||||
	s16* Samples;
 | 
			
		||||
	u32  RunningSampleIndex;
 | 
			
		||||
@@ -93,6 +93,7 @@ union KeyboardState
 | 
			
		||||
		DigitalBtn Left;
 | 
			
		||||
		DigitalBtn Right;
 | 
			
		||||
		DigitalBtn Space;
 | 
			
		||||
		DigitalBtn Pause;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,5 +12,5 @@
 | 
			
		||||
#if Build_Unity
 | 
			
		||||
#include "handmade.cpp"
 | 
			
		||||
#include "engine.cpp"
 | 
			
		||||
#include "platform/platform_win32.cpp"
 | 
			
		||||
#include "platform/win32_platform.cpp"
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include "grime.hpp"
 | 
			
		||||
 | 
			
		||||
// JoyShock does not provide a proper c-linkage definition for its structs, so we have to push this warning ignore.
 | 
			
		||||
#ifdef COMPILER_CLANG
 | 
			
		||||
#	pragma clang diagnostic push
 | 
			
		||||
#	pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,8 @@
 | 
			
		||||
	Platform abstraction layer for the project.
 | 
			
		||||
	Services the platform provides to the engine & game.
 | 
			
		||||
 | 
			
		||||
	This should be the only file the engine or game layer can include
 | 
			
		||||
	This should be the only file the engine or game layer can include related to the platform layer.
 | 
			
		||||
	(Public Interface essentially...)
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
@@ -46,9 +47,12 @@ struct Debug_FileContent
 | 
			
		||||
void              debug_file_free_content ( Debug_FileContent* file_content );
 | 
			
		||||
Debug_FileContent debug_file_read_content ( char const* file_path );
 | 
			
		||||
b32               debug_file_write_content( char const* file_path, u32 content_size, void* content_memory );
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
NS_PLATFORM_END
 | 
			
		||||
// Allows the engine or game to pause the renderering of any next frames.
 | 
			
		||||
// ( Prevents blipping of the black buffer )
 | 
			
		||||
void set_pause_rendering( b32 value );
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// On-Demand platform interface.
 | 
			
		||||
// Everything exposed here should be based on a feature a game may want to provide a user
 | 
			
		||||
@@ -56,17 +60,19 @@ NS_PLATFORM_END
 | 
			
		||||
 | 
			
		||||
// TODO(Ed) : Implement this later when settings UI is setup.
 | 
			
		||||
#pragma region Settings Exposure
 | 
			
		||||
// Exposing specific variables for user configuration in settings
 | 
			
		||||
// Exposing specific properties for user configuration in settings
 | 
			
		||||
 | 
			
		||||
// Returns the current monitor refresh rate.
 | 
			
		||||
u32 const get_monitor_refresh_rate();
 | 
			
		||||
u32 get_monitor_refresh_rate();
 | 
			
		||||
 | 
			
		||||
// Sets the monitor refresh rate
 | 
			
		||||
// Must be of the compatiable listing for the monitor the window surface is presenting to.
 | 
			
		||||
void set_monitor_refresh_rate( u32 rate_in_hz );
 | 
			
		||||
 | 
			
		||||
u32 const get_engine_frame_rate_target();
 | 
			
		||||
u32 get_engine_frame_target();
 | 
			
		||||
 | 
			
		||||
void set_engine_frame_rate_target( u32 rate_in_hz );
 | 
			
		||||
void set_engine_frame_target( u32 rate_in_hz );
 | 
			
		||||
 | 
			
		||||
#pragma endregion Settings Exposure
 | 
			
		||||
 | 
			
		||||
NS_PLATFORM_END
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,12 @@ void startup();
 | 
			
		||||
void shutdown();
 | 
			
		||||
 | 
			
		||||
// Needs a contextual reference to four things:
 | 
			
		||||
// Timing, Input, Bitmap Buffer, Sound Buffer
 | 
			
		||||
void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBuffer* sound_buffer, Memory* memory );
 | 
			
		||||
// Timing, Input, Bitmap Buffer
 | 
			
		||||
void update_and_render( InputState* input, OffscreenBuffer* back_buffer,  Memory* memory );
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
void update_audio( AudioBuffer* audio_buffer, Memory* memory );
 | 
			
		||||
 | 
			
		||||
NS_ENGINE_END
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
#	define SWORD_MIN S64_MIN
 | 
			
		||||
#	define SWORD_MAX S64_MAX
 | 
			
		||||
#else
 | 
			
		||||
#	error Unknown architecture size. This library only supports 64 bit architectures.
 | 
			
		||||
#	error Unknown architecture size. This platform only supports 64 bit architectures.
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define F32_MIN 1.17549435e-38f
 | 
			
		||||
 
 | 
			
		||||
@@ -21,16 +21,19 @@
 | 
			
		||||
#include "platform.hpp"
 | 
			
		||||
#include "jsl.hpp" // Using this to get dualsense controllers
 | 
			
		||||
#include "win32.hpp"
 | 
			
		||||
#include <malloc.h>
 | 
			
		||||
 | 
			
		||||
// Engine layer headers
 | 
			
		||||
#include "engine.hpp"
 | 
			
		||||
#include "platform_engine_api.hpp"
 | 
			
		||||
 | 
			
		||||
// Standard-Library stand-ins
 | 
			
		||||
// #include <malloc.h>
 | 
			
		||||
// TODO(Ed) : Remove these ^^
 | 
			
		||||
 | 
			
		||||
// TOOD(Ed): Redo these macros properly later.
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
// TODO(Ed): Redo these macros properly later.
 | 
			
		||||
 | 
			
		||||
#define congrats( message ) do {                                               \
 | 
			
		||||
	JslSetLightColour( 0, (255 << 16) | (215 << 8) );                          \
 | 
			
		||||
	MessageBoxA( 0, message, "Congratulations!", MB_OK | MB_ICONEXCLAMATION ); \
 | 
			
		||||
@@ -58,6 +61,7 @@ ensure_impl( bool condition, char const* message ) {
 | 
			
		||||
NS_PLATFORM_BEGIN
 | 
			
		||||
using namespace win32;
 | 
			
		||||
 | 
			
		||||
// This is the "backbuffer" data related to the windowing surface provided by the operating system.
 | 
			
		||||
struct OffscreenBuffer
 | 
			
		||||
{
 | 
			
		||||
	BITMAPINFO Info;
 | 
			
		||||
@@ -84,15 +88,22 @@ struct DirectSoundBuffer
 | 
			
		||||
	u32                 SamplesPerSecond;
 | 
			
		||||
	u32                 BytesPerSample;
 | 
			
		||||
 | 
			
		||||
	// TODO(Ed) : Makes math easier...
 | 
			
		||||
	u32                 BytesPerSecond;
 | 
			
		||||
	u32                 GuardSampleBytes;
 | 
			
		||||
 | 
			
		||||
	DWORD               IsPlaying;
 | 
			
		||||
	u32                 RunningSampleIndex;
 | 
			
		||||
 | 
			
		||||
	// TODO(Ed) : Should this be in bytes?
 | 
			
		||||
	u32                 LatencySampleCount;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#pragma region Static Data
 | 
			
		||||
// TODO(Ed) : This is a global for now.
 | 
			
		||||
global bool Running;
 | 
			
		||||
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;
 | 
			
		||||
@@ -123,8 +134,15 @@ global f32 Engine_Frame_Target_MS = 1000.f / scast(f32, Engine_Refresh_Hz);
 | 
			
		||||
#if Build_Debug
 | 
			
		||||
struct DebugTimeMarker
 | 
			
		||||
{
 | 
			
		||||
	DWORD PlayCursor;
 | 
			
		||||
	DWORD WriteCursor;
 | 
			
		||||
	DWORD OutputPlayCusror;
 | 
			
		||||
	DWORD OutputWriteCursor;
 | 
			
		||||
	DWORD OutputLocation;
 | 
			
		||||
	DWORD OutputByteCount;
 | 
			
		||||
 | 
			
		||||
	DWORD FlipPlayCursor;
 | 
			
		||||
	DWORD FlipWriteCursor;
 | 
			
		||||
 | 
			
		||||
	DWORD ExpectedFlipCursor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void debug_file_free_content( Debug_FileContent* content )
 | 
			
		||||
@@ -198,51 +216,116 @@ b32 debug_file_write_content( char const* file_path, u32 content_size, void* con
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal void
 | 
			
		||||
debug_draw_vertical( u32 x_pos, u32 top, u32 bottom, u32 color )
 | 
			
		||||
void set_pause_rendering( b32 value )
 | 
			
		||||
{
 | 
			
		||||
	Pause_Rendering = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal void
 | 
			
		||||
debug_draw_vertical( s32 x_pos, s32 top, s32 bottom, s32 color )
 | 
			
		||||
{
 | 
			
		||||
	if ( top <= 0 )
 | 
			
		||||
	{
 | 
			
		||||
		top = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( bottom > Surface_Back_Buffer.Height )
 | 
			
		||||
	{
 | 
			
		||||
		bottom = Surface_Back_Buffer.Height;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( x_pos >= 0 && x_pos < Surface_Back_Buffer.Width )
 | 
			
		||||
	{
 | 
			
		||||
		u8*
 | 
			
		||||
		pixel_byte  = rcast(u8*, Surface_Back_Buffer.Memory);
 | 
			
		||||
		pixel_byte += x_pos * Surface_Back_Buffer.BytesPerPixel;
 | 
			
		||||
		pixel_byte += top   * Surface_Back_Buffer.Pitch;
 | 
			
		||||
 | 
			
		||||
	for ( u32 y = top; y < bottom; ++ y )
 | 
			
		||||
		for ( s32 y = top; y < bottom; ++ y )
 | 
			
		||||
		{
 | 
			
		||||
		u32* pixel = rcast(u32*, pixel_byte);
 | 
			
		||||
			s32* pixel = rcast(s32*, pixel_byte);
 | 
			
		||||
			*pixel = color;
 | 
			
		||||
 | 
			
		||||
			pixel_byte += Surface_Back_Buffer.Pitch;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void
 | 
			
		||||
debug_draw_sound_buffer_marker( DirectSoundBuffer* sound_buffer, f32 coefficient
 | 
			
		||||
debug_draw_sound_buffer_marker( DirectSoundBuffer* sound_buffer, f32 ratio
 | 
			
		||||
	, u32 pad_x, u32 pad_y
 | 
			
		||||
	, u32 top, u32 bottom
 | 
			
		||||
	, DWORD value, u32 color )
 | 
			
		||||
{
 | 
			
		||||
		assert( value < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
		u32 x = pad_x + scast(u32, coefficient * scast(f32, value ));
 | 
			
		||||
	// assert( value < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
	u32 x = pad_x + scast(u32, ratio * scast(f32, value ));
 | 
			
		||||
	debug_draw_vertical( x, top, bottom, color );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal void
 | 
			
		||||
debug_sync_display( DirectSoundBuffer* sound_buffer
 | 
			
		||||
                   , u32 num_markers, DebugTimeMarker* markers
 | 
			
		||||
				   , u32 current_marker
 | 
			
		||||
                   , f32 ms_per_frame )
 | 
			
		||||
{
 | 
			
		||||
	u32 pad_x       = 16;
 | 
			
		||||
	u32 pad_x         = 32;
 | 
			
		||||
	u32 pad_y         = 16;
 | 
			
		||||
	f32 coefficient = scast(f32, Surface_Back_Buffer.Width) / scast(f32, sound_buffer->SecondaryBufferSize);
 | 
			
		||||
 | 
			
		||||
	u32 top    = pad_y;
 | 
			
		||||
	u32 bottom = Surface_Back_Buffer.Height - pad_y;
 | 
			
		||||
	f32 buffers_ratio = scast(f32, Surface_Back_Buffer.Width) / (scast(f32, sound_buffer->SecondaryBufferSize) * 1);
 | 
			
		||||
 | 
			
		||||
	u32 line_height = 64;
 | 
			
		||||
	for ( u32 marker_index = 0; marker_index < num_markers; ++ marker_index )
 | 
			
		||||
	{
 | 
			
		||||
		DebugTimeMarker* marker = & markers[marker_index];
 | 
			
		||||
		debug_draw_sound_buffer_marker( sound_buffer, coefficient, pad_x, pad_y, top, bottom, marker->PlayCursor, 0xFFFFFFFF );
 | 
			
		||||
		debug_draw_sound_buffer_marker( sound_buffer, coefficient, pad_x, pad_y, top, bottom, marker->WriteCursor, 0xFFFF0000 );
 | 
			
		||||
		assert( marker->OutputPlayCusror  < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
		assert( marker->OutputWriteCursor < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
		assert( marker->OutputLocation    < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
		assert( marker->OutputByteCount   < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
		assert( marker->FlipPlayCursor    < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
		assert( marker->FlipWriteCursor   < sound_buffer->SecondaryBufferSize );
 | 
			
		||||
 | 
			
		||||
		DWORD play_color          = 0x88888888;
 | 
			
		||||
		DWORD write_color         = 0x88800000;
 | 
			
		||||
		DWORD expected_flip_color = 0xFFFFF000;
 | 
			
		||||
		DWORD play_window_color   = 0xFFFF00FF;
 | 
			
		||||
 | 
			
		||||
		u32 top    = pad_y;
 | 
			
		||||
		u32 bottom = pad_y + line_height;
 | 
			
		||||
		if ( marker_index == current_marker )
 | 
			
		||||
		{
 | 
			
		||||
			play_color  = 0xFFFFFFFF;
 | 
			
		||||
			write_color = 0xFFFF0000;
 | 
			
		||||
 | 
			
		||||
			top    += pad_y + line_height;
 | 
			
		||||
			bottom += pad_y + line_height;
 | 
			
		||||
 | 
			
		||||
			u32 row_2_top = top;
 | 
			
		||||
 | 
			
		||||
			debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->OutputPlayCusror,  play_color );
 | 
			
		||||
			debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->OutputWriteCursor, write_color );
 | 
			
		||||
 | 
			
		||||
			play_color  = 0xFFFFFFFF;
 | 
			
		||||
			write_color = 0xFFFF0000;
 | 
			
		||||
 | 
			
		||||
			top    += pad_y + line_height;
 | 
			
		||||
			bottom += pad_y + line_height;
 | 
			
		||||
 | 
			
		||||
			debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->OutputLocation, play_color );
 | 
			
		||||
			debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->OutputLocation + marker->OutputByteCount, write_color );
 | 
			
		||||
 | 
			
		||||
			play_color  = 0xFFFFFFFF;
 | 
			
		||||
			write_color = 0xFFFF0000;
 | 
			
		||||
 | 
			
		||||
			top    += pad_y + line_height;
 | 
			
		||||
			bottom += pad_y + line_height;
 | 
			
		||||
 | 
			
		||||
			debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, row_2_top, bottom, marker->ExpectedFlipCursor, expected_flip_color );
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		DWORD play_window = marker->FlipPlayCursor + 480 * sound_buffer->BytesPerSample;
 | 
			
		||||
 | 
			
		||||
		debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->FlipPlayCursor,  play_color );
 | 
			
		||||
		debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, play_window,             play_window_color );
 | 
			
		||||
		debug_draw_sound_buffer_marker( sound_buffer, buffers_ratio, pad_x, pad_y, top, bottom, marker->FlipWriteCursor, write_color );
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -759,8 +842,9 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
	// WinDimensions dimensions = get_window_dimensions( window_handle );
 | 
			
		||||
	resize_dib_section( &Surface_Back_Buffer, 1280, 720 );
 | 
			
		||||
 | 
			
		||||
	DWORD last_play_cursor = 0;
 | 
			
		||||
	b32   sound_is_valid       = false;
 | 
			
		||||
	DWORD ds_cursor_byte_delta = 0;
 | 
			
		||||
	f32   ds_latency_ms        = 0;
 | 
			
		||||
	DirectSoundBuffer ds_sound_buffer;
 | 
			
		||||
	{
 | 
			
		||||
		ds_sound_buffer.IsPlaying        = 0;
 | 
			
		||||
@@ -778,9 +862,15 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
		// ds_clear_sound_buffer( & sound_output );
 | 
			
		||||
		ds_sound_buffer.SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
 | 
			
		||||
 | 
			
		||||
		// Direct sound requires 3 frames of audio latency for no bugs to show u
 | 
			
		||||
		constexpr u32 frames_of_audio_latency = 3;
 | 
			
		||||
		ds_sound_buffer.LatencySampleCount = frames_of_audio_latency * ( ds_sound_buffer.SamplesPerSecond / Engine_Refresh_Hz );
 | 
			
		||||
		ds_sound_buffer.BytesPerSecond   = ds_sound_buffer.SamplesPerSecond * ds_sound_buffer.BytesPerSample;
 | 
			
		||||
		ds_sound_buffer.GuardSampleBytes = (ds_sound_buffer.BytesPerSecond / Engine_Refresh_Hz) / 2;
 | 
			
		||||
 | 
			
		||||
		// TODO(Ed): When switching to core audio at minimum, this will be 1 ms of lag and guard samples wont really be needed.
 | 
			
		||||
		u32 min_guard_sample_bytes = 1540;
 | 
			
		||||
		if ( ds_sound_buffer.GuardSampleBytes < min_guard_sample_bytes )
 | 
			
		||||
		{
 | 
			
		||||
			ds_sound_buffer.GuardSampleBytes = min_guard_sample_bytes;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#if Build_Development
 | 
			
		||||
	u32             debug_marker_index = 0;
 | 
			
		||||
@@ -847,13 +937,13 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
 | 
			
		||||
	u64 last_frame_clock = timing_get_wall_clock();
 | 
			
		||||
	u64 last_frame_cycle = __rdtsc();
 | 
			
		||||
	u64 flip_wall_clock = last_frame_clock;
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
#if 0
 | 
			
		||||
// This tests the play & write cursor update frequency.
 | 
			
		||||
@@ -892,7 +982,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
			// Keyboard Polling
 | 
			
		||||
			// Keyboards are unified for now.
 | 
			
		||||
			{
 | 
			
		||||
				constexpr u32 is_down = 0x8000;
 | 
			
		||||
				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 );
 | 
			
		||||
@@ -906,6 +996,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
				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;
 | 
			
		||||
			}
 | 
			
		||||
@@ -1015,17 +1106,74 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Pain...
 | 
			
		||||
		// DWORD ds_play_cursor;
 | 
			
		||||
		// DWORD ds_write_cursor;
 | 
			
		||||
		DWORD byte_to_lock   = 0;
 | 
			
		||||
		DWORD bytes_to_write = 0;
 | 
			
		||||
		DWORD target_cursor  = 0;
 | 
			
		||||
		// if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) ))
 | 
			
		||||
		if ( sound_is_valid )
 | 
			
		||||
		// Engine's logical iteration and rendering process
 | 
			
		||||
		engine::update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & engine_memory );
 | 
			
		||||
 | 
			
		||||
		u64   audio_frame_start = timing_get_wall_clock();
 | 
			
		||||
		f32   flip_to_audio_ms  = timing_get_ms_elapsed( flip_wall_clock, audio_frame_start );
 | 
			
		||||
 | 
			
		||||
		DWORD ds_play_cursor;
 | 
			
		||||
		DWORD ds_write_cursor;
 | 
			
		||||
		do {
 | 
			
		||||
		/*
 | 
			
		||||
			Sound computation:
 | 
			
		||||
			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).
 | 
			
		||||
 | 
			
		||||
			On wakeup : Check play cursor position and forcast ahead where the cursor will be for the next sync boundary.
 | 
			
		||||
			Based on that, check the write cursor position, if its (at least) before the synch boundary, the target write position is
 | 
			
		||||
			the frame boundary plus one frame. (Low latency)
 | 
			
		||||
 | 
			
		||||
			If its after (sync boundary), we cannot sync audio.
 | 
			
		||||
			Write a frame's worth of audio plus some number of "guard" samples. (High Latency)
 | 
			
		||||
		*/
 | 
			
		||||
			if ( ! SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) ))
 | 
			
		||||
			{
 | 
			
		||||
				sound_is_valid = false;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ( ! sound_is_valid )
 | 
			
		||||
			{
 | 
			
		||||
				ds_sound_buffer.RunningSampleIndex = ds_write_cursor / ds_sound_buffer.BytesPerSample;
 | 
			
		||||
				sound_is_valid = true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			DWORD byte_to_lock   = 0;
 | 
			
		||||
			DWORD target_cursor  = 0;
 | 
			
		||||
			DWORD bytes_to_write = 0;
 | 
			
		||||
 | 
			
		||||
			byte_to_lock = (ds_sound_buffer.RunningSampleIndex * ds_sound_buffer.BytesPerSample) % ds_sound_buffer.SecondaryBufferSize;
 | 
			
		||||
			target_cursor = (last_play_cursor + (ds_sound_buffer.LatencySampleCount * ds_sound_buffer.BytesPerSample)) % ds_sound_buffer.SecondaryBufferSize;
 | 
			
		||||
 | 
			
		||||
			DWORD bytes_per_second = ds_sound_buffer.BytesPerSample * ds_sound_buffer.SamplesPerSecond;
 | 
			
		||||
 | 
			
		||||
			DWORD expected_samplebytes_per_frame = bytes_per_second / Engine_Refresh_Hz;
 | 
			
		||||
 | 
			
		||||
			f32   left_until_flip_ms        = Engine_Frame_Target_MS - flip_to_audio_ms;
 | 
			
		||||
			DWORD expected_bytes_until_flip = scast(DWORD, (left_until_flip_ms / Engine_Frame_Target_MS) * scast(f32, expected_samplebytes_per_frame));
 | 
			
		||||
 | 
			
		||||
			DWORD expected_sync_boundary_byte = ds_play_cursor + expected_bytes_until_flip;
 | 
			
		||||
 | 
			
		||||
			DWORD sync_write_cursor = ds_write_cursor;
 | 
			
		||||
			if ( sync_write_cursor < ds_play_cursor )
 | 
			
		||||
			{
 | 
			
		||||
				// unwrap the cursor so its ahead of the play curosr linearly.
 | 
			
		||||
				sync_write_cursor += ds_sound_buffer.SecondaryBufferSize;
 | 
			
		||||
			}
 | 
			
		||||
			assert( sync_write_cursor >= ds_play_cursor );
 | 
			
		||||
 | 
			
		||||
			sync_write_cursor += ds_sound_buffer.GuardSampleBytes;
 | 
			
		||||
 | 
			
		||||
			b32 audio_interface_is_low_latency = sync_write_cursor < expected_sync_boundary_byte;
 | 
			
		||||
			if ( audio_interface_is_low_latency )
 | 
			
		||||
			{
 | 
			
		||||
				target_cursor = ( expected_sync_boundary_byte + expected_samplebytes_per_frame );
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				target_cursor = (ds_write_cursor +  expected_samplebytes_per_frame + ds_sound_buffer.GuardSampleBytes);
 | 
			
		||||
			}
 | 
			
		||||
			target_cursor %= ds_sound_buffer.SecondaryBufferSize;
 | 
			
		||||
 | 
			
		||||
			if ( byte_to_lock > target_cursor)
 | 
			
		||||
			{
 | 
			
		||||
@@ -1038,48 +1186,66 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
				// Behind play cursor |--byte_to_write-->--play--|
 | 
			
		||||
				bytes_to_write = target_cursor - byte_to_lock;
 | 
			
		||||
			}
 | 
			
		||||
			// sound_is_valid = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Engine Sound
 | 
			
		||||
			// s16 samples[ 48000 * 2 ];
 | 
			
		||||
		engine::SoundBuffer sound_buffer {};
 | 
			
		||||
			engine::AudioBuffer sound_buffer {};
 | 
			
		||||
			sound_buffer.NumSamples         = bytes_to_write / ds_sound_buffer.BytesPerSample;
 | 
			
		||||
			sound_buffer.RunningSampleIndex = ds_sound_buffer.RunningSampleIndex;
 | 
			
		||||
			sound_buffer.SamplesPerSecond   = ds_sound_buffer.SamplesPerSecond;
 | 
			
		||||
			sound_buffer.Samples            = ds_sound_buffer.Samples;
 | 
			
		||||
			engine::update_audio( & sound_buffer, & engine_memory );
 | 
			
		||||
 | 
			
		||||
		engine::update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & sound_buffer, & engine_memory );
 | 
			
		||||
			DebugTimeMarker* marker = & debug_markers[ debug_marker_index ];
 | 
			
		||||
			marker->OutputPlayCusror   = ds_play_cursor;
 | 
			
		||||
			marker->OutputWriteCursor  = ds_write_cursor;
 | 
			
		||||
			marker->OutputLocation     = byte_to_lock;
 | 
			
		||||
			marker->OutputByteCount    = bytes_to_write;
 | 
			
		||||
			marker->ExpectedFlipCursor = expected_sync_boundary_byte;
 | 
			
		||||
 | 
			
		||||
		// Update audio buffer
 | 
			
		||||
		do {
 | 
			
		||||
			if ( ! sound_is_valid )
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
		#if Build_Development && 0
 | 
			
		||||
		#if 0
 | 
			
		||||
			DWORD play_cursor;
 | 
			
		||||
			DWORD write_cursor;
 | 
			
		||||
			ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor );
 | 
			
		||||
		#endif
 | 
			
		||||
			DWORD unwrapped_write_cursor = ds_write_cursor;
 | 
			
		||||
			if ( unwrapped_write_cursor < ds_play_cursor )
 | 
			
		||||
			{
 | 
			
		||||
				unwrapped_write_cursor += ds_sound_buffer.SecondaryBufferSize;
 | 
			
		||||
			}
 | 
			
		||||
			ds_cursor_byte_delta = unwrapped_write_cursor - ds_play_cursor;
 | 
			
		||||
 | 
			
		||||
			constexpr f32 to_milliseconds = 1000.f;
 | 
			
		||||
			f32 sample_delta  = scast(f32, ds_cursor_byte_delta) / scast(f32, ds_sound_buffer.BytesPerSample);
 | 
			
		||||
			f32 ds_latency_s  = sample_delta / scast(f32, ds_sound_buffer.SamplesPerSecond);
 | 
			
		||||
			    ds_latency_ms = ds_latency_s * to_milliseconds;
 | 
			
		||||
 | 
			
		||||
			char text_buffer[256];
 | 
			
		||||
			sprintf_s( text_buffer, sizeof(text_buffer), "BTL:%u TC:%u BTW:%u - PC:%u WC:%u DELTA:%u bytes %f ms\n"
 | 
			
		||||
				, (u32)byte_to_lock, (u32)target_cursor, (u32)bytes_to_write
 | 
			
		||||
				, (u32)play_cursor, (u32)write_cursor, (u32)ds_cursor_byte_delta, ds_latency_ms );
 | 
			
		||||
			OutputDebugStringA( text_buffer );
 | 
			
		||||
		#endif
 | 
			
		||||
			ds_fill_sound_buffer( & ds_sound_buffer, byte_to_lock, bytes_to_write  );
 | 
			
		||||
 | 
			
		||||
			DWORD ds_status = 0;
 | 
			
		||||
			if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetStatus( & ds_status ) ) )
 | 
			
		||||
			{
 | 
			
		||||
				ds_sound_buffer.IsPlaying = ds_status & DSBSTATUS_PLAYING;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ( ! sound_is_valid )
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			ds_fill_sound_buffer( & ds_sound_buffer, byte_to_lock, bytes_to_write  );
 | 
			
		||||
 | 
			
		||||
		#if Build_Development && 0
 | 
			
		||||
			DWORD play_cursor;
 | 
			
		||||
			DWORD write_cursor;
 | 
			
		||||
 | 
			
		||||
			ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor );
 | 
			
		||||
			char text_buffer[256];
 | 
			
		||||
			sprintf_s( text_buffer, sizeof(text_buffer), "LPC:%u BTL:%u TC:%u BTW:%u - PC:%u WC:%u\n"
 | 
			
		||||
				, (u32)last_play_cursor, (u32)byte_to_lock, (u32)target_cursor, (u32)bytes_to_write, (u32)play_cursor, (u32)write_cursor );
 | 
			
		||||
			OutputDebugStringA( text_buffer );
 | 
			
		||||
		#endif
 | 
			
		||||
 | 
			
		||||
			if ( ds_sound_buffer.IsPlaying )
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			ds_sound_buffer.SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
 | 
			
		||||
		} while(0);
 | 
			
		||||
 | 
			
		||||
		// Timing Update
 | 
			
		||||
		{
 | 
			
		||||
			u64 work_frame_end_cycle = __rdtsc();
 | 
			
		||||
			u64 work_frame_end_clock = timing_get_wall_clock();
 | 
			
		||||
 | 
			
		||||
@@ -1099,10 +1265,9 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
				frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock );
 | 
			
		||||
				if ( frame_elapsed_ms < Engine_Frame_Target_MS )
 | 
			
		||||
				{
 | 
			
		||||
				// TODO(Ed) : Log ms discrepancy here.
 | 
			
		||||
					// TODO(Ed) : Log missed sleep here.
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			assert( frame_elapsed_ms < Engine_Frame_Target_MS );
 | 
			
		||||
				while ( frame_elapsed_ms < Engine_Frame_Target_MS )
 | 
			
		||||
				{
 | 
			
		||||
					frame_clock      = timing_get_wall_clock();
 | 
			
		||||
@@ -1116,14 +1281,19 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
 | 
			
		||||
			last_frame_clock = timing_get_wall_clock(); // LastCouner
 | 
			
		||||
			last_frame_cycle = __rdtsc();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Update surface back buffer
 | 
			
		||||
		if ( ! Pause_Rendering )
 | 
			
		||||
		{
 | 
			
		||||
			WinDimensions dimensions     = get_window_dimensions( window_handle );
 | 
			
		||||
			HDC           device_context = GetDC( window_handle );
 | 
			
		||||
 | 
			
		||||
		#if Build_Development
 | 
			
		||||
			debug_sync_display( & ds_sound_buffer, debug_marker_history_size, debug_markers, Engine_Frame_Target_MS );
 | 
			
		||||
			// Note: debug_marker_index is wrong for the 0th index
 | 
			
		||||
			debug_sync_display( & ds_sound_buffer
 | 
			
		||||
				, debug_marker_history_size, debug_markers, debug_marker_index - 1
 | 
			
		||||
				, Engine_Frame_Target_MS );
 | 
			
		||||
		#endif
 | 
			
		||||
 | 
			
		||||
			display_buffer_in_window( device_context, dimensions.Width, dimensions.Height, &Surface_Back_Buffer
 | 
			
		||||
@@ -1131,34 +1301,35 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
 | 
			
		||||
				, dimensions.Width, dimensions.Height );
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		flip_wall_clock = timing_get_wall_clock();
 | 
			
		||||
		#if Build_Development
 | 
			
		||||
		{
 | 
			
		||||
			// Audio Debug
 | 
			
		||||
			DWORD play_cursor  = 0;
 | 
			
		||||
			DWORD write_cursor = 0;
 | 
			
		||||
 | 
			
		||||
			if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor ) ) )
 | 
			
		||||
			{
 | 
			
		||||
				last_play_cursor = play_cursor;
 | 
			
		||||
				if ( ! sound_is_valid )
 | 
			
		||||
				{
 | 
			
		||||
					ds_sound_buffer.RunningSampleIndex = write_cursor / ds_sound_buffer.BytesPerSample;
 | 
			
		||||
					sound_is_valid = true;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				assert( debug_marker_index < debug_marker_history_size )
 | 
			
		||||
				DebugTimeMarker* marker = & debug_markers[ debug_marker_index ];
 | 
			
		||||
 | 
			
		||||
				marker->FlipPlayCursor  = play_cursor;
 | 
			
		||||
				marker->FlipWriteCursor = write_cursor;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				sound_is_valid = false;
 | 
			
		||||
		}
 | 
			
		||||
		#endif
 | 
			
		||||
 | 
			
		||||
		#if Build_Development
 | 
			
		||||
			assert( debug_marker_index < debug_marker_history_size )
 | 
			
		||||
			debug_markers[debug_marker_index] = { play_cursor, write_cursor };
 | 
			
		||||
			debug_marker_index++;
 | 
			
		||||
 | 
			
		||||
			if ( debug_marker_index >= debug_marker_history_size )
 | 
			
		||||
				debug_marker_index = 0;
 | 
			
		||||
		#endif
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	engine::shutdown();
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user