mirror of
https://github.com/Ed94/HandmadeHero.git
synced 2024-12-22 06:14:45 -08:00
Day 20 complete
This commit is contained in:
parent
6fb75fd1ff
commit
805d9ec3a0
@ -6,7 +6,7 @@ Any code I do for this [series](https://handmadehero.org) will be here.
|
|||||||
|
|
||||||
## Scripts
|
## Scripts
|
||||||
|
|
||||||
* `build.ps1` - Builds the project use `.\scripts\build msvc debug` or `.\scripts\build clang debug`.
|
* `build.ps1` - Builds the project use `.\scripts\build msvc debug` or `.\scripts\build clang debug`.
|
||||||
* `optimize` for optimized builds.
|
* `optimize` for optimized builds.
|
||||||
* `dev` for development builds. ( Dev memory layout and code paths compiled ).
|
* `dev` for development builds. ( Dev memory layout and code paths compiled ).
|
||||||
* `clean.ps1` - Cleans the project
|
* `clean.ps1` - Cleans the project
|
||||||
@ -25,5 +25,5 @@ The build is done in two stages:
|
|||||||
|
|
||||||
## Gallery
|
## Gallery
|
||||||
|
|
||||||
![img](https://files.catbox.moe/xyh7u7.png)
|
![img](https://files.catbox.moe/9zau4s.png)
|
||||||
![img](https://files.catbox.moe/b7ifa8.png)
|
![img](https://files.catbox.moe/b7ifa8.png)
|
||||||
|
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
|
struct EngineState
|
||||||
{
|
{
|
||||||
s32 WavePeriod;
|
s32 WaveSwitch;
|
||||||
s32 WaveToneHz;
|
s32 WaveToneHz;
|
||||||
s32 ToneVolume;
|
s32 ToneVolume;
|
||||||
s32 XOffset;
|
s32 XOffset;
|
||||||
s32 YOffset;
|
s32 YOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
using GetSoundSampleValueFn = s16( EngineState* state, SoundBuffer* sound_buffer );
|
using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer );
|
||||||
|
|
||||||
internal s16
|
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;
|
state->ToneVolume : - state->ToneVolume;
|
||||||
|
|
||||||
return scast(s16, sample_value);
|
return scast(s16, sample_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal s16
|
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;
|
local_persist f32 time = 0.f;
|
||||||
|
|
||||||
|
s32 wave_period = sound_buffer->SamplesPerSecond / state->WaveToneHz;
|
||||||
|
|
||||||
// time = TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod;
|
// time = TAU * (f32)sound_buffer->RunningSampleIndex / (f32)SoundTest_WavePeriod;
|
||||||
f32 sine_value = sinf( time );
|
f32 sine_value = sinf( time );
|
||||||
s16 sample_value = scast(s16, sine_value * scast(f32, state->ToneVolume));
|
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;
|
return sample_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
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;
|
s16* sample_out = sound_buffer->Samples;
|
||||||
for ( s32 sample_index = 0; sample_index < sound_buffer->NumSamples; ++ sample_index )
|
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.
|
// 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
|
// Graphics & Input Test
|
||||||
local_persist u32 x_offset = 0;
|
local_persist u32 x_offset = 0;
|
||||||
@ -145,10 +153,10 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
|
|||||||
|
|
||||||
state->ToneVolume = 1000;
|
state->ToneVolume = 1000;
|
||||||
state->WaveToneHz = 120;
|
state->WaveToneHz = 120;
|
||||||
state->WavePeriod = sound_buffer->SamplesPerSecond / state->WaveToneHz;
|
|
||||||
|
|
||||||
state->XOffset = 0;
|
state->XOffset = 0;
|
||||||
state->YOffset = 0;
|
state->YOffset = 0;
|
||||||
|
state->WaveSwitch = false;
|
||||||
|
|
||||||
#if Build_Debug && 0
|
#if Build_Debug && 0
|
||||||
{
|
{
|
||||||
@ -186,8 +194,13 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
|
|||||||
|
|
||||||
b32 toggle_wave_tone = false;
|
b32 toggle_wave_tone = false;
|
||||||
|
|
||||||
|
b32 pause_renderer = false;
|
||||||
|
local_persist b32 renderer_paused = false;
|
||||||
|
|
||||||
f32 analog_threshold = 0.5f;
|
f32 analog_threshold = 0.5f;
|
||||||
|
|
||||||
|
#define pressed( btn ) (btn.EndedDown && btn.HalfTransitions < 1)
|
||||||
|
|
||||||
if ( controller->DSPad )
|
if ( controller->DSPad )
|
||||||
{
|
{
|
||||||
DualsensePadState* pad = 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;
|
raise_tone_hz |= pad->Square.EndedDown;
|
||||||
lower_tone_hz |= pad->X.EndedDown;
|
lower_tone_hz |= pad->X.EndedDown;
|
||||||
|
|
||||||
toggle_wave_tone |= pad->Options.EndedDown;
|
toggle_wave_tone |= pressed( pad->Options );
|
||||||
}
|
}
|
||||||
if ( controller->XPad )
|
if ( controller->XPad )
|
||||||
{
|
{
|
||||||
@ -220,7 +233,7 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
|
|||||||
raise_tone_hz |= pad->X.EndedDown;
|
raise_tone_hz |= pad->X.EndedDown;
|
||||||
lower_tone_hz |= pad->A.EndedDown;
|
lower_tone_hz |= pad->A.EndedDown;
|
||||||
|
|
||||||
toggle_wave_tone |= pad->Start.EndedDown;
|
toggle_wave_tone |= pressed( pad->Start );
|
||||||
}
|
}
|
||||||
if ( controller->Keyboard )
|
if ( controller->Keyboard )
|
||||||
{
|
{
|
||||||
@ -237,7 +250,9 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
|
|||||||
raise_tone_hz |= keyboard->Right.EndedDown;
|
raise_tone_hz |= keyboard->Right.EndedDown;
|
||||||
lower_tone_hz |= keyboard->Left.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;
|
x_offset += 3 * move_right;
|
||||||
@ -252,31 +267,54 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, SoundBu
|
|||||||
if ( lower_volume )
|
if ( lower_volume )
|
||||||
{
|
{
|
||||||
state->ToneVolume -= 10;
|
state->ToneVolume -= 10;
|
||||||
|
if ( state->ToneVolume <= 0 )
|
||||||
|
state->ToneVolume = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( raise_tone_hz )
|
if ( raise_tone_hz )
|
||||||
{
|
{
|
||||||
state->WaveToneHz += 1;
|
state->WaveToneHz += 1;
|
||||||
state->WavePeriod = sound_buffer->SamplesPerSecond / state->WaveToneHz;
|
|
||||||
}
|
}
|
||||||
if ( lower_tone_hz )
|
if ( lower_tone_hz )
|
||||||
{
|
{
|
||||||
state->WaveToneHz -= 1;
|
state->WaveToneHz -= 1;
|
||||||
state->WavePeriod = sound_buffer->SamplesPerSecond / state->WaveToneHz;
|
if ( state->WaveToneHz <= 0 )
|
||||||
|
state->WaveToneHz = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( toggle_wave_tone )
|
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
|
// TODO(Ed) : Allow sample offsets here for more robust platform options
|
||||||
if ( ! wave_switch )
|
if ( ! state->WaveSwitch )
|
||||||
output_sound( state, sound_buffer, sine_wave_sample_value );
|
output_sound( state, audio_buffer, sine_wave_sample_value );
|
||||||
else
|
else
|
||||||
output_sound( state, sound_buffer, square_wave_sample_value );
|
output_sound( state, audio_buffer, square_wave_sample_value );
|
||||||
|
|
||||||
render_weird_graident( back_buffer, x_offset, y_offset );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_ENGINE_END
|
NS_ENGINE_END
|
||||||
|
@ -44,7 +44,7 @@ struct OffscreenBuffer
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO : Will be gutting this once we have other stuff lifted.
|
// TODO : Will be gutting this once we have other stuff lifted.
|
||||||
struct SoundBuffer
|
struct AudioBuffer
|
||||||
{
|
{
|
||||||
s16* Samples;
|
s16* Samples;
|
||||||
u32 RunningSampleIndex;
|
u32 RunningSampleIndex;
|
||||||
@ -93,6 +93,7 @@ union KeyboardState
|
|||||||
DigitalBtn Left;
|
DigitalBtn Left;
|
||||||
DigitalBtn Right;
|
DigitalBtn Right;
|
||||||
DigitalBtn Space;
|
DigitalBtn Space;
|
||||||
|
DigitalBtn Pause;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,5 +12,5 @@
|
|||||||
#if Build_Unity
|
#if Build_Unity
|
||||||
#include "handmade.cpp"
|
#include "handmade.cpp"
|
||||||
#include "engine.cpp"
|
#include "engine.cpp"
|
||||||
#include "platform/platform_win32.cpp"
|
#include "platform/win32_platform.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "grime.hpp"
|
#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
|
#ifdef COMPILER_CLANG
|
||||||
# pragma clang diagnostic push
|
# pragma clang diagnostic push
|
||||||
# pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
|
# pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
Platform abstraction layer for the project.
|
Platform abstraction layer for the project.
|
||||||
Services the platform provides to the engine & game.
|
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
|
#pragma once
|
||||||
@ -46,9 +47,12 @@ struct Debug_FileContent
|
|||||||
void debug_file_free_content ( Debug_FileContent* file_content );
|
void debug_file_free_content ( Debug_FileContent* file_content );
|
||||||
Debug_FileContent debug_file_read_content ( char const* file_path );
|
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 );
|
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.
|
// On-Demand platform interface.
|
||||||
// Everything exposed here should be based on a feature a game may want to provide a user
|
// 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.
|
// TODO(Ed) : Implement this later when settings UI is setup.
|
||||||
#pragma region Settings Exposure
|
#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.
|
// Returns the current monitor refresh rate.
|
||||||
u32 const get_monitor_refresh_rate();
|
u32 get_monitor_refresh_rate();
|
||||||
|
|
||||||
// Sets the monitor refresh rate
|
// Sets the monitor refresh rate
|
||||||
// Must be of the compatiable listing for the monitor the window surface is presenting to.
|
// Must be of the compatiable listing for the monitor the window surface is presenting to.
|
||||||
void set_monitor_refresh_rate( u32 rate_in_hz );
|
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
|
#pragma endregion Settings Exposure
|
||||||
|
|
||||||
|
NS_PLATFORM_END
|
||||||
|
@ -10,7 +10,12 @@ void startup();
|
|||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
// Needs a contextual reference to four things:
|
// Needs a contextual reference to four things:
|
||||||
// Timing, Input, Bitmap Buffer, Sound Buffer
|
// Timing, Input, Bitmap Buffer
|
||||||
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 );
|
||||||
|
|
||||||
|
// 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
|
NS_ENGINE_END
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
# define SWORD_MIN S64_MIN
|
# define SWORD_MIN S64_MIN
|
||||||
# define SWORD_MAX S64_MAX
|
# define SWORD_MAX S64_MAX
|
||||||
#else
|
#else
|
||||||
# error Unknown architecture size. This library only supports 64 bit architectures.
|
# error Unknown architecture size. This platform only supports 64 bit architectures.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define F32_MIN 1.17549435e-38f
|
#define F32_MIN 1.17549435e-38f
|
||||||
|
@ -21,16 +21,19 @@
|
|||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
#include "jsl.hpp" // Using this to get dualsense controllers
|
#include "jsl.hpp" // Using this to get dualsense controllers
|
||||||
#include "win32.hpp"
|
#include "win32.hpp"
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
// Engine layer headers
|
// Engine layer headers
|
||||||
#include "engine.hpp"
|
#include "engine.hpp"
|
||||||
#include "platform_engine_api.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
|
#if 1
|
||||||
|
// TODO(Ed): Redo these macros properly later.
|
||||||
|
|
||||||
#define congrats( message ) do { \
|
#define congrats( message ) do { \
|
||||||
JslSetLightColour( 0, (255 << 16) | (215 << 8) ); \
|
JslSetLightColour( 0, (255 << 16) | (215 << 8) ); \
|
||||||
MessageBoxA( 0, message, "Congratulations!", MB_OK | MB_ICONEXCLAMATION ); \
|
MessageBoxA( 0, message, "Congratulations!", MB_OK | MB_ICONEXCLAMATION ); \
|
||||||
@ -58,6 +61,7 @@ ensure_impl( bool condition, char const* message ) {
|
|||||||
NS_PLATFORM_BEGIN
|
NS_PLATFORM_BEGIN
|
||||||
using namespace win32;
|
using namespace win32;
|
||||||
|
|
||||||
|
// This is the "backbuffer" data related to the windowing surface provided by the operating system.
|
||||||
struct OffscreenBuffer
|
struct OffscreenBuffer
|
||||||
{
|
{
|
||||||
BITMAPINFO Info;
|
BITMAPINFO Info;
|
||||||
@ -84,15 +88,22 @@ struct DirectSoundBuffer
|
|||||||
u32 SamplesPerSecond;
|
u32 SamplesPerSecond;
|
||||||
u32 BytesPerSample;
|
u32 BytesPerSample;
|
||||||
|
|
||||||
|
// TODO(Ed) : Makes math easier...
|
||||||
|
u32 BytesPerSecond;
|
||||||
|
u32 GuardSampleBytes;
|
||||||
|
|
||||||
DWORD IsPlaying;
|
DWORD IsPlaying;
|
||||||
u32 RunningSampleIndex;
|
u32 RunningSampleIndex;
|
||||||
|
|
||||||
|
// TODO(Ed) : Should this be in bytes?
|
||||||
u32 LatencySampleCount;
|
u32 LatencySampleCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#pragma region Static Data
|
#pragma region Static Data
|
||||||
// TODO(Ed) : This is a global for now.
|
// 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)
|
// Max controllers for the platform layer and thus for all other layers is 4. (Sanity and xinput limit)
|
||||||
constexpr u32 Max_Controllers = 4;
|
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
|
#if Build_Debug
|
||||||
struct DebugTimeMarker
|
struct DebugTimeMarker
|
||||||
{
|
{
|
||||||
DWORD PlayCursor;
|
DWORD OutputPlayCusror;
|
||||||
DWORD WriteCursor;
|
DWORD OutputWriteCursor;
|
||||||
|
DWORD OutputLocation;
|
||||||
|
DWORD OutputByteCount;
|
||||||
|
|
||||||
|
DWORD FlipPlayCursor;
|
||||||
|
DWORD FlipWriteCursor;
|
||||||
|
|
||||||
|
DWORD ExpectedFlipCursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
void debug_file_free_content( Debug_FileContent* content )
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
void set_pause_rendering( b32 value )
|
||||||
debug_draw_vertical( u32 x_pos, u32 top, u32 bottom, u32 color )
|
|
||||||
{
|
{
|
||||||
u8*
|
Pause_Rendering = value;
|
||||||
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 )
|
internal void
|
||||||
|
debug_draw_vertical( s32 x_pos, s32 top, s32 bottom, s32 color )
|
||||||
|
{
|
||||||
|
if ( top <= 0 )
|
||||||
{
|
{
|
||||||
u32* pixel = rcast(u32*, pixel_byte);
|
top = 0;
|
||||||
*pixel = color;
|
}
|
||||||
|
|
||||||
pixel_byte += Surface_Back_Buffer.Pitch;
|
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 ( s32 y = top; y < bottom; ++ y )
|
||||||
|
{
|
||||||
|
s32* pixel = rcast(s32*, pixel_byte);
|
||||||
|
*pixel = color;
|
||||||
|
|
||||||
|
pixel_byte += Surface_Back_Buffer.Pitch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
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 pad_x, u32 pad_y
|
||||||
, u32 top, u32 bottom
|
, u32 top, u32 bottom
|
||||||
, DWORD value, u32 color )
|
, DWORD value, u32 color )
|
||||||
{
|
{
|
||||||
assert( value < sound_buffer->SecondaryBufferSize );
|
// assert( value < sound_buffer->SecondaryBufferSize );
|
||||||
u32 x = pad_x + scast(u32, coefficient * scast(f32, value ));
|
u32 x = pad_x + scast(u32, ratio * scast(f32, value ));
|
||||||
debug_draw_vertical( x, top, bottom, color );
|
debug_draw_vertical( x, top, bottom, color );
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
debug_sync_display( DirectSoundBuffer* sound_buffer
|
debug_sync_display( DirectSoundBuffer* sound_buffer
|
||||||
, u32 num_markers, DebugTimeMarker* markers
|
, u32 num_markers, DebugTimeMarker* markers
|
||||||
|
, u32 current_marker
|
||||||
, f32 ms_per_frame )
|
, f32 ms_per_frame )
|
||||||
{
|
{
|
||||||
u32 pad_x = 16;
|
u32 pad_x = 32;
|
||||||
u32 pad_y = 16;
|
u32 pad_y = 16;
|
||||||
f32 coefficient = scast(f32, Surface_Back_Buffer.Width) / scast(f32, sound_buffer->SecondaryBufferSize);
|
f32 buffers_ratio = scast(f32, Surface_Back_Buffer.Width) / (scast(f32, sound_buffer->SecondaryBufferSize) * 1);
|
||||||
|
|
||||||
u32 top = pad_y;
|
|
||||||
u32 bottom = Surface_Back_Buffer.Height - pad_y;
|
|
||||||
|
|
||||||
|
u32 line_height = 64;
|
||||||
for ( u32 marker_index = 0; marker_index < num_markers; ++ marker_index )
|
for ( u32 marker_index = 0; marker_index < num_markers; ++ marker_index )
|
||||||
{
|
{
|
||||||
DebugTimeMarker* marker = & 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 );
|
assert( marker->OutputPlayCusror < sound_buffer->SecondaryBufferSize );
|
||||||
debug_draw_sound_buffer_marker( sound_buffer, coefficient, pad_x, pad_y, top, bottom, marker->WriteCursor, 0xFFFF0000 );
|
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
|
#endif
|
||||||
@ -759,8 +842,9 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
|||||||
// WinDimensions dimensions = get_window_dimensions( window_handle );
|
// WinDimensions dimensions = get_window_dimensions( window_handle );
|
||||||
resize_dib_section( &Surface_Back_Buffer, 1280, 720 );
|
resize_dib_section( &Surface_Back_Buffer, 1280, 720 );
|
||||||
|
|
||||||
DWORD last_play_cursor = 0;
|
b32 sound_is_valid = false;
|
||||||
b32 sound_is_valid = false;
|
DWORD ds_cursor_byte_delta = 0;
|
||||||
|
f32 ds_latency_ms = 0;
|
||||||
DirectSoundBuffer ds_sound_buffer;
|
DirectSoundBuffer ds_sound_buffer;
|
||||||
{
|
{
|
||||||
ds_sound_buffer.IsPlaying = 0;
|
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_clear_sound_buffer( & sound_output );
|
||||||
ds_sound_buffer.SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
|
ds_sound_buffer.SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
|
||||||
|
|
||||||
// Direct sound requires 3 frames of audio latency for no bugs to show u
|
ds_sound_buffer.BytesPerSecond = ds_sound_buffer.SamplesPerSecond * ds_sound_buffer.BytesPerSample;
|
||||||
constexpr u32 frames_of_audio_latency = 3;
|
ds_sound_buffer.GuardSampleBytes = (ds_sound_buffer.BytesPerSecond / Engine_Refresh_Hz) / 2;
|
||||||
ds_sound_buffer.LatencySampleCount = frames_of_audio_latency * ( ds_sound_buffer.SamplesPerSecond / Engine_Refresh_Hz );
|
|
||||||
|
// 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
|
#if Build_Development
|
||||||
u32 debug_marker_index = 0;
|
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_clock = timing_get_wall_clock();
|
||||||
u64 last_frame_cycle = __rdtsc();
|
u64 last_frame_cycle = __rdtsc();
|
||||||
|
u64 flip_wall_clock = last_frame_clock;
|
||||||
|
|
||||||
#if Build_Development
|
#if Build_Development
|
||||||
u64 startup_cycles = last_frame_cycle - launch_cycle;
|
u64 startup_cycles = last_frame_cycle - launch_cycle;
|
||||||
f32 startup_ms = timing_get_ms_elapsed( launch_clock, last_frame_clock );
|
f32 startup_ms = timing_get_ms_elapsed( launch_clock, last_frame_clock );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
Running = true;
|
Running = true;
|
||||||
#if 0
|
#if 0
|
||||||
// This tests the play & write cursor update frequency.
|
// This tests the play & write cursor update frequency.
|
||||||
@ -892,7 +982,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
|||||||
// Keyboard Polling
|
// Keyboard Polling
|
||||||
// Keyboards are unified for now.
|
// 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->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->E, & new_keyboard->E, GetAsyncKeyState( 'E' ), is_down );
|
||||||
input_process_digital_btn( & old_keyboard->W, & new_keyboard->W, GetAsyncKeyState( 'W' ), 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->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->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->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;
|
input.Controllers[0].Keyboard = new_keyboard;
|
||||||
}
|
}
|
||||||
@ -1015,17 +1106,74 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pain...
|
// Engine's logical iteration and rendering process
|
||||||
// DWORD ds_play_cursor;
|
engine::update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & engine_memory );
|
||||||
// DWORD ds_write_cursor;
|
|
||||||
DWORD byte_to_lock = 0;
|
u64 audio_frame_start = timing_get_wall_clock();
|
||||||
DWORD bytes_to_write = 0;
|
f32 flip_to_audio_ms = timing_get_ms_elapsed( flip_wall_clock, audio_frame_start );
|
||||||
DWORD target_cursor = 0;
|
|
||||||
// if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & ds_play_cursor, & ds_write_cursor ) ))
|
DWORD ds_play_cursor;
|
||||||
if ( sound_is_valid )
|
DWORD ds_write_cursor;
|
||||||
{
|
do {
|
||||||
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;
|
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;
|
||||||
|
|
||||||
|
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)
|
if ( byte_to_lock > target_cursor)
|
||||||
{
|
{
|
||||||
@ -1038,92 +1186,114 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
|||||||
// Behind play cursor |--byte_to_write-->--play--|
|
// Behind play cursor |--byte_to_write-->--play--|
|
||||||
bytes_to_write = target_cursor - byte_to_lock;
|
bytes_to_write = target_cursor - byte_to_lock;
|
||||||
}
|
}
|
||||||
// sound_is_valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// s16 samples[ 48000 * 2 ];
|
// Engine Sound
|
||||||
engine::SoundBuffer sound_buffer {};
|
// s16 samples[ 48000 * 2 ];
|
||||||
sound_buffer.NumSamples = bytes_to_write / ds_sound_buffer.BytesPerSample;
|
engine::AudioBuffer sound_buffer {};
|
||||||
sound_buffer.RunningSampleIndex = ds_sound_buffer.RunningSampleIndex;
|
sound_buffer.NumSamples = bytes_to_write / ds_sound_buffer.BytesPerSample;
|
||||||
sound_buffer.SamplesPerSecond = ds_sound_buffer.SamplesPerSecond;
|
sound_buffer.RunningSampleIndex = ds_sound_buffer.RunningSampleIndex;
|
||||||
sound_buffer.Samples = ds_sound_buffer.Samples;
|
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
|
// 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;
|
DWORD ds_status = 0;
|
||||||
if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetStatus( & ds_status ) ) )
|
if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetStatus( & ds_status ) ) )
|
||||||
{
|
{
|
||||||
ds_sound_buffer.IsPlaying = ds_status & DSBSTATUS_PLAYING;
|
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 )
|
if ( ds_sound_buffer.IsPlaying )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ds_sound_buffer.SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
|
ds_sound_buffer.SecondaryBuffer->Play( 0, 0, DSBPLAY_LOOPING );
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
u64 work_frame_end_cycle = __rdtsc();
|
// Timing Update
|
||||||
u64 work_frame_end_clock = timing_get_wall_clock();
|
|
||||||
|
|
||||||
f32 work_frame_ms = timing_get_ms_elapsed( last_frame_clock, work_frame_end_clock ); // WorkSecondsElapsed
|
|
||||||
f32 work_cycles = timing_get_ms_elapsed( last_frame_cycle, work_frame_end_cycle );
|
|
||||||
|
|
||||||
f32 frame_elapsed_ms = work_frame_ms; // SecondsElapsedForFrame
|
|
||||||
if ( frame_elapsed_ms < Engine_Frame_Target_MS )
|
|
||||||
{
|
{
|
||||||
s32 sleep_ms = scast(DWORD, (Engine_Frame_Target_MS - frame_elapsed_ms)) - 1;
|
u64 work_frame_end_cycle = __rdtsc();
|
||||||
if ( sleep_ms > 0 && ! sub_ms_granularity_required && sleep_is_granular )
|
u64 work_frame_end_clock = timing_get_wall_clock();
|
||||||
{
|
|
||||||
Sleep( scast(DWORD, sleep_ms) );
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 frame_clock = timing_get_wall_clock();
|
f32 work_frame_ms = timing_get_ms_elapsed( last_frame_clock, work_frame_end_clock ); // WorkSecondsElapsed
|
||||||
frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock );
|
f32 work_cycles = timing_get_ms_elapsed( last_frame_cycle, work_frame_end_cycle );
|
||||||
|
|
||||||
|
f32 frame_elapsed_ms = work_frame_ms; // SecondsElapsedForFrame
|
||||||
if ( frame_elapsed_ms < Engine_Frame_Target_MS )
|
if ( frame_elapsed_ms < Engine_Frame_Target_MS )
|
||||||
{
|
{
|
||||||
// TODO(Ed) : Log ms discrepancy here.
|
s32 sleep_ms = scast(DWORD, (Engine_Frame_Target_MS - frame_elapsed_ms)) - 1;
|
||||||
}
|
if ( sleep_ms > 0 && ! sub_ms_granularity_required && sleep_is_granular )
|
||||||
|
{
|
||||||
|
Sleep( scast(DWORD, sleep_ms) );
|
||||||
|
}
|
||||||
|
|
||||||
assert( frame_elapsed_ms < Engine_Frame_Target_MS );
|
u64 frame_clock = timing_get_wall_clock();
|
||||||
while ( frame_elapsed_ms < Engine_Frame_Target_MS )
|
|
||||||
{
|
|
||||||
frame_clock = timing_get_wall_clock();
|
|
||||||
frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock );
|
frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock );
|
||||||
}
|
if ( frame_elapsed_ms < Engine_Frame_Target_MS )
|
||||||
}
|
{
|
||||||
else
|
// TODO(Ed) : Log missed sleep here.
|
||||||
{
|
}
|
||||||
// TODO(Ed) : Missed the display sync window!
|
|
||||||
}
|
|
||||||
|
|
||||||
last_frame_clock = timing_get_wall_clock(); // LastCouner
|
while ( frame_elapsed_ms < Engine_Frame_Target_MS )
|
||||||
last_frame_cycle = __rdtsc();
|
{
|
||||||
|
frame_clock = timing_get_wall_clock();
|
||||||
|
frame_elapsed_ms = timing_get_ms_elapsed( last_frame_clock, frame_clock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO(Ed) : Missed the display sync window!
|
||||||
|
}
|
||||||
|
|
||||||
|
last_frame_clock = timing_get_wall_clock(); // LastCouner
|
||||||
|
last_frame_cycle = __rdtsc();
|
||||||
|
}
|
||||||
|
|
||||||
// Update surface back buffer
|
// Update surface back buffer
|
||||||
|
if ( ! Pause_Rendering )
|
||||||
{
|
{
|
||||||
WinDimensions dimensions = get_window_dimensions( window_handle );
|
WinDimensions dimensions = get_window_dimensions( window_handle );
|
||||||
HDC device_context = GetDC( window_handle );
|
HDC device_context = GetDC( window_handle );
|
||||||
|
|
||||||
#if Build_Development
|
#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
|
#endif
|
||||||
|
|
||||||
display_buffer_in_window( device_context, dimensions.Width, dimensions.Height, &Surface_Back_Buffer
|
display_buffer_in_window( device_context, dimensions.Width, dimensions.Height, &Surface_Back_Buffer
|
||||||
@ -1131,33 +1301,34 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
|||||||
, dimensions.Width, dimensions.Height );
|
, dimensions.Width, dimensions.Height );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flip_wall_clock = timing_get_wall_clock();
|
||||||
|
#if Build_Development
|
||||||
{
|
{
|
||||||
|
// Audio Debug
|
||||||
DWORD play_cursor = 0;
|
DWORD play_cursor = 0;
|
||||||
DWORD write_cursor = 0;
|
DWORD write_cursor = 0;
|
||||||
|
|
||||||
if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor ) ) )
|
if ( SUCCEEDED( ds_sound_buffer.SecondaryBuffer->GetCurrentPosition( & play_cursor, & write_cursor ) ) )
|
||||||
{
|
{
|
||||||
last_play_cursor = play_cursor;
|
|
||||||
if ( ! sound_is_valid )
|
if ( ! sound_is_valid )
|
||||||
{
|
{
|
||||||
ds_sound_buffer.RunningSampleIndex = write_cursor / ds_sound_buffer.BytesPerSample;
|
ds_sound_buffer.RunningSampleIndex = write_cursor / ds_sound_buffer.BytesPerSample;
|
||||||
sound_is_valid = true;
|
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
|
}
|
||||||
{
|
#endif
|
||||||
sound_is_valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if Build_Development
|
#if Build_Development
|
||||||
assert( debug_marker_index < debug_marker_history_size )
|
|
||||||
debug_markers[debug_marker_index] = { play_cursor, write_cursor };
|
|
||||||
debug_marker_index++;
|
debug_marker_index++;
|
||||||
|
|
||||||
if ( debug_marker_index >= debug_marker_history_size )
|
if ( debug_marker_index >= debug_marker_history_size )
|
||||||
debug_marker_index = 0;
|
debug_marker_index = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
engine::shutdown();
|
engine::shutdown();
|
Loading…
Reference in New Issue
Block a user