mirror of
				https://github.com/Ed94/HandmadeHero.git
				synced 2025-10-31 06:50:54 -07:00 
			
		
		
		
	Day 21 complete
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -22,3 +22,5 @@ build | ||||
|  | ||||
| **/*.dll | ||||
| data/test.out | ||||
| data/handmade_engine.symbols | ||||
| data/handmade_win32.exe | ||||
|   | ||||
							
								
								
									
										2
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							| @@ -8,7 +8,7 @@ | ||||
| 			"type":"cppvsdbg", | ||||
| 			"request": "launch", | ||||
| 			"name" : "Debug handmade win32 msvc", | ||||
| 			"program": "${workspaceFolder}/build/handmade_win32.exe", | ||||
| 			"program": "${workspaceFolder}/data/handmade_win32.exe", | ||||
| 			"args": [], | ||||
| 			"cwd": "${workspaceFolder}/data", | ||||
| 			"visualizerFile": "${workspaceFolder}/scripts/handmade.natvis" | ||||
|   | ||||
| @@ -8,16 +8,16 @@ | ||||
| 		<ShowEmptyFolders>true</ShowEmptyFolders> | ||||
| 		<IsVirtual>false</IsVirtual> | ||||
| 		<IsFolder>false</IsFolder> | ||||
| 		<BuildCommand>pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/build.ps1 msvc dev optimized</BuildCommand> | ||||
| 		<RebuildCommand></RebuildCommand> | ||||
| 		<BuildCommand>pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/build.ps1 msvc dev debug engine</BuildCommand> | ||||
| 		<RebuildCommand>pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/build.ps1 msvc dev debug platform</RebuildCommand> | ||||
| 		<BuildFileCommand></BuildFileCommand> | ||||
| 		<CleanCommand>pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(WorkspaceDirectory)/scripts/clean.ps1</CleanCommand> | ||||
| 		<BuildWorkingDirectory></BuildWorkingDirectory> | ||||
| 		<CancelBuild></CancelBuild> | ||||
| 		<RunCommand>$(WorkspaceDirectory)/build/handmade_win32.exe</RunCommand> | ||||
| 		<RunCommand>$(WorkspaceDirectory)/data/handmade_win32.exe</RunCommand> | ||||
| 		<RunCommandWorkingDirectory>$(WorkspaceDirectory)/data</RunCommandWorkingDirectory> | ||||
| 		<DebugCommand>$(WorkspaceDirectory)/build/handmade_win32.exe</DebugCommand> | ||||
| 		<ExePathCommand>$(WorkspaceDirectory)/build/handmade_win32.exe</ExePathCommand> | ||||
| 		<DebugCommand>$(WorkspaceDirectory)/data/handmade_win32.exe</DebugCommand> | ||||
| 		<ExePathCommand>$(WorkspaceDirectory)/data/handmade_win32.exe</ExePathCommand> | ||||
| 		<DebugSln></DebugSln> | ||||
| 		<UseVisualStudioEnvBat>true</UseVisualStudioEnvBat> | ||||
| 		<Configurations> | ||||
|   | ||||
| @@ -17,28 +17,43 @@ | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Development|x64'"> | ||||
|     <IncludePath>$(ProjectDir)project;$(IncludePath)</IncludePath> | ||||
|     <LibraryPath>$(ProjectDir)data;$(windir)System32;$(LibraryPath)</LibraryPath> | ||||
|     <NMakeBuildCommandLine>pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(ProjectDir)scripts\build.ps1 msvc dev</NMakeBuildCommandLine> | ||||
|     <NMakeBuildCommandLine>pwsh -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(ProjectDir)scripts\build.ps1 msvc dev engine</NMakeBuildCommandLine> | ||||
|     <NMakeCleanCommandLine>pwsh ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(ProjectDir)scripts\clean.ps1</NMakeCleanCommandLine> | ||||
|     <NMakePreprocessorDefinitions>GEN_TIME;Build_Development;Build_Debug;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> | ||||
|     <ExternalIncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath)</ExternalIncludePath> | ||||
|     <NMakeReBuildCommandLine> | ||||
|     </NMakeReBuildCommandLine> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="project\engine.h" /> | ||||
|     <ClInclude Include="project\handmade.hpp" /> | ||||
|     <ClInclude Include="project\platform\generics.h" /> | ||||
|     <ClInclude Include="project\platform\generics.hpp" /> | ||||
|     <ClInclude Include="project\platform\grime.h" /> | ||||
|     <ClInclude Include="project\platform\grime.hpp" /> | ||||
|     <ClInclude Include="project\platform\jsl.h" /> | ||||
|     <ClInclude Include="project\platform\jsl.hpp" /> | ||||
|     <ClInclude Include="project\platform\macros.h" /> | ||||
|     <ClInclude Include="project\platform\macros.hpp" /> | ||||
|     <ClInclude Include="project\platform\math_constants.h" /> | ||||
|     <ClInclude Include="project\platform\math_constants.hpp" /> | ||||
|     <ClInclude Include="project\platform\platform.h" /> | ||||
|     <ClInclude Include="project\platform\platform.hpp" /> | ||||
|     <ClInclude Include="project\platform\platform_engine_api.h" /> | ||||
|     <ClInclude Include="project\platform\platform_engine_api.hpp" /> | ||||
|     <ClInclude Include="project\platform\platform_game_api.hpp" /> | ||||
|     <ClInclude Include="project\platform\types.h" /> | ||||
|     <ClInclude Include="project\platform\types.hpp" /> | ||||
|     <ClInclude Include="project\platform\win32.h" /> | ||||
|     <ClInclude Include="project\platform\win32.hpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="project\engine.cpp" /> | ||||
|     <ClCompile Include="project\handmade.cpp" /> | ||||
|     <ClCompile Include="project\handmade_engine.cpp" /> | ||||
|     <ClCompile Include="project\handmade_win32.cpp" /> | ||||
|     <ClCompile Include="project\platform\platform_win32.cpp" /> | ||||
|     <ClCompile Include="project\platform\win32_platform.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <None Include="docs\Day 001.md" /> | ||||
|   | ||||
| @@ -6,6 +6,6 @@ | ||||
|   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Development|x64'"> | ||||
|     <LocalDebuggerWorkingDirectory>$(ProjectDir)data</LocalDebuggerWorkingDirectory> | ||||
|     <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> | ||||
|     <LocalDebuggerCommand>$(ProjectDir)build\handmade_win32.exe</LocalDebuggerCommand> | ||||
|     <LocalDebuggerCommand>$(ProjectDir)data\handmade_win32.exe</LocalDebuggerCommand> | ||||
|   </PropertyGroup> | ||||
| </Project> | ||||
							
								
								
									
										15
									
								
								docs/Day 021.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								docs/Day 021.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # Day 21 | ||||
|  | ||||
| So I learned today the good reason why he doesn't use static variables; So that when he makes this library dynamically loaded we don't lose the state of the game on reload.   | ||||
|  | ||||
| This day was extremely gratifying to get working. | ||||
|  | ||||
| I went the extra mile than what Casey did and allow for name mangled symbols (for both clang and msvc).   | ||||
| It took a few more steps than his solution but I get to keep the eronomics of namespaces. | ||||
|  | ||||
| After the linker finishes emitting, I use the build script to parse the .map file and extract the decorated symbols I need to load in the platform layer. Those are exported to a file called `handmade_engine.symbols` and then in the platform layer I load them up using an enum as the lookup table for the line the symbol will be in the file. From there its just loading up the symbol with GetProcAddress! | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,15 +1,21 @@ | ||||
| #include "engine.hpp" | ||||
| //#include "win32.h"
 | ||||
| #include "engine.hpp" | ||||
| #include "platform/platform_engine_api.hpp" | ||||
| #include "handmade.hpp" | ||||
| 
 | ||||
| NS_ENGINE_BEGIN | ||||
| 
 | ||||
| struct EngineState | ||||
| { | ||||
| 	s32 WaveSwitch; | ||||
| 	s32 WaveToneHz; | ||||
| 	s32 ToneVolume; | ||||
| 	s32 XOffset; | ||||
| 	s32 YOffset; | ||||
| 
 | ||||
| 	b32 RendererPaused; | ||||
| 
 | ||||
| 	f32 SampleWaveSineTime; | ||||
| 	b32 SampleWaveSwitch; | ||||
| }; | ||||
| 
 | ||||
| using GetSoundSampleValueFn = s16( EngineState* state, AudioBuffer* sound_buffer ); | ||||
| @@ -28,7 +34,7 @@ square_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer ) | ||||
| internal s16 | ||||
| sine_wave_sample_value( EngineState* state, AudioBuffer* sound_buffer ) | ||||
| { | ||||
| 	local_persist f32 time = 0.f; | ||||
| 	f32& time = state->SampleWaveSineTime; | ||||
| 
 | ||||
| 	s32 wave_period = sound_buffer->SamplesPerSecond / state->WaveToneHz; | ||||
| 
 | ||||
| @@ -104,7 +110,6 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) | ||||
| 			u8 red   = 0; | ||||
| 		#endif | ||||
| 
 | ||||
| 
 | ||||
| 			*pixel++ = u32(red << 16) | u32(green << 8) | blue; | ||||
| 		} | ||||
| 		wildcard += 0.5375f; | ||||
| @@ -112,66 +117,55 @@ render_weird_graident(OffscreenBuffer* buffer, u32 x_offset, u32 y_offset ) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| b32 input_using_analog() | ||||
| Engine_API | ||||
| void on_module_reload( Memory* memory, platform::ModuleAPI* platfom_api ) | ||||
| { | ||||
| 	return false; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void startup() | ||||
| Engine_API | ||||
| void startup( Memory* memory, platform::ModuleAPI* platform_api ) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| 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, Memory* memory ) | ||||
| { | ||||
| 	// Graphics & Input Test
 | ||||
| 	local_persist u32 x_offset = 0; | ||||
| 	local_persist u32 y_offset = 0; | ||||
| 
 | ||||
| 	// Wave Sound Test
 | ||||
| 	local_persist bool wave_switch = false; | ||||
| 
 | ||||
| #if 0 | ||||
| 	if ( input_using_analog() ) | ||||
| 	{ | ||||
| 		// TODO(Ed) : Use analog movement tuning
 | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		// TODO(Ed) : Use digital movement tuning
 | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	EngineState* state = rcast( EngineState*, memory->Persistent ); | ||||
| 	assert( sizeof(EngineState) <= memory->PersistentSize ); | ||||
| 
 | ||||
| 	do_once_start | ||||
| 		assert( sizeof(EngineState) <= memory->PersistentSize ); | ||||
| 	state->ToneVolume = 1000; | ||||
| 
 | ||||
| 		state->ToneVolume = 1000; | ||||
| 		state->WaveToneHz = 120; | ||||
| 	state->XOffset = 0; | ||||
| 	state->YOffset = 0; | ||||
| 
 | ||||
| 		state->XOffset    = 0; | ||||
| 		state->YOffset    = 0; | ||||
| 		state->WaveSwitch = false; | ||||
| 	state->SampleWaveSwitch = false; | ||||
| 	state->WaveToneHz = 120; | ||||
| 	state->SampleWaveSineTime = 0.f; | ||||
| 
 | ||||
| 		#if Build_Debug && 0 | ||||
| 	state->RendererPaused = false; | ||||
| 
 | ||||
| 	#if Build_Debug && 0 | ||||
| 	{ | ||||
| 		using namespace platform; | ||||
| 
 | ||||
| 		char const* file_path = __FILE__; | ||||
| 		Debug_FileContent file_content = platform_api->debug_file_read_content( file_path ); | ||||
| 		if ( file_content.Size ) | ||||
| 		{ | ||||
| 			using namespace platform; | ||||
| 
 | ||||
| 			char const* file_path = __FILE__; | ||||
| 			Debug_FileContent file_content = debug_file_read_content( file_path ); | ||||
| 			if ( file_content.Size ) | ||||
| 			{ | ||||
| 				debug_file_write_content( "test.out", file_content.Size, file_content.Data ); | ||||
| 				debug_file_free_content( & file_content ); | ||||
| 			} | ||||
| 			platform_api->debug_file_write_content( "test.out", file_content.Size, file_content.Data ); | ||||
| 			platform_api->debug_file_free_content( & file_content ); | ||||
| 		} | ||||
| 		#endif | ||||
| 	do_once_end | ||||
| 	} | ||||
| 	#endif | ||||
| } | ||||
| 
 | ||||
| Engine_API | ||||
| void shutdown( Memory* memory, platform::ModuleAPI* platform_api ) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| Engine_API | ||||
| // TODO : I rather expose the back_buffer and sound_buffer using getters for access in any function.
 | ||||
| void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* memory, platform::ModuleAPI* platform_api ) | ||||
| { | ||||
| 	EngineState* state = rcast( EngineState*, memory->Persistent ); | ||||
| 	assert( sizeof(EngineState) <= memory->PersistentSize ); | ||||
| 
 | ||||
| 	ControllerState* controller = & input->Controllers[0]; | ||||
| 
 | ||||
| @@ -195,7 +189,6 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* | ||||
| 	b32 toggle_wave_tone = false; | ||||
| 
 | ||||
| 	b32 pause_renderer  = false; | ||||
| 	local_persist b32 renderer_paused = false; | ||||
| 
 | ||||
| 	f32 analog_threshold = 0.5f; | ||||
| 
 | ||||
| @@ -255,10 +248,10 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* | ||||
| 		toggle_wave_tone |= pressed( keyboard->Space ); | ||||
| 	} | ||||
| 
 | ||||
| 	x_offset += 3 * move_right; | ||||
| 	x_offset -= 3 * move_left; | ||||
| 	y_offset += 3 * move_down; | ||||
| 	y_offset -= 3 * move_up; | ||||
| 	state->XOffset += 3 * move_right; | ||||
| 	state->XOffset -= 3 * move_left; | ||||
| 	state->YOffset += 3 * move_down; | ||||
| 	state->YOffset -= 3 * move_up; | ||||
| 
 | ||||
| 	if ( raise_volume ) | ||||
| 	{ | ||||
| @@ -284,26 +277,27 @@ void update_and_render( InputState* input, OffscreenBuffer* back_buffer, Memory* | ||||
| 
 | ||||
| 	if ( toggle_wave_tone ) | ||||
| 	{ | ||||
| 		state->WaveSwitch ^= true; | ||||
| 		state->SampleWaveSwitch ^= true; | ||||
| 	} | ||||
| 	render_weird_graident( back_buffer, x_offset, y_offset ); | ||||
| 	render_weird_graident( back_buffer, state->XOffset, state->YOffset ); | ||||
| 
 | ||||
| 	if ( pause_renderer ) | ||||
| 	{ | ||||
| 		if ( renderer_paused ) | ||||
| 		if ( state->RendererPaused ) | ||||
| 		{ | ||||
| 			platform::set_pause_rendering(false); | ||||
| 			renderer_paused = false; | ||||
| 			platform_api->debug_set_pause_rendering(false); | ||||
| 			state->RendererPaused = false; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			platform::set_pause_rendering(true); | ||||
| 			renderer_paused = true; | ||||
| 			platform_api->debug_set_pause_rendering(true); | ||||
| 			state->RendererPaused = true; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void update_audio( AudioBuffer* audio_buffer, Memory* memory ) | ||||
| Engine_API | ||||
| void update_audio( AudioBuffer* audio_buffer, Memory* memory, platform::ModuleAPI* platform_api ) | ||||
| { | ||||
| 	EngineState* state = rcast( EngineState*, memory->Persistent ); | ||||
| 	do_once_start | ||||
| @@ -311,11 +305,10 @@ void update_audio( AudioBuffer* audio_buffer, Memory* memory ) | ||||
| 	do_once_end | ||||
| 
 | ||||
| 	// TODO(Ed) : Allow sample offsets here for more robust platform options
 | ||||
| 	if ( ! state->WaveSwitch ) | ||||
| 	if ( ! state->SampleWaveSwitch ) | ||||
| 		output_sound( state, audio_buffer, sine_wave_sample_value ); | ||||
| 	else | ||||
| 		output_sound( state, audio_buffer, square_wave_sample_value ); | ||||
| } | ||||
| 
 | ||||
| NS_ENGINE_END | ||||
| 
 | ||||
| @@ -197,56 +197,3 @@ void input_mode_pop( InputMode* mode ); | ||||
| void input_mode_pop( InputMode* mode ); | ||||
| 
 | ||||
| NS_ENGINE_END | ||||
| 
 | ||||
| // TODO(Ed) : Move this to handmade game layer later.
 | ||||
| 
 | ||||
| #define NS_HANDMADE_BEGIN namespace handmade { | ||||
| #define NS_HANDMADE_END   } | ||||
| 
 | ||||
| NS_HANDMADE_BEGIN | ||||
| 
 | ||||
| // We want a 'binding' to have multiple binds to active it (most likely)
 | ||||
| struct Actionable | ||||
| { | ||||
| 	char const*                Name; | ||||
| 	engine::InputBindCallback* Binds; | ||||
| 	s32                        NumBinds; | ||||
| 	char _PAD_[4]; | ||||
| }; | ||||
| 
 | ||||
| struct ActionableMode | ||||
| { | ||||
| 	 | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
| 	Platform Layer: | ||||
| 
 | ||||
| 	Controller : Keyboard & Mouse, XPad, DSPad | ||||
| 
 | ||||
| 	---VV--- | ||||
| 
 | ||||
| 	Engine Layer: | ||||
| 
 | ||||
| 	InputBinding callbacks (per-game-logic frame basis) | ||||
| 	Push/Pop input modes (binding sets) | ||||
| 
 | ||||
| 	---VV--- | ||||
| 
 | ||||
| 	Game Layer: | ||||
| 
 | ||||
| 	Actionables : Binding Sets where a raw input, or input interpretation leads to an player action. | ||||
| 	ActionSet   : Actionables.Push/Pop -> Input.Push/Pop ? | ||||
| 	Player : Controller, Actionables, ActionSets | ||||
| */ | ||||
| 
 | ||||
