- Added basic filesystem method to get the base path of the app

- Added hot reload support of the game dll
- various changes and refactor
This commit is contained in:
2025-02-23 20:37:44 -05:00
parent f83a238473
commit 051939f827
33 changed files with 673 additions and 212 deletions

View File

@@ -48,10 +48,9 @@
<PropertyGroup Label="UserMacros"/>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(SolutionDir)Juliet\include\;$(SolutionDir)Game;$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)\bin\$(Platform)\$(Configuration)\;$(LibraryPath)</LibraryPath>
<OutDir>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
@@ -66,10 +65,15 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)Juliet\include\;$(SolutionDir)Game;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Juliet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalLibraryDirectories>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</AdditionalLibraryDirectories>
<ProgramDatabaseFile>$(OutDir)$(TargetName)_$(Random).pdb</ProgramDatabaseFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -83,24 +87,21 @@
<PreprocessorDefinitions>NDEBUG;CPPDYNAMICLIBRARYTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(SolutionDir)Juliet\include\;$(SolutionDir)Game;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(SolutionDir)\bin\$(Platform)\$(Configuration)\</AdditionalLibraryDirectories>
<AdditionalDependencies>Juliet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp"/>
<ClCompile Include="game.cpp" />
<ClCompile Include="Entity\EntityManager.cpp"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Juliet\Juliet.vcxproj">
<Project>{1bbc0b92-e4d8-4838-974b-439c5c501e82}</Project>
<Name>Juliet</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Entity\Entity.h"/>
<ClInclude Include="Entity\EntityManager.h"/>

View File

@@ -1,66 +0,0 @@
#include <cstdio>
#include <windows.h> // TODO: remove because our dll should not be platform dependant
#include <Entity/Entity.h>
#include <Entity/EntityManager.h>
// Test code
namespace Game
{
struct Door
{
DECLARE_ENTITY()
bool IsOpened;
};
DEFINE_ENTITY(Door);
struct Rock
{
DECLARE_ENTITY()
int Health;
};
DEFINE_ENTITY(Rock);
} // namespace Game
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
using namespace Game;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
// Entity Use case
InitEntityManager();
auto& manager = GetEntityManager();
Door* door = MakeEntity<Door>(manager, 10.0f, 2.0f);
door->IsOpened = true;
Entity* ent = door->Base;
Door* stillDoor = DownCast<Door>(ent);
Assert(door == stillDoor);
Rock* rock = MakeEntity<Rock>(manager, 1.f, 2.f);
rock->Health = 100;
Assert(door->Base != rock->Base);
printf("Door is %s\n", door->IsOpened ? "Opened" : "Closed");
printf("Rock has %d health points\n", rock->Health);
// Have to manually free for now because im not using arenas or anything
free(door->Base);
free(door);
free(rock->Base);
free(rock);
break;
}
case DLL_THREAD_ATTACH: break;
case DLL_THREAD_DETACH: break;
case DLL_PROCESS_DETACH: break;
}
return TRUE;
}

69
Game/game.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include <cstdio>
#include <windows.h> // TODO: remove because our dll should not be platform dependant
#undef min
#undef max
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Entity/Entity.h>
#include <Entity/EntityManager.h>
#include <Graphics/Graphics.h>
// Test code
namespace Game
{
struct Door
{
DECLARE_ENTITY()
bool IsOpened;
};
DEFINE_ENTITY(Door);
struct Rock
{
DECLARE_ENTITY()
int Health;
};
DEFINE_ENTITY(Rock);
} // namespace Game
using namespace Juliet;
extern "C" __declspec(dllexport) void __cdecl GameInit()
{
using namespace Game;
// Entity Use case
InitEntityManager();
auto& manager = GetEntityManager();
Door* door = MakeEntity<Door>(manager, 10.0f, 2.0f);
door->IsOpened = true;
Entity* ent = door->Base;
Door* stillDoor = DownCast<Door>(ent);
Assert(door == stillDoor);
Rock* rock = MakeEntity<Rock>(manager, 1.f, 2.f);
rock->Health = 100;
Assert(door->Base != rock->Base);
printf("Door is %s\n", door->IsOpened ? "Opened" : "Closed");
printf("Rock has %d health points\n", rock->Health);
// Have to manually free for now because im not using arenas or anything
free(door->Base);
free(door);
free(rock->Base);
free(rock);
}
extern "C" __declspec(dllexport) void __cdecl GameShutdown()
{
printf("Shutting down game...\n");
}
extern "C" __declspec(dllexport) void __cdecl GameUpdate(float deltaTime)
{
printf("Updating game...\n");
}

View File

