mirror of
https://github.com/Ed94/HandmadeHero.git
synced 2025-07-01 11:21:05 -07:00
Day 21 complete
This commit is contained in:
@ -61,6 +61,12 @@
|
||||
# define assert( expression )
|
||||
#endif
|
||||
|
||||
#ifdef COMPILER_CLANG
|
||||
# define compiler_decorated_func_name __PRETTY_NAME__
|
||||
#elif defined(COMPILER_MSVC)
|
||||
# define compiler_decorated_func_name __FUNCDNAME__
|
||||
#endif
|
||||
|
||||
// TODO(Ed) : Add this sauce later
|
||||
#if 0
|
||||
#define congrats( message )
|
||||
|
@ -32,7 +32,11 @@
|
||||
|
||||
NS_PLATFORM_BEGIN
|
||||
|
||||
#if Build_Debug
|
||||
// On-Demand platform interface.
|
||||
// Everything exposed here should be based on a feature a game may want to provide a user
|
||||
// (Example: Letting the user change the refresh-rate of the monitor or the engine's target frame-rate)
|
||||
|
||||
#if Build_Development
|
||||
/*
|
||||
IMPORTANT : These are not for shipping code - they are blocking and the write isn't protected.
|
||||
*/
|
||||
@ -44,34 +48,42 @@ struct Debug_FileContent
|
||||
char _PAD_[4];
|
||||
};
|
||||
|
||||
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 );
|
||||
|
||||
// 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 );
|
||||
using DebugFileFreeContentFn = void ( Debug_FileContent* file_content );
|
||||
using DebugFileReadContentFn = Debug_FileContent ( char const* file_path );
|
||||
using DebugFileWriteContentFn = b32 ( char const* file_path, u32 content_size, void* content_memory );
|
||||
|
||||
using DebugSetPauseRenderingFn = void (b32 value);
|
||||
#endif
|
||||
|
||||
// On-Demand platform interface.
|
||||
// Everything exposed here should be based on a feature a game may want to provide a user
|
||||
// (Example: Letting the user change the refresh-rate of the monitor or the engine's target frame-rate)
|
||||
|
||||
// TODO(Ed) : Implement this later when settings UI is setup.
|
||||
#pragma region Settings Exposure
|
||||
// Exposing specific properties for user configuration in settings
|
||||
|
||||
// Returns the current monitor refresh rate.
|
||||
u32 get_monitor_refresh_rate();
|
||||
|
||||
using GetMonitorRefreshRateFn = u32();
|
||||
// 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 );
|
||||
// Must be of the compatiable listing for the monitor the window surface is presenting to
|
||||
using SetMonitorRefreshRateFn = void ( u32 rate_in_hz );
|
||||
|
||||
u32 get_engine_frame_target();
|
||||
using GetEngineFrameTargetFn = u32 ();
|
||||
using SetEngineFrameTargetFn = void ( u32 rate_in_hz );
|
||||
|
||||
void set_engine_frame_target( u32 rate_in_hz );
|
||||
struct ModuleAPI
|
||||
{
|
||||
#if Build_Development
|
||||
DebugFileFreeContentFn* debug_file_free_content;
|
||||
DebugFileReadContentFn* debug_file_read_content;
|
||||
DebugFileWriteContentFn* debug_file_write_content;
|
||||
|
||||
DebugSetPauseRenderingFn* debug_set_pause_rendering;
|
||||
#endif
|
||||
|
||||
GetMonitorRefreshRateFn* get_monitor_refresh_rate;
|
||||
SetMonitorRefreshRateFn* set_monitor_refresh_rate;
|
||||
|
||||
GetEngineFrameTargetFn* get_engine_frame_target;
|
||||
SetEngineFrameTargetFn* set_engine_frame_target;
|
||||
};
|
||||
|
||||
#pragma endregion Settings Exposure
|
||||
|
||||
|
@ -2,20 +2,47 @@
|
||||
This represents the API only accessible to the platform layer to fullfill for the engine layer.
|
||||
*/
|
||||
#pragma once
|
||||
#include "engine.hpp"
|
||||
#include "engine/engine.hpp"
|
||||
|
||||
#ifndef Engine_API
|
||||
# define Engine_API
|
||||
#endif
|
||||
|
||||
NS_ENGINE_BEGIN
|
||||
|
||||
void startup();
|
||||
void shutdown();
|
||||
using OnModuleRelaodFn = void( Memory* memory, platform::ModuleAPI* platform_api );
|
||||
using StartupFn = void( Memory* memory, platform::ModuleAPI* platform_api );
|
||||
using ShutdownFn = void( Memory* memory, platform::ModuleAPI* platform_api );
|
||||
|
||||
// Needs a contextual reference to four things:
|
||||
// Timing, Input, Bitmap Buffer
|
||||
void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory );
|
||||
using UpdateAndRenderFn = void ( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api );
|
||||
|
||||
// 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 );
|
||||
using UpdateAudioFn = void ( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api );
|
||||
|
||||
struct ModuleAPI
|
||||
{
|
||||
enum : u32
|
||||
{
|
||||
Sym_OnModuleReload,
|
||||
Sym_Startup,
|
||||
Sym_Shutdown,
|
||||
Sym_UpdateAndRender,
|
||||
Sym_UpdateAudio,
|
||||
};
|
||||
|
||||
OnModuleRelaodFn* on_module_reload;
|
||||
StartupFn* startup;
|
||||
ShutdownFn* shutdown;
|
||||
|
||||
UpdateAndRenderFn* update_and_render;
|
||||
UpdateAudioFn* update_audio;
|
||||
|
||||
b32 IsValid;
|
||||
char _PAD_[4];
|
||||
};
|
||||
|
||||
NS_ENGINE_END
|
||||
|
3
project/platform/platform_game_api.hpp
Normal file
3
project/platform/platform_game_api.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
/*
|
||||
This represents the API only accessible to the platform layer to fullfill for the game layer.
|
||||
*/
|
@ -113,6 +113,14 @@ enum XI_State : DWORD
|
||||
XI_PluggedIn = ERROR_SUCCESS,
|
||||
};
|
||||
|
||||
|
||||
template< typename ProcSignature >
|
||||
ProcSignature* get_procedure_from_library( HMODULE library_module, char const* symbol )
|
||||
{
|
||||
void* address = rcast( void*, GetProcAddress( library_module, symbol ) );
|
||||
return rcast( ProcSignature*, address );
|
||||
}
|
||||
|
||||
#pragma region XInput
|
||||
WIN_LIB_API DWORD WINAPI XInputGetState
|
||||
(
|
||||
@ -151,11 +159,14 @@ xinput_load_library_bindings()
|
||||
{
|
||||
HMODULE xinput_lib = LoadLibraryA( XINPUT_DLL_A );
|
||||
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable: 4191 )
|
||||
xinput_get_state = rcast( XInputGetStateFn*, GetProcAddress( xinput_lib, "XInputGetState" ));
|
||||
xinput_set_state = rcast( XInputSetStateFn*, GetProcAddress( xinput_lib, "XInputSetState" ));
|
||||
#pragma warning( pop )
|
||||
XInputGetStateFn* get_state = get_procedure_from_library< XInputGetStateFn >( xinput_lib, "XInputGetState" );
|
||||
XInputSetStateFn* set_state = get_procedure_from_library< XInputSetStateFn >( xinput_lib, "XInputSetState" );
|
||||
|
||||
if ( get_state )
|
||||
xinput_get_state = get_state;
|
||||
|
||||
if ( set_state )
|
||||
xinput_set_state = set_state;
|
||||
}
|
||||
#pragma endregion XInput
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "win32.hpp"
|
||||
|
||||
// Engine layer headers
|
||||
#include "engine.hpp"
|
||||
#include "engine/engine.hpp"
|
||||
#include "platform_engine_api.hpp"
|
||||
|
||||
// Standard-Library stand-ins
|
||||
@ -216,7 +216,7 @@ b32 debug_file_write_content( char const* file_path, u32 content_size, void* con
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_pause_rendering( b32 value )
|
||||
void debug_set_pause_rendering( b32 value )
|
||||
{
|
||||
Pause_Rendering = value;
|
||||
}
|
||||
@ -425,15 +425,12 @@ init_sound(HWND window_handle, DirectSoundBuffer* sound_buffer )
|
||||
}
|
||||
|
||||
// Get direct sound object
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable: 4191 )
|
||||
direct_sound_create = rcast( DirectSoundCreateFn*, GetProcAddress( sound_library, "DirectSoundCreate" ));
|
||||
direct_sound_create = get_procedure_from_library< DirectSoundCreateFn >( sound_library, "DirectSoundCreate" );
|
||||
if ( ! ensure( direct_sound_create, "Failed to get direct_sound_create_procedure" ) )
|
||||
{
|
||||
// TOOD : Diagnostic
|
||||
return;
|
||||
}
|
||||
#pragma warning( pop )
|
||||
|
||||
LPDIRECTSOUND direct_sound;
|
||||
if ( ! SUCCEEDED(direct_sound_create( 0, & direct_sound, 0 )) )
|
||||
@ -751,6 +748,131 @@ process_pending_window_messages( engine::KeyboardState* keyboard )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region Platfom API
|
||||
u32 get_monitor_refresh_rate();
|
||||
|
||||
#pragma endregion Platform API
|
||||
|
||||
// TODO(Ed): This also assumes the symbol name is always within size of the provided buffer, needs to fail if not.
|
||||
void get_symbol_from_module_table( Debug_FileContent symbol_table, u32 symbol_ID, char* symbol_name )
|
||||
{
|
||||
struct Token
|
||||
{
|
||||
char const* Ptr;
|
||||
u32 Len;
|
||||
char _PAD_[4];
|
||||
};
|
||||
|
||||
Token tokens[256] = {};
|
||||
s32 idx = 0;
|
||||
|
||||
char const* scanner = rcast( char const*, symbol_table.Data );
|
||||
u32 left = symbol_table.Size;
|
||||
while ( left )
|
||||
{
|
||||
if ( *scanner == '\n' || *scanner == '\r' )
|
||||
{
|
||||
++ scanner;
|
||||
-- left;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokens[idx].Ptr = scanner;
|
||||
while ( left && *scanner != '\r' && *scanner != '\n' )
|
||||
{
|
||||
-- left;
|
||||
++ scanner;
|
||||
++ tokens[idx].Len;
|
||||
}
|
||||
++ idx;
|
||||
}
|
||||
}
|
||||
|
||||
Token& token = tokens[symbol_ID];
|
||||
while ( token.Len -- )
|
||||
{
|
||||
*symbol_name = *token.Ptr;
|
||||
++ symbol_name;
|
||||
++ token.Ptr;
|
||||
}
|
||||
*symbol_name = '\0';
|
||||
}
|
||||
|
||||
// Right now they are just the data directory
|
||||
#define Path_To_Symbol_Tables
|
||||
|
||||
global HMODULE Lib_Handmade_Engine = nullptr;
|
||||
|
||||
engine::ModuleAPI load_engine_module_api()
|
||||
{
|
||||
using ModuleAPI = engine::ModuleAPI;
|
||||
|
||||
// TODO(Ed) : Need proper paything to the dll (not assume is in the base directory).
|
||||
|
||||
CopyFileA( "handmade_engine.dll", "handmade_engine_temp.dll", FALSE );
|
||||
|
||||
// Engine
|
||||
Lib_Handmade_Engine = LoadLibraryA( "handmade_engine_temp.dll" );
|
||||
if ( ! Lib_Handmade_Engine )
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr char const*
|
||||
handmade_engine_symbols = Path_To_Symbol_Tables "handmade_engine.symbols";
|
||||
|
||||
Debug_FileContent symbol_table = debug_file_read_content( handmade_engine_symbols );
|
||||
if ( symbol_table.Size == 0 )
|
||||
{
|
||||
fatal( "Failed to laod symbol table for handmade engine module!" );
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO(Ed) : Clean this up later when Casey makes strings. (If he doesn't we'll do it)
|
||||
char symbol_on_module_reload[256];
|
||||
char symboL_startup[256];
|
||||
char symboL_shutdown[256];
|
||||
char symboL_update_and_render[256];
|
||||
char symbol_update_audio[256];
|
||||
get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_OnModuleReload, symbol_on_module_reload );
|
||||
get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_Startup, symboL_startup );
|
||||
get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_Shutdown, symboL_shutdown );
|
||||
get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_UpdateAndRender, symboL_update_and_render );
|
||||
get_symbol_from_module_table( symbol_table, ModuleAPI::Sym_UpdateAudio, symbol_update_audio );
|
||||
|
||||
debug_file_free_content( & symbol_table );
|
||||
|
||||
engine::ModuleAPI engine_api {};
|
||||
engine_api.on_module_reload = get_procedure_from_library< engine::OnModuleRelaodFn > ( Lib_Handmade_Engine, symbol_on_module_reload );
|
||||
engine_api.startup = get_procedure_from_library< engine::StartupFn > ( Lib_Handmade_Engine, symboL_startup );
|
||||
engine_api.shutdown = get_procedure_from_library< engine::ShutdownFn > ( Lib_Handmade_Engine, symboL_shutdown );
|
||||
engine_api.update_and_render = get_procedure_from_library< engine::UpdateAndRenderFn >( Lib_Handmade_Engine, symboL_update_and_render );
|
||||
engine_api.update_audio = get_procedure_from_library< engine::UpdateAudioFn > ( Lib_Handmade_Engine, symbol_update_audio );
|
||||
|
||||
engine_api.IsValid =
|
||||
engine_api.on_module_reload
|
||||
&& engine_api.startup
|
||||
&& engine_api.shutdown
|
||||
&& engine_api.update_and_render
|
||||
&& engine_api.update_audio;
|
||||
if ( engine_api.IsValid )
|
||||
{
|
||||
OutputDebugStringA( "Loaded engine module API\n" );
|
||||
}
|
||||
return engine_api;
|
||||
}
|
||||
|
||||
void unload_engine_module_api( engine::ModuleAPI* engine_api )
|
||||
{
|
||||
if ( engine_api->IsValid )
|
||||
{
|
||||
FreeLibrary( Lib_Handmade_Engine );
|
||||
*engine_api = {};
|
||||
OutputDebugStringA( "Unloaded engine module API\n" );
|
||||
}
|
||||
}
|
||||
|
||||
NS_PLATFORM_END
|
||||
|
||||
int CALLBACK
|
||||
@ -774,6 +896,22 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
|
||||
QueryPerformanceFrequency( rcast(LARGE_INTEGER*, & Performance_Counter_Frequency) );
|
||||
|
||||
// Prepare platform API
|
||||
ModuleAPI platform_api {};
|
||||
{
|
||||
#if Build_Development
|
||||
platform_api.debug_file_free_content = & debug_file_free_content;
|
||||
platform_api.debug_file_read_content = & debug_file_read_content;
|
||||
platform_api.debug_file_write_content = & debug_file_write_content;
|
||||
|
||||
platform_api.debug_set_pause_rendering = & debug_set_pause_rendering;
|
||||
#endif
|
||||
platform_api.get_monitor_refresh_rate = nullptr;
|
||||
platform_api.set_monitor_refresh_rate = nullptr;
|
||||
platform_api.get_engine_frame_target = nullptr;
|
||||
platform_api.set_engine_frame_target = nullptr;
|
||||
}
|
||||
|
||||
// Memory
|
||||
engine::Memory engine_memory {};
|
||||
{
|
||||
@ -802,6 +940,9 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
}
|
||||
}
|
||||
|
||||
// Load engine module
|
||||
engine::ModuleAPI engine_api = load_engine_module_api();
|
||||
|
||||
WNDCLASSW window_class {};
|
||||
HWND window_handle = nullptr;
|
||||
{
|
||||
@ -935,6 +1076,8 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
}
|
||||
}
|
||||
|
||||
engine_api.startup( & engine_memory, & platform_api );
|
||||
|
||||
u64 last_frame_clock = timing_get_wall_clock();
|
||||
u64 last_frame_cycle = __rdtsc();
|
||||
u64 flip_wall_clock = last_frame_clock;
|
||||
@ -942,8 +1085,14 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
#if Build_Development
|
||||
u64 startup_cycles = last_frame_cycle - launch_cycle;
|
||||
f32 startup_ms = timing_get_ms_elapsed( launch_clock, last_frame_clock );
|
||||
|
||||
char text_buffer[256];
|
||||
sprintf_s( text_buffer, sizeof(text_buffer), "Startup MS: %f\n", startup_ms );
|
||||
OutputDebugStringA( text_buffer );
|
||||
#endif
|
||||
|
||||
u64 module_reload_counter = 0;
|
||||
|
||||
Running = true;
|
||||
#if 0
|
||||
// This tests the play & write cursor update frequency.
|
||||
@ -960,6 +1109,14 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
#endif
|
||||
while( Running )
|
||||
{
|
||||
if ( module_reload_counter > 120 )
|
||||
{
|
||||
unload_engine_module_api( & engine_api );
|
||||
engine_api = load_engine_module_api();
|
||||
module_reload_counter = 0;
|
||||
}
|
||||
++ module_reload_counter;
|
||||
|
||||
process_pending_window_messages( new_keyboard );
|
||||
|
||||
// TODO(Ed): Offload polling to these functions later.
|
||||
@ -1107,7 +1264,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
}
|
||||
|
||||
// Engine's logical iteration and rendering process
|
||||
engine::update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & engine_memory );
|
||||
engine_api.update_and_render( & input, rcast(engine::OffscreenBuffer*, & Surface_Back_Buffer.Memory), & engine_memory, & platform_api );
|
||||
|
||||
u64 audio_frame_start = timing_get_wall_clock();
|
||||
f32 flip_to_audio_ms = timing_get_ms_elapsed( flip_wall_clock, audio_frame_start );
|
||||
@ -1194,7 +1351,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
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_api.update_audio( & sound_buffer, & engine_memory, & platform_api );
|
||||
|
||||
DebugTimeMarker* marker = & debug_markers[ debug_marker_index ];
|
||||
marker->OutputPlayCusror = ds_play_cursor;
|
||||
@ -1331,7 +1488,7 @@ WinMain( HINSTANCE instance, HINSTANCE prev_instance, LPSTR commandline, int sho
|
||||
#endif
|
||||
}
|
||||
|
||||
engine::shutdown();
|
||||
engine_api.shutdown( & engine_memory, & platform_api );
|
||||
|
||||
if ( jsl_num_devices > 0 )
|
||||
{
|
||||
|
Reference in New Issue
Block a user