| struct Player | ||||
| { | ||||
| 	// So far just has an assigned controller.
 | ||||
| 	engine::ControllerState* Controller; | ||||
| 	 | ||||
| 	// Possilby some other stuff in the future.
 | ||||
| }; | ||||
| 
 | ||||
| NS_HANDMADE_END | ||||
| 
 | ||||
							
								
								
									
										11
									
								
								project/engine/win32_engine.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								project/engine/win32_engine.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #include "platform/win32.hpp" | ||||
| #include "engine.hpp" | ||||
|  | ||||
| b32 WINAPI DllMain( | ||||
| 	HINSTANCE instance, | ||||
| 	DWORD reason_for_call, | ||||
| 	LPVOID reserved | ||||
| ) | ||||
| { | ||||
| 	return true; | ||||
| } | ||||
| @@ -4,5 +4,5 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "engine.hpp" | ||||
| #include "engine/engine.hpp" | ||||
|  | ||||
|   | ||||
							
								
								
									
										67
									
								
								project/handmade.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								project/handmade.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "engine/engine.hpp" | ||||
|  | ||||
| #define NS_HANDMADE_BEGIN namespace handmade { | ||||
| #define NS_HANDMADE_END   } | ||||
|  | ||||
| NS_HANDMADE_BEGIN | ||||
|  | ||||
| struct Memory | ||||
| { | ||||
| 	// Subscection of engine memory for the game to use. | ||||
|  | ||||
| 	void* Persistent; | ||||
| 	u64   PersistentSize; | ||||
|  | ||||
| 	// void* Frame; | ||||
| 	// u64   FrameSize; | ||||
|  | ||||
| 	void* Transient; | ||||
| 	u64   TransientSize; | ||||
| }; | ||||
|  | ||||
| // We want a 'binding' to have multiple binds to active it (most likely) | ||||
| struct Actionable | ||||
| { | ||||
| 	char const*                Name; | ||||
| 	engine::InputBindCallback* Binds; | ||||
| 	s32                        NumBinds; | ||||
| 	char _PAD_[4]; | ||||
| }; | ||||
|  | ||||
| struct ActionableMode | ||||
| { | ||||
|  | ||||
| }; | ||||
|  | ||||
| /* | ||||
| 	Platform Layer: | ||||
|  | ||||
| 	Controller : Keyboard & Mouse, XPad, DSPad | ||||
|  | ||||
| 	---VV--- | ||||
|  | ||||
| 	Engine Layer: | ||||
|  | ||||
| 	InputBinding callbacks (per-game-logic frame basis) | ||||
| 	Push/Pop input modes (binding sets) | ||||
|  | ||||
| 	---VV--- | ||||
|  | ||||
| 	Game Layer: | ||||
|  | ||||
| 	Actionables : Binding Sets where a raw input, or input interpretation leads to an player action. | ||||
| 	ActionSet   : Actionables.Push/Pop -> Input.Push/Pop ? | ||||
| 	Player : Controller, Actionables, ActionSets | ||||
| */ | ||||
|  | ||||
| struct Player | ||||
| { | ||||
| 	// So far just has an assigned controller. | ||||
| 	engine::ControllerState* Controller; | ||||
|  | ||||
| 	// Possilby some other stuff in the future. | ||||
| }; | ||||
|  | ||||
| NS_HANDMADE_END | ||||
							
								
								
									
										19
									
								
								project/handmade_engine.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								project/handmade_engine.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #ifdef __clang__ | ||||