@@ -11,6 +11,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JulietApp", "JulietApp\Juli
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game", "Game\Game.vcxproj", "{B7B12DCC-1A69-4371-A9FE-D6E7671497B0}"
ProjectSection(ProjectDependencies) = postProject
{1BBC0B92-E4D8-4838-974B-439C5C501E82} = {1BBC0B92-E4D8-4838-974B-439C5C501E82}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -113,16 +113,16 @@
<ClInclude Include="include\Core\Common\NonNullPtr.h"/>
<ClInclude Include="include\Core\Common\Singleton.h"/>
<ClInclude Include="include\Core\Container\Vector.h"/>
<ClInclude Include="include\Core\DynLib\DynamicLibrary.h"/>
<ClInclude Include="include\Core\HAL\Display\Display.h"/>
<ClInclude Include="include\Core\HAL\Event\SystemEvent.h"/>
<ClInclude Include="include\Core\HAL\Keyboard\Keyboard.h"/>
<ClInclude Include="include\Core\HAL\Keyboard\KeyCode.h"/>
<ClInclude Include="include\Core\HAL\Keyboard\ScanCode.h"/>
<ClInclude Include="include\Core\HAL\Mouse\Mouse.h"/>
<ClInclude Include="include\Core\JulietInit.h"/>
<ClInclude Include="include\Core\Logging\LogManager.h"/>
<ClInclude Include="include\Core\Logging\LogTypes.h"/>
<ClInclude Include="include\Core\HAL\Display\Display.h" />
<ClInclude Include="include\Core\HAL\Event\SystemEvent.h" />
<ClInclude Include="include\Core\HAL\Keyboard\Keyboard.h" />
<ClInclude Include="include\Core\HAL\Keyboard\KeyCode.h" />
<ClInclude Include="include\Core\HAL\Keyboard\ScanCode.h" />
<ClInclude Include="include\Core\HAL\Mouse\Mouse.h" />
<ClInclude Include="include\Core\HotReload\HotReload.h" />
<ClInclude Include="include\Core\JulietInit.h" />
<ClInclude Include="include\Core\Logging\LogManager.h" />
<ClInclude Include="include\Core\Logging\LogTypes.h" />
<ClInclude Include="include\Core\Math\Shape.h" />
<ClInclude Include="include\Core\Memory\Allocator.h" />
<ClInclude Include="include\Core\Memory\Utils.h" />
@@ -152,6 +152,8 @@
<ClInclude Include="src\Core\HAL\Event\Mouse_Private.h" />
<ClInclude Include="src\Core\HAL\Event\Win32ScanCode.h" />
<ClInclude Include="src\Core\HAL\Event\WindowEvent.h" />
<ClInclude Include="src\Core\HAL\Filesystem\Filesystem_Platform.h" />
<ClInclude Include="src\Core\HAL\Filesystem\Filesystem_Private.h" />
<ClInclude Include="src\Core\Networking\SocketPlatformImpl.h" />
<ClInclude Include="src\Core\HAL\Win32.h" />
<ClInclude Include="src\Graphics\D3D12\D3D12Common.h" />
@@ -168,69 +170,20 @@
<ItemGroup>
<ClCompile Include="src\Core\Application\ApplicationManager.cpp" />
<ClCompile Include="src\Core\Common\CoreUtils.cpp" />
<ClCompile Include="src\Core\DynLib\Win32\DynamicLibrary.cpp">
<RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
<Optimization>Disabled</Optimization>
<SupportJustMyCode>true</SupportJustMyCode>
<AssemblerOutput>NoListing</AssemblerOutput>
<AssemblerListingLocation>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\</AssemblerListingLocation>
<UndefineAllPreprocessorDefinitions>false</UndefineAllPreprocessorDefinitions>
<BrowseInformationFile>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\</BrowseInformationFile>
<CompileAs>Default</CompileAs>
<ConformanceMode>Default</ConformanceMode>
<DiagnosticsFormat>Column</DiagnosticsFormat>
<ExceptionHandling>false</ExceptionHandling>
<EnableASAN>false</EnableASAN>
<EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<InlineFunctionExpansion>Default</InlineFunctionExpansion>
<IntrinsicFunctions>false</IntrinsicFunctions>
<LanguageStandard>stdcpp20</LanguageStandard>
<LanguageStandard_C>Default</LanguageStandard_C>
<ModuleDependenciesFile>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\</ModuleDependenciesFile>
<OmitDefaultLibName>false</OmitDefaultLibName>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<ObjectFileName>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\</ObjectFileName>
<CallingConvention>Cdecl</CallingConvention>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\Juliet.pch</PrecompiledHeaderOutputFile>
<PreprocessToFile>false</PreprocessToFile>
<PreprocessKeepComments>false</PreprocessKeepComments>
<PreprocessSuppressLineNumbers>false</PreprocessSuppressLineNumbers>
<ScanSourceForModuleDependencies>false</ScanSourceForModuleDependencies>
<ShowIncludes>false</ShowIncludes>
<SourceDependenciesFile>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\</SourceDependenciesFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<BufferSecurityCheck>true</BufferSecurityCheck>
<SmallerTypeCheck>false</SmallerTypeCheck>
<StructMemberAlignment>Default</StructMemberAlignment>
<TrackerLogDirectory>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\Juliet.tlog\</TrackerLogDirectory>
<MinimalRebuildFromTracking>true</MinimalRebuildFromTracking>
<TreatWarningAsError>false</TreatWarningAsError>
<WarningLevel>Level3</WarningLevel>
<XMLDocumentationFileName>W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\</XMLDocumentationFileName>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<IntelJCCErratum>false</IntelJCCErratum>
<BuildStlModules>false</BuildStlModules>
<TreatExternalTemplatesAsInternal>true</TreatExternalTemplatesAsInternal>
<PreprocessorDefinitions>_DEBUG;JULIET_EXPORT;JULIET_WIN32;_WINDLL;_UNICODE;UNICODE;</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<LinkCompiled>true</LinkCompiled>
<ClangClMode>true</ClangClMode>
<MSCVersion>Default</MSCVersion>
<AdditionalOptions>--target=amd64-pc-windows-msvc</AdditionalOptions>
</ClCompile>
<ClCompile Include="src\Core\HAL\Display\Display.cpp" />
<ClCompile Include="src\Core\HAL\Display\Win32\Win32DisplayDevice.cpp" />
<ClCompile Include="src\Core\HAL\Display\Win32\Win32DisplayEvent.cpp" />
<ClCompile Include="src\Core\HAL\Display\Win32\Win32Window.cpp" />
<ClCompile Include="src\Core\HAL\DynLib\Win32\DynamicLibrary.cpp" />
<ClCompile Include="src\Core\HAL\Event\Keyboard.cpp" />
<ClCompile Include="src\Core\HAL\Event\KeyboardMapping.cpp" />
<ClCompile Include="src\Core\HAL\Event\Mouse.cpp" />
<ClCompile Include="src\Core\HAL\Event\SystemEvent.cpp" />
<ClCompile Include="src\Core\HAL\Event\WindowEvent.cpp" />
<ClCompile Include="src\Core\HAL\Filesystem\Filesystem.cpp" />
<ClCompile Include="src\Core\HAL\Filesystem\Win32\Win32Filesystem.cpp" />
<ClCompile Include="src\Core\HotReload\HotReload.cpp" />
<ClCompile Include="src\Core\HotReload\Win32\Win32HotReload.cpp" />
<ClCompile Include="src\Core\Juliet.cpp" />
<ClCompile Include="src\Core\Logging\LogManager.cpp" />
<ClCompile Include="src\Core\Memory\Allocator.cpp" />

View File

@@ -24,6 +24,14 @@ struct ByteBuffer
size_t Size;
};
template <typename CharType>
struct StringBuffer
{
CharType* Data;
size_t Size;
};
#define StringBufferParam(str) { (str), sizeof(str) }
using FunctionPtr = auto (*)(void) -> void;
// Limits

View File

@@ -1,7 +1,7 @@
#pragma once
#include <Core/Common/CoreTypes.h>
#include <core/Common/NonNullPtr.h>
#include <Core/Common/NonNullPtr.h>
#include <Juliet.h>
namespace Juliet

View File

@@ -1,12 +1,10 @@
#pragma once
#include <core/Common/NonNullPtr.h>
#include <Core/Common/NonNullPtr.h>
namespace Juliet
{
struct DynamicLibrary
{
};
struct DynamicLibrary;
extern JULIET_API DynamicLibrary* LoadDynamicLibrary(const char* filename);
extern JULIET_API FunctionPtr LoadFunction(NonNullPtr<DynamicLibrary> lib, const char* functionName);

View File

@@ -0,0 +1,7 @@
#pragma once
namespace Juliet
{
// Returns the path to the application directory
extern JULIET_API char * GetBasePath();
}

View File

@@ -0,0 +1,36 @@
#pragma once
namespace Juliet
{
// Fwd Declare
struct DynamicLibrary;
struct HotReloadCode
{
StringBuffer<char> DLLFullPath;
StringBuffer<char> LockFullPath;
StringBuffer<const char> TransientDLLName;
uint64 LastWriteTime;
DynamicLibrary* Dll;
void** Functions;
const char** FunctionNames;
uint32 FunctionCount;
uint32 UniqueID;
bool IsValid : 1;
};
extern JULIET_API void InitHotReloadCode(HotReloadCode& code, StringBuffer<const char> dllName,
StringBuffer<const char> transientDllName, StringBuffer<const char> lockFilename);
extern JULIET_API void ShutdownHotReloadCode(HotReloadCode& code);
extern JULIET_API void LoadCode(HotReloadCode& code);
extern JULIET_API void UnloadCode(HotReloadCode& code);
extern JULIET_API void ReloadCode(HotReloadCode& code);
extern JULIET_API bool ShouldReloadCode(const HotReloadCode& code);
} // namespace Juliet

View File

@@ -1,6 +1,7 @@
#pragma once
#include <Juliet.h>
#include <Core/Common/CoreTypes.h>
// TODO : Juliet strings
#include <string>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <core/Common/CoreTypes.h>
#include <memory>
namespace Juliet
{
@@ -21,8 +21,10 @@ namespace Juliet
template <typename Type>
void SafeFree(Type*& memory)
{
Assert(memory);
free(memory);
memory = nullptr;
if (memory)
{
free(memory);
memory = nullptr;
}
}
} // namespace Juliet

View File

@@ -5,4 +5,13 @@
namespace Juliet
{
using Thread = std::thread;
// TODO : Proper wait
inline void wait_ms(int milliseconds)
{
clock_t start_time = clock();
while (clock() < start_time + milliseconds)
{
}
}
}

View File

@@ -34,7 +34,7 @@ namespace Juliet::Win32
ReleaseDC(state->Handle, state->HDC);
DestroyWindow(state->Handle);
Free(state);
SafeFree(state);
}
window->State = nullptr;
}