| #pragma clang diagnostic push | ||||
| #pragma clang diagnostic ignored "-Wunused-const-variable" | ||||
| #pragma clang diagnostic ignored "-Wswitch" | ||||
| #pragma clang diagnostic ignored "-Wunused-variable" | ||||
| #pragma clang diagnostic ignored "-Wunknown-pragmas" | ||||
| #pragma clang diagnostic ignored "-Wvarargs" | ||||
| #pragma clang diagnostic ignored "-Wunused-function" | ||||
| #pragma clang diagnostic ignored "-Wunused-but-set-variable" | ||||
| #endif | ||||
|  | ||||
| #include "platform/grime.hpp" | ||||
|  | ||||
| #if Build_Unity | ||||
| #	include "engine/engine.cpp" | ||||
| #	if SYSTEM_WINDOWS | ||||
| #		include "engine/win32_engine.cpp" | ||||
| #	endif | ||||
| #endif | ||||
| @@ -10,7 +10,5 @@ | ||||
| #endif | ||||
|  | ||||
| #if Build_Unity | ||||
| #include "handmade.cpp" | ||||
| #include "engine.cpp" | ||||
| #include "platform/win32_platform.cpp" | ||||
| #endif | ||||
|   | ||||
| @@ -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 ) | ||||
| 	{ | ||||
|   | ||||
| @@ -14,6 +14,9 @@ Push-Location $path_root | ||||
| 	   $debug 	     = $null | ||||
| 	   $analysis	 = $false | ||||
| 	   $dev          = $false | ||||
| 	   $platform     = $null | ||||
| 	   $engine       = $null | ||||
| 	   $game         = $null | ||||
|  | ||||
| [array] $vendors = @( "clang", "msvc" ) | ||||
|  | ||||
| @@ -26,11 +29,14 @@ if ( $args ) { $args | ForEach-Object { | ||||
| 		"debug"               { $debug     = $true } | ||||
| 		"analysis"            { $analysis  = $true } | ||||
| 		"dev"                 { $dev       = $true } | ||||
| 		"platform"            { $platform  = $true } | ||||
| 		"engine"              { $engine    = $true } | ||||
| 		"game"                { $game      = $true } | ||||
| 	} | ||||
| }} | ||||
| #endregion Argument | ||||
|  | ||||
| #region Configuration | ||||
| #region Toolchain Configuration | ||||
| if ($IsWindows) { | ||||
| 	# This HandmadeHero implementation is only designed for 64-bit systems | ||||
|     & $devshell -arch amd64 | ||||
| @@ -45,7 +51,7 @@ write-host "Building HandmadeHero with $vendor" | ||||
|  | ||||
| if ( $dev ) { | ||||
| 	if ( $debug -eq $null ) { | ||||
| 		$debug = $true | ||||
| 		# $debug = $true | ||||
| 	} | ||||
|  | ||||
| 	if ( $optimize -eq $null ) { | ||||
| @@ -118,42 +124,52 @@ function run-linker | ||||
| if ( $vendor -match "clang" ) | ||||
| { | ||||
| 	# https://clang.llvm.org/docs/ClangCommandLineReference.html | ||||
| 	$flag_all_c 					 = '/TC' | ||||
| 	$flag_all_cpp                    = '/TP' | ||||
| 	$flag_compile                    = '-c' | ||||
| 	$flag_color_diagnostics          = '-fcolor-diagnostics' | ||||
| 	$flag_no_color_diagnostics       = '-fno-color-diagnostics' | ||||
| 	$flag_debug                      = '-g' | ||||
| 	$flag_debug_codeview             = '-gcodeview' | ||||
| 	$flag_define                     = '-D' | ||||
| 	$flag_exceptions_disabled		 = '-fno-exceptions' | ||||
| 	$flag_preprocess 			     = '-E' | ||||
| 	$flag_include                    = '-I' | ||||
| 	$flag_library					 = '-l' | ||||
| 	$flag_library_path				 = '-L' | ||||
| 	$flag_linker                     = '-Wl,' | ||||
| 	$flag_link_mapfile 				 = '-Map' | ||||
| 	$flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE' | ||||
| 	$flag_link_win_subsystem_windows = '/SUBSYSTEM:WINDOWS' | ||||
| 	$flag_link_win_machine_32        = '/MACHINE:X86' | ||||
| 	$flag_link_win_machine_64        = '/MACHINE:X64' | ||||
| 	$flag_link_win_debug             = '/DEBUG' | ||||
| 	$flag_link_win_pdb 			     = '/PDB:' | ||||
| 	$flag_link_win_path_output       = '/OUT:' | ||||
| 	$flag_no_optimization 		     = '-O0' | ||||
| 	$flag_optimize_fast 		     = '-O2' | ||||
| 	$flag_optimize_size 		     = '-O1' | ||||
| 	$flag_optimize_intrinsics		 = '-Oi' | ||||
| 	$flag_path_output                = '-o' | ||||
| 	$flag_preprocess_non_intergrated = '-no-integrated-cpp' | ||||
| 	$flag_profiling_debug            = '-fdebug-info-for-profiling' | ||||
| 	$flag_set_stack_size			 = '-stack=' | ||||
| 	$flag_syntax_only				 = '-fsyntax-only' | ||||
| 	$flag_target_arch				 = '-target' | ||||
| 	$flag_wall 					     = '-Wall' | ||||
| 	$flag_warning 					 = '-W' | ||||
| 	$flag_warnings_as_errors         = '-Werror' | ||||
| 	$flag_win_nologo 			     = '/nologo' | ||||
| 	$flag_all_c 					   = '/TC' | ||||
| 	$flag_all_cpp                      = '/TP' | ||||
| 	$flag_compile                      = '-c' | ||||
| 	$flag_color_diagnostics            = '-fcolor-diagnostics' | ||||
| 	$flag_no_color_diagnostics         = '-fno-color-diagnostics' | ||||
| 	$flag_debug                        = '-g' | ||||
| 	$flag_debug_codeview               = '-gcodeview' | ||||
| 	$flag_define                       = '-D' | ||||
| 	$flag_exceptions_disabled		   = '-fno-exceptions' | ||||
| 	$flag_preprocess 			       = '-E' | ||||
| 	$flag_include                      = '-I' | ||||
| 	$flag_section_data                 = '-fdata-sections' | ||||
| 	$flag_section_functions            = '-ffunction-sections' | ||||
| 	$flag_library					   = '-l' | ||||
| 	$flag_library_path				   = '-L' | ||||
| 	$flag_linker                       = '-Wl,' | ||||
| 	if ( $IsWindows ) { | ||||
| 		$flag_link_dll                 = '/DLL' | ||||
| 		$flag_link_mapfile 		       = '/MAP:' | ||||
| 		$flag_link_optimize_references = '/OPT:REF' | ||||
| 	} | ||||
| 	if ( $IsLinux ) { | ||||
| 		$flag_link_mapfile              = '--Map=' | ||||
| 		$flag_link_optimize_references  = '--gc-sections' | ||||
| 	} | ||||
| 	$flag_link_win_subsystem_console    = '/SUBSYSTEM:CONSOLE' | ||||
| 	$flag_link_win_subsystem_windows    = '/SUBSYSTEM:WINDOWS' | ||||
| 	$flag_link_win_machine_32           = '/MACHINE:X86' | ||||
| 	$flag_link_win_machine_64           = '/MACHINE:X64' | ||||
| 	$flag_link_win_debug                = '/DEBUG' | ||||
| 	$flag_link_win_pdb 			        = '/PDB:' | ||||
| 	$flag_link_win_path_output          = '/OUT:' | ||||
| 	$flag_no_optimization 		        = '-O0' | ||||
| 	$flag_optimize_fast 		        = '-O2' | ||||
| 	$flag_optimize_size 		        = '-O1' | ||||
| 	$flag_optimize_intrinsics		    = '-Oi' | ||||
| 	$flag_path_output                   = '-o' | ||||
| 	$flag_preprocess_non_intergrated    = '-no-integrated-cpp' | ||||
| 	$flag_profiling_debug               = '-fdebug-info-for-profiling' | ||||
| 	$flag_set_stack_size			    = '-stack=' | ||||
| 	$flag_syntax_only				    = '-fsyntax-only' | ||||
| 	$flag_target_arch				    = '-target' | ||||
| 	$flag_wall 					        = '-Wall' | ||||
| 	$flag_warning 					    = '-W' | ||||
| 	$flag_warnings_as_errors            = '-Werror' | ||||
| 	$flag_win_nologo 			        = '/nologo' | ||||
|  | ||||
| 	$ignore_warning_ms_include            = 'no-microsoft-include' | ||||
| 	$ignore_warning_return_type_c_linkage = 'no-return-type-c-linkage' | ||||
| @@ -175,12 +191,12 @@ if ( $vendor -match "clang" ) | ||||
|  | ||||
| 	function build-simple | ||||
| 	{ | ||||
| 		param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$executable ) | ||||
| 		param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary ) | ||||
| 		Write-Host "build-simple: clang" | ||||
|  | ||||
| 		$object = $executable -replace '\.exe', '.obj' | ||||
| 		$pdb    = $executable -replace '\.exe', '.pdb' | ||||
| 		$map    = $executable -replace '\.exe', '.map' | ||||
| 		$object = $binary -replace '\.exe', '.obj' | ||||
| 		$pdb    = $binary -replace '\.exe', '.pdb' | ||||
| 		$map    = $binary -replace '\.exe', '.map' | ||||
|  | ||||
| 		$compiler_args += @( | ||||
| 			$flag_no_color_diagnostics, | ||||
| @@ -188,6 +204,8 @@ if ( $vendor -match "clang" ) | ||||
| 			$flag_target_arch, $target_arch, | ||||
| 			$flag_wall, | ||||
| 			$flag_preprocess_non_intergrated, | ||||
| 			$flag_section_data, | ||||
| 			$flag_section_functions, | ||||
| 			( $flag_path_output + $object ) | ||||
| 		) | ||||
| 		if ( $optimized ) { | ||||
| @@ -214,12 +232,12 @@ if ( $vendor -match "clang" ) | ||||
|  | ||||
| 		$linker_args += @( | ||||
| 			$flag_link_win_machine_64, | ||||
| 			$( $flag_link_win_path_output + $executable ) | ||||
| 			$( $flag_link_win_path_output + $binary ) | ||||
| 		) | ||||
| 		if ( $debug ) { | ||||
| 			$linker_args += $flag_link_win_debug | ||||
| 			$linker_args += $flag_link_win_pdb + $pdb | ||||
| 			# $linker_args += $flag_link_mapfile + $map | ||||
| 			$linker_args += $flag_link_mapfile + $map | ||||
| 		} | ||||
|  | ||||
| 		$libraries | ForEach-Object { | ||||
| @@ -227,7 +245,7 @@ if ( $vendor -match "clang" ) | ||||
| 		} | ||||
|  | ||||
| 		$linker_args += $object | ||||
| 		run-linker $linker $executable $linker_args | ||||
| 		run-linker $linker $binary $linker_args | ||||
|  | ||||
| 		# $compiler_args += $unit | ||||
| 		# $linker_args | ForEach-Object { | ||||
| @@ -256,6 +274,7 @@ if ( $vendor -match "msvc" ) | ||||
| 	$flag_dll 				         = '/LD' | ||||
| 	$flag_dll_debug 			     = '/LDd' | ||||
| 	$flag_linker 		             = '/link' | ||||
| 	$flag_link_dll                   = '/DLL' | ||||
| 	$flag_link_mapfile 				 = '/MAP:' | ||||
| 	$flag_link_optimize_references   = '/OPT:REF' | ||||
| 	$flag_link_win_debug 	         = '/DEBUG' | ||||
| @@ -287,12 +306,12 @@ if ( $vendor -match "msvc" ) | ||||
| 	# This works because this project uses a single unit to build | ||||
| 	function build-simple | ||||
| 	{ | ||||
| 		param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$executable ) | ||||
| 		param( [array]$includes, [array]$compiler_args, [array]$linker_args, [string]$unit, [string]$binary ) | ||||
| 		Write-Host "build-simple: msvc" | ||||
|  | ||||
| 		$object = $executable -replace '\.exe', '.obj' | ||||
| 		$pdb    = $executable -replace '\.exe', '.pdb' | ||||
| 		$map    = $executable -replace '\.exe', '.map' | ||||
| 		$object = $binary -replace '\.(exe|dll)$', '.obj' | ||||
| 		$pdb    = $binary -replace '\.(exe|dll)$', '.pdb' | ||||
| 		$map    = $binary -replace '\.(exe|dll)$', '.map' | ||||
|  | ||||
| 		$compiler_args += @( | ||||
| 			$flag_nologo, | ||||
| @@ -336,18 +355,18 @@ if ( $vendor -match "msvc" ) | ||||
| 		$linker_args += @( | ||||
| 			$flag_nologo, | ||||
| 			$flag_link_win_machine_64, | ||||
| 			( $flag_link_win_path_output + $executable ) | ||||
| 			( $flag_link_win_path_output + $binary ) | ||||
| 		) | ||||
| 		if ( $debug ) { | ||||
| 			$linker_args += $flag_link_win_debug | ||||
| 			$linker_args += $flag_link_win_pdb + $pdb | ||||
| 			# $linker_args += $flag_link_mapfile + $map | ||||
| 			$linker_args += $flag_link_mapfile + $map | ||||
| 		} | ||||
| 		else { | ||||
| 		} | ||||
|  | ||||
| 		$linker_args += $object | ||||
| 		run-linker $linker $executable $linker_args | ||||
| 		run-linker $linker $binary $linker_args | ||||
|  | ||||
| 		# $compiler_args += $unit | ||||
| 		# $compiler_args += $flag_linker | ||||
| @@ -360,11 +379,14 @@ if ( $vendor -match "msvc" ) | ||||
| } | ||||
| #endregion Configuration | ||||
|  | ||||
| #region Building | ||||
| $path_project  = Join-Path $path_root    'project' | ||||
| $path_build    = Join-Path $path_root    'build' | ||||
| $path_data     = Join-Path $path_root	 'data' | ||||
| $path_deps     = Join-Path $path_project 'dependencies' | ||||
| $path_gen      = Join-Path $path_project 'gen' | ||||
| $path_platform = Join-Path $path_project 'platform' | ||||
| $path_engine   = Join-Path $path_project 'engine' | ||||
|  | ||||
| $update_deps = Join-Path $PSScriptRoot 'update_deps.ps1' | ||||
|  | ||||
| @@ -376,21 +398,21 @@ if ( (Test-Path $path_deps) -eq $false ) { | ||||
| 	& $update_deps | ||||
| } | ||||
|  | ||||
| $includes = @( | ||||
| 	$path_project, | ||||
| 	$path_gen, | ||||
| 	# $path_deps, | ||||
| 	$path_platform | ||||
| ) | ||||
| $compiler_args = @() | ||||
| $compiler_args += ( $flag_define + 'GEN_TIME' ) | ||||
|  | ||||
| $linker_args = @( | ||||
| 	$flag_link_win_subsystem_console | ||||
| ) | ||||
|  | ||||
| #region Handmade Generate | ||||
| if ( $false ) { | ||||
| 	$includes = @( | ||||
| 		$path_project, | ||||
| 		$path_gen, | ||||
| 		# $path_deps, | ||||
| 		$path_platform | ||||
| 	) | ||||
| 	$compiler_args = @() | ||||
| 	$compiler_args += ( $flag_define + 'GEN_TIME' ) | ||||
|  | ||||
| 	$linker_args = @( | ||||
| 		$flag_link_win_subsystem_console | ||||
| 	) | ||||
|  | ||||
| 	$unit       = Join-Path $path_gen   'handmade_gen.cpp' | ||||
| 	$executable = Join-Path $path_build 'handmade_gen.exe' | ||||
|  | ||||
| @@ -408,10 +430,7 @@ if ( $false ) { | ||||
|  | ||||
| #region Handmade Runtime | ||||
| $includes = @( | ||||
| 	$path_project, | ||||
| 	$path_gen, | ||||
| 	$path_deps, | ||||
| 	$path_platform | ||||
| 	$path_project | ||||
| ) | ||||
|  | ||||
| # Microsoft | ||||
| @@ -423,9 +442,6 @@ $lib_winmm  = 'Winmm.lib' | ||||
| # Github | ||||
| $lib_jsl = Join-Path $path_deps 'JoyShockLibrary/x64/JoyShockLibrary.lib' | ||||
|  | ||||
| $unit       = Join-Path $path_project 'handmade_win32.cpp' | ||||
| $executable = Join-Path $path_build   'handmade_win32.exe' | ||||
|  | ||||
| $stack_size = 1024 * 1024 * 4 | ||||
|  | ||||
| $compiler_args = @( | ||||
| @@ -436,9 +452,6 @@ $compiler_args = @( | ||||
| 	$flag_warnings_as_errors | ||||
| 	$flag_optimize_intrinsics | ||||
|  | ||||
| 	($flag_define + 'Build_DLL=0' ) | ||||
|  | ||||
| 	# For now this script only supports unity builds... (for the full binary) | ||||
| 	($flag_define + 'Build_Unity=1' ) | ||||
| ) | ||||
|  | ||||
| @@ -449,19 +462,108 @@ else { | ||||
| 	$compiler_args += ( $flag_define + 'Build_Development=0' ) | ||||
| } | ||||
|  | ||||
| $linker_args = @( | ||||
| 	$lib_gdi32, | ||||
| 	# $lib_xinput, | ||||
| 	$lib_user32, | ||||
| 	$lib_winmm, | ||||
| if ( $engine ) | ||||
| { | ||||
| 	$engine_compiler_args = $compiler_args | ||||
| 	$engine_compiler_args += ($flag_define + 'Build_DLL=1' ) | ||||
|  | ||||
| 	$lib_jsl, | ||||
| 	if ( $vendor -eq 'msvc' ) | ||||
| 	{ | ||||
| 		$engine_compiler_args += ($flag_define + 'Engine_API=__declspec(dllexport)') | ||||
| 	} | ||||
| 	if ( $vendor -eq 'clang' ) | ||||
| 	{ | ||||
| 		$engine_compiler_args += ($flag_define + 'Engine_API=__attribute__((visibility("default")))') | ||||
| 	} | ||||
|  | ||||
| 	$flag_link_win_subsystem_windows | ||||
| 	$flag_link_optimize_references | ||||
| ) | ||||
| 	$linker_args = @( | ||||
| 		$flag_link_dll, | ||||
| 		$flag_link_optimize_references | ||||
| 	) | ||||
|  | ||||
| build-simple $includes $compiler_args $linker_args $unit $executable | ||||
| 	$unit            = Join-Path $path_project 'handmade_engine.cpp' | ||||
| 	$dynamic_library = Join-Path $path_build   'handmade_engine.dll' | ||||
|  | ||||
| 	build-simple $includes $engine_compiler_args $linker_args $unit $dynamic_library | ||||
|  | ||||
| 	if ( Test-Path $dynamic_library ) | ||||
| 	{ | ||||
| 		$data_path = Join-Path $path_data 'handmade_engine.dll' | ||||
| 		move-item $dynamic_library $data_path -Force | ||||
|  | ||||
| 		# We need to generate the symbol table so that we can lookup the symbols we need when loading/reloading the library at runtime. | ||||
| 		# This is done by sifting through the emitter.map file from the linker for the base symbol names | ||||
| 		# and mapping them to their found decorated name | ||||
|  | ||||
| 		$engine_symbols = @( | ||||
| 			'on_module_reload', | ||||
| 			'startup', | ||||
| 			'shutdown', | ||||
| 			'update_and_render', | ||||
| 			'update_audio' | ||||
| 		) | ||||
|  | ||||
| 		$path_engine_map = Join-Path $path_build 'handmade_engine.map' | ||||
|  | ||||
| 		$engine_symbol_table = @() | ||||
| 		$engine_symbol_list  = @() | ||||
| 		Get-Content -Path $path_engine_map | ForEach-Object { | ||||
| 			# Split each line by whitespace | ||||
| 			$tokens = $_ -split '\s+', 3 | ||||
|  | ||||
| 			# Extract only the decorated name using regex for both MSVC and Clang conventions | ||||
| 			$decoratedName = ($tokens[2] -match '(\?[\w@]+|_Z[\w@]+)' ) ? $matches[1] : $null | ||||
|  | ||||
| 			# Check each regular name against the current line | ||||
| 			foreach ($name in $engine_symbols) { | ||||
| 				if ($decoratedName -like "*$name*") { | ||||
| 					$engine_symbol_table += $name + ', ' + $decoratedName | ||||
| 					$engine_symbol_list  += $decoratedName | ||||
| 					$engine_symbols = $engine_symbols -ne $name  # Remove the found symbol from the array | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		write-host "Engine Symbol Table:" -ForegroundColor Green | ||||
| 		$engine_symbol_table | ForEach-Object { | ||||
| 			write-host "`t$_" -ForegroundColor Green | ||||
| 		} | ||||
|  | ||||
| 		# Write the symbol table to a file | ||||
| 		$path_engine_symbols = Join-Path $path_data 'handmade_engine.symbols' | ||||
| 		$engine_symbol_list | Out-File -Path $path_engine_symbols | ||||
| 	} | ||||
| } | ||||
|  | ||||
| if ( $platform ) | ||||
| { | ||||
| 	$platform_compiler_args  = $compiler_args | ||||
| 	$platform_compiler_args += ($flag_define + 'Build_DLL=0' ) | ||||
|  | ||||
| 	$linker_args = @( | ||||
| 		$lib_gdi32, | ||||
| 		# $lib_xinput, | ||||
| 		$lib_user32, | ||||
| 		$lib_winmm, | ||||
|  | ||||
| 		$lib_jsl, | ||||
|  | ||||
| 		$flag_link_win_subsystem_windows | ||||
| 		$flag_link_optimize_references | ||||
| 	) | ||||
|  | ||||
| 	$unit       = Join-Path $path_project 'handmade_win32.cpp' | ||||
| 	$executable = Join-Path $path_build   'handmade_win32.exe' | ||||
|  | ||||
| 	build-simple $includes $platform_compiler_args $linker_args $unit $executable | ||||
|  | ||||
| 	if ( Test-Path $executable ) | ||||
| 	{ | ||||
| 		$data_path = Join-Path $path_data 'handmade_win32.exe' | ||||
| 		move-item $executable $data_path -Force | ||||
| 	} | ||||
| } | ||||
| #endregion Handmade Runtime | ||||
|  | ||||
| Pop-Location | ||||
| #endregion Building | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -23,5 +23,6 @@ if ( -not (Test-Path $vs_devshell) ) { | ||||
|  | ||||
| # Launch the Visual Studio Developer Shell | ||||
| Push-Location | ||||
| write-host @args | ||||
| & $vs_devshell @args | ||||
| Pop-Location | ||||
|   | ||||
		Reference in New Issue
	
	Block a user