View File

@@ -1,6 +1,6 @@
#include <pch.h>
#include <Core/DynLib/DynamicLibrary.h>
#include <Core/HAL/DynLib/DynamicLibrary.h>
#include <Core/HAL/Win32.h>
namespace Juliet
@@ -10,7 +10,7 @@ namespace Juliet
// TODO : Move into string file
// Use portable code + pass the memory array into parameter and not use new
// This is from http://www.rohitab.com/discuss/topic/41257-char-to-lpcwstr/
wchar_t* UTF8ToWideChar(const char* utf8)
static wchar_t* UTF8ToWideChar(const char* utf8)
{
wchar_t* w;
@@ -60,7 +60,7 @@ namespace Juliet
FunctionPtr LoadFunction(NonNullPtr<DynamicLibrary> lib, const char* functionName)
{
auto function = (FunctionPtr)GetProcAddress(reinterpret_cast<HMODULE>(lib.Get()), functionName);
auto function = reinterpret_cast<FunctionPtr>(GetProcAddress(reinterpret_cast<HMODULE>(lib.Get()), functionName));
if (!function)
{
Log(LogLevel::Error, LogCategory::Core, "Failed loading %s", functionName);

View File

@@ -0,0 +1,32 @@
#include <pch.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HAL/Filesystem/Filesystem_Private.h>
#include <Core/HAL/Filesystem/Filesystem_Platform.h>
#include <Core/Memory/Allocator.h>
namespace Juliet
{
namespace
{
char* CachedBasePath = nullptr;
}
char* GetBasePath()
{
if (CachedBasePath == nullptr)
{
CachedBasePath = Platform::GetBasePath();
}
return CachedBasePath;
}
void InitFilesystem() {}
void ShutdownFilesystem()
{
if (CachedBasePath != nullptr)
{
SafeFree(CachedBasePath);
}
}
} // namespace Juliet

View File

@@ -0,0 +1,6 @@
#pragma once
namespace Juliet::Platform
{
extern char* GetBasePath();
}

View File

@@ -0,0 +1,7 @@
#pragma once
namespace Juliet
{
extern void InitFilesystem();
extern void ShutdownFilesystem();
}

View File

@@ -0,0 +1,90 @@
#include <pch.h>
#include <Core/HAL/Filesystem/Filesystem_Platform.h>
#include <Core/HAL/Win32.h>
#include <Core/Memory/Allocator.h>
namespace Juliet::Platform
{
namespace
{
// TODO : Move into string file
// Use portable code + pass the memory array into parameter and not use new
// From: https://stackoverflow.com/questions/215963/how-do-you-properly-use-widechartomultibyte
char* WideCharToUTF8(wchar_t* wcharStr)
{
char* result = nullptr;
size_t length = WideCharToMultiByte(CP_UTF8, 0, wcharStr, -1, nullptr, 0, nullptr, nullptr);
if (length <= 0)
{
return nullptr;
}
result = new char[length];
if (!result)
{
return nullptr;
}
if (WideCharToMultiByte(CP_UTF8, 0, wcharStr, -1, result, length, nullptr, nullptr) <= 0)
{
delete[] result;
return nullptr;
}
return result;
}
} // namespace
char* GetBasePath()
{
// Allocate a buffer that could fit the module size.
// Max Path is a good start but could be bigger if the path include long path prefix
StringBuffer<WCHAR> buffer{ .Data = nullptr, .Size = MAX_PATH };
buffer.Data = static_cast<WCHAR*>(Calloc(MAX_PATH, sizeof(WCHAR)));
if (buffer.Data == nullptr)
{
return nullptr;
}
size_t moduleFilenameLength = 0;
while (true)
{
moduleFilenameLength = GetModuleFileNameW(nullptr, buffer.Data, static_cast<uint32>(buffer.Size));
// If the module filename length is bigger than the buffer size, we need to reallocate a bigger buffer
if (moduleFilenameLength >= buffer.Size - 1)
{
buffer.Size *= 2;
buffer.Data = static_cast<WCHAR*>(Realloc(buffer.Data, buffer.Size * sizeof(WCHAR)));
}
else
{
break;
}
}
if (moduleFilenameLength == 0)
{
SafeFree(buffer.Data);
Log(LogLevel::Error, LogCategory::Core, "Filesystem: Cannot locate executable path");
}
size_t idx = 0;
for (idx = moduleFilenameLength - 1; idx > 0; --idx)
{
if (buffer.Data[idx] == '\\')
{
break;
}
}
Assert(idx > 0 && "Path is not absolute!");
buffer.Data[idx + 1] = '\0'; // Chop chop
// TODO: Add utils to Convert to/from UTF8W
char* basePath = WideCharToUTF8(buffer.Data);
SafeFree(buffer.Data);
return basePath;
}
} // namespace Juliet::Platform

View File

@@ -0,0 +1,70 @@
#include <pch.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HotReload/HotReload.h>
#include <Core/Memory/Allocator.h>
#include <Core/Thread/Thread.h>
#define MAX_TRIES 100
namespace Juliet
{
void InitHotReloadCode(HotReloadCode& code, StringBuffer<const char> dllName,
StringBuffer<const char> transientDllName, StringBuffer<const char> lockFilename)
{
// Get the app base path and build the dll path from there.
const char* basePath = GetBasePath();
size_t basePathLength = strlen(basePath);
// Assign Transient dll path
code.TransientDLLName = transientDllName;
// First allocate all the full path.
// TODO: Add path composition into filesystem + string format + string builder
const size_t dllFullPathLength = basePathLength + dllName.Size;
code.DLLFullPath.Data = static_cast<char*>(Calloc(dllFullPathLength, sizeof(char)));
int writtenSize = snprintf(code.DLLFullPath.Data, dllFullPathLength, "%s%s", basePath, dllName.Data);
if (writtenSize < static_cast<int>(dllFullPathLength) - 1)
{
SafeFree(code.DLLFullPath.Data);
Log(LogLevel::Error, LogCategory::Core, "Cannot create DLL Full Path");
return;
}
code.DLLFullPath.Size = writtenSize + 1;
// Lock filename path
const size_t lockPathLength = basePathLength + lockFilename.Size;
code.LockFullPath.Data = static_cast<char*>(Calloc(lockPathLength, sizeof(char)));
writtenSize = snprintf(code.LockFullPath.Data, lockPathLength, "%s%s", basePath, lockFilename.Data);
if (writtenSize < static_cast<int>(lockPathLength) - 1)
{
code.LockFullPath.Size = 0;
SafeFree(code.LockFullPath.Data);
Log(LogLevel::Error, LogCategory::Core, "Cannot create lock file full path");
return;
}
code.LockFullPath.Size = writtenSize + 1;
LoadCode(code);
}
void ShutdownHotReloadCode(HotReloadCode& code)
{
UnloadCode(code);
code.DLLFullPath.Size = 0;
SafeFree(code.DLLFullPath.Data);
code.LockFullPath.Size = 0;
SafeFree(code.LockFullPath.Data);
}
void ReloadCode(HotReloadCode& code)
{
UnloadCode(code);
for (uint32 tryItr = 0; !code.IsValid && tryItr < MAX_TRIES; ++tryItr)
{
LoadCode(code);
wait_ms(100);
}
}
} // namespace Juliet

View File

@@ -0,0 +1,134 @@
#include <pch.h>
#include <Core/HAL/DynLib/DynamicLibrary.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HAL/Win32.h>
#include <Core/HotReload/HotReload.h>
#include <Core/Memory/Allocator.h>
#define MAX_ATTEMPT 256
#define MAX_DLL_ID 32767
#define TEMP_DLL_BUFFER_SIZE_FOR_ID 6 // MAX_DLL_ID + _
namespace Juliet
{
namespace
{
FILETIME GetLastWriteTime(const char* filename)
{
FILETIME lastWriteTime = {};
WIN32_FILE_ATTRIBUTE_DATA Data;
if (GetFileAttributesExA(filename, GetFileExInfoStandard, &Data))
{
lastWriteTime = Data.ftLastWriteTime;
}
return lastWriteTime;
}
} // namespace
void LoadCode(HotReloadCode& code)
{
// TODO : Create and use a TransientAllocator
// Create temp dll name
char* lockFilename = code.LockFullPath.Data;
WIN32_FILE_ATTRIBUTE_DATA Ignored;
if (!GetFileAttributesExA(lockFilename, GetFileExInfoStandard, &Ignored))
{
const char* dllName = code.DLLFullPath.Data;
FILETIME lastWriteTime = GetLastWriteTime(dllName);
ULARGE_INTEGER result{ .LowPart = lastWriteTime.dwLowDateTime, .HighPart = lastWriteTime.dwHighDateTime };
code.LastWriteTime = result.QuadPart;
// Create filename for the temp dll until we find a valid id.
// This is not infinite, in a big session we could reach the 128 attempts and fail...
// We'll see for better later.
// Get the app base path and build the dll path from there.
const char* basePath = GetBasePath();
size_t basePathLength = strlen(basePath);
StringBuffer<char> tempDllPath = {};
const size_t tempDllMaxBufferSize = basePathLength + code.TransientDLLName.Size + TEMP_DLL_BUFFER_SIZE_FOR_ID;
tempDllPath.Data = static_cast<char*>(Calloc(tempDllMaxBufferSize, sizeof(char)));
char idToStr[TEMP_DLL_BUFFER_SIZE_FOR_ID];
for (uint32 attempt = 0; attempt < MAX_ATTEMPT; ++attempt)
{
// int to char
int idLength = snprintf(idToStr, sizeof(idToStr), "%u", code.UniqueID);
int writtenSize = snprintf(tempDllPath.Data, tempDllMaxBufferSize, "%s%u_%s", basePath, code.UniqueID,
code.TransientDLLName.Data);
if (writtenSize < static_cast<int>(basePathLength + idLength + code.TransientDLLName.Size))
{
SafeFree(tempDllPath.Data);
Log(LogLevel::Error, LogCategory::Core, "Cannot create temp full path");
return;
}
tempDllPath.Size = writtenSize + 1;
if (++code.UniqueID >= MAX_DLL_ID)
{
code.UniqueID = 0;
}
if (CopyFileA(dllName, tempDllPath.Data, false))
{
break;
}
}
code.Dll = LoadDynamicLibrary(tempDllPath.Data);
if (code.Dll)
{
code.IsValid = true;
for (size_t FunctionIndex = 0; FunctionIndex < code.FunctionCount; ++FunctionIndex)
{
if (auto function = reinterpret_cast<void*>(LoadFunction(code.Dll, code.FunctionNames[FunctionIndex])))
{
code.Functions[FunctionIndex] = function;
}
else
{
code.IsValid = false;
}
}
}
SafeFree(tempDllPath.Data);
tempDllPath = {};
}
if (!code.IsValid)
{
UnloadCode(code);
}
}
void UnloadCode(HotReloadCode& code)
{
code.IsValid = false;
UnloadDynamicLibrary(code.Dll);
code.Dll = nullptr;
code.LastWriteTime = 0;
ZeroDynArray(code.FunctionCount, code.Functions);
}
bool ShouldReloadCode(const HotReloadCode& code)
{
ULARGE_INTEGER largeInt = {};
FILETIME codeLastWriteTime = {};
largeInt.QuadPart = code.LastWriteTime;
codeLastWriteTime.dwHighDateTime = largeInt.HighPart;
codeLastWriteTime.dwLowDateTime = largeInt.LowPart;
FILETIME lastWriteTime = GetLastWriteTime(code.DLLFullPath.Data);
int compare = CompareFileTime(&lastWriteTime, &codeLastWriteTime);
return compare != 0;
}
} // namespace Juliet

View File

@@ -2,6 +2,7 @@
#include <Core/Common/EnumUtils.h>
#include <Core/HAL/Display/Display_Private.h>
#include <Core/HAL/Filesystem/Filesystem_Private.h>
#include <Core/JulietInit.h>
namespace Juliet
@@ -40,6 +41,10 @@ namespace Juliet
void JulietInit(JulietInit_Flags flags)
{
// Mandatory systems
InitFilesystem();
// Optional systems
if ((flags | JulietInit_Flags::Display) != JulietInit_Flags::None)
{
InitializeDisplaySystem();
@@ -55,6 +60,8 @@ namespace Juliet
DecrementSystemRefCount(JulietInit_Flags::Display);
ShutdownDisplaySystem();
}
ShutdownFilesystem();
}
} // namespace Juliet

View File

@@ -37,22 +37,11 @@ namespace Juliet
EngineInstance.Application = nullptr;
}
// Todo: proper fixed tick
void wait_ms(int milliseconds)
{
clock_t start_time = clock();
while (clock() < start_time + milliseconds)
{
}
}
void RunEngine()
{
while (EngineInstance.Application->IsRunning())
{
EngineInstance.Application->Update();
wait_ms(16);
}
}
} // namespace Juliet

View File

@@ -392,7 +392,7 @@ namespace Juliet::D3D12
}
}
Free(commandList->PresentDatas);
SafeFree(commandList->PresentDatas);
Free(commandList.Get());
}

View File

@@ -1,7 +1,7 @@
#include <pch.h>
#include <core/Common/NonNullPtr.h>
#include <Core/DynLib/DynamicLibrary.h>
#include <Core/Common/NonNullPtr.h>
#include <Core/HAL/DynLib/DynamicLibrary.h>
#include <Core/Memory/Allocator.h>
#include <Graphics/D3D12/D3D12RenderPass.h>
#include <Graphics/D3D12/D3D12Synchronization.h>
@@ -200,10 +200,10 @@ namespace Juliet::D3D12
}
// Clean allocations
Free(driver->AvailableCommandLists);
Free(driver->SubmittedCommandLists);
//Free(driver->WindowData); // TODO Should free the vector of WindowData, but we have only one for now
Free(driver->AvailableFences);
SafeFree(driver->AvailableCommandLists);
SafeFree(driver->SubmittedCommandLists);
// Free(driver->WindowData); // TODO Should free the vector of WindowData, but we have only one for now
SafeFree(driver->AvailableFences);
if (driver->IndirectDrawCommandSignature)
{
@@ -310,7 +310,7 @@ namespace Juliet::D3D12
Internal::DestroySwapChain(d3d12Driver, d3d12Driver->WindowData);
Free(d3d12Driver->WindowData);
SafeFree(d3d12Driver->WindowData);
d3d12Driver->WindowData = nullptr;
}

View File

@@ -1 +0,0 @@
#include "EditorMain.h"

View File

@@ -1,5 +0,0 @@
#pragma once
class EditorMain
{
};

View File

@@ -11,21 +11,70 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Editor\EditorMain_win32.cpp"/>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Editor\EditorMain_win32.h"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Game\Game.vcxproj">
<Project>{b7b12dcc-1a69-4371-a9fe-d6e7671497b0}</Project>
<Name>Game</Name>
</ProjectReference>
<ProjectReference Include="..\Juliet\Juliet.vcxproj">
<Project>{1bbc0b92-e4d8-4838-974b-439c5c501e82}</Project>
<Name>Juliet</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="main.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary>
<Optimization>Disabled</Optimization>
<SupportJustMyCode>true</SupportJustMyCode>
<AssemblerOutput>NoListing</AssemblerOutput>
<AssemblerListingLocation>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\</AssemblerListingLocation>
<UndefineAllPreprocessorDefinitions>false</UndefineAllPreprocessorDefinitions>
<BrowseInformationFile>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\</BrowseInformationFile>
<CompileAs>Default</CompileAs>
<ConformanceMode>Default</ConformanceMode>
<DiagnosticsFormat>Column</DiagnosticsFormat>
<ExceptionHandling>false</ExceptionHandling>
<EnableASAN>false</EnableASAN>
<EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<InlineFunctionExpansion>Default</InlineFunctionExpansion>
<IntrinsicFunctions>false</IntrinsicFunctions>
<LanguageStandard>stdcpp20</LanguageStandard>
<LanguageStandard_C>Default</LanguageStandard_C>
<ModuleDependenciesFile>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\</ModuleDependenciesFile>
<OmitDefaultLibName>false</OmitDefaultLibName>
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<ObjectFileName>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\</ObjectFileName>
<CallingConvention>Cdecl</CallingConvention>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\JulietApp.pch</PrecompiledHeaderOutputFile>
<PreprocessToFile>false</PreprocessToFile>
<PreprocessKeepComments>false</PreprocessKeepComments>
<PreprocessSuppressLineNumbers>false</PreprocessSuppressLineNumbers>
<ScanSourceForModuleDependencies>false</ScanSourceForModuleDependencies>
<ShowIncludes>false</ShowIncludes>
<SourceDependenciesFile>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\</SourceDependenciesFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<BufferSecurityCheck>true</BufferSecurityCheck>
<SmallerTypeCheck>false</SmallerTypeCheck>
<StructMemberAlignment>Default</StructMemberAlignment>
<TrackerLogDirectory>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\JulietApp.tlog\</TrackerLogDirectory>
<MinimalRebuildFromTracking>true</MinimalRebuildFromTracking>
<TreatWarningAsError>false</TreatWarningAsError>
<WarningLevel>Level3</WarningLevel>
<XMLDocumentationFileName>W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\</XMLDocumentationFileName>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<IntelJCCErratum>false</IntelJCCErratum>
<BuildStlModules>false</BuildStlModules>
<TreatExternalTemplatesAsInternal>true</TreatExternalTemplatesAsInternal>
<PreprocessorDefinitions>_DEBUG;JULIET_WIN32;_UNICODE;UNICODE;</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<LinkCompiled>true</LinkCompiled>
<ClangClMode>true</ClangClMode>
<MSCVersion>Default</MSCVersion>
<AdditionalOptions>--target=amd64-pc-windows-msvc </AdditionalOptions>
</ClCompile>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>

View File

@@ -1,31 +1,52 @@
#include <Editor/EditorMain_win32.h>
#include <main.h>
#include <Core/Application/ApplicationManager.h>
#include <Core/HAL/Display/Display.h>
#include <Core/HAL/DynLib/DynamicLibrary.h>
#include <Core/HAL/Event/SystemEvent.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/JulietInit.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Graphics/GraphicsConfig.h>
#include <cstdlib>
// TODO : Replace with message box from framework + call main and not winmain + subsystem
#include <Core/DynLib/DynamicLibrary.h>
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsConfig.h>
#include <Graphics/RenderPass.h>
#include <Windows.h>
#include <Core/Memory/Utils.h>
#include <cstdlib>
// TODO : Replace with message box from framework + call main and not winmain + subsystem
// TODO : Think how to do the draw pipeline.
// Ex: Expose a Draw method ?
// Store a graphics context ?
// For now. Put everything in Update down below.
// Should split update from draw, update should have a != timestep than graphics (60fps or more)
// TODO : Remove main.h. Useless
// May be remove that Application class, useless too.
using namespace Juliet;
void Win32EditorApplication::Init()
namespace
{
Log(LogLevel::Message, LogCategory::Editor, "Initializing Editor Application...");
using GameInit_t = void (*)(void);
using GameShutdown_t = void (*)(void);
using GameUpdate_t = void (*)(float deltaTime);
struct GameFunctionTable
{
GameInit_t Init = nullptr;
GameShutdown_t Shutdown = nullptr;
GameUpdate_t Update = nullptr;
} Game;
const char* GameFunctionTable[] = { "GameInit", "GameShutdown", "GameUpdate" };
} // namespace
void JulietApplication::Init()
{
Log(LogLevel::Message, LogCategory::Editor, "Initializing Juliet Application...");
Log(LogLevel::Message, LogCategory::Editor, "%s", GetBasePath());
GraphicsConfig config;
GraphicsDevice = CreateGraphicsDevice(config);
@@ -37,18 +58,25 @@ void Win32EditorApplication::Init()
if (Running)
{
AttachToWindow(GraphicsDevice, MainWindow);
Game = LoadDynamicLibrary("Game.dll");
//Game = LoadDynamicLibrary("Game.dll");
GameCode.Functions = reinterpret_cast<void**>(&Game);
GameCode.FunctionCount = ArraySize(GameFunctionTable);
GameCode.FunctionNames = GameFunctionTable;
InitHotReloadCode(GameCode, StringBufferParam("Game.dll"),StringBufferParam("Game_Temp.dll"), StringBufferParam("lock.tmp"));
if ((Running = GameCode.IsValid))
{
Game.Init();
}
}
}
void Win32EditorApplication::Shutdown()
void JulietApplication::Shutdown()
{
Log(LogLevel::Message, LogCategory::Editor, "Shutdown Editor Application...");
Log(LogLevel::Message, LogCategory::Editor, "Shutting down Juliet Application...");
if (Game)
{
UnloadDynamicLibrary(Game);
}
Game.Shutdown();
ShutdownHotReloadCode(GameCode);
if (MainWindow && GraphicsDevice)
{
@@ -64,9 +92,11 @@ void Win32EditorApplication::Shutdown()
{
DestroyGraphicsDevice(GraphicsDevice);
}
Log(LogLevel::Message, LogCategory::Editor, "Juliet App shutdown Completed");
}
void Win32EditorApplication::Update()
void JulietApplication::Update()
{
SystemEvent evt;
while (GetEvent(evt))
@@ -80,6 +110,13 @@ void Win32EditorApplication::Update()
}
}
Game.Update(0.0f);
if (ShouldReloadCode(GameCode))
{
ReloadCode(GameCode);
}
// Draw here for now
// 1) Acquire a Command Buffer
CommandList* cmdList = AcquireCommandList(GraphicsDevice, QueueType::Graphics);
@@ -114,24 +151,24 @@ void Win32EditorApplication::Update()
SubmitCommandLists(cmdList);
}
bool Win32EditorApplication::IsRunning()
bool JulietApplication::IsRunning()
{
return Running;
}
namespace
{
Win32EditorApplication EditorApplication;
JulietApplication EditorApplication;
}
Win32EditorApplication& GetEditorApplication()
JulietApplication& GetEditorApplication()
{
return EditorApplication;
}
int main(int argc, char** argv)
{
CreateMutex(0, false, L"Local\\Juliet.Editor");
CreateMutex(0, false, L"Local\\Juliet.App");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
MessageBox(nullptr, L"An instance of Juliet is already running.", L"Juliet", MB_OK | MB_ICONEXCLAMATION);

View File

@@ -1,8 +1,9 @@
#pragma once
#include <Core/Application/IApplication.h>
#include <Core/DynLib/DynamicLibrary.h>
#include <Core/HAL/Display/Display.h>
#include <Core/HAL/DynLib/DynamicLibrary.h>
#include <Core/HotReload/HotReload.h>
namespace Juliet
{
@@ -10,7 +11,7 @@ namespace Juliet
struct Window;
} // namespace Juliet
class Win32EditorApplication : public Juliet::IApplication
class JulietApplication : public Juliet::IApplication
{
protected:
void Init() override;
@@ -21,9 +22,9 @@ class Win32EditorApplication : public Juliet::IApplication
private:
Juliet::Window* MainWindow = {};
Juliet::GraphicsDevice* GraphicsDevice = {};
Juliet::DynamicLibrary* Game = {};
Juliet::HotReloadCode GameCode = {};
bool Running = false;
};
Win32EditorApplication& GetEditorApplication();
JulietApplication& GetEditorApplication();

BIN
ShellGameBuild.lnk Normal file

Binary file not shown.

16
misc/build_game.bat Normal file
View File

@@ -0,0 +1,16 @@
@echo off
del /s ..\Game*.pdb > NUL 2> NUL
pushd %~dp0\..\bin\x64\Debug
echo WAITING FOR PDB > lock.tmp
popd
pushd %~dp0
msbuild -p:Random=%random% /p:SolutionDir=..\ ..\Game\Game.vcxproj
popd
pushd %~dp0\..\bin\x64\Debug
del lock.tmp
popd

8
misc/shell.bat Normal file
View File

@@ -0,0 +1,8 @@
@echo off
pushd .
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
popd
set path=W:\Classified\Juliet\misc;%path%
set _NO_DEBUG_HEAP=1