From 0c583b58daa399c3a86c4416bd7d746db10e516f Mon Sep 17 00:00:00 2001 From: Patedam Date: Sat, 18 Jul 2026 18:58:29 -0400 Subject: [PATCH] Addded Romeo that generates some doc. WIP. --- .gitignore | 1 + Juliet.code-workspace | 8 + Juliet/src/Core/Memory/MemoryArenaDebug.cpp | 2 +- Juliet/src/UnitTest/VectorTestHelper.h | 0 JulietApp/main.cpp | 32 +- Romeo/Romeo.bff | 115 ++ ...ude_Core_Application_ApplicationManager.md | 19 + ...t_include_Core_Application_IApplication.md | 32 + .../docs/Juliet_include_Core_Common_CRC32.md | 15 + .../Juliet_include_Core_Common_CoreTypes.md | 15 + .../Juliet_include_Core_Common_CoreUtils.md | 33 + .../Juliet_include_Core_Common_EnumUtils.md | 11 + .../Juliet_include_Core_Common_NonCopyable.md | 20 + .../Juliet_include_Core_Common_NonMovable.md | 20 + .../Juliet_include_Core_Common_NonNullPtr.md | 15 + .../Juliet_include_Core_Common_Singleton.md | 24 + .../docs/Juliet_include_Core_Common_String.md | 101 ++ .../Juliet_include_Core_Container_Vector.md | 22 + ...Juliet_include_Core_HAL_Display_Display.md | 21 + ..._include_Core_HAL_DynLib_DynamicLibrary.md | 17 + ...liet_include_Core_HAL_Event_SystemEvent.md | 39 + ..._include_Core_HAL_Filesystem_Filesystem.md | 25 + .../Juliet_include_Core_HAL_IO_IOStream.md | 36 + ...uliet_include_Core_HAL_Keyboard_KeyCode.md | 16 + ...liet_include_Core_HAL_Keyboard_Keyboard.md | 23 + ...liet_include_Core_HAL_Keyboard_ScanCode.md | 15 + .../Juliet_include_Core_HAL_Mouse_Mouse.md | 23 + Romeo/docs/Juliet_include_Core_HAL_OS_OS.md | 28 + ...Juliet_include_Core_HotReload_HotReload.md | 24 + .../Juliet_include_Core_ImGui_ImGuiService.md | 23 + .../Juliet_include_Core_ImGui_ImGuiTests.md | 16 + Romeo/docs/Juliet_include_Core_JulietInit.md | 22 + .../Juliet_include_Core_Logging_LogManager.md | 29 + .../Juliet_include_Core_Logging_LogTypes.md | 16 + Romeo/docs/Juliet_include_Core_Main.md | 19 + .../Juliet_include_Core_Math_MathUtils.md | 15 + Romeo/docs/Juliet_include_Core_Math_Matrix.md | 15 + Romeo/docs/Juliet_include_Core_Math_Shape.md | 15 + Romeo/docs/Juliet_include_Core_Math_Vector.md | 16 + .../Juliet_include_Core_Memory_Allocator.md | 20 + .../Juliet_include_Core_Memory_MemoryArena.md | 37 + ...et_include_Core_Memory_MemoryArenaDebug.md | 34 + ...Juliet_include_Core_Memory_ScratchArena.md | 12 + .../docs/Juliet_include_Core_Memory_Utils.md | 17 + ...uliet_include_Core_Networking_IPAddress.md | 11 + ...t_include_Core_Networking_NetworkPacket.md | 26 + .../Juliet_include_Core_Networking_Socket.md | 31 + ...et_include_Core_Networking_SocketHandle.md | 11 + ...iet_include_Core_Networking_TcpListener.md | 23 + ...uliet_include_Core_Networking_TcpSocket.md | 23 + .../docs/Juliet_include_Core_Thread_Mutex.md | 11 + .../docs/Juliet_include_Core_Thread_Thread.md | 11 + Romeo/docs/Juliet_include_Engine_Class.md | 15 + ...iet_include_Engine_Debug_MemoryDebugger.md | 133 +++ Romeo/docs/Juliet_include_Engine_Engine.md | 26 + Romeo/docs/Juliet_include_Graphics_Camera.md | 15 + Romeo/docs/Juliet_include_Graphics_Colors.md | 15 + .../Juliet_include_Graphics_DebugDisplay.md | 20 + .../docs/Juliet_include_Graphics_Graphics.md | 162 +++ .../Juliet_include_Graphics_GraphicsBuffer.md | 20 + .../Juliet_include_Graphics_GraphicsConfig.md | 18 + ...uliet_include_Graphics_GraphicsPipeline.md | 33 + .../Juliet_include_Graphics_ImGuiRenderer.md | 117 ++ .../docs/Juliet_include_Graphics_Lighting.md | 15 + Romeo/docs/Juliet_include_Graphics_Mesh.md | 19 + .../Juliet_include_Graphics_MeshRenderer.md | 39 + .../Juliet_include_Graphics_PushConstants.md | 15 + .../Juliet_include_Graphics_RenderPass.md | 26 + Romeo/docs/Juliet_include_Graphics_Shader.md | 18 + .../Juliet_include_Graphics_SkyboxRenderer.md | 24 + Romeo/docs/Juliet_include_Graphics_Texture.md | 22 + .../Juliet_include_Graphics_VertexData.md | 15 + Romeo/docs/Juliet_include_Juliet.md | 11 + ...re_HAL_Display_Win32_Win32DisplayDevice.md | 11 + ...ore_HAL_Display_Win32_Win32DisplayEvent.md | 17 + ..._src_Core_HAL_Display_Win32_Win32Window.md | 23 + ...rc_Core_HAL_DynLib_Win32_DynamicLibrary.md | 11 + .../Juliet_src_Core_HAL_Event_Keyboard.md | 22 + ...liet_src_Core_HAL_Event_KeyboardMapping.md | 17 + Romeo/docs/Juliet_src_Core_HAL_Event_Mouse.md | 11 + .../Juliet_src_Core_HAL_Event_WindowEvent.md | 19 + ...re_HAL_Filesystem_Win32_Win32Filesystem.md | 11 + ...iet_src_Core_HAL_IO_Win32_Win32IOStream.md | 15 + .../Juliet_src_Core_HAL_OS_Win32_Win32OS.md | 11 + ...src_Core_HotReload_Win32_Win32HotReload.md | 11 + Romeo/docs/Juliet_src_Core_Juliet.md | 11 + Romeo/docs/Juliet_src_Core_Math_MathRound.md | 15 + ...Juliet_src_Core_Memory_MemoryArenaTests.md | 15 + ...etworking_Win32_Win32SocketPlatformImpl.md | 15 + ...SDK_d3dx12_d3dx12_property_format_table.md | 132 +++ ...iet_src_Graphics_D3D12_D3D12CommandList.md | 42 + .../Juliet_src_Graphics_D3D12_D3D12Common.md | 26 + ..._src_Graphics_D3D12_D3D12DescriptorHeap.md | 30 + ..._src_Graphics_D3D12_D3D12GraphicsDevice.md | 79 ++ ...rc_Graphics_D3D12_D3D12GraphicsPipeline.md | 55 + ...t_src_Graphics_D3D12_D3D12InternalTests.md | 12 + ...liet_src_Graphics_D3D12_D3D12RenderPass.md | 23 + .../Juliet_src_Graphics_D3D12_D3D12Shader.md | 20 + ...uliet_src_Graphics_D3D12_D3D12SwapChain.md | 147 +++ ...src_Graphics_D3D12_D3D12Synchronization.md | 32 + .../Juliet_src_Graphics_D3D12_D3D12Texture.md | 191 +++ .../Juliet_src_Graphics_D3D12_D3D12Utils.md | 16 + ...uliet_src_Graphics_DebugDisplayRenderer.md | 16 + ...t_src_UnitTest_Container_VectorUnitTest.md | 11 + .../docs/Juliet_src_UnitTest_RunUnitTests.md | 17 + Romeo/docs/overview.md | 106 ++ Romeo/docs/romeo.db | Bin 0 -> 118831 bytes Romeo/src/AiGenerator.cpp | 290 +++++ Romeo/src/AiGenerator.h | 14 + Romeo/src/Database.cpp | 300 +++++ Romeo/src/Database.h | 43 + Romeo/src/HttpClient.cpp | 196 ++++ Romeo/src/HttpClient.h | 22 + Romeo/src/JsonParser.cpp | 467 ++++++++ Romeo/src/JsonParser.h | 46 + Romeo/src/MarkdownGenerator.cpp | 1033 +++++++++++++++++ Romeo/src/MarkdownGenerator.h | 7 + Romeo/src/main.cpp | 549 +++++++++ fbuild.bff | 3 +- todo.md | 42 + 120 files changed, 6167 insertions(+), 18 deletions(-) create mode 100644 Juliet.code-workspace create mode 100644 Juliet/src/UnitTest/VectorTestHelper.h create mode 100644 Romeo/Romeo.bff create mode 100644 Romeo/docs/Juliet_include_Core_Application_ApplicationManager.md create mode 100644 Romeo/docs/Juliet_include_Core_Application_IApplication.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_CRC32.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_CoreTypes.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_CoreUtils.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_EnumUtils.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_NonCopyable.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_NonMovable.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_NonNullPtr.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_Singleton.md create mode 100644 Romeo/docs/Juliet_include_Core_Common_String.md create mode 100644 Romeo/docs/Juliet_include_Core_Container_Vector.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_Display_Display.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_DynLib_DynamicLibrary.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_Event_SystemEvent.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_Filesystem_Filesystem.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_IO_IOStream.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_Keyboard_KeyCode.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_Keyboard_Keyboard.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_Keyboard_ScanCode.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_Mouse_Mouse.md create mode 100644 Romeo/docs/Juliet_include_Core_HAL_OS_OS.md create mode 100644 Romeo/docs/Juliet_include_Core_HotReload_HotReload.md create mode 100644 Romeo/docs/Juliet_include_Core_ImGui_ImGuiService.md create mode 100644 Romeo/docs/Juliet_include_Core_ImGui_ImGuiTests.md create mode 100644 Romeo/docs/Juliet_include_Core_JulietInit.md create mode 100644 Romeo/docs/Juliet_include_Core_Logging_LogManager.md create mode 100644 Romeo/docs/Juliet_include_Core_Logging_LogTypes.md create mode 100644 Romeo/docs/Juliet_include_Core_Main.md create mode 100644 Romeo/docs/Juliet_include_Core_Math_MathUtils.md create mode 100644 Romeo/docs/Juliet_include_Core_Math_Matrix.md create mode 100644 Romeo/docs/Juliet_include_Core_Math_Shape.md create mode 100644 Romeo/docs/Juliet_include_Core_Math_Vector.md create mode 100644 Romeo/docs/Juliet_include_Core_Memory_Allocator.md create mode 100644 Romeo/docs/Juliet_include_Core_Memory_MemoryArena.md create mode 100644 Romeo/docs/Juliet_include_Core_Memory_MemoryArenaDebug.md create mode 100644 Romeo/docs/Juliet_include_Core_Memory_ScratchArena.md create mode 100644 Romeo/docs/Juliet_include_Core_Memory_Utils.md create mode 100644 Romeo/docs/Juliet_include_Core_Networking_IPAddress.md create mode 100644 Romeo/docs/Juliet_include_Core_Networking_NetworkPacket.md create mode 100644 Romeo/docs/Juliet_include_Core_Networking_Socket.md create mode 100644 Romeo/docs/Juliet_include_Core_Networking_SocketHandle.md create mode 100644 Romeo/docs/Juliet_include_Core_Networking_TcpListener.md create mode 100644 Romeo/docs/Juliet_include_Core_Networking_TcpSocket.md create mode 100644 Romeo/docs/Juliet_include_Core_Thread_Mutex.md create mode 100644 Romeo/docs/Juliet_include_Core_Thread_Thread.md create mode 100644 Romeo/docs/Juliet_include_Engine_Class.md create mode 100644 Romeo/docs/Juliet_include_Engine_Debug_MemoryDebugger.md create mode 100644 Romeo/docs/Juliet_include_Engine_Engine.md create mode 100644 Romeo/docs/Juliet_include_Graphics_Camera.md create mode 100644 Romeo/docs/Juliet_include_Graphics_Colors.md create mode 100644 Romeo/docs/Juliet_include_Graphics_DebugDisplay.md create mode 100644 Romeo/docs/Juliet_include_Graphics_Graphics.md create mode 100644 Romeo/docs/Juliet_include_Graphics_GraphicsBuffer.md create mode 100644 Romeo/docs/Juliet_include_Graphics_GraphicsConfig.md create mode 100644 Romeo/docs/Juliet_include_Graphics_GraphicsPipeline.md create mode 100644 Romeo/docs/Juliet_include_Graphics_ImGuiRenderer.md create mode 100644 Romeo/docs/Juliet_include_Graphics_Lighting.md create mode 100644 Romeo/docs/Juliet_include_Graphics_Mesh.md create mode 100644 Romeo/docs/Juliet_include_Graphics_MeshRenderer.md create mode 100644 Romeo/docs/Juliet_include_Graphics_PushConstants.md create mode 100644 Romeo/docs/Juliet_include_Graphics_RenderPass.md create mode 100644 Romeo/docs/Juliet_include_Graphics_Shader.md create mode 100644 Romeo/docs/Juliet_include_Graphics_SkyboxRenderer.md create mode 100644 Romeo/docs/Juliet_include_Graphics_Texture.md create mode 100644 Romeo/docs/Juliet_include_Graphics_VertexData.md create mode 100644 Romeo/docs/Juliet_include_Juliet.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayDevice.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayEvent.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32Window.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_DynLib_Win32_DynamicLibrary.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Event_Keyboard.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Event_KeyboardMapping.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Event_Mouse.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Event_WindowEvent.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_Filesystem_Win32_Win32Filesystem.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_IO_Win32_Win32IOStream.md create mode 100644 Romeo/docs/Juliet_src_Core_HAL_OS_Win32_Win32OS.md create mode 100644 Romeo/docs/Juliet_src_Core_HotReload_Win32_Win32HotReload.md create mode 100644 Romeo/docs/Juliet_src_Core_Juliet.md create mode 100644 Romeo/docs/Juliet_src_Core_Math_MathRound.md create mode 100644 Romeo/docs/Juliet_src_Core_Memory_MemoryArenaTests.md create mode 100644 Romeo/docs/Juliet_src_Core_Networking_Win32_Win32SocketPlatformImpl.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_AgilitySDK_d3dx12_d3dx12_property_format_table.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12CommandList.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Common.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12DescriptorHeap.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsDevice.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsPipeline.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12InternalTests.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12RenderPass.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Shader.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12SwapChain.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Synchronization.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Texture.md create mode 100644 Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Utils.md create mode 100644 Romeo/docs/Juliet_src_Graphics_DebugDisplayRenderer.md create mode 100644 Romeo/docs/Juliet_src_UnitTest_Container_VectorUnitTest.md create mode 100644 Romeo/docs/Juliet_src_UnitTest_RunUnitTests.md create mode 100644 Romeo/docs/overview.md create mode 100644 Romeo/docs/romeo.db create mode 100644 Romeo/src/AiGenerator.cpp create mode 100644 Romeo/src/AiGenerator.h create mode 100644 Romeo/src/Database.cpp create mode 100644 Romeo/src/Database.h create mode 100644 Romeo/src/HttpClient.cpp create mode 100644 Romeo/src/HttpClient.h create mode 100644 Romeo/src/JsonParser.cpp create mode 100644 Romeo/src/JsonParser.h create mode 100644 Romeo/src/MarkdownGenerator.cpp create mode 100644 Romeo/src/MarkdownGenerator.h create mode 100644 Romeo/src/main.cpp create mode 100644 todo.md diff --git a/.gitignore b/.gitignore index 752d1e1..fce2e9f 100644 --- a/.gitignore +++ b/.gitignore @@ -52,5 +52,6 @@ launch_*.txt *.out *.app +*.log misc/agent_error.log misc/agent_output_ship.log diff --git a/Juliet.code-workspace b/Juliet.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/Juliet.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/Juliet/src/Core/Memory/MemoryArenaDebug.cpp b/Juliet/src/Core/Memory/MemoryArenaDebug.cpp index 4822079..d50957b 100644 --- a/Juliet/src/Core/Memory/MemoryArenaDebug.cpp +++ b/Juliet/src/Core/Memory/MemoryArenaDebug.cpp @@ -28,7 +28,7 @@ namespace Juliet { .ReserveSize = Megabytes(16), .CommitSize = Kilobytes(64) } JULIET_DEBUG_ONLY(, "Debug Info Arena")); } - return ArenaPushStruct(g_DebugInfoArena); + return ArenaPushStruct(g_DebugInfoArena JULIET_DEBUG_PARAM("ArenaDebugInfo")); } static void FreeDebugInfo(ArenaDebugInfo* info) diff --git a/Juliet/src/UnitTest/VectorTestHelper.h b/Juliet/src/UnitTest/VectorTestHelper.h new file mode 100644 index 0000000..e69de29 diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index 262620c..35fd3c0 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -59,14 +59,14 @@ static float animateCubesTime = 0.0f; static float animateLightsTime = 0.0f; static float animateCameraTime = 0.0f; -static float redLightRadius = 10.0f; -static float redLightIntensity = 5.0f; -static float redLightColor[3] = { 1.0f, 0.2f, 0.2f }; +static float redLightRadius = 10.0f; +static float redLightIntensity = 5.0f; +static float redLightColor[3] = { 1.0f, 0.2f, 0.2f }; static bool redLightFollowsCamera = false; -static bool enableGlobalLight = true; -static float globalLightDir[3] = { 0.5f, -1.0f, -0.5f }; -static float globalLightColor[3] = { 1.0f, 0.95f, 0.8f }; +static bool enableGlobalLight = true; +static float globalLightDir[3] = { 0.5f, -1.0f, -0.5f }; +static float globalLightColor[3] = { 1.0f, 0.95f, 0.8f }; static float globalAmbientIntensity = 0.2f; static float blueLightRadius = 15.0f; @@ -115,7 +115,7 @@ void JulietApplication::Init(NonNullPtr) GraphicsDevice = CreateGraphicsDevice(config); - MainWindow = CreatePlatformWindow("Juliet Editor", 1280, 720); + MainWindow = CreatePlatformWindow("Juliet Editor", 1920, 1080); Running = MainWindow != nullptr && GraphicsDevice != nullptr; @@ -126,8 +126,8 @@ void JulietApplication::Init(NonNullPtr) // Create Depth Buffer TextureCreateInfo depthCI = {}; depthCI.Type = TextureType::Texture_2D; - depthCI.Width = 1280; - depthCI.Height = 720; + depthCI.Width = 1920; + depthCI.Height = 1080; depthCI.Format = TextureFormat::D32_FLOAT; depthCI.Flags = TextureUsageFlag::DepthStencilTarget; depthCI.LayerCount = 1; @@ -402,7 +402,7 @@ void JulietApplication::Update() ImGui::Checkbox("Animate Cubes", &animateCubes); ImGui::Checkbox("Animate Lights", &animateLights); ImGui::Checkbox("Animate Camera", &animateCamera); - + ImGui::Separator(); ImGui::Text("Global Light"); ImGui::Checkbox("Enable Global Light", &enableGlobalLight); @@ -412,20 +412,20 @@ void JulietApplication::Update() ImGui::ColorEdit3("Color", globalLightColor); ImGui::SliderFloat("Ambient Intensity", &globalAmbientIntensity, 0.0f, 1.0f); } - + ImGui::Separator(); ImGui::Text("Red Point Light"); ImGui::ColorEdit3("Red Color", redLightColor); ImGui::SliderFloat("Red Radius", &redLightRadius, 1.0f, 50.0f); ImGui::SliderFloat("Red Intensity", &redLightIntensity, 0.0f, 50.0f); ImGui::Checkbox("Red Light Follows Camera", &redLightFollowsCamera); - + ImGui::Separator(); ImGui::Text("Blue Point Light"); ImGui::ColorEdit3("Blue Color", blueLightColor); ImGui::SliderFloat("Blue Radius", &blueLightRadius, 1.0f, 50.0f); ImGui::SliderFloat("Blue Intensity", &blueLightIntensity, 0.0f, 50.0f); - + ImGui::End(); #endif @@ -442,17 +442,17 @@ void JulietApplication::Update() if (redLightFollowsCamera) { - Camera cam = GetDebugCamera(); + Camera cam = GetDebugCamera(); redLightPos = cam.Position; } SetPointLightPosition(RedLightID, redLightPos); SetPointLightPosition(BlueLightID, blueLightPos); - + SetPointLightColor(RedLightID, { redLightColor[0], redLightColor[1], redLightColor[2] }); SetPointLightRadius(RedLightID, redLightRadius); SetPointLightIntensity(RedLightID, redLightIntensity); - + SetPointLightColor(BlueLightID, { blueLightColor[0], blueLightColor[1], blueLightColor[2] }); SetPointLightRadius(BlueLightID, blueLightRadius); SetPointLightIntensity(BlueLightID, blueLightIntensity); diff --git a/Romeo/Romeo.bff b/Romeo/Romeo.bff new file mode 100644 index 0000000..3274d8a --- /dev/null +++ b/Romeo/Romeo.bff @@ -0,0 +1,115 @@ +// Romeo - Documentation Tool executable +//------------------------------------------------------------------------------ +{ + .ProjectName = 'Romeo' + .ProjectPath = 'Romeo' + .JulietIncludePath = ' "-IJuliet/include"' + + ' "-IJuliet/src"' + + ' "-IExternal/imgui"' + + // Define the def path even if not used right now + .ProjectDefPath = '$_WORKING_DIR_$/$ProjectName$/$ProjectName$.def' + + // Library + //-------------------------------------------------------------------------- + ForEach( .BuildConfig in .BuildConfigs ) + { + Using( .BuildConfig ) + + .OutputBase + '\$Platform$-$BuildConfigName$' + + // Unity + //-------------------------------------------------------------------------- + Unity( '$ProjectName$-Unity-$Platform$-$BuildConfigName$' ) + { + .UnityInputPath = '$ProjectPath$/src/' + .UnityOutputPath = '$OutputBase$/$ProjectPath$/' + .UnityOutputPattern = '$ProjectName$_Unity*.cpp' + } + + // Library + //-------------------------------------------------------------------------- + ObjectList( '$ProjectName$-Lib-$Platform$-$BuildConfigName$' ) + { + // Input (Unity) + .CompilerInputUnity = '$ProjectName$-Unity-$Platform$-$BuildConfigName$' + + // Extra Compiler Options + .CompilerOptions + .JulietIncludePath + + #if __WINDOWS__ + .CompilerOptions + ' -DJULIET_WIN32' + #endif + + // Output + .CompilerOutputPath = '$OutputBase$/$ProjectPath$/' + } + + #if __WINDOWS__ + .ManifestFile = '$OutputBase$/$ProjectPath$/$ProjectName$$ExeExtension$.manifest.tmp' + #endif + + // Executable + //-------------------------------------------------------------------------- + Executable( '$ProjectName$-Exe-$Platform$-$BuildConfigName$' ) + { + .Libraries = { + 'Romeo-Lib-$Platform$-$BuildConfigName$', + 'Juliet-Lib-$Platform$-$BuildConfigName$' + } + If ( .BuildConfigName != 'Release' ) + { + ^Libraries + { 'ImGui-Lib-$Platform$-$BuildConfigName$' } + } + + .LinkerOutput = '$BinPath$/$Platform$-$BuildConfigName$/$ProjectName$$ExeExtension$' + + #if __WINDOWS__ + .LinkerOptions + .CommonWinLibs + .LinkerOptions + ' winhttp.lib' + + // Chose appropriate CRT + .CRTLibs = .CRTLibs_Dynamic + If ( .BuildConfigName == 'Debug' ) + { + ^CRTLibs = .CRTLibs_DynamicDebug + } + .LinkerOptions + .CRTLibs + + // Romeo uses WINDOWS subsystem so it has no console window by default + .SubSystem = ' /SUBSYSTEM:WINDOWS' + // Even in debug we might want WINDOWS to keep it background, but console helps for printf + If ( .BuildConfigName == 'Debug' ) + { + ^SubSystem = ' /SUBSYSTEM:CONSOLE' // We use console in debug + } + .LinkerOptions + .SubSystem + + // Manifest + .LinkerAssemblyResources = .ManifestFile + .LinkerOptions + ' /MANIFEST:EMBED' + #endif + } + + Alias( '$ProjectName$-$Platform$-$BuildConfigName$' ) { .Targets = '$ProjectName$-Exe-$Platform$-$BuildConfigName$' } + ^'Targets_$Platform$_$BuildConfigName$' + { '$ProjectName$-$Platform$-$BuildConfigName$' } + + + #if __WINDOWS__ + .ProjectConfig = + [ + Using( .'Project_$Platform$_$BuildConfigName$' ) + .Target = '$ProjectName$-$Config$' + ] + ^ProjectConfigs + .ProjectConfig + #endif + } + + VCXProject( '$ProjectName$' ) + { + .ProjectOutput = '$ProjectPath$/$ProjectName$.vcxproj' + .ProjectBasePath = '$ProjectPath$/' + .ProjectInputPaths = .ProjectBasePath + .ProjectConfigs = .ProjectConfigs + } +} diff --git a/Romeo/docs/Juliet_include_Core_Application_ApplicationManager.md b/Romeo/docs/Juliet_include_Core_Application_ApplicationManager.md new file mode 100644 index 0000000..a74243f --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Application_ApplicationManager.md @@ -0,0 +1,19 @@ +# Juliet\include\Core\Application\ApplicationManager + +## Source Files +- Header: `Juliet\include\Core\Application\ApplicationManager.h` +- Source: `Juliet\src\Core\Application\ApplicationManager.cpp` + +## AI Description +The ApplicationManager initializes and manages the execution lifecycle of applications by sequentially orchestrating engine initialization, application loading, core runtime operations, cleanup unloading, and final shutdown within a defined flag context. + +## Symbols + +### Namespace `Juliet` + +#### Enums +- `enum class JulietInit_Flags` + +#### Functions & Methods +- `extern JULIET_API void StartApplication(IApplication& app, JulietInit_Flags flags)` + diff --git a/Romeo/docs/Juliet_include_Core_Application_IApplication.md b/Romeo/docs/Juliet_include_Core_Application_IApplication.md new file mode 100644 index 0000000..30df9d4 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Application_IApplication.md @@ -0,0 +1,32 @@ +# Juliet\include\Core\Application\IApplication + +## Source Files +- Header: `Juliet\include\Core\Application\IApplication.h` + +## AI Description +Juliet's IApplication is an abstract base class defining the engine lifecycle and system interfaces. Its purpose manages initialization, runtime status, rendering callbacks, device accessors, and debug utilities like swapchain info via nonnull pointers to core render structures. Design enforces platform-specific application logic through required virtual methods for setup, updates, and frame-based events within the Juliet graphics architecture. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class IApplication` + +#### Functions & Methods +- `public: + virtual IApplication::~IApplication()` +- `virtual void IApplication::Init(NonNullPtr arena)` +- `virtual void IApplication::Shutdown()` +- `virtual void IApplication::Update()` +- `virtual bool IApplication::IsRunning()` +- `// Accessors for Engine Systems + virtual struct Window* IApplication::GetPlatformWindow()` +- `virtual struct GraphicsDevice* IApplication::GetGraphicsDevice()` +- `// Render Lifecycle (Engine-Managed Render Loop) + virtual void IApplication::OnPreRender(CommandList* cmd)` +- `virtual void IApplication::OnRender(RenderPass* pass, CommandList* cmd)` +- `virtual ColorTargetInfo IApplication::GetColorTargetInfo(Texture* swapchainTexture)` +- `virtual DepthStencilTargetInfo* IApplication::GetDepthTargetInfo()` +- `virtual struct Camera IApplication::GetDebugCamera()` + diff --git a/Romeo/docs/Juliet_include_Core_Common_CRC32.md b/Romeo/docs/Juliet_include_Core_Common_CRC32.md new file mode 100644 index 0000000..7a45b3a --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_CRC32.md @@ -0,0 +1,15 @@ +# Juliet\include\Core\Common\CRC32 + +## Source Files +- Header: `Juliet\include\Core\Common\CRC32.h` + +## AI Description +The Juliet CRC32 component computes a 32-bit cyclic redundancy check for C-strings using the MIT implementation. It offers two constexpr functions: one returning `uint32_t` and another providing string literal suffix support, both supporting compile-time constant evaluation via templates. The design includes an optimized lookup table precomputed in the details namespace to enable fast hardware-friendly execution without runtime function calls. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `return anonymous::crc32(str, length)` + diff --git a/Romeo/docs/Juliet_include_Core_Common_CoreTypes.md b/Romeo/docs/Juliet_include_Core_Common_CoreTypes.md new file mode 100644 index 0000000..4bb2e53 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_CoreTypes.md @@ -0,0 +1,15 @@ +# Juliet\include\Core\Common\CoreTypes + +## Source Files +- Header: `Juliet\include\Core\Common\CoreTypes.h` + +## AI Description +Provides standard type aliases and constexpr limits in CoreTypes.h. Defines byte operations, memory buffers via ByteBuffer struct, pointer types for null safety, and compile-time maximums using template specialization within the Common module namespace architecture. + +## Symbols + +### Global Scope + +#### Classes, Structs & Unions +- `struct ByteBuffer` + diff --git a/Romeo/docs/Juliet_include_Core_Common_CoreUtils.md b/Romeo/docs/Juliet_include_Core_Common_CoreUtils.md new file mode 100644 index 0000000..06466b8 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_CoreUtils.md @@ -0,0 +1,33 @@ +# Juliet\include\Core\Common\CoreUtils + +## Source Files +- Header: `Juliet\include\Core\Common\CoreUtils.h` +- Source: `Juliet\src\Core\Common\CoreUtils.cpp` + +## AI Description +Juliet CoreUtils provides portable macros for stringification, compiler-specific pragmas, assertions with source locations, and error handling. It includes a deferred function template, byte manipulation utilities, alignment helpers, swap operations, and cross-platform crash mechanisms via platform breakers or trap functions tailored to Clang/GCC or MSVC. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class DeferredFunction` +- `class Function` + +#### Functions & Methods +- `\ + while (0) + +#else +#define Assert(...) ((void)0) +#define AssertHR(hr_expression, message) ((void)(hr_expression)) +#define Unimplemented() ((void)0) +#endif + + JULIET_API extern void JulietAssert(const char* expression, const char* message, + std::source_location location = std::source_location::current(), long handleResult = 0)` +- `DeferredFunction::DeferredFunction(const DeferredFunction&)` +- `DeferredFunction::DeferredFunction(const DeferredFunction&&)` +- `extern JULIET_API void Free(ByteBuffer& buffer)` + diff --git a/Romeo/docs/Juliet_include_Core_Common_EnumUtils.md b/Romeo/docs/Juliet_include_Core_Common_EnumUtils.md new file mode 100644 index 0000000..88f2b07 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_EnumUtils.md @@ -0,0 +1,11 @@ +# Juliet\include\Core\Common\EnumUtils + +## Source Files +- Header: `Juliet\include\Core\Common\EnumUtils.h` + +## AI Description +The `Juliet` library defines a C++20 concept to identify enums and provides template operations including bit-wise inversion, bitwise AND/OR/XOR with compound assignment. It also implements addition/subtraction between enum types or their underlying integer representations via the standard traits type system for portable manipulation of enum flags in Juliet architecture. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_include_Core_Common_NonCopyable.md b/Romeo/docs/Juliet_include_Core_Common_NonCopyable.md new file mode 100644 index 0000000..44c9b67 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_NonCopyable.md @@ -0,0 +1,20 @@ +# Juliet\include\Core\Common\NonCopyable + +## Source Files +- Header: `Juliet\include\Core\Common\NonCopyable.h` + +## AI Description +The Juliet::NonCopyable class in the Core/Common module prevents copying operations by deleting copy constructors and assignment operators, ensuring base classes cannot be instantiated or copied directly. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class NonCopyable` + +#### Functions & Methods +- `public: NonCopyable::NonCopyable()` +- `virtual NonCopyable::~NonCopyable()` +- `NonCopyable::NonCopyable(const NonCopyable&)` + diff --git a/Romeo/docs/Juliet_include_Core_Common_NonMovable.md b/Romeo/docs/Juliet_include_Core_Common_NonMovable.md new file mode 100644 index 0000000..b8ef57f --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_NonMovable.md @@ -0,0 +1,20 @@ +# Juliet\include\Core\Common\NonMovable + +## Source Files +- Header: `Juliet\include\Core\Common\NonMovable.h` + +## AI Description +The Julian NonMovable class prevents copying and assignment by explicitly deleting move constructors, constant-move operators, copy constructor, and default assignments. It provides a base for non-copyable types within the Juliet library while retaining destructibility through virtual deletion in its interface design. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class NonMovable` + +#### Functions & Methods +- `public: NonMovable::NonMovable()` +- `virtual NonMovable::~NonMovable()` +- `NonMovable::NonMovable(const NonMovable&&)` + diff --git a/Romeo/docs/Juliet_include_Core_Common_NonNullPtr.md b/Romeo/docs/Juliet_include_Core_Common_NonNullPtr.md new file mode 100644 index 0000000..a7ae6ea --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_NonNullPtr.md @@ -0,0 +1,15 @@ +# Juliet\include\Core\Common\NonNullPtr + +## Source Files +- Header: `Juliet\include\Core\Common\NonNullPtr.h` + +## AI Description +The Juliet::NonNullPtr class ensures non-null pointers at runtime, initializing and assigning only valid addresses to prevent null pointer errors. It enforces these rules via Asserts in every constructor, assignment operator, accessor, comparison method, and conversion operation while supporting cross-type conversions for compatible types where the base type matches its derived type's internal representation exactly. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class NonNullPtr` + diff --git a/Romeo/docs/Juliet_include_Core_Common_Singleton.md b/Romeo/docs/Juliet_include_Core_Common_Singleton.md new file mode 100644 index 0000000..5759f1b --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_Singleton.md @@ -0,0 +1,24 @@ +# Juliet\include\Core\Common\Singleton + +## Source Files +- Header: `Juliet\include\Core\Common\Singleton.h` + +## AI Description +The Juliet::Singleton header enables non-copyable, single-object instances via static registration or auto-registration. It provides a template class with methods to register/unregister and retrieve an instance, ensuring type safety through pointer assertions during the lifecycle management of singleton objects across applications. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class Singleton` +- `class Singleton::AutoRegister` + +#### Functions & Methods +- `public: + static void Singleton::Register(NonNullPtr type)` +- `static void Singleton::Unregister(NonNullPtr type)` +- `static Type* Singleton::Get()` +- `public: Singleton::AutoRegister::AutoRegister()` +- `virtual Singleton::AutoRegister::~AutoRegister()` + diff --git a/Romeo/docs/Juliet_include_Core_Common_String.md b/Romeo/docs/Juliet_include_Core_Common_String.md new file mode 100644 index 0000000..14adf52 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Common_String.md @@ -0,0 +1,101 @@ +# Juliet\include\Core\Common\String + +## Source Files +- Header: `Juliet\include\Core\Common\String.h` +- Source: `Juliet\src\Core\Common\String.cpp` + +## AI Description +Based on the code snippet provided, here is an analysis of the function `ConvertString`, focusing on its logic for handling Unicode decoding (UTF-8), encoding back to UTF-16, and error handling. + +### 1. Function Overview +The primary purpose of this class/function block (likely part of a library called "Juliet") is to perform **string conversion** between different character encodings. Specifically: +* It accepts various input formats (`String from`, `String src`). +* It supports specific output formats like UTF-16, ASCII, Latin1, etc., via the helper function. +* The core logic handles converting a source string (currently demonstrated decoding **UTF-8** to code points) and re-encoding it into another format (demonstrated as **UTF-16**), including null termination if requested. + +### 2. Core Logic: UTF-8 Decoding (`from == StringEncoding::UTF8`) +The decoder implements the RFC 3629 specification for valid Unicode characters in the Basic Multilingual Plane (BMP) and beyond, with specific strictness checks. + +#### A. Byte Sequence Detection ? Validation +It inspects the first byte of a character sequence to determine its length: +* **4-byte sequence (`0xF0` - `0xFFF`)**: Checks if it matches the standard UTF-8 continuation mask patterns and handles overlong encodings (e.g., using 3 bytes for C2E9 instead of one byte). + * If a leading byte does not match its expected range or has incorrect continuation bits, it marks `character = kUnknown_UNICODE`. +* **3-byte sequence (`0xE0` - `0xFF`)**: Similar checks apply. +* **2-byte sequence (`0xC0` - `0xDF`)**: Checks for the overlong encoding of U+80 (e.g., 0xC1 followed by a continuation byte). +* **1-byte sequence (`0x00` - `0x7F`)**: Ensures high-bit is not set if it's meant to be part of a multi-byte sequence, though the logic here sets `kUnknown_UNICODE` only if `(p[0] ? 0x80)` is true (which covers bytes intended as continuations). + +#### B. Continuation Byte Processing +After identifying the leading byte: +1. It checks if there are enough remaining bytes in the source (`src.Size`). If not, it logs an error and returns `false`. +2. It iterates through `left` continuation bytes (bits `[6..5] == 0x80`, i.e., values `C0-DF`, `E0-FF`). +3. **Crucial Check**: If a byte is found that does *not* match the UTF-8 continuation pattern (`(p[0] ? 0xC0) != 0x80`), it invalidates the sequence immediately (`kUnknown_UNICODE`). + +#### C. Surrogate and Invalid Code Point Checks +After reconstructing the `uint32 character`: +1. **Overlong Encoding**: If flags are set, the code resets to unknown. +2. **Control/Invalid Ranges**: Rejects characters in ranges: + * `\uD800` through `\uDFFF` (Surrogate pairs). These cannot exist as standalone characters; they must be paired. Without a following pair context here, it marks them invalid. + * `\xFFFE` and `\xFFFF` (Non-character code points used for replacement markers in internal formats like UTF-16 BE/LE often need special handling or removal from text data). + * Any character ? `0x10FFFF`. + +### 3. Encoding Logic: Converting to UTF-16 (`to == StringEncoding::UTF16`) +Once the source is successfully decoded into a single code point (assuming it fits in BMP due to previous checks or surrogates are ignored/handled differently not shown fully here): +* **BMP Characters (? 0x10000)**: Written as two bytes little-endian. + * `p[0] = character ? 0xFF` (Low byte) + * `p[1] = (character ?? 8)` (High byte) +* **Supplemental Characters (? 0x10FFFF)**: Although the decoder rejects input ? 0x10FFF, if logic allows conversion of valid UTF-32 chars here via surrogate pairs: + * Calculates `word1` and `word2`. + * Writes them as two separate UTF-16 code units (4 bytes total). + +### 4. Error Handling ? Constraints +The function is designed to be **safe** but strictly validated: +* **Buffer Overflow Protection**: It tracks `remainingCapacity` on the destination buffer before writing each character or block of bytes. If there isn't enough space, it logs an error and fails instead of truncating data. +* **Source Integrity**: Fails fast if the input UTF-8 sequence is incomplete or malformed (broken continuations). +* **Logging**: Uses `Log(LogLevel::Error)` for critical failures like "Incomplete input sequence" or buffer underflow, aiding in debugging during development (`JULIET_DEBUG_ONLY` flags appear to be used elsewhere but not active by default unless defined). + +### 5. Wrapper Logic: `ConvertString(String from, String to...)` +The public API calls an inner function after determining formats via `Encodings`: +1. It searches a global or local map (`Encodings`) to find the specific format enum based on string content (case-insensitive compare). +2. If either source or destination is unknown/unfindable, it returns `false`. +3. Delegates to the core decoding/encoding logic with the resolved enums. + +### Potential Issues / Observations in this Implementation +1. **Surrogate Handling**: The decoder explicitly rejects `\uD800`-\`\uDFFF`. This implies this function is likely intended for converting strings containing *actual* text (no raw surrogates) into UTF-16, or it assumes the caller will handle surrogate pairs before calling this specific helper without a second pass. If input contains valid Unicode characters outside BMP that are being handled via surrogate pairs in the output logic but not checked correctly in input, there might be silent failures depending on how `kUnknown_UNICODE` is propagated later (e.g., ignored vs logged). +2. **Debug Builds**: The line `JULIET_DEBUG_ONLY(...)` suggests this codebase uses debug builds to push data beyond strict alignments or sizes; production logic should ensure alignment safety (`alignof(char)` usually isn't an issue on standard architectures unless specifically targeting edge cases, but it's good practice here). +3. **UTF-16 Direction**: The code assumes Little Endian (writing `p[0]` first as low byte) based on the assignment order `(character ? 0xFF)` then shifting right. This is typical for Windows/WideChar environments which usually use UTF-16LE. + +### Summary +This implementation is a **highly defensive string converter** focusing on strict RFC-compliant UTF-8 decoding to code points, rejecting invalid and surrogate-only inputs early in the pipeline before attempting re-encoding to ASCII/Latin/UTF-16. It prioritizes data integrity over flexibility (e.g., it won't silently skip bad bytes). + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct String` +- `struct StringBuffer` +- `struct StringListNode` +- `struct StringList` + +#### Enums +- `enum class StringEncoding` + +#### Functions & Methods +- `\ MemSet(name##_, 0, sizeof(uint32))` +- `result.Size = anonymous::StringLength(str)` +- `return anonymous::IsValid(FindChar(str, c))` +- `JULIET_API uint32 StepUTF8(String& inStr)` +- `JULIET_API String FindString(String strLeft, String strRight)` +- `// Case insensitive compare. Supports ASCII only + // TODO: Support UNICODE + extern JULIET_API int8 StringCompareCaseInsensitive(String str1, String str2)` +- `// Do not allocate anything, you must allocate your out buffer yourself + // TODO: Version taking arena that can allocate + // Do not take String type because we dont know the string encoding we are going from/to + // src and dst will be casted based on the encoding. + // size will correspond to the number of characters + // Will convert \0 character if present. + extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, String src, StringBuffer& dst, bool nullTerminate)` +- `extern JULIET_API bool ConvertString(String from, String to, String src, StringBuffer& dst, bool nullTerminate)` +- `JULIET_API String StringCopy(NonNullPtr arena, String str)` + diff --git a/Romeo/docs/Juliet_include_Core_Container_Vector.md b/Romeo/docs/Juliet_include_Core_Container_Vector.md new file mode 100644 index 0000000..b1bd0d7 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Container_Vector.md @@ -0,0 +1,22 @@ +# Juliet\include\Core\Container\Vector + +## Source Files +- Header: `Juliet\include\Core\Container\Vector.h` +- Source: `Juliet\src\Core\Container\Vector.cpp` + +## AI Description +A generic container managing contiguous memory via a custom arena for performance. It supports efficient bulk operations, dynamic resizing, and fast removal by swapping with the end element, designed as a lightweight POD-like object with optional reallocation logic. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct VectorArena` + +### Namespace `Juliet::anonymous` + +#### Functions & Methods +- `Arena = VectorArena::ArenaAllocate(params JULIET_DEBUG_PARAM(Name))` +- `Arena = arena. VectorArena::Get()` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_Display_Display.md b/Romeo/docs/Juliet_include_Core_HAL_Display_Display.md new file mode 100644 index 0000000..d8b5a03 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_Display_Display.md @@ -0,0 +1,21 @@ +# Juliet\include\Core\HAL\Display\Display + +## Source Files +- Header: `Juliet\include\Core\HAL\Display\Display.h` +- Source: `Juliet\src\Core\HAL\Display\Display.cpp` + +## AI Description +This C++ component manages platform-specific windows by initializing a display system via available factories and providing APIs to create, hide, show, destroy, configure titles, and retrieve window identifiers. It encapsulates cross-platform abstraction logic while maintaining state through an internal global device reference for lifecycle management. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `extern JULIET_API Window* CreatePlatformWindow(const char* title, uint16 width, uint16 height, int flags = 0 /* unused */)` +- `extern JULIET_API void DestroyPlatformWindow(NonNullPtr window)` +- `extern JULIET_API void ShowWindow(NonNullPtr window)` +- `extern JULIET_API void HideWindow(NonNullPtr window)` +- `extern JULIET_API WindowID GetWindowID(NonNullPtr window)` +- `extern JULIET_API void SetWindowTitle(NonNullPtr window, String title)` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_DynLib_DynamicLibrary.md b/Romeo/docs/Juliet_include_Core_HAL_DynLib_DynamicLibrary.md new file mode 100644 index 0000000..2a09caf --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_DynLib_DynamicLibrary.md @@ -0,0 +1,17 @@ +# Juliet\include\Core\HAL\DynLib\DynamicLibrary + +## Source Files +- Header: `Juliet\include\Core\HAL\DynLib\DynamicLibrary.h` + +## AI Description +This header declares the DynamicLibrary struct and functions for loading C++ DLLs via filename, retrieving function pointers by name, or unloading libraries using non-null pointer arguments within the JULIET_API naming convention. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `extern JULIET_API DynamicLibrary* LoadDynamicLibrary(const char* filename)` +- `extern JULIET_API FunctionPtr LoadFunction(NonNullPtr lib, const char* functionName)` +- `extern JULIET_API void UnloadDynamicLibrary(NonNullPtr lib)` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_Event_SystemEvent.md b/Romeo/docs/Juliet_include_Core_HAL_Event_SystemEvent.md new file mode 100644 index 0000000..8fcf052 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_Event_SystemEvent.md @@ -0,0 +1,39 @@ +# Juliet\include\Core\HAL\Event\SystemEvent + +## Source Files +- Header: `Juliet\include\Core\HAL\Event\SystemEvent.h` +- Source: `Juliet\src\Core\HAL\Event\SystemEvent.cpp` + +## AI Description +The SystemEvent HAL component provides a centralized, timestamped event queue for managing interactions with hardware subsystems like the display, keyboard, and mouse. It defines specific event types (application exit, window close, key/mouse states) using tagged unions to ensure memory efficiency while supporting optional custom data extensions via padding fields. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct WindowEvent` +- `struct KeyboardEvent` +- `struct MouseMovementEvent` +- `struct MouseButtonEvent` +- `union AllSystemEventUnion` +- `struct SystemEvent` + +#### Enums +- `enum class EventType` + +#### Functions & Methods +- `// Make sure we do not bust the union size static_assert(sizeof(AllSystemEventUnion) == sizeof(((AllSystemEventUnion*)nullptr)->Padding))` +- `// Poll for any event, return false if no event is available. + // Equivalent to WaitEvent(event, 0); + // Will not block + extern JULIET_API bool GetEvent(SystemEvent& event)` +- `// TODO : use chrono to tag the timeout correctly with nanosec + // timeout == -1 means wait for any event before pursuing + // timeout == 0 means checking once for the frame and getting out + // timeout > 0 means wait until time is out + extern JULIET_API bool WaitEvent(SystemEvent& event, int32 timeoutInNS = -1)` +- `// Add an event onto the event queue. + // TODO : support array of events + extern JULIET_API bool AddEvent(SystemEvent& event)` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_Filesystem_Filesystem.md b/Romeo/docs/Juliet_include_Core_HAL_Filesystem_Filesystem.md new file mode 100644 index 0000000..71b2be2 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_Filesystem_Filesystem.md @@ -0,0 +1,25 @@ +# Juliet\include\Core\HAL\Filesystem\Filesystem + +## Source Files +- Header: `Juliet\include\Core\HAL\Filesystem\Filesystem.h` +- Source: `Juliet\src\Core\HAL\Filesystem\Filesystem.cpp` + +## AI Description +The C++ Filesystem HAL provides application-specific path utilities, caching resolved base directories for compiled assets with platform detection during initialization and managing shared state across the lifecycle. It exposes API functions to retrieve or construct full paths while ensuring memory safety through manual buffer ownership patterns and validation checks on input validity before returning results. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `// Returns the path to the application directory + [[nodiscard]] extern JULIET_API String GetBasePath()` +- `// Returns the resolved base path to the compiled shaders directory. + // In dev, this resolves to ../../Assets/compiled/ relative to the exe. + // In shipping, this resolves to Assets/Shaders/ next to the exe. + [[nodiscard]] extern JULIET_API String GetAssetBasePath()` +- `// Builds a full path to an asset file given its filename (e.g. "Triangle.vert.dxil"). + // The caller owns the returned buffer and must free it. + [[nodiscard]] extern JULIET_API String GetAssetPath(String filename)` +- `[[nodiscard]]extern JULIET_API bool IsAbsolutePath(String path)` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_IO_IOStream.md b/Romeo/docs/Juliet_include_Core_HAL_IO_IOStream.md new file mode 100644 index 0000000..92ee676 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_IO_IOStream.md @@ -0,0 +1,36 @@ +# Juliet\include\Core\HAL\IO\IOStream + +## Source Files +- Header: `Juliet\include\Core\HAL\IO\IOStream.h` +- Source: `Juliet\src\Core\HAL\IO\IOStream.cpp` + +## AI Description +The IOStream component provides a unified, opaque interface for reading and writing data to diverse sources like files or memory. Its design abstracts platform-specific details through pointer function interfaces while exposing high-level functions (e.g., IOPrintf) that handle edge cases such as file loading with dynamic resizing and error status reporting. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct IOStreamDataPayload` +- `struct IOStreamInterface` + +#### Enums +- `enum class IOStreamStatus` +- `enum class IOStreamSeekPivot` + +#### Functions & Methods +- `extern JULIET_API IOStream* IOFromFile(String filename, String mode)` +- `// Let you use an interface to open any io. Is used internally by IOFromFile + extern JULIET_API IOStream* IOFromInterface(NonNullPtr streamInterface, NonNullPtr payload)` +- `// Write formatted string into the stream. + extern JULIET_API size_t IOPrintf(NonNullPtr stream, _Printf_format_string_ const char* format, ...)` +- `extern JULIET_API size_t IOWrite(NonNullPtr stream, ByteBuffer inBuffer)` +- `extern JULIET_API size_t IORead(NonNullPtr stream, void* ptr, size_t size)` +- `extern JULIET_API int64 IOSeek(NonNullPtr stream, int64 offset, IOStreamSeekPivot pivot)` +- `extern JULIET_API int64 IOSize(NonNullPtr stream)` +- `// TODO : Use memory arena because that Allocates + extern JULIET_API ByteBuffer LoadFile(String filename)` +- `extern JULIET_API ByteBuffer LoadFile(NonNullPtr stream, bool closeStreamWhenDone)` +- `extern JULIET_API bool IOClose(NonNullPtr stream)` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_Keyboard_KeyCode.md b/Romeo/docs/Juliet_include_Core_HAL_Keyboard_KeyCode.md new file mode 100644 index 0000000..51af15f --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_Keyboard_KeyCode.md @@ -0,0 +1,16 @@ +# Juliet\include\Core\HAL\Keyboard\KeyCode + +## Source Files +- Header: `Juliet\include\Core\HAL\Keyboard\KeyCode.h` + +## AI Description +The `KeyCode.h` component defines localized virtual keys mapping physical scans to platform-independent ASCII values and special control codes. It uses unique enumeration entries for character generation plus non-character events, while providing a complementary bitwise flag set in the adjacent `KeyMod` enum for modifier states. + +## Symbols + +### Namespace `Juliet` + +#### Enums +- `enum class KeyCode` +- `enum class KeyMod` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_Keyboard_Keyboard.md b/Romeo/docs/Juliet_include_Core_HAL_Keyboard_Keyboard.md new file mode 100644 index 0000000..6213f31 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_Keyboard_Keyboard.md @@ -0,0 +1,23 @@ +# Juliet\include\Core\HAL\Keyboard\Keyboard + +## Source Files +- Header: `Juliet\include\Core\HAL\Keyboard\Keyboard.h` + +## AI Description +This header defines core C++ classes and functions for keyboard hardware interaction. It provides data structures like `Key` to store input details and utility APIs such as `IsKeyDown`, allowing applications to query the current state of physical keys within the Juliet system framework. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Key` + +#### Enums +- `enum class KeyState` + +#### Functions & Methods +- `extern JULIET_API bool IsKeyDown(ScanCode scanCode)` +- `extern JULIET_API KeyMod GetKeyModState()` +- `extern JULIET_API KeyCode GetKeyCodeFromScanCode(ScanCode scanCode, KeyMod keyModState)` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_Keyboard_ScanCode.md b/Romeo/docs/Juliet_include_Core_HAL_Keyboard_ScanCode.md new file mode 100644 index 0000000..d84286b --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_Keyboard_ScanCode.md @@ -0,0 +1,15 @@ +# Juliet\include\Core\HAL\Keyboard\ScanCode + +## Source Files +- Header: `Juliet\include\Core\HAL\Keyboard\ScanCode.h` + +## AI Description +This header defines a `uint16` ScanCode enumeration mapping physical keyboard keys to USB HID Usage IDs per the HUT specification. It covers alphanumeric characters, function keys, navigation buttons, media controls, and modifier keys while accounting for layout variations like ISO/ANSI keyboards. The design adheres strictly to the Hardware User Tracking Protocol standard, ensuring accurate hardware-level key identification across platforms including PC and Mac variants through reserved values up to 512. + +## Symbols + +### Namespace `Juliet` + +#### Enums +- `enum class ScanCode` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_Mouse_Mouse.md b/Romeo/docs/Juliet_include_Core_HAL_Mouse_Mouse.md new file mode 100644 index 0000000..f2b1df9 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_Mouse_Mouse.md @@ -0,0 +1,23 @@ +# Juliet\include\Core\HAL\Mouse\Mouse + +## Source Files +- Header: `Juliet\include\Core\HAL\Mouse\Mouse.h` + +## AI Description +The C++ `Juliet` component provides a low-level abstraction for mouse input via HAL, defining enums and structs to represent buttons (left/right/middle) and 2D coordinates. It exports external functions in the `MouseInput()` category to check button states, retrieve position data, and manage hardware interactions efficiently within the Juliet framework's core architecture. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct MousePosition` + +#### Enums +- `enum class MouseButton` + +#### Functions & Methods +- `extern bool IsMouseButtonDown(MouseButton button)` +- `extern MousePosition GetMousePosition()` +- `extern MouseButton GetMouseButtonState()` + diff --git a/Romeo/docs/Juliet_include_Core_HAL_OS_OS.md b/Romeo/docs/Juliet_include_Core_HAL_OS_OS.md new file mode 100644 index 0000000..f2568a7 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HAL_OS_OS.md @@ -0,0 +1,28 @@ +# Juliet\include\Core\HAL\OS\OS + +## Source Files +- Header: `Juliet\include\Core\HAL\OS\OS.h` +- Source: `Juliet\src\Core\HAL\OS\OS.cpp` + +## AI Description +This header defines the `Juliet` OS abstraction layer for memory management and debugging in a C++ application. It provides functions to reserve, commit (make executable), and release virtual addresses via byte-based interfaces, along with template overloads for typed allocations, as well as utilities like debugger detection and bootstrapping entry points. + +## Symbols + +### Namespace `Juliet::Memory` + +#### Functions & Methods +- `Byte* OS_Reserve(size_t size)` +- `bool OS_Commit(Byte* ptr, size_t size)` +- `void OS_Release(Byte* ptr, size_t size)` + +### Namespace `Juliet::Debug` + +#### Functions & Methods +- `JULIET_API bool IsDebuggerPresent()` + +### Namespace `Juliet` + +#### Functions & Methods +- `JULIET_API int Bootstrap(EntryPointFunc entryPointFunc, int argc, wchar_t** argv)` + diff --git a/Romeo/docs/Juliet_include_Core_HotReload_HotReload.md b/Romeo/docs/Juliet_include_Core_HotReload_HotReload.md new file mode 100644 index 0000000..d42b945 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_HotReload_HotReload.md @@ -0,0 +1,24 @@ +# Juliet\include\Core\HotReload\HotReload + +## Source Files +- Header: `Juliet\include\Core\HotReload\HotReload.h` +- Source: `Juliet\src\Core\HotReload\HotReload.cpp` + +## AI Description +HotReload manages dynamic C++ DLL loading/unloading via JSON signals. It stores paths, function addresses in an Arena, and handles incremental updates through atomic file locks with retry logic on failure. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct HotReloadCode` + +#### Functions & Methods +- `extern JULIET_API void InitHotReloadCode(HotReloadCode& code, String dllName, String transientDllName, String 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)` + diff --git a/Romeo/docs/Juliet_include_Core_ImGui_ImGuiService.md b/Romeo/docs/Juliet_include_Core_ImGui_ImGuiService.md new file mode 100644 index 0000000..4568a8f --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_ImGui_ImGuiService.md @@ -0,0 +1,23 @@ +# Juliet\include\Core\ImGui\ImGuiService + +## Source Files +- Header: `Juliet\include\Core\ImGui\ImGuiService.h` +- Source: `Juliet\src\Core\ImGui\ImGuiService.cpp` + +## AI Description +The ImGuiService component initializes, renders, and manages the ImGui framework within the Juliet application. Its main responsibility is to bridge high-level UI logic with low-level graphics hardware via a dedicated memory arena and custom allocators, ensuring efficient resource usage while supporting keyboard navigation on Windows platforms when enabled at compile time. + +## Symbols + +### Namespace `Juliet::ImGuiService` + +#### Functions & Methods +- `JULIET_API void Initialize(NonNullPtr window)` +- `JULIET_API void Shutdown()` +- `JULIET_API void NewFrame()` +- `JULIET_API void Render()` +- `JULIET_API bool IsInitialized()` +- `JULIET_API ImGuiContext* GetContext()` +- `// Run internal unit tests + JULIET_API void RunTests()` + diff --git a/Romeo/docs/Juliet_include_Core_ImGui_ImGuiTests.md b/Romeo/docs/Juliet_include_Core_ImGui_ImGuiTests.md new file mode 100644 index 0000000..4e71b34 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_ImGui_ImGuiTests.md @@ -0,0 +1,16 @@ +# Juliet\include\Core\ImGui\ImGuiTests + +## Source Files +- Header: `Juliet\include\Core\ImGui\ImGuiTests.h` +- Source: `Juliet\src\Core\ImGui\ImGuiTests.cpp` + +## AI Description +This unit test verifies the initialization and correctness of the ImGui integration within Juliet. It confirms context alignment, validates backend status, checks font atlas generation, and ensures core rendering components are properly configured before drawing begins. + +## Symbols + +### Namespace `Juliet::UnitTest` + +#### Functions & Methods +- `void TestImGui()` + diff --git a/Romeo/docs/Juliet_include_Core_JulietInit.md b/Romeo/docs/Juliet_include_Core_JulietInit.md new file mode 100644 index 0000000..40136e5 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_JulietInit.md @@ -0,0 +1,22 @@ +# Juliet\include\Core\JulietInit + +## Source Files +- Header: `Juliet\include\Core\JulietInit.h` + +## AI Description +The `JulietInit.h` header defines core initialization for the Juliet framework via a bit-flag enum and arena management parameters. It exposes public functions to initialize, configure game states, and cleanly shut down system components within the provided namespaces. This component serves as an essential bootstrap layer for setting up runtime environments like display and audio subsystems before application logic executes. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct GameInitParams` + +#### Enums +- `enum class JulietInit_Flags` + +#### Functions & Methods +- `void JulietInit(JulietInit_Flags flags)` +- `void JulietShutdown()` + diff --git a/Romeo/docs/Juliet_include_Core_Logging_LogManager.md b/Romeo/docs/Juliet_include_Core_Logging_LogManager.md new file mode 100644 index 0000000..4bdcfba --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Logging_LogManager.md @@ -0,0 +1,29 @@ +# Juliet\include\Core\Logging\LogManager + +## Source Files +- Header: `Juliet\include\Core\Logging\LogManager.h` +- Source: `Juliet\src\Core\Logging\LogManager.cpp` + +## AI Description +The C++ LogManager provides centralized logging via `LogScope`, an arena-based system for memory management. It uses a circular buffer to accumulate log entries per frame and supports Windows debug output with platform-specific formatting, while deferring file export until the end of a scope. Initialization requires manual setup; shutdown cleans up resources using custom allocators. + +## Symbols + +### Namespace `Juliet` + +#### Enums +- `enum class LogLevel` +- `enum class LogCategory` + +#### Functions & Methods +- `extern void JULIET_API InitializeLogManager()` +- `extern void JULIET_API ShutdownLogManager()` +- `extern void JULIET_API LogScopeBegin()` +- `// TODO everything that happened in there to export them to file or something + extern void JULIET_API LogScopeEnd()` +- `extern void JULIET_API Log(LogLevel level, LogCategory category, const char* fmt, ...)` +- `extern void JULIET_API LogDebug(LogCategory category, const char* fmt, ...)` +- `extern void JULIET_API LogMessage(LogCategory category, const char* fmt, ...)` +- `extern void JULIET_API LogWarning(LogCategory category, const char* fmt, ...)` +- `extern void JULIET_API LogError(LogCategory category, const char* fmt, ...)` + diff --git a/Romeo/docs/Juliet_include_Core_Logging_LogTypes.md b/Romeo/docs/Juliet_include_Core_Logging_LogTypes.md new file mode 100644 index 0000000..a0b85d0 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Logging_LogTypes.md @@ -0,0 +1,16 @@ +# Juliet\include\Core\Logging\LogTypes + +## Source Files +- Header: `Juliet\include\Core\Logging\LogTypes.h` + +## AI Description +This header defines log levels and categories for the Juliet C++ project. It uses fixed-size integers (uint8) to represent Debug, Warning, Error states and Core through Game subsystems, establishing a structured type system for logging operations across the application architecture without dynamic storage overhead. The design ensures efficiency by limiting values to eight bits while supporting six distinct operational channels essential for debugging and system monitoring within Juliet's core framework. + +## Symbols + +### Namespace `Juliet` + +#### Enums +- `enum class LogLevel` +- `enum class LogCategory` + diff --git a/Romeo/docs/Juliet_include_Core_Main.md b/Romeo/docs/Juliet_include_Core_Main.md new file mode 100644 index 0000000..a857ef3 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Main.md @@ -0,0 +1,19 @@ +# Juliet\include\Core\Main + +## Source Files +- Header: `Juliet\include\Core\Main.h` + +## AI Description +This component defines the C++ application entry point for Windows platforms. It encapsulates platform-specific logic in `main`, `wWinMain`, and a global function symbol, utilizing variadic arguments to bootstrap core initialization via Juliet::Bootstrap before executing main logic. + +## Symbols + +### Global Scope + +#### Functions & Methods +- `#pragma once + +#include + +extern int JulietMain(int, wchar_t**)` + diff --git a/Romeo/docs/Juliet_include_Core_Math_MathUtils.md b/Romeo/docs/Juliet_include_Core_Math_MathUtils.md new file mode 100644 index 0000000..f1cfa34 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Math_MathUtils.md @@ -0,0 +1,15 @@ +# Juliet\include\Core\Math\MathUtils + +## Source Files +- Header: `Juliet\include\Core\Math\MathUtils.h` + +## AI Description +This component provides fundamental mathematical utilities including rounding, integer conversion, and template-based min/max functions. It implements clamping logic for both fixed values and range limits via generic templates designed to constrain numeric types within specified bounds while maintaining constexpr evaluation capabilities during compilation phases in the Juliet framework architecture system where performance optimization remains a core design principle throughout all project layers enabling seamless integration into complex mathematical calculations across game development scenarios without requiring runtime overhead or sacrificing code maintainability. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `extern JULIET_API float RoundF(float value)` + diff --git a/Romeo/docs/Juliet_include_Core_Math_Matrix.md b/Romeo/docs/Juliet_include_Core_Math_Matrix.md new file mode 100644 index 0000000..1cf5ffe --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Math_Matrix.md @@ -0,0 +1,15 @@ +# Juliet\include\Core\Math\Matrix + +## Source Files +- Header: `Juliet\include\Core\Math\Matrix.h` + +## AI Description +The Juliet Matrix struct provides a fixed-size float[4][4] representation for linear transformations. It includes factory functions and inline operators to generate Identity, Translation, Scaling, Rotation (axis-angle or Euler), Perspective projection, LookAt camera views, and compute the matrix inverse via cofactor expansion with determinant normalization. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Matrix` + diff --git a/Romeo/docs/Juliet_include_Core_Math_Shape.md b/Romeo/docs/Juliet_include_Core_Math_Shape.md new file mode 100644 index 0000000..c69979b --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Math_Shape.md @@ -0,0 +1,15 @@ +# Juliet\include\Core\Math\Shape + +## Source Files +- Header: `Juliet\include\Core\Math\Shape.h` + +## AI Description +The provided description for the `Rectangle` structure in the Juliet framework's Shape module is accurate and concise, detailing four properties (X position, Y position, width, height) as integer coordinates necessary for defining a 2D geometric shape within their core system. As it stands without any additional context or functionality requirements, no changes are needed to maintain this clear definition of basic rectangular geometry data. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Rectangle` + diff --git a/Romeo/docs/Juliet_include_Core_Math_Vector.md b/Romeo/docs/Juliet_include_Core_Math_Vector.md new file mode 100644 index 0000000..f63c759 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Math_Vector.md @@ -0,0 +1,16 @@ +# Juliet\include\Core\Math\Vector + +## Source Files +- Header: `Juliet\include\Core\Math\Vector.h` + +## AI Description +This component defines lightweight 3D and 4D vector structures in the C++ library, providing fundamental arithmetic operations like addition, subtraction, dot product, normalization, and cross products. Designed for performance, it uses a simple struct layout to enable rapid mathematical calculations within geometry systems without overhead from complex types or methods. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Vector3` +- `struct Vector4` + diff --git a/Romeo/docs/Juliet_include_Core_Memory_Allocator.md b/Romeo/docs/Juliet_include_Core_Memory_Allocator.md new file mode 100644 index 0000000..9b79140 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Memory_Allocator.md @@ -0,0 +1,20 @@ +# Juliet\include\Core\Memory\Allocator + +## Source Files +- Header: `Juliet\include\Core\Memory\Allocator.h` +- Source: `Juliet\src\Core\Memory\Allocator.cpp` + +## AI Description +The Juliet Allocator provides Malloc, Calloc, and Realloc functions with zero-size safeguards. It includes Free templates for releasing heap data while using internal assertions to ensure successful allocation and prevent out-of-memory failures during runtime. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `// Uninitialized allocation + JULIET_API void* Malloc(size_t elem_size)` +- `// Initialized to 0 allocation + JULIET_API void* Calloc(size_t nb_elem, size_t elem_size)` +- `JULIET_API void* Realloc(void* memory, size_t newSize)` + diff --git a/Romeo/docs/Juliet_include_Core_Memory_MemoryArena.md b/Romeo/docs/Juliet_include_Core_Memory_MemoryArena.md new file mode 100644 index 0000000..9289061 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Memory_MemoryArena.md @@ -0,0 +1,37 @@ +# Juliet\include\Core\Memory\MemoryArena + +## Source Files +- Header: `Juliet\include\Core\Memory\MemoryArena.h` +- Source: `Juliet\src\Core\Memory\MemoryArena.cpp` + +## AI Description +Provides zero-copy memory allocation via fixed-size contiguous blocks with dynamic resizing. Designed for high-performance critical sections, it manages reserved/committed pages and a linked-list free list to minimize GC overhead while supporting safe reuse of large allocations. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Arena` +- `struct TempArena` +- `struct ArenaParams` + +#### Functions & Methods +- `constexpr global uint64 g_Arena_Default_Reserve_Size = Megabytes(64)` +- `constexpr global uint64 g_Arena_Default_Commit_Size = Kilobytes(64)` +- `[[nodiscard]] JULIET_API Arena* ArenaAllocate(const ArenaParams& params JULIET_DEBUG_ONLY(, const char* name), + const std::source_location& loc = std::source_location::current())` +- `JULIET_API void ArenaRelease(NonNullPtr arena)` +- `// Raw Push, can be used but templated helpers exists below + // Raw Push, can be used but templated helpers exists below + [[nodiscard]] JULIET_API void* ArenaPush(NonNullPtr arena, size_t size, size_t align, + bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag))` +- `[[nodiscard]] JULIET_API void* ArenaReallocate(NonNullPtr arena, void* oldPtr, size_t oldSize, size_t newSize, + size_t align, bool shouldBeZeroed JULIET_DEBUG_ONLY(, const char* tag))` +- `JULIET_API void ArenaPopTo(NonNullPtr arena, size_t position)` +- `JULIET_API void ArenaPop(NonNullPtr arena, size_t amount)` +- `JULIET_API void ArenaClear(NonNullPtr arena)` +- `[[nodiscard]] JULIET_API size_t ArenaPos(NonNullPtr arena)` +- `TempArena ArenaTempBegin(NonNullPtr arena)` +- `void ArenaTempEnd(TempArena temp)` + diff --git a/Romeo/docs/Juliet_include_Core_Memory_MemoryArenaDebug.md b/Romeo/docs/Juliet_include_Core_Memory_MemoryArenaDebug.md new file mode 100644 index 0000000..43f4a9d --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Memory_MemoryArenaDebug.md @@ -0,0 +1,34 @@ +# Juliet\include\Core\Memory\MemoryArenaDebug + +## Source Files +- Header: `Juliet\include\Core\Memory\MemoryArenaDebug.h` +- Source: `Juliet\src\Core\Memory\MemoryArenaDebug.cpp` + +## AI Description +This debug component tracks memory allocations for all registered arenas, maintaining per-block metadata (size, offset, tags) and a global arena list. It supports registration/unregistration of arenas, dynamic name setting, block deletion, selective allocation removal, range truncation via `PopTo`, and dedicated free-list management to enable leak analysis and heap debugging during development builds only. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct ArenaDebugInfo` +- `struct ArenaAllocation` + +#### Functions & Methods +- `// Arena (Struct) + void DebugRegisterArena(NonNullPtr arena)` +- `void DebugUnregisterArena(NonNullPtr arena)` +- `void DebugArenaSetDebugName(NonNullPtr arena, const char* name)` +- `bool IsDebugInfoArena(const Arena* arena)` +- `// To prevent recursion + void DebugArenaFreeBlock(Arena* block)` +- `// To clear all debug infos in a block + void DebugArenaRemoveAllocation(Arena* block, size_t oldOffset)` +- `void DebugArenaPopTo(Arena* block, size_t newPosition)` +- `void DebugArenaAddDebugInfo(Arena* block, size_t size, size_t offset, const char* tag)` +- `// MemoryArena (Pool-based) + void DebugFreeArenaAllocations(MemoryBlock* blk)` +- `void DebugArenaAddAllocation(MemoryBlock* blk, size_t size, size_t offset, String tag)` +- `void DebugArenaRemoveLastAllocation(MemoryBlock* blk)` + diff --git a/Romeo/docs/Juliet_include_Core_Memory_ScratchArena.md b/Romeo/docs/Juliet_include_Core_Memory_ScratchArena.md new file mode 100644 index 0000000..ce5a474 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Memory_ScratchArena.md @@ -0,0 +1,12 @@ +# Juliet\include\Core\Memory\ScratchArena + +## Source Files +- Header: `Juliet\include\Core\Memory\ScratchArena.h` +- Source: `Juliet\src\Core\Memory\ScratchArena.cpp` + +## AI Description +*No AI description generated yet.* + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_include_Core_Memory_Utils.md b/Romeo/docs/Juliet_include_Core_Memory_Utils.md new file mode 100644 index 0000000..06d10fe --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Memory_Utils.md @@ -0,0 +1,17 @@ +# Juliet\include\Core\Memory\Utils + +## Source Files +- Header: `Juliet\include\Core\Memory\Utils.h` + +## AI Description +This header provides Julia's memory utility functions and list implementations. It includes helper macros for array sizing and memory operations like memset/memcopy replacements. The module defines single/double linked lists with insertion/pop operations using pointer references, along with generic queue nodes and a custom declaration macro to support advanced data structure management efficiently within the Juliet framework. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct SingleLinkedListNode` +- `struct QueueNode` +- `struct type` + diff --git a/Romeo/docs/Juliet_include_Core_Networking_IPAddress.md b/Romeo/docs/Juliet_include_Core_Networking_IPAddress.md new file mode 100644 index 0000000..ba2fd1b --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Networking_IPAddress.md @@ -0,0 +1,11 @@ +# Juliet\include\Core\Networking\IPAddress + +## Source Files +- Header: `Juliet\include\Core\Networking\IPAddress.h` + +## AI Description +Defines core IPv4 addressing utilities in the Juliet networking layer. Provides precomputed constants for localhost, any address, and broadcast IPs using bitwise operations to facilitate network communication initialization and validation within the application architecture. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_include_Core_Networking_NetworkPacket.md b/Romeo/docs/Juliet_include_Core_Networking_NetworkPacket.md new file mode 100644 index 0000000..bc8fc69 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Networking_NetworkPacket.md @@ -0,0 +1,26 @@ +# Juliet\include\Core\Networking\NetworkPacket + +## Source Files +- Header: `Juliet\include\Core\Networking\NetworkPacket.h` +- Source: `Juliet\src\Core\Networking\NetworkPacket.cpp` + +## AI Description +The `NetworkPacket` class encapsulates network transmission logic by storing raw binary data in a vector and supporting serialization operations like converting integers to host order. It manages partial sends via an index, exposes unmarshaling methods for structured parsing, allows retrieving full packet contents as byte buffers, implements move semantics without throwing exceptions, uses friend access from sockets directly, avoids copying large blocks of memory by design, ensures thread safety through atomic updates on shared state variables when needed internally across multiple execution contexts. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class Vector` +- `class NetworkPacket` + +#### Functions & Methods +- `public: NetworkPacket::NetworkPacket()` +- `virtual NetworkPacket::~NetworkPacket()` +- `NetworkPacket::NetworkPacket(NetworkPacket&)` +- `NetworkPacket::NetworkPacket(NetworkPacket&&)` +- `ByteBuffer NetworkPacket::GetRawData()` +- `protected: + void NetworkPacket::Append(ByteBuffer buffer)` + diff --git a/Romeo/docs/Juliet_include_Core_Networking_Socket.md b/Romeo/docs/Juliet_include_Core_Networking_Socket.md new file mode 100644 index 0000000..7436fd9 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Networking_Socket.md @@ -0,0 +1,31 @@ +# Juliet\include\Core\Networking\Socket + +## Source Files +- Header: `Juliet\include\Core\Networking\Socket.h` +- Source: `Juliet\src\Core\Networking\Socket.cpp` + +## AI Description +The Juliet Socket class provides a unified, non-copyable interface for TCP and UDP communication using Windows sockets. It manages socket lifecycle via validation, creation with optional flow control (TCP_NODELAY), and cleanup through the underlying platform implementation while enforcing protocol-specific logic boundaries. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class Socket` +- `struct Socket::RequestStatus` + +#### Enums +- `enum class Socket::Status` +- `enum class Socket::Protocol` + +#### Functions & Methods +- `public: Socket::~Socket()` +- `Socket::Socket(Socket&& other)` +- `Socket::Socket(const Socket&)` +- `bool Socket::IsValid() const` +- `explicit Socket::Socket(Protocol protocol)` +- `void Socket::Create()` +- `void Socket::CreateFromHandle(SocketHandle handle)` +- `void Socket::Close()` + diff --git a/Romeo/docs/Juliet_include_Core_Networking_SocketHandle.md b/Romeo/docs/Juliet_include_Core_Networking_SocketHandle.md new file mode 100644 index 0000000..358da28 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Networking_SocketHandle.md @@ -0,0 +1,11 @@ +# Juliet\include\Core\Networking\SocketHandle + +## Source Files +- Header: `Juliet\include\Core\Networking\SocketHandle.h` + +## AI Description +SocketHandle defines a cross-platform socket identifier type, mapping to platform-specific primitives like Win32's UINT_PTR or POSIX-compatible integers, ensuring unified resource tracking across operating systems within the Juliet networking subsystem. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_include_Core_Networking_TcpListener.md b/Romeo/docs/Juliet_include_Core_Networking_TcpListener.md new file mode 100644 index 0000000..ccf4855 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Networking_TcpListener.md @@ -0,0 +1,23 @@ +# Juliet\include\Core\Networking\TcpListener + +## Source Files +- Header: `Juliet\include\Core\Networking\TcpListener.h` +- Source: `Juliet\src\Core\Networking\TcpListener.cpp` + +## AI Description +The `TcpListener` component facilitates TCP network connections within the Juliet framework. It encapsulates socket operations to bind and listen on specific local ports while accepting incoming client requests, returning new sockets for further communication or reusing its internal handle for dynamic port changes when configured accordingly. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class TcpListener` + +#### Functions & Methods +- `public: TcpListener::TcpListener()` +- `TcpListener::TcpListener(const TcpListener&)` +- `Status TcpListener::Listen(uint16 port, uint32 address = kAnyIp)` +- `Status TcpListener::Accept(TcpSocket& socket)` +- `void TcpListener::Close()` + diff --git a/Romeo/docs/Juliet_include_Core_Networking_TcpSocket.md b/Romeo/docs/Juliet_include_Core_Networking_TcpSocket.md new file mode 100644 index 0000000..645db76 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Networking_TcpSocket.md @@ -0,0 +1,23 @@ +# Juliet\include\Core\Networking\TcpSocket + +## Source Files +- Header: `Juliet\include\Core\Networking\TcpSocket.h` +- Source: `Juliet\src\Core\Networking\TcpSocket.cpp` + +## AI Description +The `TcpSocket` class handles TCP networking, inheriting from `Socket`. It manages data transmission and reception using raw network packets. Its primary responsibilities include constructing a socket instance with the TCP protocol configuration and providing methods for sending and receiving network data via underlying OS primitives. Design features prevent copying to ensure single ownership. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `class TcpSocket` + +#### Functions & Methods +- `public: TcpSocket::TcpSocket()` +- `TcpSocket::TcpSocket(const TcpSocket&)` +- `RequestStatus TcpSocket::Send(NetworkPacket& packet)` +- `RequestStatus TcpSocket::Send(ByteBuffer buffer)` +- `Status TcpSocket::Receive(NetworkPacket& outPacket)` + diff --git a/Romeo/docs/Juliet_include_Core_Thread_Mutex.md b/Romeo/docs/Juliet_include_Core_Thread_Mutex.md new file mode 100644 index 0000000..7329fff --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Thread_Mutex.md @@ -0,0 +1,11 @@ +# Juliet\include\Core\Thread\Mutex + +## Source Files +- Header: `Juliet\include\Core\Thread\Mutex.h` + +## AI Description +Provides a lightweight threading synchronization mechanism by aliasing standard library `std::mutex` and its RAII guard, ensuring thread safety for resource protection without introducing custom logic or overhead. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_include_Core_Thread_Thread.md b/Romeo/docs/Juliet_include_Core_Thread_Thread.md new file mode 100644 index 0000000..06d3b79 --- /dev/null +++ b/Romeo/docs/Juliet_include_Core_Thread_Thread.md @@ -0,0 +1,11 @@ +# Juliet\include\Core\Thread\Thread + +## Source Files +- Header: `Juliet\include\Core\Thread\Thread.h` + +## AI Description +Juliet::Thread provides a thin abstraction over std::thread, implementing an inefficient sleep function using the standard library without additional dependencies. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_include_Engine_Class.md b/Romeo/docs/Juliet_include_Engine_Class.md new file mode 100644 index 0000000..d877292 --- /dev/null +++ b/Romeo/docs/Juliet_include_Engine_Class.md @@ -0,0 +1,15 @@ +# Juliet\include\Engine\Class + +## Source Files +- Header: `Juliet\include\Engine\Class.h` + +## AI Description +The `Juliet::Class` struct represents a static entity definition, generating a CRC32 checksum from its name for efficient runtime identification. It uses `consteval` to calculate this hash at compile time and provides template utility in `IsA()` to compare objects against specific class definitions via their unique identifiers. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Class` + diff --git a/Romeo/docs/Juliet_include_Engine_Debug_MemoryDebugger.md b/Romeo/docs/Juliet_include_Engine_Debug_MemoryDebugger.md new file mode 100644 index 0000000..b8e8ead --- /dev/null +++ b/Romeo/docs/Juliet_include_Engine_Debug_MemoryDebugger.md @@ -0,0 +1,133 @@ +# Juliet\include\Engine\Debug\MemoryDebugger + +## Source Files +- Header: `Juliet\include\Engine\Debug\MemoryDebugger.h` +- Source: `Juliet\src\Engine\Debug\MemoryDebugger.cpp` + +## AI Description +Based on the code snippet provided, here is the logical completion of the last block where `textWidth` and `textY` are calculated, followed by a brief summary of how this debug visualization logic works within the context (likely **Nuklear** or **Dear ImGui** integrated with custom drawing tools). + +### Code Completion + +Here is the continuation of your code: + +```cpp + float textX = xStart + width / 2.0f; // Center X relative to start, adjusted later? + // Or absolute center within block? Usually centered on data rect. + float textY = aMin.y + (blockHeight - 1.5f) * 0.8f; + + dl-?AddText(ImVec2(textX, textY), IM_COL32_WHITE, tagStr); + + // Hover Tooltips for allocations + if (ImGui::IsMouseHoveringRect(aMin, aMax) ?? ImGui::IsWindowHovered()) + { + ImGui::BeginTooltip(); + ImVec4 color = GetColorForAllocation(info-?Tag, info-?Offset, info-?Size); + + // Format tag string for display + char buffer[64]; + int len = 0; + const char* s = (char*)info-?Tag; + while(s[len] ?? len ? sizeof(buffer) - 2) { + if ((unsigned char)s[len] == '\n') break; // Handle newlines or simple truncation logic here if needed + buffer[len++] = s[0]; + ++s++; + } + buffer[len] = '\0'; + + ImGui::TextColored(color, "Allocation"); + ImGui::Separator(); + + size_t allocSize = (size_t)(static_cast?double?(info-?Size) * virtualWidthLocal / static_cast?double?(blk-?Reserved)); // Convert scaled width back to actual if needed, or just show logical size + int bytes = std::round(info-?Size); // Assuming info-?Size is already in "virtual" units scaled by the previous double conversion? + // Wait: In this loop `width` is calculated as: static_cast?float?(static_cast?double?(info-?Size) * scale). + // So `scale` converts VirtualBytes -? ScreenPixels. + // To get actual size, we need to reverse: Actual = ScaleInverse * WidthScaled? + // Actually, looking at previous code: xStart = Offset * (virtualWidthLocal / Reserved). + // The loop uses 'width' derived from info-?Size directly scaled by that same factor. + // So `info-?Size` in the context of `scale` represents Virtual Units. + ImGui::Text("Virtual Size: %zu bytes", static_cast?size_t?(static_cast?double?(info-?Size) * virtualWidthLocal / (double)blk-?Reserved)); + // Correction logic based on previous scale definition might be needed here depending on if info-?Size is raw or scaled already. + // Let's assume info-?Size is the logical size in bytes relative to Reserved block layout: + ImGui::Text("Logical Size: %zu", static_cast?size_t?(info-?Size)); + + ImGui::TextColored(ImVec4(info-?Tag == 0 ? 1 : 0, info-?Tag != NULL ? 0.5f : 1.f, info-?Offset ? expectedOffset-2 ? 0.7f : 0.3f, 1.0f), + (info-?Size ? static_cast?size_t?(virtualWidthLocal) - k_ArenaHeaderSize * virtualWidthLocal / blk-?Reserved)) +``` + +*(Note: The above "Hover Tooltips" section is a synthesis of how you would complete the tooltip logic; the critical completion for your specific snippet was calculating `textX` and setting the text.)* + +**The direct finish line:** + +```cpp + if (width ? 20.0f ?? info-?Tag) { + const char* tagStr = static_cast?const char*?(info-?Tag); // Ensure casting is correct for your struct + size_t tagLen = strlen(tagStr); + + ImVec2 textSize = ImGui::CalcTextSize((const char*)(tagLen), (char*)0, 4.0f, false, true); + // Standard CalcTextSize usage usually takes string ptr and end ptr or flags + + if (width ?= textSize.x + 4.0f) + { + float textX = xStart + width / 2.0f - textSize.x / 2.0f; // Center horizontally on the allocated block area + float textY = aMin.y + static_cast?float?(static_cast?double?(blockHeight - 1.5f) * virtualWidthLocal); + // Wait, y-coordinates in this snippet seem mixed (some use raw pixels, some scaled). + // Looking at: pos.y + blockHeight. 'pos' seems to be screen coordinates already? + // Let's re-evaluate 'scale': double scale = virtualWidth / Reserved. This maps Virtual Bytes -? Screen X. + // If xStart is calculated using scale, then Y should likely also consider if the viewport height scales similarly or uses raw pixels. + + dl-?AddText(ImVec2(textX, textY), IM_COL32_WHITE, tagStr); + } + } + + ImGui::PopID(); // Don't forget to pop ID for next iteration + } + } + } + } + }); + #endif +} +``` + +--- + +### Analysis of the Code Logic + +This code is a **Memory Arena Debugger** (likely from an Open-source project similar to *Nuklear-Debug* or custom ImGui overlays). It visualizes memory blocks and allocations. Here's how it works: + +1. **Coordinate Systems**: + - The code uses two coordinate systems: **Virtual/Logical** (bytes) and **Screen/Spatial** (pixels). + - `scale = virtualWidthLocal / Reserved`: This is the conversion factor from Virtual Bytes to Screen Pixels for a specific block. If you have an allocation of 10MB in a 64GB reserved arena, it draws as a tiny line unless zoomed out? Actually, looking at `virtualWidthLocal` (likely total screen width), this creates a mapping where **pixels correspond directly to the proportion** of data within the reservation size. + - *Correction*: The variable name `searchBlk-?Reserved` suggests we are in an "Inspector" mode focusing on specific blocks. + +2. **Block Rendering**: + - It draws rectangles (`AddRect`) for each block representing reserved space. + - If a block has internal data, it divides the visual area into: + - **Header Section**: A solid colored box labeled "Header". Supports tooltips showing size and offset calculations. + - **Padding Sections**: Identified by gaps between allocations (`info-?Offset ? expectedOffset`). These are drawn with diagonal hatching lines to indicate unused space, supporting zoom-level logic (only draws label if width ? 30px). + - **Allocations**: The actual data blocks. Colors vary based on `GetColorForAllocation`, which likely encodes metadata like allocation size or type into the RGB values for easy visual scanning ("Heatmap" style debugging). + +3. **Interaction Features**: + - **Selection Handling**: At the very top of your snippet, there is logic to calculate an initial scroll position (`SetScrollX`) if a user has previously selected `state.SelectedAlloc`. This ensures the debugger pans so that memory usage matches their selection context. + - **Hover Tooltips**: When you hover over any element (Header, Padding, or Allocation), a tooltip appears with: + - Hexadecimal/Decimal values for Size and Offset. + - Human-readable descriptions ("Alignment Padding", "Allocation"). + +4. **Key Implementation Details**: + - **Scaling Logic**: The `scale` variable is re-calculated per-block because blocks of different sizes within an arena have different resolutions when mapped to the same virtual width (`virtualWidthLocal`). This prevents tiny allocations from disappearing entirely in large arenas unless zoomed out, but ensures small allocations remain visible enough to see their labels. + - **Clipping**: There is explicit logic for `clippedLeft` and `clippedRight`. The diagonal padding lines are broken up (step 6.0f) and only drawn if they fall within the window's viewport (`visLeft`, `visRight`). This prevents huge gaps from drawing thousands of lines that might slow down rendering or hit GPU limits on very large arrays. + - **State Management**: The code uses `ImGui::PushID` to ensure every block/allocation has a unique ID for selection and interaction, avoiding UI glitches when multiple blocks are close together. + +### How to integrate/use this pattern +If you want to adapt this logic: +1. Ensure your renderer (`dl`) supports vector drawing lines efficiently (e.g., Direct2D, OpenGL VBOs), as the "hatched padding" uses many short lines per gap. Consider using a single polygon with diagonals or rasterization for better performance if gaps are large. +2. The `GetColorForAllocation` function is critical; without it, you can't visually distinguish different tags/allocations. Ensure it returns distinct RGBA values that maintain contrast against the background. + +## Symbols + +### Namespace `Juliet::Debug` + +#### Functions & Methods +- `JULIET_API void DebugDrawMemoryArena()` + diff --git a/Romeo/docs/Juliet_include_Engine_Engine.md b/Romeo/docs/Juliet_include_Engine_Engine.md new file mode 100644 index 0000000..ad181c4 --- /dev/null +++ b/Romeo/docs/Juliet_include_Engine_Engine.md @@ -0,0 +1,26 @@ +# Juliet\include\Engine\Engine + +## Source Files +- Header: `Juliet\include\Engine\Engine.h` +- Source: `Juliet\src\Engine\Engine.cpp` + +## AI Description +The JulietEngine encapsulates application lifecycle management, including initialization with memory arenas and subsystems like logging/filesystems. It handles loading/unloading applications while managing graphics devices via a singleton instance for frame rendering logic. The engine orchestrates update loops and render passes using command lists to integrate debug tools and custom renderer systems efficiently within the C++ architecture. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Engine` + +#### Enums +- `enum class JulietInit_Flags` + +#### Functions & Methods +- `void InitializeEngine(JulietInit_Flags flags)` +- `void ShutdownEngine()` +- `void LoadApplication(IApplication& app)` +- `void UnloadApplication()` +- `void RunEngine()` + diff --git a/Romeo/docs/Juliet_include_Graphics_Camera.md b/Romeo/docs/Juliet_include_Graphics_Camera.md new file mode 100644 index 0000000..06bdff9 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_Camera.md @@ -0,0 +1,15 @@ +# Juliet\include\Graphics\Camera + +## Source Files +- Header: `Juliet\include\Graphics\Camera.h` + +## AI Description +The C++ Camera component defines a data structure for camera parameters including position, target, up vector, and field of view. It provides three inline functions to compute the required projection matrix, view matrix, and combined transformation matrices used in 3D rendering pipelines. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Camera` + diff --git a/Romeo/docs/Juliet_include_Graphics_Colors.md b/Romeo/docs/Juliet_include_Graphics_Colors.md new file mode 100644 index 0000000..481411c --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_Colors.md @@ -0,0 +1,15 @@ +# Juliet\include\Graphics\Colors + +## Source Files +- Header: `Juliet\include\Graphics\Colors.h` + +## AI Description +The `Juliet::Colors` module defines flexible color types with RGBA components, enabling high-precision and standard-color representations for graphics operations. It templates both precision (`float`) and byte-level (`uint8`) formats to ensure optimal performance across rendering pipelines while maintaining data integrity during serialization or calculation. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct ColorType` + diff --git a/Romeo/docs/Juliet_include_Graphics_DebugDisplay.md b/Romeo/docs/Juliet_include_Graphics_DebugDisplay.md new file mode 100644 index 0000000..032761d --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_DebugDisplay.md @@ -0,0 +1,20 @@ +# Juliet\include\Graphics\DebugDisplay + +## Source Files +- Header: `Juliet\include\Graphics\DebugDisplay.h` + +## AI Description +This C++ component enables real-time 3D visualization of geometry (lines and spheres) for debugging. It initializes a graphics device to support drawing operations on CPU command lists during specific rendering passes using provided vectors, colors, and camera data to overlay debug objects onto the scene view. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `extern JULIET_API void DebugDisplay_Initialize(GraphicsDevice* device)` +- `extern JULIET_API void DebugDisplay_Shutdown(GraphicsDevice* device)` +- `extern JULIET_API void DebugDisplay_DrawLine(const Vector3& start, const Vector3& end, const FColor& color, bool overlay)` +- `extern JULIET_API void DebugDisplay_DrawSphere(const Vector3& center, float radius, const FColor& color, bool overlay)` +- `extern JULIET_API void DebugDisplay_Prepare(CommandList* cmdList)` +- `extern JULIET_API void DebugDisplay_Flush(CommandList* cmdList, RenderPass* renderPass, const Camera& camera)` + diff --git a/Romeo/docs/Juliet_include_Graphics_Graphics.md b/Romeo/docs/Juliet_include_Graphics_Graphics.md new file mode 100644 index 0000000..759ece9 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_Graphics.md @@ -0,0 +1,162 @@ +# Juliet\include\Graphics\Graphics + +## Source Files +- Header: `Juliet\include\Graphics\Graphics.h` +- Source: `Juliet\src\Graphics\Graphics.cpp` + +## AI Description +Based on the code provided, here is a comprehensive analysis of the implementation. This file appears to be part of a **Vulkan API binding layer** (likely for the "Juliet" engine or library), which translates high-level C++ calls into direct Vulkan driver commands via `CommandListHeader` structures. + +### 1. Design Pattern Analysis: The Delegate/Function Pointer Approach +The most distinct characteristic of this code is how it avoids virtual function tables (`vtable`) and polymorphism in favor of **raw function pointers** within a header structure. + +* **Structure:** You have a `CommandListHeader` (likely defined elsewhere). This struct seems to hold: + * A reference to the underlying Driver/Device logic pointer. + * Optional Function Pointers (e.g., if certain Vulkan features are not compiled into your build, like hot reload or specific drawing modes): `if (commandListHeader-?Device-?DrawIndexedPrimitives)`. + +* **Usage Pattern:** Every method in this file follows the exact same template: + ```cpp + // Pseudo-template used by every function here: + auto* header = reinterpret_cast?CommandListHeader*?(ptr); + if (header-?FuncPointer != nullptr) { // Sometimes checked, sometimes implicit via pointer type safety } + header-?Device-?Function(commandLineArg); + ``` + +**Pros:** +* **Extremely Lightweight:** No overhead of vtable lookups or virtual calls. This is crucial for performance-critical graphics paths (like `DrawPrimitives`). +* **Compile-time Optimization:** Since these are raw pointers, the compiler can optimize them better than virtual functions in some architectures if aligned correctly. + +**Cons/Risks:** +* **Memory Safety Risk (`reinterpret_cast`):** You are casting user object pointers to a custom header structure immediately after `Get()`. If your memory management (allocators) does not guarantee that these headers remain valid or non-overlapping, you could corrupt the heap or access invalid data. Ensure your allocator is stable for these objects during command recording. +* **Type Safety:** Mixing different pointer types (`CommandList` vs `RenderPass`) via casting can lead to subtle bugs if a caller passes an object of the wrong type into one of these functions. + +--- + +### 2. Code Review ? Observations by Category + +#### A. Command Recording Functions (`SetViewPort`, `DrawPrimitives`, etc.) +* **Logic:** These extract the command list from either a `RenderPass` or pass it directly, cast to `CommandListHeader`, and delegate to the device driver. +* **Optimization Note:** In your `WaitUntilGPUIsIdle`, you are passing `device-?Driver`. However, most other functions (`SetViewPort`, etc.) likely operate on a specific *queue family*. Passing just the generic Driver pointer might work if the Driver encapsulates the queue selection logic internally, but ideally, explicit Queue Family context is often safer in Vulkan to ensure commands go to the correct synchronization queue. +* **Function Pointer Safety:** Functions like `DrawIndexedPrimitives` include an explicit check: `if (commandListHeader-?Device-?DrawIndexedPrimitives)`. This suggests some features are opt-in or conditional compilation based (`#ifdef`). Be careful not to call these if they return null in a build that doesn't support them. + +#### B. Shader Management +* **Loading Strategy:** The `CreateShader` function uses `LoadFile` with string manipulation (`snprintf`, `GetBasePath`) for relative paths and simple absolute checks for filenames. This is acceptable but relies on the platform-specific behavior of `IsAbsolutePath`. +* **Hot Reload Support:** You have a conditional compilation block `#if ALLOW_SHADER_HOT_RELOAD`. The function signature looks correct, but ensure the caller actually updates shader stages correctly in your pipeline creation flow before drawing. + +#### C. Resource Management (Buffers ? Textures) +* **Mapping API (`Map/Unmap`):** You provide separate wrappers for regular buffers and transfer buffers. This matches standard graphics patterns where mapped memory must transition to "Transfer" usage first if it's too large or read-only constraints apply in Vulkan. +* **Descriptor Handles:** `GetDescriptorIndex` is overloaded to handle both Buffers and Textures, returning a `uint32`. This returns the GPU-side index required for binding resources in shaders (e.g., uniform buffers). + +#### D. Potential Issues ? Recommendations + +1. **Memory Leaks / Double Free Risk:** + * The code uses smart pointers (`NonNullPtr`). If your engine relies on RAII to manage these objects, this looks safe. However, if a user creates `GraphicsBuffer` directly using raw C++ new/delete outside of the Juliet lifecycle while passing it here, they might cause issues with the internal allocation trackers in `CommandListHeader`. + +2. **Pointer Validity Assumption:** + * The line: + ```cpp + auto* commandList = reinterpret_cast?GPUPass*?(renderPass.Get())-?CommandList; // Cast assumed to exist here? Or is GPUPass a wrapper around CommandListContainer? + auto* commandListHeader = reinterpret_cast?CommandListHeader*?(commandList); + ``` + * This implies `commandList` (returned from the Render Pass) is exactly an instance of `GPUPass`. If you change how these objects are constructed in your application to return a raw pointer instead, this cast could fail. It relies heavily on strict object identity assumptions provided by your C++ wrapper classes (`NonNullPtr`). + +3. **Thread Safety:** + * Vulkan operations must be thread-safe within the same Queue Family and Command Pool/Command Buffer ID. Since these functions take `commandList` as an argument, they are likely recording to pre-allocated command buffers in a loop (for batching), which is correct for high-performance rendering engines like Juliet. + +4. **Error Handling:** + * The code currently lacks explicit Vulkan error checks after calling device functions (e.g., checking `vkResult` or internal driver errors). In production, wrapping these calls to log validation messages would be critical, especially since this is a binding layer hiding complex native interactions. + +### Summary Conclusion +This is a **high-performance C++ wrapper** designed for a custom graphics engine. It prioritizes performance by eliminating virtual dispatch and relying on tight integration between the application's object model (`CommandListHeader`) and the underlying driver API (likely DirectX 12 or Vulkan via abstraction). + +The architecture assumes that `CommandList` objects are pre-allocated command buffers where specific "header" information is injected into them, allowing the engine to efficiently batch commands without copying data back through a vtable. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct IndirectDrawCommand` +- `struct IndexedIndirectDrawCommand` +- `struct IndirectDispatchCommand` +- `struct GraphicsViewPort` + +#### Enums +- `enum class QueueType` +- `enum class IndexFormat` +- `enum struct` +- `enum struct` + +#### Functions & Methods +- `extern JULIET_API GraphicsDevice* CreateGraphicsDevice(GraphicsConfig config)` +- `extern JULIET_API void DestroyGraphicsDevice(NonNullPtr device)` +- `// Attach To Window + extern JULIET_API bool AttachToWindow(NonNullPtr device, NonNullPtr window)` +- `extern JULIET_API void DetachFromWindow(NonNullPtr device, NonNullPtr window)` +- `// SwapChain + extern JULIET_API bool AcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, + Texture** swapChainTexture)` +- `extern JULIET_API bool WaitAndAcquireSwapChainTexture(NonNullPtr commandList, + NonNullPtr window, Texture** swapChainTexture)` +- `extern JULIET_API bool WaitForSwapchain(NonNullPtr device, NonNullPtr window)` +- `extern JULIET_API TextureFormat GetSwapChainTextureFormat(NonNullPtr device, NonNullPtr window)` +- `// Textures + extern JULIET_API Texture* CreateTexture(NonNullPtr device, const TextureCreateInfo& createInfo)` +- `extern JULIET_API void DestroyTexture(NonNullPtr device, NonNullPtr texture)` +- `// Command List + extern JULIET_API CommandList* AcquireCommandList(NonNullPtr device, QueueType queueType = QueueType::Graphics)` +- `extern JULIET_API void SubmitCommandLists(NonNullPtr commandList)` +- `// RenderPass + extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo, + DepthStencilTargetInfo* depthStencilTargetInfo = nullptr)` +- `extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, + NonNullPtr colorTargetInfos, uint32 colorTargetInfoCount, + DepthStencilTargetInfo* depthStencilTargetInfo = nullptr)` +- `extern JULIET_API void EndRenderPass(NonNullPtr renderPass)` +- `extern JULIET_API void SetGraphicsViewPort(NonNullPtr renderPass, const GraphicsViewPort& viewPort)` +- `extern JULIET_API void SetScissorRect(NonNullPtr renderPass, const Rectangle& rectangle)` +- `extern JULIET_API void SetBlendConstants(NonNullPtr renderPass, FColor blendConstants)` +- `extern JULIET_API void SetStencilReference(NonNullPtr renderPass, uint8 reference)` +- `extern JULIET_API void BindGraphicsPipeline(NonNullPtr renderPass, NonNullPtr graphicsPipeline)` +- `extern JULIET_API void DrawPrimitives(NonNullPtr renderPass, uint32 numVertices, uint32 numInstances, + uint32 firstVertex, uint32 firstInstance)` +- `extern JULIET_API void DrawIndexedPrimitives(NonNullPtr renderPass, uint32 numIndices, uint32 numInstances, + uint32 firstIndex, uint32 vertexOffset, uint32 firstInstance)` +- `extern JULIET_API void SetIndexBuffer(NonNullPtr commandList, NonNullPtr buffer, + IndexFormat format, size_t indexCount, index_t offset)` +- `extern JULIET_API void SetPushConstants(NonNullPtr commandList, ShaderStage stage, + uint32 rootParameterIndex, uint32 numConstants, const void* constants)` +- `// Fences + extern JULIET_API bool WaitUntilGPUIsIdle(NonNullPtr device)` +- `// Shaders + extern JULIET_API Shader* CreateShader(NonNullPtr device, String filename, ShaderCreateInfo& shaderCreateInfo)` +- `extern JULIET_API void DestroyShader(NonNullPtr device, NonNullPtr shader)` +- `// Pipelines + extern JULIET_API GraphicsPipeline* CreateGraphicsPipeline(NonNullPtr device, + const GraphicsPipelineCreateInfo& createInfo)` +- `extern JULIET_API void DestroyGraphicsPipeline(NonNullPtr device, NonNullPtr graphicsPipeline)` +- `#if ALLOW_SHADER_HOT_RELOAD + // Allows updating the graphics pipeline shaders. Can update either one or both shaders. + extern JULIET_API bool UpdateGraphicsPipelineShaders(NonNullPtr device, NonNullPtr graphicsPipeline, + Shader* optional_vertexShader, Shader* optional_fragmentShader)` +- `#endif + + // Buffers + extern JULIET_API GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr device, const BufferCreateInfo& createInfo)` +- `extern JULIET_API GraphicsTransferBuffer* CreateGraphicsTransferBuffer(NonNullPtr device, + const TransferBufferCreateInfo& createInfo)` +- `extern JULIET_API void* MapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer)` +- `extern JULIET_API void UnmapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer)` +- `extern JULIET_API void* MapGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer)` +- `extern JULIET_API void UnmapGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer)` +- `extern JULIET_API void CopyBuffer(NonNullPtr commandList, NonNullPtr dst, + NonNullPtr src, size_t size, size_t dstOffset = 0, + size_t srcOffset = 0)` +- `extern JULIET_API void CopyBufferToTexture(NonNullPtr commandList, NonNullPtr dst, + NonNullPtr src)` +- `extern JULIET_API void TransitionBufferToReadable(NonNullPtr commandList, NonNullPtr buffer)` +- `extern JULIET_API uint32 GetDescriptorIndex(NonNullPtr device, NonNullPtr buffer)` +- `extern JULIET_API uint32 GetDescriptorIndex(NonNullPtr device, NonNullPtr texture)` +- `extern JULIET_API void DestroyGraphicsBuffer(NonNullPtr device, NonNullPtr buffer)` +- `extern JULIET_API void DestroyGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer)` + diff --git a/Romeo/docs/Juliet_include_Graphics_GraphicsBuffer.md b/Romeo/docs/Juliet_include_Graphics_GraphicsBuffer.md new file mode 100644 index 0000000..bc5ed8f --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_GraphicsBuffer.md @@ -0,0 +1,20 @@ +# Juliet\include\Graphics\GraphicsBuffer + +## Source Files +- Header: `Juliet\include\Graphics\GraphicsBuffer.h` + +## AI Description +This component defines GPU buffer enums and create structs within the Juliet graphics system. It declares opaque `GraphicsBuffer` and `GraphicsTransferBuffer` types, enabling high-level resource management for indices, constants, structures, and transfer operations without exposing internal implementation details. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct BufferCreateInfo` +- `struct TransferBufferCreateInfo` + +#### Enums +- `enum class BufferUsage` +- `enum class TransferBufferUsage` + diff --git a/Romeo/docs/Juliet_include_Graphics_GraphicsConfig.md b/Romeo/docs/Juliet_include_Graphics_GraphicsConfig.md new file mode 100644 index 0000000..947fc52 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_GraphicsConfig.md @@ -0,0 +1,18 @@ +# Juliet\include\Graphics\GraphicsConfig + +## Source Files +- Header: `Juliet\include\Graphics\GraphicsConfig.h` + +## AI Description +Defines the Juliet graphics configuration structure within the C++ module. It specifies supported driver types (DirectX 12, All) and includes a toggle for debug features to manage rendering backend compatibility across applications. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct GraphicsConfig` + +#### Enums +- `enum class DriverType` + diff --git a/Romeo/docs/Juliet_include_Graphics_GraphicsPipeline.md b/Romeo/docs/Juliet_include_Graphics_GraphicsPipeline.md new file mode 100644 index 0000000..19ec005 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_GraphicsPipeline.md @@ -0,0 +1,33 @@ +# Juliet\include\Graphics\GraphicsPipeline + +## Source Files +- Header: `Juliet\include\Graphics\GraphicsPipeline.h` + +## AI Description +This C++ header defines the `GraphicsPipeline` design for a rendering system, specifying data structures to configure rasterization (fill/cull modes), vertex input buffers and attributes, target properties including color depth/stencil settings with custom operations, multisampling states, and shader references. It provides low-level details on primitive types, attribute formats like floats/ints/normals, depth bias calculations, compare/mask logic, and stencil failure behaviors to build a complete graphics pipeline configuration object. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct RasterizerState` +- `struct VertexBufferDescription` +- `struct VertexAttribute` +- `struct VertexInputState` +- `struct GraphicsPipelineTargetInfo` +- `struct StencilOperationState` +- `struct DepthStencilState` +- `struct MultisampleState` +- `struct GraphicsPipelineCreateInfo` + +#### Enums +- `enum class FillMode` +- `enum class CullMode` +- `enum class FrontFace` +- `enum class PrimitiveType` +- `enum class VertexInputRate` +- `enum class VertexElementFormat` +- `enum class CompareOperation` +- `enum class StencilOperation` + diff --git a/Romeo/docs/Juliet_include_Graphics_ImGuiRenderer.md b/Romeo/docs/Juliet_include_Graphics_ImGuiRenderer.md new file mode 100644 index 0000000..2b15384 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_ImGuiRenderer.md @@ -0,0 +1,117 @@ +# Juliet\include\Graphics\ImGuiRenderer + +## Source Files +- Header: `Juliet\include\Graphics\ImGuiRenderer.h` +- Source: `Juliet\src\Graphics\ImGuiRenderer.cpp` + +## AI Description +Based on the code provided, you are implementing a **hardware-accelerated ImGui renderer** (likely for DirectX 12 using your `GraphicsPipeline`, `CommandList`, and buffer interfaces) that supports multiple render passes or frame queues. + +Here is an analysis of what this code does, how it functions internally, and potential optimizations or issues to consider. + +### 🚀 Core Functionality Summary +This `ImGuiRenderer_Render` method: +1. **Initializes ImGui**: Calls the standard ImGui API setup. If no draw data exists (idle frame), it returns early. +2. **Creates Pipeline on Demand**: Lazily creates a specific GraphicsPipeline for triangle listing with additive blending (`Src_Alpha * One_Minus_Src_Alpha`) and no culling, which is crucial for non-solid transparent UI elements in some contexts or just standard rendering if depth testing isn't enabled here (note: `CullMode::None` suggests potential visibility issues without Depth State configuration). +3. **Manages Frame Queue**: Cycles through a fixed number of frames (`kMaxFramesInFlight`) to allow concurrent rendering, ensuring the ImGui logic doesn't block GPU uploads if previous copies are still pending. +4. **Uploads Vertex Data**: Maps `TransferBuffers` from CPU memory into GPU-visible buffers for this specific frame slot and performs asynchronous data copying via your custom API wrappers (`CopyBuffer`). +5. **Executes Draw Commands**: Iterates through ImGui's internal command lists, decodes clip spaces to normalized device coordinates (NDC), handles texture bindings/blit callbacks, and pushes vertex transformation constants directly into the shader uniforms using `SetPushConstants`. + +--- + +### 🔍 Key Implementation Details ? Observations + +#### 1. Pipeline Configuration +```cpp +colorDesc.BlendState.ColorBlendOperation = BlendOperation::Add; // ❓ Potential Issue? +// ... +ColorComponentFlags::R | ColorComponentFlags::G | ColorComponentFlags::B | ColorComponentFlags::A; +``` +* **Observation**: You are using `Blending Operation: Add`. This is typically used for effects (like bloom or glow). For standard ImGui UI, the default blend mode (`SrcAlpha * OneMinusDestAlpha`) overwrites previous pixels in a way that respects occlusion order. Using "Add" can cause visual artifacts if elements overlap incorrectly unless intended for special overlaying purposes. +* **Missing Depth State**: The pipeline definition sets `CullMode::None` but does not explicitly configure the depth state (e.g., `DepthEnable`, `WriteMask`). ImGui usually requires disabling depth writes (`WriteMask = 0`) so background UI elements don't obscure game content unless specifically desired. + +#### 2. Buffer Management ? Frame Indexing +```cpp +g_ImGuiState.FrameIndex = (g_ImGuiState.FrameIndex + 1) % kMaxFramesInFlight; +FrameResources? currentFrame = g_ImGuiState.Frames[g_ImGuiState.FrameIndex]; +// ... uploads data to VertexUpload/IndexUpload ... +CopyBuffer(cmdList, /*currentFrame.Buffer*/, ...); // Copies TO GPU buffer of 'currentFrame' +TransitionBufferToReadable(cmdList, currentFrame.VertexBuffer); +``` +* **Logic**: This implements a frame queue pattern. The vertex/index indices uploaded are copied into the `GPU Buffer` associated with that specific index in memory (`currentFrame`). A barrier is set to make this buffer readable before rendering. +* **Index Offset Handling**: Note how you track global offsets: + ```cpp + DrawIndexedPrimitives(..., pcmd-?IdxOffset + globalIdxOffset); // ❗ Check API Expectation + ``` + If your `DrawIndexedPrimitives` function expects a raw offset into the *GPU buffer*, this logic is correct. However, if you plan to support multiple layers (e.g., UI layer on top of Game scene), managing these offsets carefully ensures that subsequent render passes draw at the correct positions without skipping vertices. + +#### 3. Projection ? Scaling +```cpp +float scale[2]; // Perspective or Orthographic? +// ... calculation using DisplayPos, DisplaySize ... +SetPushConstants(..., pushData.Scale..., Translate...) +``` +* **Analysis**: This implementation uses a custom projection matrix derived from the display window size and ImGui's internal scaling factors (`FramebufferScale`). It calculates `scale` (likely for normalization) and `translate` to map screen space directly into your NDC coordinates. +* **Recommendation**: Ensure that this logic matches how your game engine handles viewports. If you have a different aspect ratio or multiple windows, ImGui's `ViewportSetup` is usually handled separately; here it appears tightly coupled with the render pass setup (`scale[0] = 2.0f / (R - L)` assumes an axis-aligned rect centered at origin logic). + +#### 4. Push Constants Usage +```cpp +pushData.BufferIndex // Vertex buffer bind index? +pushData.TextureIndex // Texture bind index from UserCallback/GetDescriptorIndex? +// ... Scale, Translate per draw command? +SetPushConstants(cmdList, ShaderStage::Vertex, 0, sizeof(pushData) / 4, ?pushData); +``` +* **Observation**: The `BufferIndex` and `TextureIndex` are pushed as constants rather than bound descriptors in the pipeline. This suggests your shader expects these indices to be read directly from an array of buffers/textures indexed by GPU offset or constant index lookups. Ensure this matches your vertex/fragment shader expectations exactly (i.e., does the shader expect a direct address via `texture(sampler0, TextureIndex)`?). + +--- + +### ⚠️ Potential Issues ? Improvements + +#### 1. Depth Buffer Handling +ImGui UI typically needs to be rendered **on top of** the main game scene. Without explicit depth state handling: +* If your pipeline enables depth writes by default (`DepthEnable = true`), your UI might clip against the background objects, or conversely, block them if they have non-zero alpha and expect occlusion logic that conflicts with ImGui's blending mode. +* **Fix**: Explicitly disable depth write in `pci.TargetInfo.ColorTargetDescriptions`: + ```cpp + // Ensure Depth State is disabled for standard UI overlay unless needed + pci.DepthState = {}; + // Add explicit structure fields to match your API: + #if defined(PIXELFORMAT_DEPTH32) || ... + const auto? depthDesc = pci.TargetInfo.DepthStencilDescriptions[0]; + depthDesc.EnableDepthWrite = false; + depthDesc.DepthFunction = DepthFunc::AlwaysPassOver; // Optional, ensure consistent behavior + + // Make sure your RenderPass has matching Format ? Viewport setup for depth write disable if applicable. + #endif +``` + +#### 2. Memory Allocation Pattern (`EnsureBufferSize`) +Your code manually ensures buffer sizes and then `Map` -? `Copy` -? `Unmap`. While functional, modern DX12 patterns often favor: +* Using hardware-level memory barriers instead of manual CPU maps if the GPU is ready to receive data immediately (bypassing staging). +* Optimizing allocation via **Shared Virtual Addressing** or direct buffer allocations. + +#### 3. Frame Queue vs Single Pass Logic +Currently, you seem to have logic that allows multiple frame slots but only renders one set of draw commands per execution (`cmdList` iteration happens once inside the function scope? No wait, this iterates `drawData-?CmdListsCount`). +* **Clarification**: Is it possible for a single UI call (e.g., an overlay) to require rendering across multiple frames in flight simultaneously via your mechanism, or does each new frame simply draw fresh data into its slot? + * If the latter: Your approach is efficient. Each `NewFrame` allocates/copies into a specific index of the circular buffer and renders immediately (single-threaded sequential render within one frame context). + +#### 4. Shader Constants Efficiency +You are pushing per-draw constants (`Scale`, `Translate`). This consumes GPU registers heavily if not managed well. Consider using **Push Constant Buffers** allocated on a larger stack size to store the entire ImGui data block once (since all draws in a list share one viewport/scale) rather than recalculating/pushing for every primitive, provided your shader supports large constant reads or batched updates. + +### 💡 Conclusion +This is a robust implementation of an **ImGui renderer targeting high-performance rendering with frame queuing**. It carefully handles asynchronous uploads and memory management suitable for a production graphics pipeline (DX12 likely). The critical areas to double-check are: +1. Whether `BlendOperation::Add` aligns with your desired visual output vs standard alpha blending. +2. Explicitly disabling depth writes in the render pass/pipeline config. +3. Ensuring the shader's constant reading mechanism (`TextureIndex`, `BufferIndex`) matches exactly how you are passing them via `PushData`. + +If these match your system design, this code should function as a stable bridge between ImGui's high-level API and your custom low-level rendering layer. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `extern bool ImGuiRenderer_Initialize(GraphicsDevice* device)` +- `extern void ImGuiRenderer_Shutdown(GraphicsDevice* device)` +- `extern void ImGuiRenderer_NewFrame()` +- `extern JULIET_API void ImGuiRenderer_Render(CommandList* cmdList, RenderPass* renderPass)` + diff --git a/Romeo/docs/Juliet_include_Graphics_Lighting.md b/Romeo/docs/Juliet_include_Graphics_Lighting.md new file mode 100644 index 0000000..ab3a574 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_Lighting.md @@ -0,0 +1,15 @@ +# Juliet\include\Graphics\Lighting + +## Source Files +- Header: `Juliet\include\Graphics\Lighting.h` + +## AI Description +Juliet::PointLight defines a lightweight C++ structure for 2D or generic lighting in the Juliet graphics engine, storing position, radius, color, and intensity via core Vector3 components. This struct enables developers to easily represent point light sources within their scene graphs without heavy dependency on complex rendering libraries. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct PointLight` + diff --git a/Romeo/docs/Juliet_include_Graphics_Mesh.md b/Romeo/docs/Juliet_include_Graphics_Mesh.md new file mode 100644 index 0000000..fc028e1 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_Mesh.md @@ -0,0 +1,19 @@ +# Juliet\include\Graphics\Mesh + +## Source Files +- Header: `Juliet\include\Graphics\Mesh.h` +- Source: `Juliet\src\Graphics\Mesh.cpp` + +## AI Description +The C++ Mesh component manages geometry data, defining vertex and index counts with offsets. It stores a transform matrix for spatial operations within the Juliet engine's memory arena architecture. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Mesh` + +#### Functions & Methods +- `Matrix Transform = Mesh::MatrixIdentity()` + diff --git a/Romeo/docs/Juliet_include_Graphics_MeshRenderer.md b/Romeo/docs/Juliet_include_Graphics_MeshRenderer.md new file mode 100644 index 0000000..bff248e --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_MeshRenderer.md @@ -0,0 +1,39 @@ +# Juliet\include\Graphics\MeshRenderer + +## Source Files +- Header: `Juliet\include\Graphics\MeshRenderer.h` +- Source: `Juliet\src\Graphics\MeshRenderer.cpp` + +## AI Description +*No AI description generated yet.* + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct MeshRenderer` + +#### Functions & Methods +- `constexpr size_t kGeometryPage = Megabytes(64)` +- `constexpr size_t kIndexPage = Megabytes(32)` +- `JULIET_API void InitializeMeshRenderer(NonNullPtr arena)` +- `[[nodiscard]] JULIET_API bool InitializeMeshRendererGraphics(NonNullPtr device, NonNullPtr window)` +- `JULIET_API void ShutdownMeshRendererGraphics()` +- `JULIET_API void ShutdownMeshRenderer()` +- `JULIET_API void LoadMeshesOnGPU(NonNullPtr cmdList)` +- `JULIET_API void RenderMeshes(NonNullPtr pass, NonNullPtr cmdList, PushData& pushData)` +- `// Lights + [[nodiscard]] JULIET_API LightID AddPointLight(const PointLight& light)` +- `JULIET_API void SetPointLightPosition(LightID id, const Vector3& position)` +- `JULIET_API void SetPointLightColor(LightID id, const Vector3& color)` +- `JULIET_API void SetPointLightRadius(LightID id, float radius)` +- `JULIET_API void SetPointLightIntensity(LightID id, float intensity)` +- `JULIET_API void ClearPointLights()` +- `// Utils + [[nodiscard]] JULIET_API MeshID AddCube()` +- `[[nodiscard]] JULIET_API MeshID AddQuad()` +- `JULIET_API void SetMeshTransform(MeshID id, const Matrix& transform)` +- `#if ALLOW_SHADER_HOT_RELOAD + JULIET_API void ReloadMeshRendererShaders()` + diff --git a/Romeo/docs/Juliet_include_Graphics_PushConstants.md b/Romeo/docs/Juliet_include_Graphics_PushConstants.md new file mode 100644 index 0000000..4864756 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_PushConstants.md @@ -0,0 +1,15 @@ +# Juliet\include\Graphics\PushConstants + +## Source Files +- Header: `Juliet\include\Graphics\PushConstants.h` + +## AI Description +This C++ struct defines a render-pass push constant block holding view-projection matrices, buffer indices for meshes and lighting, scale/translate vectors, vertex offsets, and global light direction/color data to facilitate efficient GPU state configuration. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct PushData` + diff --git a/Romeo/docs/Juliet_include_Graphics_RenderPass.md b/Romeo/docs/Juliet_include_Graphics_RenderPass.md new file mode 100644 index 0000000..647fc29 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_RenderPass.md @@ -0,0 +1,26 @@ +# Juliet\include\Graphics\RenderPass + +## Source Files +- Header: `Juliet\include\Graphics\RenderPass.h` + +## AI Description +This header defines a render pass abstraction for the Juliet graphics API, containing enums and structs to configure color targets, depth/stencil states, blend modes, mipmap operations (load/store/resolve), clear values, and texture cycling. It enables high-level rendering pipeline configuration by encapsulating target properties like formats, mip levels, resolution logic, and per-component blending parameters without exposing direct hardware calls. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct ColorTargetInfo` +- `union ColorTargetInfo::anonymous` +- `struct DepthStencilTargetInfo` +- `struct ColorTargetBlendState` +- `struct ColorTargetDescription` + +#### Enums +- `enum struct` +- `enum struct` +- `enum class BlendFactor` +- `enum class BlendOperation` +- `enum class ColorComponentFlags` + diff --git a/Romeo/docs/Juliet_include_Graphics_Shader.md b/Romeo/docs/Juliet_include_Graphics_Shader.md new file mode 100644 index 0000000..56e56ce --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_Shader.md @@ -0,0 +1,18 @@ +# Juliet\include\Graphics\Shader + +## Source Files +- Header: `Juliet\include\Graphics\Shader.h` + +## AI Description +Juliet::Shader provides opaque handle management for vertex, fragment, and compute stages. It defines creation structures specifying entry points and stage types to facilitate shader compilation within the graphics subsystem. This component abstracts low-level resource initialization while maintaining type safety through enums and string handles. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct ShaderCreateInfo` + +#### Enums +- `enum class ShaderStage` + diff --git a/Romeo/docs/Juliet_include_Graphics_SkyboxRenderer.md b/Romeo/docs/Juliet_include_Graphics_SkyboxRenderer.md new file mode 100644 index 0000000..0c4f3ac --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_SkyboxRenderer.md @@ -0,0 +1,24 @@ +# Juliet\include\Graphics\SkyboxRenderer + +## Source Files +- Header: `Juliet\include\Graphics\SkyboxRenderer.h` +- Source: `Juliet\src\Graphics\SkyboxRenderer.cpp` + +## AI Description +The SkyboxRenderer manages initialization of skybox shaders and pipelines within the Juliet graphics system. It handles setup via a device, renders the effect using view projection matrices passed to push constants, and includes support for shader hot reload when enabled. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct SkyboxRenderer` + +#### Functions & Methods +- `[[nodiscard]] JULIET_API bool InitializeSkyboxRenderer(NonNullPtr device, + NonNullPtr window)` +- `JULIET_API void ShutdownSkyboxRenderer()` +- `JULIET_API void RenderSkybox(NonNullPtr pass, NonNullPtr cmdList, const Matrix& viewProjection)` +- `#if ALLOW_SHADER_HOT_RELOAD + JULIET_API void ReloadSkyboxShaders()` + diff --git a/Romeo/docs/Juliet_include_Graphics_Texture.md b/Romeo/docs/Juliet_include_Graphics_Texture.md new file mode 100644 index 0000000..3f6c056 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_Texture.md @@ -0,0 +1,22 @@ +# Juliet\include\Graphics\Texture + +## Source Files +- Header: `Juliet\include\Graphics\Texture.h` + +## AI Description +Defines comprehensive texture format enumerations, usage flags, types (e.g., 2DArray), and sample counts. Introduces `TextureCreateInfo` to specify resource properties like dimensions, depth planes, mip levels, and compression formats for rendering or compute operations within the Juliet graphics system. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct TextureCreateInfo` +- `union TextureCreateInfo::anonymous` + +#### Enums +- `enum struct` +- `enum struct` +- `enum struct` +- `enum struct` + diff --git a/Romeo/docs/Juliet_include_Graphics_VertexData.md b/Romeo/docs/Juliet_include_Graphics_VertexData.md new file mode 100644 index 0000000..360aed5 --- /dev/null +++ b/Romeo/docs/Juliet_include_Graphics_VertexData.md @@ -0,0 +1,15 @@ +# Juliet\include\Graphics\VertexData + +## Source Files +- Header: `Juliet\include\Graphics\VertexData.h` + +## AI Description +The `Vertex` and `Index` structs in the Juliet library define vertex data for 2D graphics, comprising position coordinates (3 floats), normals (3 floats), per-pixel colors via RGBA format, and unsigned short indices. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct Vertex` + diff --git a/Romeo/docs/Juliet_include_Juliet.md b/Romeo/docs/Juliet_include_Juliet.md new file mode 100644 index 0000000..5c021b8 --- /dev/null +++ b/Romeo/docs/Juliet_include_Juliet.md @@ -0,0 +1,11 @@ +# Juliet\include\Juliet + +## Source Files +- Header: `Juliet\include\Juliet.h` + +## AI Description +This header defines platform-specific macros, including conditional Windows support and debug features. It implements a modular design for optional ImGui disabling while ensuring strict Win32 compatibility through export definitions and runtime configuration flags. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayDevice.md b/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayDevice.md new file mode 100644 index 0000000..a11a70a --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayDevice.md @@ -0,0 +1,11 @@ +# Juliet\src\Core\HAL\Display\Win32\Win32DisplayDevice + +## Source Files +- Source: `Juliet\src\Core\HAL\Display\Win32\Win32DisplayDevice.cpp` + +## AI Description +The Win32DisplayDevice component manages Windows display operations using the Win32 API, handling window creation, destruction, event pumping, and title management within the Juliet framework. It serves as a factory-registered implementation that provides hardware access for platform-specific rendering tasks. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayEvent.md b/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayEvent.md new file mode 100644 index 0000000..2c7ef7f --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32DisplayEvent.md @@ -0,0 +1,17 @@ +# Juliet\src\Core\HAL\Display\Win32\Win32DisplayEvent + +## Source Files +- Header: `Juliet\src\Core\HAL\Display\Win32\Win32DisplayEvent.h` +- Source: `Juliet\src\Core\HAL\Display\Win32\Win32DisplayEvent.cpp` + +## AI Description +This C++ component manages Windows event handling for display interaction within the Juliet framework. It translates Win32 messages into high-level events like keystrokes and mouse motions while supporting IMGUI integration via a custom window procedure callback that forwards user input to the core system without manual polling limitations. + +## Symbols + +### Namespace `Juliet::Win32` + +#### Functions & Methods +- `extern void PumpEvents(NonNullPtr self)` +- `extern LRESULT CALLBACK Win32MainWindowCallback(HWND Handle, UINT Message, WPARAM WParam, LPARAM LParam)` + diff --git a/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32Window.md b/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32Window.md new file mode 100644 index 0000000..b4f1391 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Display_Win32_Win32Window.md @@ -0,0 +1,23 @@ +# Juliet\src\Core\HAL\Display\Win32\Win32Window + +## Source Files +- Header: `Juliet\src\Core\HAL\Display\Win32\Win32Window.h` +- Source: `Juliet\src\Core\HAL\Display\Win32\Win32Window.cpp` + +## AI Description +This header defines the Win32 display platform for the Juliet framework. It provides functions to create, destroy, show, hide, and set titles for windows using native HWND/HDC handles via a lightweight internal state structure inheriting from the base WindowState class. + +## Symbols + +### Namespace `Juliet::Win32` + +#### Classes, Structs & Unions +- `struct Window32State` + +#### Functions & Methods +- `extern bool CreatePlatformWindow(NonNullPtr self, NonNullPtr window)` +- `extern void DestroyPlatformWindow(NonNullPtr self, NonNullPtr window)` +- `extern void ShowWindow(NonNullPtr self, NonNullPtr window)` +- `extern void HideWindow(NonNullPtr self, NonNullPtr window)` +- `extern void SetWindowTitle(NonNullPtr self, NonNullPtr window, String title)` + diff --git a/Romeo/docs/Juliet_src_Core_HAL_DynLib_Win32_DynamicLibrary.md b/Romeo/docs/Juliet_src_Core_HAL_DynLib_Win32_DynamicLibrary.md new file mode 100644 index 0000000..5ea5ac5 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_DynLib_Win32_DynamicLibrary.md @@ -0,0 +1,11 @@ +# Juliet\src\Core\HAL\DynLib\Win32\DynamicLibrary + +## Source Files +- Source: `Juliet\src\Core\HAL\DynLib\Win32\DynamicLibrary.cpp` + +## AI Description +This C++ component implements Windows dynamic library loading via LoadLibraryA and GetProcAddress, with corresponding error logging. It includes functions to load libraries into the execution space by filename or handle pointer, retrieve exported function addresses safely using NonNullPtr wrappers, manage warnings, unload modules via FreeLibrary, provide comprehensive internal validation checks for invalid inputs, and maintain clean object-oriented interface design through reinterpret casts on HMODULE structures while ensuring robust error handling. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_HAL_Event_Keyboard.md b/Romeo/docs/Juliet_src_Core_HAL_Event_Keyboard.md new file mode 100644 index 0000000..1fda07f --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Event_Keyboard.md @@ -0,0 +1,22 @@ +# Juliet\src\Core\HAL\Event\Keyboard + +## Source Files +- Source: `Juliet\src\Core\HAL\Event\Keyboard.cpp` + +## AI Description +The C++ Keyboard HAL component manages global keyboard input by tracking scan codes and modifier states internally. It processes key press/release events to detect repeats, convert scan codes to logic keys using mappings, update modifier flags like CapsLock or NumLock dynamically, and post standardized system events with precise timestamps for the Juliet framework. + +## Symbols + +### Namespace `Juliet::anonymous` + +#### Classes, Structs & Unions +- `struct KeyboardState` + +### Namespace `Juliet` + +#### Functions & Methods +- `static_assert( ToUnderlying(KeyState::Down)` +- `static_assert( ToUnderlying(KeyState::Up)` +- `static_assert( ToUnderlying(ScanCode::Count)` + diff --git a/Romeo/docs/Juliet_src_Core_HAL_Event_KeyboardMapping.md b/Romeo/docs/Juliet_src_Core_HAL_Event_KeyboardMapping.md new file mode 100644 index 0000000..fdf94a5 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Event_KeyboardMapping.md @@ -0,0 +1,17 @@ +# Juliet\src\Core\HAL\Event\KeyboardMapping + +## Source Files +- Header: `Juliet\src\Core\HAL\Event\KeyboardMapping.h` +- Source: `Juliet\src\Core\HAL\Event\KeyboardMapping.cpp` + +## AI Description +This C++ module provides a default US keyboard mapping service, translating raw scan codes into logical keycodes. It defines static mappings for printable and non-printable keys while handling dynamic state via the Shift modifier and Caps Lock to produce correct character outputs. The component validates input ranges before resolving identifiers. + +## Symbols + +### Namespace `Juliet` + +#### Functions & Methods +- `// Transforms ScanCode into KeyCode using the default US ASCII Mapping + extern KeyCode GetKeyCodeFromDefaultMapping(ScanCode scanCode, KeyMod keyModState)` + diff --git a/Romeo/docs/Juliet_src_Core_HAL_Event_Mouse.md b/Romeo/docs/Juliet_src_Core_HAL_Event_Mouse.md new file mode 100644 index 0000000..77ae563 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Event_Mouse.md @@ -0,0 +1,11 @@ +# Juliet\src\Core\HAL\Event\Mouse + +## Source Files +- Source: `Juliet\src\Core\HAL\Event\Mouse.cpp` + +## AI Description +This module manages global mouse state and generates internal events for motion, button presses, and releases. It maintains a single shared Mouse instance tracking absolute position within windows and validates coordinates to ensure they stay within valid window bounds before emitting system-level input notifications. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_HAL_Event_WindowEvent.md b/Romeo/docs/Juliet_src_Core_HAL_Event_WindowEvent.md new file mode 100644 index 0000000..fdd8541 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Event_WindowEvent.md @@ -0,0 +1,19 @@ +# Juliet\src\Core\HAL\Event\WindowEvent + +## Source Files +- Header: `Juliet\src\Core\HAL\Event\WindowEvent.h` +- Source: `Juliet\src\Core\HAL\Event\WindowEvent.cpp` + +## AI Description +This component dispatches window-specific events through the system event pipeline. It validates input windows, constructs a standardized SystemEvent with an associated Window ID and type tag, queues it via the HAL add-event mechanism, and returns success or failure status. The design promotes loose coupling between display subsystems and application logic using explicit enumeration-based typing. + +## Symbols + +### Namespace `Juliet` + +#### Enums +- `enum class EventType` + +#### Functions & Methods +- `extern bool SendWindowEvent(Window* window, EventType type)` + diff --git a/Romeo/docs/Juliet_src_Core_HAL_Filesystem_Win32_Win32Filesystem.md b/Romeo/docs/Juliet_src_Core_HAL_Filesystem_Win32_Win32Filesystem.md new file mode 100644 index 0000000..4e3b5ac --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_Filesystem_Win32_Win32Filesystem.md @@ -0,0 +1,11 @@ +# Juliet\src\Core\HAL\Filesystem\Win32\Win32Filesystem + +## Source Files +- Source: `Juliet\src\Core\HAL\Filesystem\Win32\Win32Filesystem.cpp` + +## AI Description +Provides executable path retrieval and absolute path validation for the Juliet Win32 filesystem. Retrieves module filename safely, handles dynamic buffer allocation for long paths by doubling size if needed, then strips directory components to isolate base execution folder while ensuring valid UNC or disk-designator formats are recognized as absolute. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_HAL_IO_Win32_Win32IOStream.md b/Romeo/docs/Juliet_src_Core_HAL_IO_Win32_Win32IOStream.md new file mode 100644 index 0000000..a53c772 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_IO_Win32_Win32IOStream.md @@ -0,0 +1,15 @@ +# Juliet\src\Core\HAL\IO\Win32\Win32IOStream + +## Source Files +- Source: `Juliet\src\Core\HAL\IO\Win32\Win32IOStream.cpp` + +## AI Description +This C++ component implements a high-performance Win32 file I/O abstraction for the Juliet framework. It provides optimized read/write operations, seeks support, and memory management via an `IOStream` interface using native functions like CreateFileA and ReadFile on Windows systems. + +## Symbols + +### Namespace `Juliet::Internal::anonymous` + +#### Classes, Structs & Unions +- `struct Win32IOStreamDataPayload` + diff --git a/Romeo/docs/Juliet_src_Core_HAL_OS_Win32_Win32OS.md b/Romeo/docs/Juliet_src_Core_HAL_OS_Win32_Win32OS.md new file mode 100644 index 0000000..06c2778 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HAL_OS_Win32_Win32OS.md @@ -0,0 +1,11 @@ +# Juliet\src\Core\HAL\OS\Win32\Win32OS + +## Source Files +- Source: `Juliet\src\Core\HAL\OS\Win32\Win32OS.cpp` + +## AI Description +This Win32OS component provides a C++ OS abstraction layer for the Juliet framework. It implements memory management via VirtualAlloc/Free, includes exception handling and instance protection logic, initializes Windows networking functions to access RIO APIs, and registers its function table globally. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_HotReload_Win32_Win32HotReload.md b/Romeo/docs/Juliet_src_Core_HotReload_Win32_Win32HotReload.md new file mode 100644 index 0000000..ae843b3 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_HotReload_Win32_Win32HotReload.md @@ -0,0 +1,11 @@ +# Juliet\src\Core\HotReload\Win32\Win32HotReload + +## Source Files +- Source: `Juliet\src\Core\HotReload\Win32\Win32HotReload.cpp` + +## AI Description +This component implements Windows-specific hot-reloading logic for DLLs. It copies original DLLs to unique temporary files with atomic IDs and dynamically loads functions from them. The design tracks file modification times to detect updates, manages transient memory via scratch arenas, and supports reloading or unloading based on validity checks within the Juliet namespace architecture. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_Juliet.md b/Romeo/docs/Juliet_src_Core_Juliet.md new file mode 100644 index 0000000..93a2a75 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_Juliet.md @@ -0,0 +1,11 @@ +# Juliet\src\Core\Juliet + +## Source Files +- Source: `Juliet\src\Core\Juliet.cpp` + +## AI Description +This C++ component manages the initialization and shutdown of optional Juliet systems like displays using a reference-counting mechanism. It tracks system state via atomic counters to ensure safe lifecycle management, initializing subsystems on demand while preventing premature destruction during startup sequences. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Core_Math_MathRound.md b/Romeo/docs/Juliet_src_Core_Math_MathRound.md new file mode 100644 index 0000000..32f2769 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_Math_MathRound.md @@ -0,0 +1,15 @@ +# Juliet\src\Core\Math\MathRound + +## Source Files +- Source: `Juliet\src\Core\Math\MathRound.cpp` + +## AI Description +This C++ component provides a highly precise, single-precision floating-point rounding function that manipulates IEEE 754 bit patterns for optimal performance. It adapts to different float evaluation modes using consteval and avoids compiler optimizations by employing ForceEval alongside union reinterpretation of bits for exact arithmetic control. + +## Symbols + +### Namespace `Juliet::anonymous` + +#### Functions & Methods +- `constexpr float EPS = GetEps()` + diff --git a/Romeo/docs/Juliet_src_Core_Memory_MemoryArenaTests.md b/Romeo/docs/Juliet_src_Core_Memory_MemoryArenaTests.md new file mode 100644 index 0000000..92e85d8 --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_Memory_MemoryArenaTests.md @@ -0,0 +1,15 @@ +# Juliet\src\Core\Memory\MemoryArenaTests + +## Source Files +- Source: `Juliet\src\Core\Memory\MemoryArenaTests.cpp` + +## AI Description +This file contains unit tests for the C++ MemoryArena component, verifying its allocation of structured data and raw arrays. It checks core behaviors including arena position tracking, clearing, commit sizing policies based on reserved memory blocks, dynamic reallocation (growth/shrink), boundary management via `AllowRealloc`, and protection against buffer overflow during shrinking operations. + +## Symbols + +### Namespace `Juliet::UnitTest` + +#### Classes, Structs & Unions +- `struct TestStruct` + diff --git a/Romeo/docs/Juliet_src_Core_Networking_Win32_Win32SocketPlatformImpl.md b/Romeo/docs/Juliet_src_Core_Networking_Win32_Win32SocketPlatformImpl.md new file mode 100644 index 0000000..7cf08eb --- /dev/null +++ b/Romeo/docs/Juliet_src_Core_Networking_Win32_Win32SocketPlatformImpl.md @@ -0,0 +1,15 @@ +# Juliet\src\Core\Networking\Win32\Win32SocketPlatformImpl + +## Source Files +- Source: `Juliet\src\Core\Networking\Win32\Win32SocketPlatformImpl.cpp` + +## AI Description +The file implements Windows-specific socket functionality using Win32 APIs, including address conversion and error handling via WSAError codes. It utilizes a singleton `WSAAutoRelease` struct to ensure global initialization of the WinSock library within the process lifecycle through constructor execution on startup and destructor cleanup at exit. + +## Symbols + +### Namespace `Juliet` + +#### Classes, Structs & Unions +- `struct WSAAutoRelease` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_AgilitySDK_d3dx12_d3dx12_property_format_table.md b/Romeo/docs/Juliet_src_Graphics_D3D12_AgilitySDK_d3dx12_d3dx12_property_format_table.md new file mode 100644 index 0000000..428a39f --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_AgilitySDK_d3dx12_d3dx12_property_format_table.md @@ -0,0 +1,132 @@ +# Juliet\src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table.h` +- Source: `Juliet\src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table.cpp` + +## AI Description +This code snippet appears to be part of a **DirectX 12 (D3D12) runtime source file**, specifically defining internal data structures used for **format casting** and **property layout analysis**. It looks like it was extracted from `d3d12core.cpp` or similar, where the compiler generates tables to handle format conversions efficiently. + +Here is a breakdown of what each section does: + +### 1. Cast Sets (`YOUR_BC7`, etc.) +These arrays define groups of related formats that can be cast to one another without data conversion (e.g., converting `DXGI_FORMAT_R8G8B8A8_UNORM` to its typeless variant is usually free). +* **Purpose:** They allow the hardware or shader compiler to perform fast pointer arithmetic or register mapping between similar formats. +* **The "Null Terminator":** Every array ends with `{ DXGI_FORMAT_UNKNOWN, ... }`. This acts as a sentinel value (similar to `\0` in C strings) so that when searching for a specific format within this list using loops or binary search algorithms defined elsewhere in the file, the loop knows exactly where the valid data stops. + +### 2. Property Layout Table (`s_FormatDetail[]`) +This is the most complex part of your snippet. It defines how individual formats are interpreted by the D3D12 runtime regarding alignment, storage size, and component semantics. + +Each object in `s_FormatDetail` contains: +* **Format Info:** The specific format (e.g., `R32G32B32A32_TYPELESS`). +* **Parent Format:** Which "group" or cast set this belongs to. +* **Default Cast Set ID:** Used for mapping conversions. +* **BitsPerComponent:** How many bits each channel uses (e.g., `32` for float types). +* **Alignment Values (`Width/Height/DepthAlignment`):** Crucial for D12 textures. These determine how the GPU aligns pixel data in memory to optimize bandwidth and SIMD performance. For example, 4x8 alignment is often required for high-precision floats on specific hardware. +* **Flags:** Boolean flags indicating if it uses DX9 Vertex/Index formats (`bDX9...`), Planar format support (like YUV or NV12 which split RGB channels into planes), Float normalization behavior, etc. + +#### Key Flags Explained in your snippet: +* `D3DFL_STANDARD`: Indicates a standard linear texture layout (no special packing). +* `_FLOAT`, `_UINT`, `_SINT` vs `_TYPELESS`: Defines the actual data type expected when casting from that specific format to a generic "typeless" wrapper. This helps shader compilers determine how many registers are needed for an operation like `Add`. +* **YUV/Planar Formats:** You see entries related to Y410, NV12, etc., where flags like `bPlanar` might be set (though explicitly in the snippet provided is tricky without seeing every flag, usually these have specific width alignments due to interleaving of Luma and Chroma). + +### 3. Truncated Data +The last block starts with: +```cpp + { + DXGI_FORMAT_R32G32B_TYPELESS, // Note: Snippet cuts off here/next line + ... +``` +This indicates the list continues for formats like `R16`, `RG16A8_UNORM` (which is often aliased under typeless prefixes in D3D9 headers), and other internal core formats. + +### Contextual Observation +The inclusion of `{ DXGI_FORMAT_UNKNOWN }` at the end of these arrays suggests this code is likely generated or maintained to support **backward compatibility** with DX10/DX11 semantics while ensuring robustness against invalid format lookups in a dynamic environment (like creating shaders on-the-fly). + +If you are modifying this for game development, typically: +1. You would not edit these arrays unless working directly on the source of `dxgi_core.h` or the D3D runtime headers (as changing them affects how Windows maps your textures to GPU hardware registers globally). +2. The alignment values (`WidthAlignment`) are highly specific to the **DXGIDirectCompute** implementation for that specific generation; they often dictate whether a texture fits in SRAM, L1 Cache, or requires more expensive memory transactions when accessed via shaders. + +Are you trying to understand how format casting works here? Or do you need help calculating alignment requirements based on these flags? + +## Symbols + +### Global Scope + +#### Classes, Structs & Unions +- `struct D3D12_PROPERTY_LAYOUT_FORMAT_TABLE` +- `struct D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::FORMAT_DETAIL` + +#### Functions & Methods +- `// separate from above structure so it can be compiled out of runtime. +public: + static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetNumFormats()` +- `static const FORMAT_DETAIL* D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetFormatTable()` +- `static D3D_FEATURE_LEVEL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetHighestDefinedFeatureLevel()` +- `static DXGI_FORMAT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetFormat(SIZE_T Index)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::FormatExists(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::FormatExistsInHeader(DXGI_FORMAT Format, bool bExternalHeader = true)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetByteAlignment(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::IsBlockCompressFormat(DXGI_FORMAT Format)` +- `static LPCSTR D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetName(DXGI_FORMAT Format, bool bHideInternalFormats = true)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::IsSRGBFormat(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetBitsPerStencil(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetBitsPerDepth(DXGI_FORMAT Format)` +- `static void D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetFormatReturnTypes(DXGI_FORMAT Format, D3D_FORMAT_COMPONENT_INTERPRETATION* pInterpretations)` +- `// return array of 4 components + static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetNumComponentsInFormat(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetMinNumComponentsInFormats(DXGI_FORMAT FormatA, DXGI_FORMAT FormatB)` +- `// Converts the sequential component index (range from 0 to GetNumComponentsInFormat()) to + // the absolute component index (range 0 to 3). + static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::Sequential2AbsoluteComponentIndex(DXGI_FORMAT Format, UINT SequentialComponentIndex)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::CanBeCastEvenFullyTyped(DXGI_FORMAT Format, D3D_FEATURE_LEVEL fl)` +- `static UINT8 D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetAddressingBitsPerAlignedSize(DXGI_FORMAT Format)` +- `static DXGI_FORMAT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetParentFormat(DXGI_FORMAT Format)` +- `static const DXGI_FORMAT* D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetFormatCastSet(DXGI_FORMAT Format)` +- `static D3D_FORMAT_LAYOUT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetLayout(DXGI_FORMAT Format)` +- `static D3D_FORMAT_TYPE_LEVEL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetTypeLevel(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetBitsPerUnit(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetBitsPerUnitThrow(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetBitsPerElement(DXGI_FORMAT Format)` +- `// Legacy function used to support D3D10on9 only. Do not use. + static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetWidthAlignment(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetHeightAlignment(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetDepthAlignment(DXGI_FORMAT Format)` +- `static BOOL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::Planar(DXGI_FORMAT Format)` +- `static BOOL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::NonOpaquePlanar(DXGI_FORMAT Format)` +- `static BOOL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::YUV(DXGI_FORMAT Format)` +- `static BOOL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::Opaque(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::FamilySupportsStencil(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::NonOpaquePlaneCount(DXGI_FORMAT Format)` +- `static BOOL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::DX9VertexOrIndexFormat(DXGI_FORMAT Format)` +- `static BOOL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::DX9TextureFormat(DXGI_FORMAT Format)` +- `static BOOL D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::FloatNormTextureFormat(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::DepthOnlyFormat(DXGI_FORMAT format)` +- `static UINT8 D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetPlaneCount(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::MotionEstimatorAllowedInputFormat(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::SupportsSamplerFeedback(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::DecodeHistogramAllowedForOutputFormatSupport(DXGI_FORMAT Format)` +- `static UINT8 D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetPlaneSliceFromViewFormat(DXGI_FORMAT ResourceFormat, DXGI_FORMAT ViewFormat)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::FloatAndNotFloatFormats(DXGI_FORMAT FormatA, DXGI_FORMAT FormatB)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::SNORMAndUNORMFormats(DXGI_FORMAT FormatA, DXGI_FORMAT FormatB)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::ValidCastToR32UAV(DXGI_FORMAT from, DXGI_FORMAT to)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::IsSupportedTextureDisplayableFormat(DXGI_FORMAT, bool bMediaFormatOnly)` +- `static D3D_FORMAT_COMPONENT_INTERPRETATION D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetFormatComponentInterpretation(DXGI_FORMAT Format, UINT AbsoluteComponentIndex)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetBitsPerComponent(DXGI_FORMAT Format, UINT AbsoluteComponentIndex)` +- `static D3D_FORMAT_COMPONENT_NAME D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetComponentName(DXGI_FORMAT Format, UINT AbsoluteComponentIndex)` +- `static HRESULT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::CalculateExtraPlanarRows(DXGI_FORMAT format, UINT plane0Height, _Out_ UINT& totalHeight)` +- `static HRESULT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::CalculateMinimumRowMajorRowPitch(DXGI_FORMAT Format, UINT Width, _Out_ UINT& RowPitch)` +- `static HRESULT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::CalculateMinimumRowMajorSlicePitch(DXGI_FORMAT Format, UINT ContextBasedRowPitch, UINT Height, _Out_ UINT& SlicePitch)` +- `static void D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetYCbCrChromaSubsampling(DXGI_FORMAT Format, _Out_ UINT& HorizontalSubsampling, _Out_ UINT& VerticalSubsampling)` +- `static HRESULT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::CalculateResourceSize(UINT width, UINT height, UINT depth, DXGI_FORMAT format, UINT mipLevels, UINT subresources, _Out_ SIZE_T& totalByteSize, _Out_writes_opt_(subresources) D3D12_MEMCPY_DEST* pDst = nullptr)` +- `static void D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetTileShape(D3D12_TILE_SHAPE* pTileShape, DXGI_FORMAT Format, D3D12_RESOURCE_DIMENSION Dimension, UINT SampleCount)` +- `static void D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::Get4KTileShape(D3D12_TILE_SHAPE* pTileShape, DXGI_FORMAT Format, D3D12_RESOURCE_DIMENSION Dimension, UINT SampleCount)` +- `static void D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetMipDimensions(UINT8 mipSlice, _Inout_ UINT64* pWidth, _Inout_opt_ UINT64* pHeight = nullptr, _Inout_opt_ UINT64* pDepth = nullptr)` +- `static void D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetPlaneSubsampledSizeAndFormatForCopyableLayout(UINT PlaneSlice, DXGI_FORMAT Format, UINT Width, UINT Height, _Out_ DXGI_FORMAT& PlaneFormat, _Out_ UINT& MinPlanePitchWidth, _Out_ UINT& PlaneWidth, _Out_ UINT& PlaneHeight)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetDetailTableIndex(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetDetailTableIndexNoThrow(DXGI_FORMAT Format)` +- `static UINT D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetDetailTableIndexThrow(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::SupportsDepth(DXGI_FORMAT Format)` +- `static bool D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::SupportsStencil(DXGI_FORMAT Format)` +- `private: + static const FORMAT_DETAIL* D3D12_PROPERTY_LAYOUT_FORMAT_TABLE::GetFormatDetail(DXGI_FORMAT Format)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12CommandList.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12CommandList.md new file mode 100644 index 0000000..bf2cd53 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12CommandList.md @@ -0,0 +1,42 @@ +# Juliet\src\Graphics\D3D12\D3D12CommandList + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12CommandList.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12CommandList.cpp` + +## AI Description +*No AI description generated yet.* + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Classes, Structs & Unions +- `struct D3D12CommandListBaseData` +- `struct D3D12CopyCommandListData` +- `struct D3D12GraphicsCommandListData` +- `struct D3D12PresentData` +- `struct D3D12CommandList` + +#### Functions & Methods +- `extern CommandList* AcquireCommandList(NonNullPtr driver, QueueType queueType)` +- `extern bool SubmitCommandLists(NonNullPtr commandList)` +- `extern void SetViewPort(NonNullPtr commandList, const GraphicsViewPort& viewPort)` +- `extern void SetScissorRect(NonNullPtr commandList, const Rectangle& rectangle)` +- `extern void SetBlendConstants(NonNullPtr commandList, FColor blendConstants)` +- `extern void SetBlendConstants(NonNullPtr commandList, FColor blendConstants)` +- `extern void SetStencilReference(NonNullPtr commandList, uint8 reference)` +- `extern void SetIndexBuffer(NonNullPtr commandList, NonNullPtr buffer, + IndexFormat format, size_t indexCount, index_t offset)` +- `extern void SetPushConstants(NonNullPtr commandList, ShaderStage stage, uint32 rootParameterIndex, + uint32 numConstants, const void* constants)` + +### Namespace `Juliet::D3D12::Internal` + +#### Functions & Methods +- `extern void SetDescriptorHeaps(NonNullPtr commandList)` +- `extern void DestroyCommandList(NonNullPtr commandList)` +- `extern bool CleanCommandList(NonNullPtr driver, NonNullPtr commandList, bool cancel)` +- `extern void TrackGraphicsPipeline(NonNullPtr commandList, NonNullPtr pipeline)` +- `extern void TrackTexture(NonNullPtr commandList, NonNullPtr texture)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Common.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Common.md new file mode 100644 index 0000000..4bc6dd7 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Common.md @@ -0,0 +1,26 @@ +# Juliet\src\Graphics\D3D12\D3D12Common + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12Common.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12Common.cpp` + +## AI Description +The `Juliet::D3D12` module manages DirectStorage 12 descriptor pools and CPU-side handle indexing. It provides thread-safe mechanisms for allocating, assigning, and releasing resource handles across different heap types (RTV, DSV, SRV) to optimize GPU memory access patterns within the Juliet graphics engine. + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Classes, Structs & Unions +- `struct D3D12StagingDescriptorPool` +- `struct D3D12StagingDescriptor` + +### Namespace `Juliet::D3D12::Internal` + +#### Functions & Methods +- `extern D3D12StagingDescriptorPool* CreateStagingDescriptorPool(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type)` +- `extern bool AssignStagingDescriptor(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type, + D3D12StagingDescriptor& outDescriptor)` +- `extern void ReleaseStagingDescriptor(NonNullPtr driver, D3D12StagingDescriptor& cpuDescriptor)` +- `extern void DestroyStagingDescriptorPool(NonNullPtr pool)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12DescriptorHeap.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12DescriptorHeap.md new file mode 100644 index 0000000..92b22a7 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12DescriptorHeap.md @@ -0,0 +1,30 @@ +# Juliet\src\Graphics\D3D12\D3D12DescriptorHeap + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12DescriptorHeap.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12DescriptorHeap.cpp` + +## AI Description +This C++ component manages DirectX 12 descriptor heaps for efficient GPU resource binding. It provides structures and functions to create pools of reusable descriptor stacks, handle allocation from free lists or creation via the driver, and release resources back to memory while tracking usage indices. The design includes internal arena management and type-specific initialization flags like shader visibility to optimize performance in gaming contexts within a larger framework architecture. + +## Symbols + +### Namespace `Juliet::D3D12::Internal` + +#### Classes, Structs & Unions +- `struct D3D12DescriptorHeap` +- `struct D3D12Descriptor` +- `struct D3D12DescriptorHeapPool` + +#### Functions & Methods +- `extern void CreateDescriptorHeapPool(NonNullPtr driver, D3D12DescriptorHeapPool& heapPool, + D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count)` +- `extern void DestroyDescriptorHeapPool(D3D12DescriptorHeapPool& pool)` +- `extern D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr driver, NonNullPtr arena, + D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging)` +- `extern void DestroyDescriptorHeap(NonNullPtr heap)` +- `extern D3D12DescriptorHeap* AcquireSamplerHeapFromPool(NonNullPtr d3d12Driver)` +- `extern void ReturnSamplerHeapToPool(NonNullPtr d3d12Driver, NonNullPtr heap)` +- `extern bool AssignDescriptor(D3D12DescriptorHeap* heap, D3D12Descriptor& outDescriptor)` +- `extern void ReleaseDescriptor(const D3D12Descriptor& descriptor)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsDevice.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsDevice.md new file mode 100644 index 0000000..da4f736 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsDevice.md @@ -0,0 +1,79 @@ +# Juliet\src\Graphics\D3D12\D3D12GraphicsDevice + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12GraphicsDevice.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12GraphicsDevice.cpp` + +## AI Description +Based on the code snippet you provided and its context (likely a framework like **Juliet** or a similar cross-platform DirectX 12 backend), here is an analysis of what this block does. + +### Code Context: Debug Initialization for Windows/Win32 +This section initializes various layers of the Microsoft DX12 debugging infrastructure. It attempts to enable debug capabilities, register callbacks for logging errors, and configure break conditions if something goes wrong (like memory corruption). This usually runs during the driver/device creation phase. + +### Key Components Analyzed + +#### 1. DXGI Debug Interface (`InitializeDXGIDebug`) +* **Purpose**: Loads the external `dxgidebug.dll` to access low-level graphics debugging interfaces available only on certain Windows versions (Win 10/11). +* **Interfaces Accessed**: + * `IDXGIGetDebugInterface`: Used primarily for getting interface pointers dynamically. + * `IDXGIDebug`: Provides tools like `ReportLiveObjects` to help developers identify leaks or memory usage at runtime. + * `IDXGIInfoQueue`: Manages message queues from the graphics pipeline. +* **Configuration**: It explicitly sets break conditions for **Errors**, **Corruption**, and **Warnings**. This means if a GPU driver error occurs (crash-inducing), the debugger will pause execution to aid investigation. + +#### 2. D3D12 Debug Layer (`InitializeD3D12DebugLayer`) +* **Purpose**: Enables the official "DirectX 12 Diagnostic Library" debug layer, which adds significant instrumentation overhead but provides detailed trace information about shader compilation and state management. +* **Platform Note**: While often available on Win 10/11, some versions of this function call might fail depending on specific driver configurations or OS builds (indicated by the warning log if it fails). + +#### 3. D3D12 Info Queue (`InitializeD3D12DebugInfoQueue`) +* **Purpose**: Configures `ID3D12InfoQueue` to filter which messages are passed up to application logic. +* **Filtering**: It currently only pushes `INFO` severity messages (though the commented-out line suggests `CORRUPTION` was considered but not enabled yet). This prevents log spam while keeping critical info. + +#### 4. Message Callback Registration (`OnD3D12DebugInfoMsg`) ? Logger +* **Mechanism**: If using a newer API level (`ID3D12InfoQueue1`), it registers a custom callback `OnD3D12DebugInfoMsg`. +* **Processing Logic**: + * It translates internal Microsoft enum IDs (like `STATE_CREATION`, `CORRUPTION`) into human-readable strings. + * It logs these messages via the framework's logging system (`LogWarning` or `LogError`). + * The logic checks severity: If it is an **ERROR** or **CORRUPTION**, it uses high-priority warnings; otherwise, standard warnings/info. + +#### 5. Error Handling ? Cleanup (`ShutdownDXGIDebug`) +* Ensures proper cleanup of loaded DLLs and released COM objects to prevent memory leaks in the debugger itself when shutting down (though this function is defined here, execution happens elsewhere). + +### Potential Issues or Observations in Your Snippet + +1. **Incomplete Line**: The snippet cuts off at `Log(LogLevel::Message, Log`. You likely need to close the statement with a category string and closing parenthesis: + ```cpp + // ... inside InitializeD3D12DebugInfoLogger + driver-?D3D12Device); + } catch (...) { /* handle exception if necessary */ } + Log(LogLevel::Message, LogCategory::Graphics, "Debug info logger initialized"); + #endif + ``` + +2. **Conditional Compilation**: Notice the `#if JULIET_DEBUG` macro. This entire block will be compiled out unless DEBUG is defined. Ensure this flag matches your project's debug settings correctly so runtime checks actually execute. + +3. **Platform Restrictions**: + * `dxgidebug.dll` loading fails on older Windows versions (e.g., Win7) or specific virtualized environments without the necessary graphics stack support. The code handles failure gracefully (`if (FAILED(result))`). + * `ID3D12InfoQueue1` and message callbacks are relatively new; if you target very old GPUs/driver stacks, this might return early silently due to failed queries. + +### How to Use/Verify This Code + +* **To Enable Debugging**: Set the build configuration flag (e.g., `JULIET_DEBUG=TRUE`). +* **Verification**: Run your application under a debugger (WinDbg or Visual Studio) attached while initializing these components. You should see messages printed immediately about loading DLLs and registering callbacks if successful, followed by any warnings/errors generated during D3D12 device creation from the snippets you posted earlier. +* **Testing Crash Recovery**: Trigger a GPU error manually in your app (e.g., access out-of-bounds memory) while this debug layer is active. The application should stop execution and allow inspection via `ReportLiveObjects` if configured correctly with IDXGIDebug, or simply log the corruption message before crashing depending on break settings. + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Classes, Structs & Unions +- `struct D3D12WindowData` +- `struct D3D12Driver` + +#### Enums +- `enum class RootParameters` + +### Namespace `Juliet::D3D12::Internal` + +#### Functions & Methods +- `void DisposePendingResourcces(NonNullPtr driver)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsPipeline.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsPipeline.md new file mode 100644 index 0000000..8080a8b --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12GraphicsPipeline.md @@ -0,0 +1,55 @@ +# Juliet\src\Graphics\D3D12\D3D12GraphicsPipeline + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12GraphicsPipeline.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12GraphicsPipeline.cpp` + +## AI Description +Based on the code snippet provided, here is a summary of its functionality and analysis: + +### **Purpose** +The primary function defined at the bottom (`GraphicsPipeline::Create`) creates a DirectX 12 Graphics Pipeline State Object (PSO) from high-level rendering states. It acts as an adapter layer, converting Juliet's internal data structures into `D3D12_GRAPHICS_PIPELINE_STATE_DESC` and calling the hardware-accelerated `ID3D12Device::CreateGraphicsPipelineState`. + +### **Key Workflow Steps** +1. **Initialization**: Retrieves D3D12 driver handles (vertex/fragment shaders). Initializes a local structure (`psoDesc`) with shader bytecode pointers and lengths. +2. **Input Conversion**: Converts `VertexInputState` into `INPUT_ELEMENT_DESCs` if attributes exist, defining the layout of vertex data. +3. **Topology ? Primitive Setup**: Sets the primitive topology (e.g., lines, triangles) using mapping tables (`JulietToD3D12_...`). +4. **Helper Calls**: Relies on three conversion helper functions to populate state descriptors: + * `ConvertRasterizerState`: Handles culling, fill mode, etc. + * `ConvertBlendState`: Maps blend factors and write masks for color/alpha channels (supports multiple render targets). + * `ConvertDepthStencilState`: Converts depth comparison and stencil operations (front/back faces). +5. **Multisample Handling**: Calculates sample mask, count, and quality level based on the creation info (`createInfo.MultisampleState`). It sets a standard multi-sampling quality pattern if not manually specified. +6. **Format Conversion**: Translates Juliet's color formats into `D3D12` compatible RTV/DSV formats (handling both depth textures and color render targets). +7. **Root Signature Association**: Uses the driver's pre-compiled "Bindless" root signature to allow flexible state variables in shaders without explicit parameter declaration in vertex structures. +8. **Hardware Creation (`CreateGraphicsPipelineState`)**: The core call that returns an `ID3D12PipelineState`. This is where the actual pipeline logic lives on the GPU side. +9. **Error Handling**: Checks for hardware failures and logs errors to `dual-OS::Log`, cleaning up allocated resources upon failure. + +### **Code Specifics ? Observations** +* **Mapping Strategy**: Extensive use of helper arrays like `JulietToD3D12_BlendFactor` and mapping functions like `ToUnderlying`. This suggests a design pattern where high-level enums are mapped to D3D's underlying integer/enum values (often 0-7 or specific constants) via tables, allowing the same logic to run across different rendering backends. +* **Independent Blending**: The code checks if there is more than one color target (`if (i ? 0)`), enabling "independent blend enable." This allows two separate sprites to be drawn on top of each other with independent blending settings, which requires the `D3D12_BLEND_ENABLE_INDEPENDENT_blend` flag in D3D12. +* **Sample Quality Heuristic**: The sample quality is set to a default standard if it exceeds single-sample count; otherwise, custom values might be required by specific games (like older PS/DS titles). +* **Unfinished Line**: The snippet cuts off mid-line at the very end (`pipeline-?PrimitiveType = createInfo.`), indicating incomplete logic for copying other pipeline properties or error states. + +### **Potential Improvements / Questions** +1. **Caching**: Does this code handle GPU caching (using `D3DKBCache`)? Currently, it looks like every time a new PSO is requested, the hardware creates a fresh one unless explicitly told otherwise via `CachedPSO`. For modern games with many unique draw calls, explicit caching logic would improve performance. +2. **Static Pipeline States**: Modern D3D12 workflows often move state creation to a separate static pipeline class or function (static PSOs) rather than creating them on-the-fly per scene update if the states don't change between frames. This code seems to be doing dynamic creation, which is simpler but potentially less performant for complex scenes with many variations of blending/rasterization settings per frame in older engines like Juliet's era. + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Classes, Structs & Unions +- `struct D3D12GraphicsRootSignature` +- `struct D3D12GraphicsPipeline` + +#### Functions & Methods +- `extern GraphicsPipeline* CreateGraphicsPipeline(NonNullPtr driver, const GraphicsPipelineCreateInfo& createInfo)` +- `extern void DestroyGraphicsPipeline(NonNullPtr driver, NonNullPtr graphicsPipeline)` +- `extern bool UpdateGraphicsPipelineShaders(NonNullPtr driver, NonNullPtr graphicsPipeline, + Shader* optional_vertexShader, Shader* optional_fragmentShader)` + +### Namespace `Juliet::D3D12::Internal` + +#### Functions & Methods +- `extern void ReleaseGraphicsPipeline(NonNullPtr d3d12GraphicsPipeline)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12InternalTests.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12InternalTests.md new file mode 100644 index 0000000..df16a9f --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12InternalTests.md @@ -0,0 +1,12 @@ +# Juliet\src\Graphics\D3D12\D3D12InternalTests + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12InternalTests.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12InternalTests.cpp` + +## AI Description +This C++ component provides D3D12 unit test utilities, including allocation management and descriptor heap testing. Designed exclusively for debug builds via JULIET_DEBUG flags, it isolates verification logic to ensure graphics system integrity without impacting production performance or runtime efficiency. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12RenderPass.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12RenderPass.md new file mode 100644 index 0000000..4657fdb --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12RenderPass.md @@ -0,0 +1,23 @@ +# Juliet\src\Graphics\D3D12\D3D12RenderPass + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12RenderPass.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12RenderPass.cpp` + +## AI Description +The D3D12RenderPass module bridges the Juliet rendering framework with DirectX 12. It initializes render targets and depth states via BeginRenderPass, manages pipeline binding through BindGraphicsPipeline, handles primitive drawing operations, and performs post-render cleanup in EndRenderPass to reset command list resources for reuse. + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Functions & Methods +- `extern void BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, + uint32 colorTargetInfoCount, const DepthStencilTargetInfo* depthStencilTargetInfo)` +- `extern void EndRenderPass(NonNullPtr commandList)` +- `extern void BindGraphicsPipeline(NonNullPtr commandList, NonNullPtr graphicsPipeline)` +- `extern void DrawPrimitives(NonNullPtr commandList, uint32 numVertices, uint32 numInstances, + uint32 firstVertex, uint32 firstInstance)` +- `void DrawIndexedPrimitives(NonNullPtr commandList, uint32 numIndices, uint32 numInstances, + uint32 firstIndex, uint32 vertexOffset, uint32 firstInstance)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Shader.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Shader.md new file mode 100644 index 0000000..4b90b94 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Shader.md @@ -0,0 +1,20 @@ +# Juliet\src\Graphics\D3D12\D3D12Shader + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12Shader.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12Shader.cpp` + +## AI Description +This D3D12 header defines a structure and factory functions for creating GPU shaders in the Juliet engine. The source allocates memory dynamically to hold shader byte code, returning it as an abstract Shader pointer while managing internal buffer stats like sampler counts within the concrete struct. + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Classes, Structs & Unions +- `struct D3D12Shader` + +#### Functions & Methods +- `extern Shader* CreateShader(NonNullPtr driver, ByteBuffer shaderByteCode, ShaderCreateInfo& shaderCreateInfo)` +- `extern void DestroyShader(NonNullPtr driver, NonNullPtr shader)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12SwapChain.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12SwapChain.md new file mode 100644 index 0000000..f4ef2e0 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12SwapChain.md @@ -0,0 +1,147 @@ +# Juliet\src\Graphics\D3D12\D3D12SwapChain + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12SwapChain.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12SwapChain.cpp` + +## AI Description +Based on the C++ code provided, here is a structured analysis of the D3D12 Swap Chain implementation within what appears to be a game engine or graphics library (likely "Juliet"). + +### 1. Core Functionality Summary +The module handles: +* **Swap Chain Creation:** Initializing DXGI swap chains with HDR support options and frame rate control. +* **Texture Management:** Managing the backing D3D12 textures for each back buffer, transitioning states between Presentation (`PRESENT`) and Rendering (`RENDER_TARGET`). +* **Fence Synchronization:** Ensuring previous frames are finished before starting new ones to prevent tearing or accessing dirty buffers during rendering. + +--- + +### 2. Detailed Code Analysis by Function + +#### A. State Transition ? Acquisition (`AcquireSwapChainTexture` / `WaitAndAcquire`) +These functions manage the lifecycle of individual back buffer textures. + +```cpp +// ... inside AcquireSwapChainTexture logic +barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; +barrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; +barrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; // Source state (from GPU) +barrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; // Destination state (for CPU/GPU read/write) + +// The transition barrier is issued on the command list. +d3d12CommandList-?GraphicsCommandList.CommandList-?ResourceBarrier(1, ?barrierDesc); + +*swapchainTexture = reinterpret_cast?Texture*?(?windowData-?SwapChainTextureContainers[...].ActiveTexture); // Returns a pointer wrapper to access TextureDetails struct directly. +``` +**Key Insight:** The code explicitly transitions the resource from `PRESENT` (owned by DXGI) back to `RENDER_TARGET`. This is standard practice because: +1. Rendering commands (`Draw`, etc.) typically operate on `RENDER_TARGET` or `COPY_SRC/DST` states depending on context, but transitioning immediately allows for faster access without additional fences if the renderer supports it. +2. It prepares the texture to be read by CPU (if using staging buffers) and written to again in subsequent frame passes via copy commands (`ResourceBarrier` with `D3D12_RESOURCE_BARRIER_FLAG_ALLOW_VISUAL_LOSS_DISCARD`). + +#### B. Synchronization Logic (`WaitForSwapchain`) +Before any command list is executed on a new back buffer, this function ensures the previous frame has finished encoding and presenting. + +```cpp +if (windowData-?InFlightFences[windowData-?WindowFrameCounter] != nullptr) { + if (!Wait(d3d12Driver, true, ?windowData-?InFlightFences[...], 1 JULIET_DEBUG_PARAM(...))) return false; +} +return true; +``` +**Key Insight:** This checks the fence associated with the specific frame being processed. If a fence exists (meaning previous work is pending), it waits for that fence to signal completion before proceeding. + +#### C. Format Resolution ? Metadata (`GetSwapChainTextureFormat`) +This helper determines the pixel format used by the swap chain, which is crucial for shader compilation and resource generation. + +```cpp +// Returns TextureFormat enum derived from DXGI_FORMAT of active buffer +auto* d3d12Driver = static_cast?D3D12Driver*?(driver.Get()); +return windowData-?SwapChainTextureContainers[windowData-?WindowFrameCounter].Header.CreateInfo.Format; +``` +**Key Insight:** It accesses the `CreateInfo` header stored within the texture container to ensure consistent format handling throughout the engine, rather than querying DXGI directly every time. + +#### D. Swap Chain Creation (`Internal::CreateSwapChain`) +This is the heavy lifter that interfaces with Windows API and DirectX 12 factories. Notable features: + +**1. Configuration:** +* **Resolution:** Uses `0` for width/height, implying full screen usage where the factory determines size based on window client area. +* **Present Mode ? Composition:** Supports SDR (`BG8`) or HDR via a mapping table `SwapchainCompositionToColorSpace`. It dynamically sets color space and format (e.g., RGB10A2 for HDR). +* **Tearing Support:** Checks `driver-?IsTearingSupported` to set the `DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING` flag if present, preventing frame skips when V-Sync is off. + +**2. Factory Interactions ? WMA (Windows Meta Association):** +```cpp +// Queries for IDXGISwapChain3 interface needed for HDR settings and specific behaviors. +IDXGISwapChain3* swapChain3 = nullptr; +... +swapChain-?QueryInterface(IID_IDXGISwapChain3, ...); // Note: The original CreateSwapChainForHwnd returns a 1 pointer usually, but here it queries then releases the old one? +// Correction in logic analysis: The code does 'query', assigns to swapChain3 variable, and calls Release on swapChain immediately. This implies swapChain returned by QueryInterface was temporary or they are managing two pointers (original + queried) briefly before swapping roles. + +if (!parentFactory-?MakeWindowAssociation(windowHandle, DXGI_MWA_NO_WINDOW_CHANGES)) + Log(LogLevel::Warning, ... "Cannot handle window resizing"); // IMPORTANT: Disables automatic resize messages to D3D12 logic? +``` + +**Key Insight on WMA:** The code calls `DXGI_MWA_NO_WINDOW_CHANGES`. This tells the system **not** to send a `WM_SIZE` message when the user resizes or moves the window. +* *Why?* If the app handles resizing by simply calling `Resize` functions internally without checking Win32 events, this is an optimization (avoiding extra logic). However, if dynamic re-resizing is needed externally, this needs to be reconsidered based on how else size changes are handled in your engine. + +**3. HDR Setup:** +It specifically sets the color space only for `SwapChainComposition::HDR` or other non-SDR types: +```cpp +if (composition != SwapChainComposition::SDR) { + swapChain3-?SetColorSpace1(SwapchainCompositionToColorSpace[...]); +} +``` + +--- + +### 3. Critical Logic Check ? Observations + +#### 🚨 Potential Issue in `CreateSwapChain` Interface Handling +There is a slight logical flow quirk regarding the Swap Chain pointer assignment: + +```cpp +IDXGISwapChain1* swapChain = nullptr; +// Creates chain, returns as IDXGISwapChain1 (or higher if capabilities met) +HRESULT result = driver-?DXGIFactory-?CreateSwapChainForHwnd(..., ?swapChain); + +// Queries for the specific interface immediately after creation +result = swapChain-?QueryInterface(IID_IDXGISwapChain3, reinterpret_cast?void**?(?swapChain3)); +swapChain-?Release(); // ?--- Releases the original pointer passed by reference? +if (FAILED(result)) { ... } +``` +* **Behavior:** `CreateSwapChainForHwnd` creates an interface and returns it. The caller is expected to release *one* copy of that interface eventually. By calling `QueryInterface`, you create a new handle (`swapChain3`). If the original return value from Create doesn't support QueryInterface directly (unlikely) or if memory management isn't strict, releasing via Release() on the temporary variable might be premature depending on D3D12 ownership rules here. +* **Standard Pattern:** Usually: `Create...(?SwapChain)`, then use it until done with all interfaces required for HDR/Feature queries before doing cleanup logic in other parts of your engine's destroy sequence (like `DestroySwapChain`). + +#### 🚨 Memory Management ? Cleanup (`Internal::DestroySwapChain`) +The destructor releases staging resources and frees D3D12 texture objects manually. This indicates a custom resource pool system rather than just relying on DXGI to clean up everything at shutdown, which is good for performance profiling but requires careful `Free` implementations (likely global Free lists common in Rust/Juliet ecosystems). + +#### 📝 Resource Barrier Nuance +In the transition block: +```cpp +barrierDesc.Transition.pResource = ...; // Pointer math looks correct based on struct layout. +barrierDesc.Transition.Subresource = 0; +// Note: Since this is not a multi-sub-resource texture (like video textures), Subresource=0 covers everything. +``` + +### 4. Recommendations / Improvements + +1. **Validate WMA Logic:** Ensure `DXGI_MWA_NO_WINDOW_CHANGES` doesn't break dynamic resolution support in your game engine. If the window moves/resizes but you don't update texture dimensions, it could cause black screens or crashes upon resize. +2. **Interface Management:** The immediate release of the original Swap Chain pointer after acquiring `IDXGISwapChain3` looks unusual if the engine expects to keep using generic features later without re-acquisition every time (e.g., in a destructor). Consider storing the generic interface and only converting up/down when HDR is specifically accessed or during teardown. +3. **Error Handling:** The `CreateSwapChainForHwnd` call checks for failure, but if it fails due to invalid window handle (`IsWindow` check passed earlier), ensure robust logging regarding *why* (e.g., focus lost, incompatible OS version). + +### Conclusion +This is a well-structured implementation of D3D12 Swap Chain logic tailored for an engine that likely supports HDR and custom resolution scaling. The explicit fence waiting and manual state transitions indicate high control over the rendering pipeline to ensure frame timing consistency and prevent visual + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Functions & Methods +- `extern bool AcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture)` +- `extern bool WaitAndAcquireSwapChainTexture(NonNullPtr commandList, NonNullPtr window, Texture** swapChainTexture)` +- `extern bool WaitForSwapchain(NonNullPtr driver, NonNullPtr window)` +- `extern TextureFormat GetSwapChainTextureFormat(NonNullPtr driver, NonNullPtr window)` + +### Namespace `Juliet::D3D12::Internal` + +#### Functions & Methods +- `extern bool CreateSwapChain(NonNullPtr driver, NonNullPtr windowData, + SwapChainComposition composition, PresentMode presentMode)` +- `extern void DestroySwapChain(NonNullPtr driver, NonNullPtr windowData)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Synchronization.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Synchronization.md new file mode 100644 index 0000000..d362373 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Synchronization.md @@ -0,0 +1,32 @@ +# Juliet\src\Graphics\D3D12\D3D12Synchronization + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12Synchronization.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12Synchronization.cpp` + +## AI Description +*No AI description generated yet.* + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Classes, Structs & Unions +- `struct D3D12Fence` + +#### Functions & Methods +- `extern bool WaitUntilGPUIsIdle(NonNullPtr driver)` +- `extern bool Wait(NonNullPtr driver, bool waitForAll, Fence* const* fences, + uint32 numFences JULIET_DEBUG_PARAM(String querier))` +- `extern bool QueryFence(NonNullPtr driver, NonNullPtr fence)` +- `extern void ReleaseFence(NonNullPtr driver, NonNullPtr fence JULIET_DEBUG_PARAM(String querier))` + +### Namespace `Juliet::D3D12::Internal` + +#### Functions & Methods +- `extern void ResourceBarrier(NonNullPtr commandList, D3D12_RESOURCE_STATES sourceState, + D3D12_RESOURCE_STATES destinationState, ID3D12Resource* resource, + uint32 subresourceIndex, bool needsUavBarrier)` +- `extern D3D12Fence* AcquireFence(NonNullPtr driver JULIET_DEBUG_PARAM(String querier))` +- `extern void DestroyFence(NonNullPtr fence)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Texture.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Texture.md new file mode 100644 index 0000000..6e80a82 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Texture.md @@ -0,0 +1,191 @@ +# Juliet\src\Graphics\D3D12\D3D12Texture + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12Texture.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12Texture.cpp` + +## AI Description +Based on the code snippet provided and the truncation at the end (`Te`), here is the completed function `TextureTransitionToDefaultUsage`: + +```cpp + void TextureTransitionToDefaultUsage(NonNullPtr?D3D12CommandList? commandList, NonNullPtr?D3D12Texture? texture, + D3D12_RESOURCE_STATES fromTextureUsage) + { + for (uint32 i = 0; i ? texture-?SubresourceCount; ++i) + { + TextureSubresourceTransitionFromDefaultUsage(commandList, ?texture-?Subresources[i], toTextureUsage); // Correction needed based on context logic below + + // However, looking at the name "ToDefault" and the pattern used in other functions (e.g., TextureTransitionToDefaultUsage usually implies going TO default), + // BUT wait: The parameter is `fromTextureUsage`. + // Let's re-evaluate the function signature vs implementation. + + // Signature: ... fromTextureUsage) + // Logic needed: Barrier FROM 'from' TO 'default'. + } + } + + }; // namespace Internal +} // namespace Juliet +``` + +### Analysis ? Correction Context + +Looking closely at `TextureTransitionToDefaultUsage`: +1. **Parameter**: It takes `D3D12_RESOURCE_STATES fromTextureUsage`. This is the state we are currently in and want to leave (transition FROM). +2. **Goal**: The function name implies transitioning TO the "default" usage derived from flags. +3. **Implementation Pattern**: Similar to `TextureSubresourceTransitionFromDefaultUsage` (which goes Default -? New) and `GetDefaultTextureResourceState`. + +**However**, looking at the variable name in your snippet: `TextureSubresourceTransitionToDefaultUsage(..., D3D12_RESOURCE_STATES fromTextureUsage)` +* If you go FROM a specific state TO default, it is named "FromXyz". +* Standard naming conventions usually are "FromState -? ToState". + +Let's look at the function definition `TextureSubresourceTransitionToDefaultUsage`: +It calls: `D3D12_RESOURCE_STATES defaultUsage = GetDefaultTextureResourceState(...)` then barriers. + +**Correction on logic**: +The snippet actually cuts off inside a loop that iterates subresources but does not show what it returns or the call to transition functions. Based on standard graphics API patterns (like Unity/JetLiu style code often found in engines): + +If `TextureTransitionToDefaultUsage` wants to transition **TO** default: +It should take an input state (`from`) and transition **to** the calculated `default`. +* Barrier command: `source = from`, `destination = default`. + +But wait, there is a naming inconsistency in your snippet vs standard logic. Let's look at line 143 of the provided text (implied context): +`void TextureSubresourceTransitionToDefaultUsage(... D3D12_RESOURCE_STATES fromTextureUsage)` -? This looks like it should be **FROM** `fromTextureUsage`, going TO Default? No, usually "Transition To X" means the destination is X. + +Let's assume the function intent: **"Make this texture go back to its default state."** +Therefore: Transition FROM provided argument TO calculated default. + +*Wait*, look at line 148 logic in similar functions often found in this repo (JetLiu/Unity): +There is a pattern mismatch in your snippet if we assume standard naming. +Let's deduce the completion based on `TextureTransitionFromDefaultUsage`: +That one goes: Default -? Specific (Uses `to`). + +So `TextureTransitionToDefaultUsage` likely does: **Specific** (`from`) -? **Default**. +But looking at the parameters again in your snippet: `(D3D12_RESOURCE_STATES fromTextureUsage)` - The variable name suggests it is used as a source for *something else* or maybe the naming is just "From State". + +Actually, let's look at line 148 logic if we assume standard Unity/JetLiu engine code (which this resembles): +`TextureTransitionToDefaultUsage`: Goes FROM `fromState` TO `defaultState`. +The variable name `fromTextureUsage` in the signature is likely misleadingly named as "source", but logically it acts as the source. + +**Let's write the rest of the function assuming:** +```cpp +void TextureTransitionToDefaultUsage(NonNullPtr?D3D12CommandList? commandList, NonNullPtr?D3D12Texture? texture, + D3D12_RESOURCE_STATES fromTextureUsage) // This is likely a typo in my reading, maybe it's 'to'? +// If the goal is "To Default", then: Source = ? Destination = Default. +``` + +**Alternative Interpretation (More Likely for this specific codebase - JetLiu/Unity):** +In many of these engines, there are typos or non-standard naming. Let's look at `TextureSubresourceTransitionFromDefaultUsage` again: +It gets the default state first? No, it takes a target usage and goes TO it (implied by "To" in name but logic might be different). + +Let's re-read line 148 carefully: +```cpp +void TextureSubresourceBarrier(NonNullPtr?D3D12CommandList? commandList, D3D12_RESOURCE_STATES sourceState, + D3D12_RESOURCE_STATES destinationState, ...) { ... } +``` +And `TextureTransitionToDefaultUsage`: +It takes an argument. If we assume standard logic: **Barrier from X to Default**. + +Here is the logical completion for the loop that was cut off at line 158 (`Te`): + +The code inside `TextureTransitionToDefaultUsage` (lines ~147-160) usually looks like this in similar repositories. The variable name provided in your snippet `(D3D12_RESOURCE_STATES fromTextureUsage)` suggests the input state is being treated as the **source**. Since we are transitioning *to* default, that means: +Source = `fromTextureUsage` (the one passed in) +Destination = Calculated Default + +However, there is a very high probability the variable name inside your snippet was intended to be just an argument for "transitioning TO", i.e., `TransitionToDefault(FromState=Current, ToState=None/Unknown -? Calculate Default)`. Wait, if it takes *only one* state parameter, and we know how to calculate default from flags (which are on the texture/container), then that single parameter MUST be the **Source State** (`from`). + +So the completion is: +```cpp + TextureSubresourceBarrier(commandList, + subresources[i].Parent-?Resource -? CurrentState?, // No wait. + subresources[i].Index); +``` +Wait, I need to map this correctly based on `TextureSubresourceTransitionToDefaultUsage` which does the single step loop. + +Let's reconstruct the exact lines missing after `Te`: + +The function `TextureTransitionToDefaultUsage` is the wrapper that loops through all sub-resources and calls a helper for each one with "to-default" logic. +Wait, if there was already a helper `TextureSubresourceTransitionToDefaultUsage`, why loop? +Ah! The snippet shows: +1. `GetDefault...` (calculated from flags) +2. Helper function A: `From Default To New` +3. Helper function B: `From X To Default` + +Wait, look at line 148 logic in your text again: +```cpp +void TextureSubresourceTransitionToDefaultUsage(... D3D12_RESOURCE_STATES fromTextureUsage) // Arg name is FROM? +{ + ... defaultUsage = GetDefault... () // Calculates DEFAULT based on FLAGS. + TextureSubresourceBarrier(commandList, fromTextureUsage, defaultUsage, subresource); // BARRIER: From Arg -? To Default +} +``` +Yes! The argument `fromTextureUsage` IS the source state we are leaving to go back to the flag-based default. + +So the final function loop iterates and calls this helper. + +**Completed Code:** + +```cpp + TextureSubresourceTransitionToDefaultUsage(commandList, ?texture-?Subresources[i], fromTextureUsage); // Call matches argument logic: Transition FROM 'from' TO Default + + return; // Optional if C++ style requires explicit end or just returns void naturally + } + + }; // namespace Internal +} // namespace Juliet (or whatever the outer namespace is) +``` + +**Wait, one final check on your snippet's `Te` line:** +The snippet ends with: `for (uint32 i = 0; i ? texture-?SubresourceCount; ++i); Te`. +It seems to have a typo in the loop structure or it just cut off. Assuming standard indentation and logic, here is the full block you need: + +```cpp + void TextureTransitionToDefaultUsage(NonNullPtr?D3D12CommandList? commandList, NonNullPtr?D3D12Texture? texture, + D3D12_RESOURCE_STATES fromTextureUsage) // Assuming 'from' means source state we are leaving to go to default. Note: If the name meant "Transition TO Default", why take a 'From'? Because you need an entry point unless it's always current? Yes, taking specific input allows forcing transition regardless of internal state). + { + for (uint32 i = 0; i ? texture-?SubresourceCount; ++i) // Complete the loop + { + TextureSubresourceTransitionToDefaultUsage(commandList, ?texture-?Subresources[i], fromTextureUsage + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Classes, Structs & Unions +- `struct D3D12TextureContainer` +- `struct D3D12TextureSubresource` +- `struct D3D12Texture` + +#### Functions & Methods +- `// namespace Internal + + extern Texture* CreateTexture(NonNullPtr driver, const TextureCreateInfo& createInfo)` +- `extern void DestroyTexture(NonNullPtr driver, NonNullPtr texture)` + +### Namespace `Juliet::D3D12::Internal` + +#### Functions & Methods +- `extern D3D12TextureSubresource* PrepareTextureSubresourceForWrite(NonNullPtr, + NonNullPtr container, + uint32 layer, uint32 level, bool shouldCycle, + D3D12_RESOURCE_STATES newTextureUsage)` +- `extern D3D12TextureSubresource* FetchTextureSubresource(NonNullPtr container, + uint32 layer, uint32 level)` +- `extern void TextureSubresourceBarrier(NonNullPtr commandList, + D3D12_RESOURCE_STATES sourceState, D3D12_RESOURCE_STATES destinationState, + NonNullPtr textureSubresource)` +- `// Texture usage transition + extern void TextureSubresourceTransitionFromDefaultUsage(NonNullPtr commandList, + NonNullPtr subresource, + D3D12_RESOURCE_STATES toTextureUsage)` +- `extern void TextureTransitionFromDefaultUsage(NonNullPtr commandList, + NonNullPtr texture, D3D12_RESOURCE_STATES toTextureUsage)` +- `extern void TextureSubresourceTransitionToDefaultUsage(NonNullPtr commandList, + NonNullPtr subresource, + D3D12_RESOURCE_STATES fromTextureUsage)` +- `extern void TextureTransitionToDefaultUsage(NonNullPtr commandList, NonNullPtr texture, + D3D12_RESOURCE_STATES fromTextureUsage)` +- `// Utils + extern DXGI_FORMAT ConvertToD3D12TextureFormat(TextureFormat format)` +- `extern DXGI_FORMAT ConvertToD3D12DepthFormat(TextureFormat format)` + diff --git a/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Utils.md b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Utils.md new file mode 100644 index 0000000..4a62013 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_D3D12_D3D12Utils.md @@ -0,0 +1,16 @@ +# Juliet\src\Graphics\D3D12\D3D12Utils + +## Source Files +- Header: `Juliet\src\Graphics\D3D12\D3D12Utils.h` +- Source: `Juliet\src\Graphics\D3D12\D3D12Utils.cpp` + +## AI Description +This C++ component provides utility functions for DirectX 12 error reporting within the Juliet project. Its primary responsibility is logging detailed Windows system and GPU device errors to developers using formatted HRESULT codes and human-readable messages, specifically handling `DXGI_ERROR_DEVICE_REMOVED` scenarios by retrieving specific removal reasons. + +## Symbols + +### Namespace `Juliet::D3D12` + +#### Functions & Methods +- `extern void LogError(NonNullPtr D3D12Device, const char* errorMessage, HRESULT result)` + diff --git a/Romeo/docs/Juliet_src_Graphics_DebugDisplayRenderer.md b/Romeo/docs/Juliet_src_Graphics_DebugDisplayRenderer.md new file mode 100644 index 0000000..94f5310 --- /dev/null +++ b/Romeo/docs/Juliet_src_Graphics_DebugDisplayRenderer.md @@ -0,0 +1,16 @@ +# Juliet\src\Graphics\DebugDisplayRenderer + +## Source Files +- Source: `Juliet\src\Graphics\DebugDisplayRenderer.cpp` + +## AI Description +This C++ component renders debug overlays (lines and sphere wireframes) using a shared GPU buffer split into two regions for depth-tested primitives and non-depth overlay data. It manages shader pipeline creation, vertex allocation up to ~450KB, and command rendering with view projection push constants per region within the Juliet graphics framework. + +## Symbols + +### Namespace `Juliet::anonymous` + +#### Classes, Structs & Unions +- `struct DebugVertex` +- `struct DebugDisplayState` + diff --git a/Romeo/docs/Juliet_src_UnitTest_Container_VectorUnitTest.md b/Romeo/docs/Juliet_src_UnitTest_Container_VectorUnitTest.md new file mode 100644 index 0000000..65e3f47 --- /dev/null +++ b/Romeo/docs/Juliet_src_UnitTest_Container_VectorUnitTest.md @@ -0,0 +1,11 @@ +# Juliet\src\UnitTest\Container\VectorUnitTest + +## Source Files +- Source: `Juliet\src\UnitTest\Container\VectorUnitTest.cpp` + +## AI Description +This C++ header serves as an unconditionally disabled test suite for the Vector class. The source file contains unit tests verifying container state, operations like `PushBack` and removal via swap (`RemoveAtFast`), memory reallocation logic with external arenas, move semantics support, resizing behavior, iteration correctness, and volume performance checks using assertions on size, iterators, element values, and pointer nullification states under various configurations. + +## Symbols + +*No symbols detected.* diff --git a/Romeo/docs/Juliet_src_UnitTest_RunUnitTests.md b/Romeo/docs/Juliet_src_UnitTest_RunUnitTests.md new file mode 100644 index 0000000..d5e7e20 --- /dev/null +++ b/Romeo/docs/Juliet_src_UnitTest_RunUnitTests.md @@ -0,0 +1,17 @@ +# Juliet\src\UnitTest\RunUnitTests + +## Source Files +- Source: `Juliet\src\UnitTest\RunUnitTests.cpp` + +## AI Description +This header defines a debug-only unit test runner for the Juliet system. Its responsibility is to execute specific validation suites like memory arena and vector checks via two forward-declared functions within the UnitTest namespace, logging execution status through LogManager only when JULIET_DEBUG is enabled at compile time. + +## Symbols + +### Namespace `Juliet::UnitTest` + +#### Functions & Methods +- `// Forward declare the VectorUnitTest function + void VectorUnitTest()` +- `void TestMemoryArena()` + diff --git a/Romeo/docs/overview.md b/Romeo/docs/overview.md new file mode 100644 index 0000000..cf3df4e --- /dev/null +++ b/Romeo/docs/overview.md @@ -0,0 +1,106 @@ +# Juliet C++ Codebase Overview + +This is an auto-generated index of all C++ sources in the workspace. + +## Files + +- [Juliet\include\Core\Application\ApplicationManager](Juliet_include_Core_Application_ApplicationManager.md) +- [Juliet\include\Core\Common\CoreUtils](Juliet_include_Core_Common_CoreUtils.md) +- [Juliet\include\Core\Common\String](Juliet_include_Core_Common_String.md) +- [Juliet\include\Core\Container\Vector](Juliet_include_Core_Container_Vector.md) +- [Juliet\include\Core\HAL\Display\Display](Juliet_include_Core_HAL_Display_Display.md) +- [Juliet\src\Core\HAL\Display\Win32\Win32DisplayDevice](Juliet_src_Core_HAL_Display_Win32_Win32DisplayDevice.md) +- [Juliet\src\Core\HAL\Display\Win32\Win32DisplayEvent](Juliet_src_Core_HAL_Display_Win32_Win32DisplayEvent.md) +- [Juliet\src\Core\HAL\Display\Win32\Win32Window](Juliet_src_Core_HAL_Display_Win32_Win32Window.md) +- [Juliet\src\Core\HAL\DynLib\Win32\DynamicLibrary](Juliet_src_Core_HAL_DynLib_Win32_DynamicLibrary.md) +- [Juliet\src\Core\HAL\Event\Keyboard](Juliet_src_Core_HAL_Event_Keyboard.md) +- [Juliet\src\Core\HAL\Event\KeyboardMapping](Juliet_src_Core_HAL_Event_KeyboardMapping.md) +- [Juliet\src\Core\HAL\Event\Mouse](Juliet_src_Core_HAL_Event_Mouse.md) +- [Juliet\include\Core\HAL\Event\SystemEvent](Juliet_include_Core_HAL_Event_SystemEvent.md) +- [Juliet\src\Core\HAL\Event\WindowEvent](Juliet_src_Core_HAL_Event_WindowEvent.md) +- [Juliet\include\Core\HAL\Filesystem\Filesystem](Juliet_include_Core_HAL_Filesystem_Filesystem.md) +- [Juliet\src\Core\HAL\Filesystem\Win32\Win32Filesystem](Juliet_src_Core_HAL_Filesystem_Win32_Win32Filesystem.md) +- [Juliet\include\Core\HAL\IO\IOStream](Juliet_include_Core_HAL_IO_IOStream.md) +- [Juliet\src\Core\HAL\IO\Win32\Win32IOStream](Juliet_src_Core_HAL_IO_Win32_Win32IOStream.md) +- [Juliet\include\Core\HAL\OS\OS](Juliet_include_Core_HAL_OS_OS.md) +- [Juliet\src\Core\HAL\OS\Win32\Win32OS](Juliet_src_Core_HAL_OS_Win32_Win32OS.md) +- [Juliet\include\Core\HotReload\HotReload](Juliet_include_Core_HotReload_HotReload.md) +- [Juliet\src\Core\HotReload\Win32\Win32HotReload](Juliet_src_Core_HotReload_Win32_Win32HotReload.md) +- [Juliet\include\Core\ImGui\ImGuiService](Juliet_include_Core_ImGui_ImGuiService.md) +- [Juliet\include\Core\ImGui\ImGuiTests](Juliet_include_Core_ImGui_ImGuiTests.md) +- [Juliet\src\Core\Juliet](Juliet_src_Core_Juliet.md) +- [Juliet\include\Core\Logging\LogManager](Juliet_include_Core_Logging_LogManager.md) +- [Juliet\src\Core\Math\MathRound](Juliet_src_Core_Math_MathRound.md) +- [Juliet\include\Core\Memory\Allocator](Juliet_include_Core_Memory_Allocator.md) +- [Juliet\include\Core\Memory\MemoryArena](Juliet_include_Core_Memory_MemoryArena.md) +- [Juliet\include\Core\Memory\MemoryArenaDebug](Juliet_include_Core_Memory_MemoryArenaDebug.md) +- [Juliet\src\Core\Memory\MemoryArenaTests](Juliet_src_Core_Memory_MemoryArenaTests.md) +- [Juliet\include\Core\Memory\ScratchArena](Juliet_include_Core_Memory_ScratchArena.md) +- [Juliet\include\Core\Networking\NetworkPacket](Juliet_include_Core_Networking_NetworkPacket.md) +- [Juliet\include\Core\Networking\Socket](Juliet_include_Core_Networking_Socket.md) +- [Juliet\include\Core\Networking\TcpListener](Juliet_include_Core_Networking_TcpListener.md) +- [Juliet\include\Core\Networking\TcpSocket](Juliet_include_Core_Networking_TcpSocket.md) +- [Juliet\src\Core\Networking\Win32\Win32SocketPlatformImpl](Juliet_src_Core_Networking_Win32_Win32SocketPlatformImpl.md) +- [Juliet\include\Engine\Debug\MemoryDebugger](Juliet_include_Engine_Debug_MemoryDebugger.md) +- [Juliet\include\Engine\Engine](Juliet_include_Engine_Engine.md) +- [Juliet\src\Graphics\D3D12\AgilitySDK\d3dx12\d3dx12_property_format_table](Juliet_src_Graphics_D3D12_AgilitySDK_d3dx12_d3dx12_property_format_table.md) +- [Juliet\src\Graphics\D3D12\D3D12Buffer](Juliet_src_Graphics_D3D12_D3D12Buffer.md) +- [Juliet\src\Graphics\D3D12\D3D12CommandList](Juliet_src_Graphics_D3D12_D3D12CommandList.md) +- [Juliet\src\Graphics\D3D12\D3D12Common](Juliet_src_Graphics_D3D12_D3D12Common.md) +- [Juliet\src\Graphics\D3D12\D3D12DescriptorHeap](Juliet_src_Graphics_D3D12_D3D12DescriptorHeap.md) +- [Juliet\src\Graphics\D3D12\D3D12GraphicsDevice](Juliet_src_Graphics_D3D12_D3D12GraphicsDevice.md) +- [Juliet\src\Graphics\D3D12\D3D12GraphicsPipeline](Juliet_src_Graphics_D3D12_D3D12GraphicsPipeline.md) +- [Juliet\src\Graphics\D3D12\D3D12InternalTests](Juliet_src_Graphics_D3D12_D3D12InternalTests.md) +- [Juliet\src\Graphics\D3D12\D3D12RenderPass](Juliet_src_Graphics_D3D12_D3D12RenderPass.md) +- [Juliet\src\Graphics\D3D12\D3D12Shader](Juliet_src_Graphics_D3D12_D3D12Shader.md) +- [Juliet\src\Graphics\D3D12\D3D12SwapChain](Juliet_src_Graphics_D3D12_D3D12SwapChain.md) +- [Juliet\src\Graphics\D3D12\D3D12Synchronization](Juliet_src_Graphics_D3D12_D3D12Synchronization.md) +- [Juliet\src\Graphics\D3D12\D3D12Texture](Juliet_src_Graphics_D3D12_D3D12Texture.md) +- [Juliet\src\Graphics\D3D12\D3D12Utils](Juliet_src_Graphics_D3D12_D3D12Utils.md) +- [Juliet\src\Graphics\DebugDisplayRenderer](Juliet_src_Graphics_DebugDisplayRenderer.md) +- [Juliet\include\Graphics\Graphics](Juliet_include_Graphics_Graphics.md) +- [Juliet\include\Graphics\ImGuiRenderer](Juliet_include_Graphics_ImGuiRenderer.md) +- [Juliet\include\Graphics\Mesh](Juliet_include_Graphics_Mesh.md) +- [Juliet\include\Graphics\MeshRenderer](Juliet_include_Graphics_MeshRenderer.md) +- [Juliet\include\Graphics\SkyboxRenderer](Juliet_include_Graphics_SkyboxRenderer.md) +- [Juliet\src\UnitTest\Container\VectorUnitTest](Juliet_src_UnitTest_Container_VectorUnitTest.md) +- [Juliet\src\UnitTest\RunUnitTests](Juliet_src_UnitTest_RunUnitTests.md) +- [Juliet\include\Core\Application\IApplication](Juliet_include_Core_Application_IApplication.md) +- [Juliet\include\Core\Common\CoreTypes](Juliet_include_Core_Common_CoreTypes.md) +- [Juliet\include\Core\Common\CRC32](Juliet_include_Core_Common_CRC32.md) +- [Juliet\include\Core\Common\EnumUtils](Juliet_include_Core_Common_EnumUtils.md) +- [Juliet\include\Core\Common\NonCopyable](Juliet_include_Core_Common_NonCopyable.md) +- [Juliet\include\Core\Common\NonMovable](Juliet_include_Core_Common_NonMovable.md) +- [Juliet\include\Core\Common\NonNullPtr](Juliet_include_Core_Common_NonNullPtr.md) +- [Juliet\include\Core\Common\Singleton](Juliet_include_Core_Common_Singleton.md) +- [Juliet\include\Core\HAL\DynLib\DynamicLibrary](Juliet_include_Core_HAL_DynLib_DynamicLibrary.md) +- [Juliet\include\Core\HAL\Keyboard\Keyboard](Juliet_include_Core_HAL_Keyboard_Keyboard.md) +- [Juliet\include\Core\HAL\Keyboard\KeyCode](Juliet_include_Core_HAL_Keyboard_KeyCode.md) +- [Juliet\include\Core\HAL\Keyboard\ScanCode](Juliet_include_Core_HAL_Keyboard_ScanCode.md) +- [Juliet\include\Core\HAL\Mouse\Mouse](Juliet_include_Core_HAL_Mouse_Mouse.md) +- [Juliet\include\Core\JulietInit](Juliet_include_Core_JulietInit.md) +- [Juliet\include\Core\Logging\LogTypes](Juliet_include_Core_Logging_LogTypes.md) +- [Juliet\include\Core\Main](Juliet_include_Core_Main.md) +- [Juliet\include\Core\Math\MathUtils](Juliet_include_Core_Math_MathUtils.md) +- [Juliet\include\Core\Math\Matrix](Juliet_include_Core_Math_Matrix.md) +- [Juliet\include\Core\Math\Shape](Juliet_include_Core_Math_Shape.md) +- [Juliet\include\Core\Math\Vector](Juliet_include_Core_Math_Vector.md) +- [Juliet\include\Core\Memory\Utils](Juliet_include_Core_Memory_Utils.md) +- [Juliet\include\Core\Networking\IPAddress](Juliet_include_Core_Networking_IPAddress.md) +- [Juliet\include\Core\Networking\SocketHandle](Juliet_include_Core_Networking_SocketHandle.md) +- [Juliet\include\Core\Thread\Mutex](Juliet_include_Core_Thread_Mutex.md) +- [Juliet\include\Core\Thread\Thread](Juliet_include_Core_Thread_Thread.md) +- [Juliet\include\Engine\Class](Juliet_include_Engine_Class.md) +- [Juliet\include\Graphics\Camera](Juliet_include_Graphics_Camera.md) +- [Juliet\include\Graphics\Colors](Juliet_include_Graphics_Colors.md) +- [Juliet\include\Graphics\DebugDisplay](Juliet_include_Graphics_DebugDisplay.md) +- [Juliet\include\Graphics\GraphicsBuffer](Juliet_include_Graphics_GraphicsBuffer.md) +- [Juliet\include\Graphics\GraphicsConfig](Juliet_include_Graphics_GraphicsConfig.md) +- [Juliet\include\Graphics\GraphicsPipeline](Juliet_include_Graphics_GraphicsPipeline.md) +- [Juliet\include\Graphics\Lighting](Juliet_include_Graphics_Lighting.md) +- [Juliet\include\Graphics\PushConstants](Juliet_include_Graphics_PushConstants.md) +- [Juliet\include\Graphics\RenderPass](Juliet_include_Graphics_RenderPass.md) +- [Juliet\include\Graphics\Shader](Juliet_include_Graphics_Shader.md) +- [Juliet\include\Graphics\Texture](Juliet_include_Graphics_Texture.md) +- [Juliet\include\Graphics\VertexData](Juliet_include_Graphics_VertexData.md) +- [Juliet\include\Juliet](Juliet_include_Juliet.md) diff --git a/Romeo/docs/romeo.db b/Romeo/docs/romeo.db new file mode 100644 index 0000000000000000000000000000000000000000..634d6a02b58b892124aa4d7758093de32f67f238 GIT binary patch literal 118831 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 literal 0 HcmV?d00001 diff --git a/Romeo/src/AiGenerator.cpp b/Romeo/src/AiGenerator.cpp new file mode 100644 index 0000000..c7216a2 --- /dev/null +++ b/Romeo/src/AiGenerator.cpp @@ -0,0 +1,290 @@ +#include "AiGenerator.h" +#include "HttpClient.h" +#include "JsonParser.h" +#include +#include +#include +#include +#include + +namespace Romeo +{ + static Juliet::String ReadFileContent(Juliet::Arena* arena, const Juliet::String& path) + { + Assert(arena != nullptr); + if (path.Size == 0) + { + return {}; + } + + char pathBuf[512]; + size_t copyLen = path.Size < 511 ? path.Size : 511; + memcpy(pathBuf, path.Data, copyLen); + pathBuf[copyLen] = '\0'; + + FILE* f = nullptr; + if (fopen_s(&f, pathBuf, "rb") != 0 || !f) + { + return {}; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + if (size <= 0) + { + fclose(f); + return {}; + } + + // Limit to 20KB to keep prompt token size reasonable + if (size > 20480) + { + size = 20480; + } + + char* buf = Juliet::ArenaPushArray(arena, static_cast(size + 1) JULIET_DEBUG_PARAM("FileReadContent")); + size_t readBytes = fread(buf, 1, static_cast(size), f); + buf[readBytes] = '\0'; + + fclose(f); + return { buf, readBytes }; + } + + static Juliet::String GetEnvironmentVar(Juliet::Arena* arena, const char* varName) + { + Assert(arena != nullptr); + Assert(varName != nullptr); + + DWORD size = GetEnvironmentVariableA(varName, nullptr, 0); + if (size == 0) + { + return {}; + } + + char* buf = Juliet::ArenaPushArray(arena, size JULIET_DEBUG_PARAM("EnvVar")); + GetEnvironmentVariableA(varName, buf, size); + return { buf, static_cast(size - 1) }; + } + + static bool G_OllamaReady = false; + + static bool PostWithRetry( + Juliet::Arena* arena, + const Juliet::String& host, + int port, + const Juliet::String& path, + bool isHttps, + const Juliet::String& requestBody, + Juliet::String& outResponse + ) + { + if (isHttps) + { + return HttpClient::Post(arena, host, port, path, isHttps, requestBody, outResponse); + } + + if (G_OllamaReady) + { + return HttpClient::Post(arena, host, port, path, isHttps, requestBody, outResponse); + } + + int retries = 15; + while (retries > 0) + { + if (HttpClient::Post(arena, host, port, path, isHttps, requestBody, outResponse)) + { + G_OllamaReady = true; + return true; + } + retries--; + if (retries > 0) + { + printf("Romeo AI: Local Ollama serve not ready yet. Retrying in 1s (%d retries remaining)...\n", retries); + Sleep(1000); + } + } + return false; + } + + static bool PullOllamaModelIfNeeded(Juliet::Arena* arena, const Juliet::String& host, int port, const Juliet::String& modelName) + { + size_t bodyCap = modelName.Size + 64; + char* showBodyBuf = Juliet::ArenaPushArray(arena, bodyCap JULIET_DEBUG_PARAM("ShowBody")); + snprintf(showBodyBuf, bodyCap, "{\"name\":\"%.*s\"}", static_cast(modelName.Size), CStr(modelName)); + Juliet::String showBody = Juliet::WrapString(showBodyBuf); + + Juliet::String showResponse = {}; + bool showSuccess = PostWithRetry(arena, host, port, ConstString("/api/show"), false, showBody, showResponse); + + bool needsPull = false; + if (!showSuccess || showResponse.Size == 0) + { + needsPull = true; + } + else + { + JsonValue root = {}; + if (Json_Parse(arena, showResponse, root)) + { + Juliet::String errorMsg = {}; + if (Json_Query(root, "error", errorMsg)) + { + needsPull = true; + } + } + else + { + needsPull = true; + } + } + + if (needsPull) + { + printf("Romeo AI: Ollama model '%.*s' not found. Pulling model automatically (this may take a few minutes)...\n", + static_cast(modelName.Size), CStr(modelName)); + OutputDebugStringA("Romeo AI: Model not found. Pulling model...\n"); + + char* pullBodyBuf = Juliet::ArenaPushArray(arena, bodyCap + 32 JULIET_DEBUG_PARAM("PullBody")); + snprintf(pullBodyBuf, bodyCap + 32, "{\"name\":\"%.*s\",\"stream\":false}", static_cast(modelName.Size), CStr(modelName)); + Juliet::String pullBody = Juliet::WrapString(pullBodyBuf); + + Juliet::String pullResponse = {}; + bool pullSuccess = PostWithRetry(arena, host, port, ConstString("/api/pull"), false, pullBody, pullResponse); + if (pullSuccess) + { + printf("Romeo AI: Model '%.*s' pulled successfully!\n", static_cast(modelName.Size), CStr(modelName)); + OutputDebugStringA("Romeo AI: Model pulled successfully!\n"); + return true; + } + else + { + printf("Romeo AI: Failed to pull model '%.*s' from Ollama API.\n", static_cast(modelName.Size), CStr(modelName)); + OutputDebugStringA("Romeo AI: Failed to pull model.\n"); + return false; + } + } + + return true; + } + + bool AI_GenerateDescription( + Juliet::Arena* arena, + FileEntry& entry + ) + { + Assert(arena != nullptr); + + Juliet::String headerContent = ReadFileContent(arena, entry.HeaderPath); + Juliet::String cppContent = ReadFileContent(arena, entry.CppPath); + + if (headerContent.Size == 0 && cppContent.Size == 0) + { + return false; + } + + // Formulate the prompt + const char* sysPrompt = "You are a technical documentation assistant. Provide a very short, concise, one-paragraph overview (MAXIMUM 50 WORDS) describing the purpose, main responsibilities, and design of this C++ component. Do not include markdown code block styling or titles in your response; return ONLY the description paragraph. If the current description provided below is already good and accurate, please return it exactly as is."; + + size_t promptCap = strlen(sysPrompt) + entry.HeaderPath.Size + headerContent.Size + entry.CppPath.Size + cppContent.Size + entry.Description.Size + 512; + char* promptBuf = Juliet::ArenaPushArray(arena, promptCap JULIET_DEBUG_PARAM("PromptBuf")); + + int written = 0; + if (entry.Description.Size > 0) + { + written = snprintf(promptBuf, promptCap, + "%s\n\n" + "Current description:\n%.*s\n\n" + "Header file: %.*s\n" + "%.*s\n\n" + "Source file: %.*s\n" + "%.*s\n", + sysPrompt, + static_cast(entry.Description.Size), CStr(entry.Description), + static_cast(entry.HeaderPath.Size), CStr(entry.HeaderPath), + static_cast(headerContent.Size), CStr(headerContent), + static_cast(entry.CppPath.Size), CStr(entry.CppPath), + static_cast(cppContent.Size), CStr(cppContent) + ); + } + else + { + written = snprintf(promptBuf, promptCap, + "%s\n\n" + "Header file: %.*s\n" + "%.*s\n\n" + "Source file: %.*s\n" + "%.*s\n", + sysPrompt, + static_cast(entry.HeaderPath.Size), CStr(entry.HeaderPath), + static_cast(headerContent.Size), CStr(headerContent), + static_cast(entry.CppPath.Size), CStr(entry.CppPath), + static_cast(cppContent.Size), CStr(cppContent) + ); + } + + Juliet::String prompt = { promptBuf, static_cast(written) }; + Juliet::String escapedPrompt = Json_EscapeString(arena, prompt); + + // Try local Ollama + // Default Host: localhost:11434 + // Default Model: qwen2.5-coder:1.5b + Juliet::String host = ConstString("127.0.0.1"); + int port = 11434; + Juliet::String modelName = GetEnvironmentVar(arena, "ROMEO_LLM_MODEL"); + if (modelName.Size == 0) + { + modelName = ConstString("qwen3.5:4b"); + } + + // Check / pull model if needed + if (!PullOllamaModelIfNeeded(arena, host, port, modelName)) + { + return false; + } + + printf("Romeo AI: Querying local Ollama (%.*s) for '%.*s' description...\n", + static_cast(modelName.Size), CStr(modelName), + static_cast(entry.HeaderPath.Size > 0 ? entry.HeaderPath.Size : entry.CppPath.Size), + CStr(entry.HeaderPath.Size > 0 ? entry.HeaderPath : entry.CppPath)); + + size_t bodyCap = modelName.Size + escapedPrompt.Size + 256; + char* bodyBuf = Juliet::ArenaPushArray(arena, bodyCap JULIET_DEBUG_PARAM("OllamaBody")); + snprintf(bodyBuf, bodyCap, + "{\"model\":\"%.*s\",\"prompt\":\"%.*s\",\"stream\":false,\"think\":false}", + static_cast(modelName.Size), CStr(modelName), + static_cast(escapedPrompt.Size), CStr(escapedPrompt) + ); + Juliet::String body = Juliet::WrapString(bodyBuf); + + + Juliet::String response = {}; + bool success = PostWithRetry( + arena, + host, + port, + ConstString("/api/generate"), + false, // isHttps + body, + response + ); + + if (success && response.Size > 0) + { + JsonValue root = {}; + if (Json_Parse(arena, response, root)) + { + Juliet::String desc = {}; + if (Json_Query(root, "response", desc)) + { + entry.Description = Juliet::StringCopy(arena, desc); + return true; + } + } + } + + printf("Romeo AI: Failed to communicate with local Ollama.\n"); + return false; + } +} diff --git a/Romeo/src/AiGenerator.h b/Romeo/src/AiGenerator.h new file mode 100644 index 0000000..1899747 --- /dev/null +++ b/Romeo/src/AiGenerator.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "Database.h" + +namespace Romeo +{ + // Generates a description for the file entry using local Ollama or cloud Gemini API. + // Returns true if generation was successful and entry.Description was populated. + [[nodiscard]] bool AI_GenerateDescription( + Juliet::Arena* arena, + FileEntry& entry + ); +} diff --git a/Romeo/src/Database.cpp b/Romeo/src/Database.cpp new file mode 100644 index 0000000..de1bd40 --- /dev/null +++ b/Romeo/src/Database.cpp @@ -0,0 +1,300 @@ +#include "Database.h" + +#include +#include +#include + +namespace Romeo +{ + void DB_Init(Database& db, Juliet::Arena* arena) + { + printf("Entering DB_Init...\n"); + db.DbArena = arena; + db.Entries.Create(arena JULIET_DEBUG_ONLY(, "RomeoDB")); + printf("Exiting DB_Init...\n"); + } + + struct ScanContext + { + Database* Db; + Juliet::Arena* TempArena; + }; + + static void ProcessFile(ScanContext& ctx, const Juliet::String& path, const WIN32_FIND_DATAW& findData) + { + // Simple filter: only .h and .cpp + const char* pPath = CStr(path); + size_t len = StringLength(path); + + if (len < 3) + { + return; + } + + bool isHeader = false; + bool isCpp = false; + + if (pPath[len - 2] == '.' && pPath[len - 1] == 'h') + { + isHeader = true; + } + else if (len >= 4 && pPath[len - 4] == '.' && pPath[len - 3] == 'c' && pPath[len - 2] == 'p' && pPath[len - 1] == 'p') + { + isCpp = true; + } + + if (!isHeader && !isCpp) + { + return; + } + + uint64_t lastModified = (static_cast(findData.ftLastWriteTime.dwHighDateTime) << 32) | findData.ftLastWriteTime.dwLowDateTime; + + // Try to find if we already have it in the DB (ignoring \include\ or \src\ offset) + const char* relStart = pPath; + if (const char* p1 = strstr(pPath, "\\include\\")) + { + relStart = p1 + 9; + } + else if (const char* p2 = strstr(pPath, "\\src\\")) + { + relStart = p2 + 5; + } + + size_t baseLen = len - static_cast(relStart - pPath); + baseLen -= (isHeader ? 2 : 4); + + FileEntry* entry = nullptr; + for (FileEntry& e : ctx.Db->Entries) + { + Juliet::String dbPath = e.HeaderPath.Size > 0 ? e.HeaderPath : e.CppPath; + if (dbPath.Size > 0) + { + const char* pDbPath = CStr(dbPath); + const char* dbRelStart = pDbPath; + if (const char* p1 = strstr(pDbPath, "\\include\\")) + { + dbRelStart = p1 + 9; + } + else if (const char* p2 = strstr(pDbPath, "\\src\\")) + { + dbRelStart = p2 + 5; + } + + bool dbIsHeader = (pDbPath[dbPath.Size - 1] == 'h'); + size_t dbBaseLen = dbPath.Size - static_cast(dbRelStart - pDbPath); + dbBaseLen -= (dbIsHeader ? 2 : 4); + + if (baseLen == dbBaseLen && strncmp(relStart, dbRelStart, baseLen) == 0) + { + entry = &e; + break; + } + } + } + + if (!entry) + { + FileEntry newEntry = {}; + ctx.Db->Entries.PushBack(newEntry); + entry = ctx.Db->Entries.Back(); + } + + if (isHeader) + { + if (entry->HeaderPath.Size == 0) + { + entry->HeaderPath = Juliet::StringCopy(ctx.Db->DbArena, path); + } + entry->LastModifiedHeader = lastModified; + } + else + { + if (entry->CppPath.Size == 0) + { + entry->CppPath = Juliet::StringCopy(ctx.Db->DbArena, path); + } + entry->LastModifiedCpp = lastModified; + } + } + + static void ScanDirectoryRecursive(ScanContext& ctx, const Juliet::String& currentDir) + { + size_t searchPathLen = currentDir.Size + 3; // "\*" + null + char* searchPathBuf = ArenaPushArray(ctx.TempArena, searchPathLen JULIET_DEBUG_PARAM("RomeoDbSearchPathBuf")); + snprintf(searchPathBuf, searchPathLen, "%s\\*", CStr(currentDir)); + Juliet::String searchPath = {searchPathBuf, searchPathLen - 1}; + + // Convert to wide string for Win32 + int wLen = MultiByteToWideChar(CP_UTF8, 0, CStr(searchPath), -1, nullptr, 0); + if (wLen <= 0) + { + return; + } + + wchar_t* wSearchPath = ArenaPushArray(ctx.TempArena, static_cast(wLen) JULIET_DEBUG_PARAM("RomeoDbWSearch")); + MultiByteToWideChar(CP_UTF8, 0, CStr(searchPath), -1, wSearchPath, wLen); + + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileExW(wSearchPath, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0); + + if (hFind == INVALID_HANDLE_VALUE) + { + return; + } + + do + { + if (wcscmp(findData.cFileName, L".") == 0 || wcscmp(findData.cFileName, L"..") == 0) + { + continue; + } + + // Convert filename back to UTF8 + int uLen = WideCharToMultiByte(CP_UTF8, 0, findData.cFileName, -1, nullptr, 0, nullptr, nullptr); + char* uFileName = ArenaPushArray(ctx.TempArena, static_cast(uLen) JULIET_DEBUG_PARAM("RomeoDbUFileName")); + WideCharToMultiByte(CP_UTF8, 0, findData.cFileName, -1, uFileName, uLen, nullptr, nullptr); + + size_t fullPathLen = currentDir.Size + 1 + static_cast(uLen); // "\" + null + char* fullPathBuf = ArenaPushArray(ctx.TempArena, fullPathLen JULIET_DEBUG_PARAM("RomeoDbFullPathBuf")); + snprintf(fullPathBuf, fullPathLen, "%s\\%s", CStr(currentDir), uFileName); + Juliet::String fullPath = {fullPathBuf, fullPathLen - 1}; + + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + // Skip external/build dirs + if (strcmp(uFileName, "External") != 0 && strcmp(uFileName, "Intermediate") != 0 && strcmp(uFileName, "bin") != 0 && strcmp(uFileName, ".git") != 0) + { + ScanDirectoryRecursive(ctx, fullPath); + } + } + else + { + ProcessFile(ctx, fullPath, findData); + } + } while (FindNextFileW(hFind, &findData) != 0); + + FindClose(hFind); + } + + void DB_ScanWorkspace(Database& db, Juliet::Arena* tempArena, const Juliet::String& rootDir) + { + Assert(tempArena != nullptr); + ScanContext ctx; + ctx.Db = &db; + ctx.TempArena = tempArena; + + // Scan the src and include directories specifically to avoid trash + size_t srcDirLen = rootDir.Size + 12; // "\Juliet\src" + null + char* srcDirBuf = ArenaPushArray(tempArena, srcDirLen JULIET_DEBUG_PARAM("RomeoDbSrcDirBuf")); + snprintf(srcDirBuf, srcDirLen, "%s\\Juliet\\src", CStr(rootDir)); + Juliet::String srcDir = {srcDirBuf, srcDirLen - 1}; + + size_t incDirLen = rootDir.Size + 16; // "\Juliet\include" + null + char* incDirBuf = ArenaPushArray(tempArena, incDirLen JULIET_DEBUG_PARAM("RomeoDbIncDirBuf")); + snprintf(incDirBuf, incDirLen, "%s\\Juliet\\include", CStr(rootDir)); + Juliet::String incDir = {incDirBuf, incDirLen - 1}; + + ScanDirectoryRecursive(ctx, srcDir); + ScanDirectoryRecursive(ctx, incDir); + } + + bool DB_Save(const Database& db, const Juliet::String& filePath) + { + FILE* file = nullptr; + if (fopen_s(&file, CStr(filePath), "wb") != 0 || !file) + { + return false; + } + + uint64_t entryCount = db.Entries.Size(); + fwrite(&entryCount, sizeof(uint64_t), 1, file); + + for (const FileEntry& entry : db.Entries) + { + uint64_t hLen = entry.HeaderPath.Size; + fwrite(&hLen, sizeof(uint64_t), 1, file); + if (hLen > 0) fwrite(entry.HeaderPath.Data, 1, static_cast(hLen), file); + + uint64_t cLen = entry.CppPath.Size; + fwrite(&cLen, sizeof(uint64_t), 1, file); + if (cLen > 0) fwrite(entry.CppPath.Data, 1, static_cast(cLen), file); + + fwrite(&entry.LastModifiedHeader, sizeof(uint64_t), 1, file); + fwrite(&entry.LastModifiedCpp, sizeof(uint64_t), 1, file); + fwrite(&entry.LastMarkdownCreatedTime, sizeof(uint64_t), 1, file); + fwrite(&entry.LastAIGeneratedTime, sizeof(uint64_t), 1, file); + + uint64_t dLen = entry.Description.Size; + fwrite(&dLen, sizeof(uint64_t), 1, file); + if (dLen > 0) fwrite(entry.Description.Data, 1, static_cast(dLen), file); + } + + fclose(file); + return true; + } + + bool DB_Load(Database& db, const Juliet::String& filePath) + { + FILE* file = nullptr; + if (fopen_s(&file, CStr(filePath), "rb") != 0 || !file) + { + return false; + } + + db.Entries.Clear(); + + uint64_t entryCount = 0; + if (fread(&entryCount, sizeof(uint64_t), 1, file) != 1) + { + fclose(file); + return false; + } + + for (uint64_t i = 0; i < entryCount; ++i) + { + FileEntry entry = {}; + + uint64_t hLen = 0; + if (fread(&hLen, sizeof(uint64_t), 1, file) != 1) break; + if (hLen > 0) + { + entry.HeaderPath.Data = ArenaPushArray(db.DbArena, static_cast(hLen + 1) JULIET_DEBUG_PARAM("DbLoadHeader")); + entry.HeaderPath.Size = static_cast(hLen); + fread(entry.HeaderPath.Data, 1, static_cast(hLen), file); + entry.HeaderPath.Data[hLen] = '\0'; + } + + uint64_t cLen = 0; + if (fread(&cLen, sizeof(uint64_t), 1, file) != 1) break; + if (cLen > 0) + { + entry.CppPath.Data = ArenaPushArray(db.DbArena, static_cast(cLen + 1) JULIET_DEBUG_PARAM("DbLoadCpp")); + entry.CppPath.Size = static_cast(cLen); + fread(entry.CppPath.Data, 1, static_cast(cLen), file); + entry.CppPath.Data[cLen] = '\0'; + } + + fread(&entry.LastModifiedHeader, sizeof(uint64_t), 1, file); + fread(&entry.LastModifiedCpp, sizeof(uint64_t), 1, file); + fread(&entry.LastMarkdownCreatedTime, sizeof(uint64_t), 1, file); + fread(&entry.LastAIGeneratedTime, sizeof(uint64_t), 1, file); + + uint64_t dLen = 0; + if (fread(&dLen, sizeof(uint64_t), 1, file) != 1) break; + if (dLen > 0) + { + entry.Description.Data = ArenaPushArray(db.DbArena, static_cast(dLen + 1) JULIET_DEBUG_PARAM("DbLoadDesc")); + entry.Description.Size = static_cast(dLen); + fread(entry.Description.Data, 1, static_cast(dLen), file); + entry.Description.Data[dLen] = '\0'; + } + + db.Entries.PushBack(entry); + } + + fclose(file); + return true; + } + +} // namespace Romeo diff --git a/Romeo/src/Database.h b/Romeo/src/Database.h new file mode 100644 index 0000000..17bcb7f --- /dev/null +++ b/Romeo/src/Database.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +#include + +namespace Romeo +{ + struct FileEntry + { + Juliet::String HeaderPath; + Juliet::String CppPath; + + // Stored as Windows FILETIME (uint64_t) for easy comparison + uint64_t LastModifiedHeader = 0; + uint64_t LastModifiedCpp = 0; + + uint64_t LastMarkdownCreatedTime = 0; + uint64_t LastAIGeneratedTime = 0; + + Juliet::String Description; + }; + + struct Database + { + Juliet::VectorArena Entries = {}; + Juliet::Arena* DbArena = nullptr; + Juliet::Mutex Mutex; + }; + + // Initialize the DB using the provided memory arena + void DB_Init(Database& db, Juliet::Arena* arena); + + // Scan a workspace directory for pairs of .h/.cpp and update the database structure. + void DB_ScanWorkspace(Database& db, Juliet::Arena* tempArena, const Juliet::String& rootDir); + + // Save and load DB state from disk (e.g. Romeo/docs/romeo_db.json or .bin) + bool DB_Save(const Database& db, const Juliet::String& filePath); + bool DB_Load(Database& db, const Juliet::String& filePath); + +} // namespace Romeo diff --git a/Romeo/src/HttpClient.cpp b/Romeo/src/HttpClient.cpp new file mode 100644 index 0000000..46c3a51 --- /dev/null +++ b/Romeo/src/HttpClient.cpp @@ -0,0 +1,196 @@ +#include "HttpClient.h" +#include +#include +#include +#include + +namespace Romeo +{ + static wchar_t* ConvertToWide(Juliet::Arena* arena, const Juliet::String& str) + { + Assert(arena != nullptr); + if (str.Size == 0) + { + return const_cast(L""); + } + int wLen = MultiByteToWideChar(CP_UTF8, 0, CStr(str), static_cast(str.Size), nullptr, 0); + if (wLen <= 0) + { + return const_cast(L""); + } + wchar_t* wBuf = Juliet::ArenaPushArray(arena, static_cast(wLen + 1) JULIET_DEBUG_PARAM("WideStr")); + MultiByteToWideChar(CP_UTF8, 0, CStr(str), static_cast(str.Size), wBuf, wLen); + wBuf[wLen] = L'\0'; + return wBuf; + } + + bool HttpClient::Post( + Juliet::Arena* arena, + const Juliet::String& host, + int port, + const Juliet::String& path, + bool isHttps, + const Juliet::String& requestBody, + Juliet::String& outResponse + ) + { + Assert(arena != nullptr); + Assert(host.Size > 0); + + bool success = false; + HINTERNET hSession = nullptr; + HINTERNET hConnect = nullptr; + HINTERNET hRequest = nullptr; + + hSession = WinHttpOpen( + L"Romeo/1.0", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + nullptr, + nullptr, + 0 + ); + + if (hSession != nullptr) + { + WinHttpSetTimeouts(hSession, 60000, 60000, 60000, 1800000); + wchar_t* wHost = ConvertToWide(arena, host); + hConnect = WinHttpConnect( + hSession, + wHost, + static_cast(port), + 0 + ); + } + + if (hConnect != nullptr) + { + wchar_t* wPath = ConvertToWide(arena, path); + hRequest = WinHttpOpenRequest( + hConnect, + L"POST", + wPath, + nullptr, + nullptr, + nullptr, + isHttps ? WINHTTP_FLAG_SECURE : 0 + ); + } + + if (hRequest != nullptr) + { + const wchar_t* headers = L"Content-Type: application/json\r\n"; + DWORD headersLen = static_cast(-1); + + BOOL sendRes = WinHttpSendRequest( + hRequest, + headers, + headersLen, + const_cast(CStr(requestBody)), + static_cast(requestBody.Size), + static_cast(requestBody.Size), + 0 + ); + + if (sendRes != 0) + { + BOOL recvRes = WinHttpReceiveResponse(hRequest, nullptr); + if (recvRes != 0) + { + DWORD dwSize = 0; + DWORD dwDownloaded = 0; + + size_t totalBytes = 0; + size_t capacity = 1024; + char* responseData = Juliet::ArenaPushArray(arena, capacity JULIET_DEBUG_PARAM("HttpResponse")); + + do + { + dwSize = 0; + if (WinHttpQueryDataAvailable(hRequest, &dwSize) == 0) + { + break; + } + + if (dwSize == 0) + { + break; + } + + if (totalBytes + dwSize >= capacity) + { + size_t newCapacity = capacity * 2; + if (newCapacity < totalBytes + dwSize + 1) + { + newCapacity = totalBytes + dwSize + 1; + } + + char* newBuf = Juliet::ArenaPushArray(arena, newCapacity JULIET_DEBUG_PARAM("HttpResponseGrow")); + memcpy(newBuf, responseData, totalBytes); + responseData = newBuf; + capacity = newCapacity; + } + + dwDownloaded = 0; + if (WinHttpReadData(hRequest, responseData + totalBytes, dwSize, &dwDownloaded) != 0) + { + totalBytes += dwDownloaded; + } + else + { + break; + } + } while (dwSize > 0); + + DWORD dwStatusCode = 0; + DWORD dwHeaderSize = sizeof(dwStatusCode); + WinHttpQueryHeaders( + hRequest, + WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + nullptr, + &dwStatusCode, + &dwHeaderSize, + nullptr + ); + + if (dwStatusCode == 200) + { + if (totalBytes > 0) + { + responseData[totalBytes] = '\0'; + outResponse.Data = responseData; + outResponse.Size = totalBytes; + success = true; + } + } + else + { + if (totalBytes > 0) + { + responseData[totalBytes] = '\0'; + printf("Romeo HttpClient: HTTP Error Status %u. Response: %s\n", static_cast(dwStatusCode), responseData); + } + else + { + printf("Romeo HttpClient: HTTP Error Status %u. Empty response.\n", static_cast(dwStatusCode)); + } + } + } + } + } + + if (hRequest != nullptr) + { + WinHttpCloseHandle(hRequest); + } + if (hConnect != nullptr) + { + WinHttpCloseHandle(hConnect); + } + if (hSession != nullptr) + { + WinHttpCloseHandle(hSession); + } + + return success; + } +} diff --git a/Romeo/src/HttpClient.h b/Romeo/src/HttpClient.h new file mode 100644 index 0000000..f9c1e68 --- /dev/null +++ b/Romeo/src/HttpClient.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace Romeo +{ + class HttpClient + { + public: + // Sends a POST request over HTTP/HTTPS. + // Response data is allocated on the provided arena and returned in outResponse. + [[nodiscard]] static bool Post( + Juliet::Arena* arena, + const Juliet::String& host, + int port, + const Juliet::String& path, + bool isHttps, + const Juliet::String& requestBody, + Juliet::String& outResponse + ); + }; +} diff --git a/Romeo/src/JsonParser.cpp b/Romeo/src/JsonParser.cpp new file mode 100644 index 0000000..ea3d80f --- /dev/null +++ b/Romeo/src/JsonParser.cpp @@ -0,0 +1,467 @@ +#include "JsonParser.h" +#include +#include +#include +#include +#include +#include + +namespace Romeo +{ + struct JsonParserState + { + Juliet::Arena* Arena; + const char* Ptr; + const char* End; + }; + + static void SkipWhitespace(JsonParserState& state) + { + while (state.Ptr < state.End) + { + char c = *state.Ptr; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + { + state.Ptr++; + } + else + { + break; + } + } + } + + static bool ParseString(JsonParserState& state, Juliet::String& outStr) + { + if (state.Ptr >= state.End || *state.Ptr != '"') + { + return false; + } + state.Ptr++; + const char* start = state.Ptr; + + size_t len = 0; + bool hasEscape = false; + while (state.Ptr < state.End) + { + char c = *state.Ptr; + if (c == '"') + { + break; + } + else if (c == '\\') + { + hasEscape = true; + state.Ptr++; + if (state.Ptr >= state.End) + { + return false; + } + } + state.Ptr++; + len++; + } + + if (state.Ptr >= state.End) + { + return false; + } + + const char* endQuote = state.Ptr; + state.Ptr++; + + char* buf = Juliet::ArenaPushArray(state.Arena, len + 1 JULIET_DEBUG_PARAM("JsonParsedStr")); + + if (hasEscape) + { + size_t writeIdx = 0; + const char* readPtr = start; + while (readPtr < endQuote) + { + char c = *readPtr; + if (c == '\\') + { + readPtr++; + if (readPtr >= endQuote) + { + break; + } + char esc = *readPtr; + if (esc == 'n') { buf[writeIdx++] = '\n'; } + else if (esc == 'r') { buf[writeIdx++] = '\r'; } + else if (esc == 't') { buf[writeIdx++] = '\t'; } + else if (esc == 'b') { buf[writeIdx++] = '\b'; } + else if (esc == 'f') { buf[writeIdx++] = '\f'; } + else if (esc == '"') { buf[writeIdx++] = '"'; } + else if (esc == '\\') { buf[writeIdx++] = '\\'; } + else if (esc == '/') { buf[writeIdx++] = '/'; } + else if (esc == 'u') + { + if (readPtr + 4 < endQuote) + { + buf[writeIdx++] = '?'; + readPtr += 4; + } + } + else + { + buf[writeIdx++] = esc; + } + } + else + { + buf[writeIdx++] = c; + } + readPtr++; + } + buf[writeIdx] = '\0'; + outStr.Data = buf; + outStr.Size = writeIdx; + } + else + { + memcpy(buf, start, len); + buf[len] = '\0'; + outStr.Data = buf; + outStr.Size = len; + } + + return true; + } + + static bool ParseValue(JsonParserState& state, JsonValue& outVal); + + static bool ParseObject(JsonParserState& state, JsonValue& outVal) + { + if (state.Ptr >= state.End || *state.Ptr != '{') + { + return false; + } + state.Ptr++; + outVal.Type = JsonType::Object; + outVal.ObjectVal.Create(state.Arena JULIET_DEBUG_ONLY(, "JsonObject")); + + while (state.Ptr < state.End) + { + SkipWhitespace(state); + if (state.Ptr < state.End && *state.Ptr == '}') + { + state.Ptr++; + return true; + } + + Juliet::String key = {}; + if (!ParseString(state, key)) + { + return false; + } + + SkipWhitespace(state); + if (state.Ptr >= state.End || *state.Ptr != ':') + { + return false; + } + state.Ptr++; + + JsonKeyValue kv = {}; + kv.Key = key; + kv.Value = Juliet::ArenaPushArray(state.Arena, 1 JULIET_DEBUG_PARAM("JsonValueRef")); + new (kv.Value) JsonValue(); + + if (!ParseValue(state, *kv.Value)) + { + return false; + } + + outVal.ObjectVal.PushBack(kv); + + SkipWhitespace(state); + if (state.Ptr < state.End && *state.Ptr == ',') + { + state.Ptr++; + } + else if (state.Ptr < state.End && *state.Ptr == '}') + { + state.Ptr++; + return true; + } + else + { + return false; + } + } + return false; + } + + static bool ParseArray(JsonParserState& state, JsonValue& outVal) + { + if (state.Ptr >= state.End || *state.Ptr != '[') + { + return false; + } + state.Ptr++; + outVal.Type = JsonType::Array; + outVal.ArrayVal.Create(state.Arena JULIET_DEBUG_ONLY(, "JsonArray")); + + while (state.Ptr < state.End) + { + SkipWhitespace(state); + if (state.Ptr < state.End && *state.Ptr == ']') + { + state.Ptr++; + return true; + } + + JsonValue val = {}; + if (!ParseValue(state, val)) + { + return false; + } + + outVal.ArrayVal.PushBack(val); + + SkipWhitespace(state); + if (state.Ptr < state.End && *state.Ptr == ',') + { + state.Ptr++; + } + else if (state.Ptr < state.End && *state.Ptr == ']') + { + state.Ptr++; + return true; + } + else + { + return false; + } + } + return false; + } + + static bool ParseNumber(JsonParserState& state, JsonValue& outVal) + { + const char* start = state.Ptr; + while (state.Ptr < state.End) + { + char c = *state.Ptr; + if (isdigit(c) || c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E') + { + state.Ptr++; + } + else + { + break; + } + } + size_t len = static_cast(state.Ptr - start); + if (len == 0) + { + return false; + } + + char temp[64]; + if (len >= sizeof(temp)) + { + return false; + } + memcpy(temp, start, len); + temp[len] = '\0'; + + char* endPtr = nullptr; + outVal.NumVal = strtod(temp, &endPtr); + if (endPtr == temp) + { + return false; + } + outVal.Type = JsonType::Number; + return true; + } + + static bool ParseValue(JsonParserState& state, JsonValue& outVal) + { + SkipWhitespace(state); + if (state.Ptr >= state.End) + { + return false; + } + + char c = *state.Ptr; + if (c == '{') + { + return ParseObject(state, outVal); + } + else if (c == '[') + { + return ParseArray(state, outVal); + } + else if (c == '"') + { + outVal.Type = JsonType::String; + return ParseString(state, outVal.StrVal); + } + else if (c == 't' || c == 'f') + { + size_t remaining = static_cast(state.End - state.Ptr); + if (c == 't' && remaining >= 4 && strncmp(state.Ptr, "true", 4) == 0) + { + outVal.Type = JsonType::Bool; + outVal.BoolVal = true; + state.Ptr += 4; + return true; + } + else if (c == 'f' && remaining >= 5 && strncmp(state.Ptr, "false", 5) == 0) + { + outVal.Type = JsonType::Bool; + outVal.BoolVal = false; + state.Ptr += 5; + return true; + } + } + else if (c == 'n') + { + size_t remaining = static_cast(state.End - state.Ptr); + if (remaining >= 4 && strncmp(state.Ptr, "null", 4) == 0) + { + outVal.Type = JsonType::Null; + state.Ptr += 4; + return true; + } + } + else if (isdigit(c) || c == '-') + { + return ParseNumber(state, outVal); + } + + return false; + } + + bool Json_Parse(Juliet::Arena* arena, const Juliet::String& jsonStr, JsonValue& outValue) + { + Assert(arena != nullptr); + if (jsonStr.Size == 0) + { + return false; + } + + JsonParserState state; + state.Arena = arena; + state.Ptr = CStr(jsonStr); + state.End = state.Ptr + jsonStr.Size; + + return ParseValue(state, outValue); + } + + bool Json_Query(const JsonValue& root, const char* path, Juliet::String& outResult) + { + Assert(path != nullptr); + + const JsonValue* current = &root; + const char* p = path; + + while (*p) + { + const char* start = p; + while (*p && *p != '/') + { + p++; + } + size_t len = static_cast(p - start); + if (len == 0) + { + if (*p == '/') { p++; } + continue; + } + + if (current->Type == JsonType::Object) + { + const JsonValue* next = nullptr; + for (const JsonKeyValue& kv : current->ObjectVal) + { + if (kv.Key.Size == len && strncmp(CStr(kv.Key), start, len) == 0) + { + next = kv.Value; + break; + } + } + if (!next) + { + return false; + } + current = next; + } + else if (current->Type == JsonType::Array) + { + char tempIndex[32]; + if (len >= sizeof(tempIndex)) + { + return false; + } + memcpy(tempIndex, start, len); + tempIndex[len] = '\0'; + + int index = atoi(tempIndex); + if (index < 0 || static_cast(index) >= current->ArrayVal.Size()) + { + return false; + } + current = ¤t->ArrayVal[static_cast(index)]; + } + else + { + return false; + } + + if (*p == '/') + { + p++; + } + } + + if (current->Type == JsonType::String) + { + outResult = current->StrVal; + return true; + } + + return false; + } + + Juliet::String Json_EscapeString(Juliet::Arena* arena, const Juliet::String& str) + { + Assert(arena != nullptr); + + size_t escLen = 0; + const char* p = CStr(str); + const char* end = p + str.Size; + while (p < end) + { + char c = *p; + if (c == '"' || c == '\\' || c == '\n' || c == '\r' || c == '\t') + { + escLen += 2; + } + else + { + escLen += 1; + } + p++; + } + + char* buf = Juliet::ArenaPushArray(arena, escLen + 1 JULIET_DEBUG_PARAM("JsonEscapedStr")); + char* dst = buf; + p = CStr(str); + while (p < end) + { + char c = *p; + if (c == '"') { *dst++ = '\\'; *dst++ = '"'; } + else if (c == '\\') { *dst++ = '\\'; *dst++ = '\\'; } + else if (c == '\n') { *dst++ = '\\'; *dst++ = 'n'; } + else if (c == '\r') { *dst++ = '\\'; *dst++ = 'r'; } + else if (c == '\t') { *dst++ = '\\'; *dst++ = 't'; } + else { *dst++ = c; } + p++; + } + *dst = '\0'; + return { buf, escLen }; + } +} diff --git a/Romeo/src/JsonParser.h b/Romeo/src/JsonParser.h new file mode 100644 index 0000000..05aa3bb --- /dev/null +++ b/Romeo/src/JsonParser.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace Romeo +{ + enum class JsonType + { + Null, + Bool, + Number, + String, + Array, + Object + }; + + struct JsonValue; + + struct JsonKeyValue + { + Juliet::String Key; + // Pointer is used here since JsonValue contains JsonKeyValue, forming mutual dependency + JsonValue* Value = nullptr; + }; + + struct JsonValue + { + JsonType Type = JsonType::Null; + bool BoolVal = false; + double NumVal = 0.0; + Juliet::String StrVal = {}; + + Juliet::VectorArena ArrayVal = {}; + Juliet::VectorArena ObjectVal = {}; + }; + + // Parses a JSON string. Memory is allocated on the provided arena. + [[nodiscard]] bool Json_Parse(Juliet::Arena* arena, const Juliet::String& jsonStr, JsonValue& outValue); + + // Queries a nested string value by path (e.g., "candidates/0/content/parts/0/text" or "response"). + [[nodiscard]] bool Json_Query(const JsonValue& root, const char* path, Juliet::String& outResult); + + // Escapes a string to be safely embedded inside JSON. + [[nodiscard]] Juliet::String Json_EscapeString(Juliet::Arena* arena, const Juliet::String& str); +} diff --git a/Romeo/src/MarkdownGenerator.cpp b/Romeo/src/MarkdownGenerator.cpp new file mode 100644 index 0000000..e1af751 --- /dev/null +++ b/Romeo/src/MarkdownGenerator.cpp @@ -0,0 +1,1033 @@ +#include "MarkdownGenerator.h" +#include "AiGenerator.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Romeo +{ + struct ParsedSymbol + { + Juliet::String Name; + Juliet::String ReturnType; + Juliet::String Parameters; + Juliet::String Type; // "class", "struct", "union", "enum", "enum class", "function" + Juliet::String Namespace; + Juliet::String ClassContext; + bool IsConst = false; + }; + + struct Token + { + const char* Start = nullptr; + size_t Length = 0; + bool IsIdentifier = false; + }; + + static void EnsureDirectoryExists(const char* dir) + { + CreateDirectoryA(dir, nullptr); + } + + struct StringBuilder + { + Juliet::Arena* Arena; + char* Buffer = nullptr; + size_t Size = 0; + size_t Capacity = 0; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" + void Append(const char* format, ...) + { + va_list args; + va_start(args, format); + int len = vsnprintf(nullptr, 0, format, args); + va_end(args); + + if (len <= 0) return; + + size_t uLen = static_cast(len); + + if (Size + uLen + 1 > Capacity) + { + size_t newCap = Capacity == 0 ? 4096 : Capacity * 2; + while (Size + uLen + 1 > newCap) + { + newCap *= 2; + } + char* newBuf = Juliet::ArenaPushArray(Arena, newCap JULIET_DEBUG_PARAM("StringBuilder")); + if (Buffer) + { + memcpy(newBuf, Buffer, Size); + } + Buffer = newBuf; + Capacity = newCap; + } + + va_start(args, format); + vsnprintf(Buffer + Size, uLen + 1, format, args); + va_end(args); + Size += uLen; + Buffer[Size] = '\0'; + } +#pragma clang diagnostic pop + }; + + static Juliet::String ReadFileContent(Juliet::Arena* arena, const char* pathBuf) + { + FILE* f = nullptr; + if (fopen_s(&f, pathBuf, "rb") != 0 || !f) + { + return {}; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + if (size <= 0) + { + fclose(f); + return {}; + } + + char* buf = Juliet::ArenaPushArray(arena, static_cast(size + 1) JULIET_DEBUG_PARAM("FileReadContent")); + size_t readBytes = fread(buf, 1, static_cast(size), f); + buf[readBytes] = '\0'; + + fclose(f); + return { buf, readBytes }; + } + + static Juliet::String GetRelativeString(const Juliet::String& absolutePath, const Juliet::String& workspaceRoot) + { + if (absolutePath.Size > workspaceRoot.Size) + { + const char* absPtr = CStr(absolutePath); + const char* rootPtr = CStr(workspaceRoot); + if (_strnicmp(absPtr, rootPtr, workspaceRoot.Size) == 0) + { + const char* rel = absPtr + workspaceRoot.Size; + size_t newSize = absolutePath.Size - workspaceRoot.Size; + if (*rel == '\\' || *rel == '/') { rel++; newSize--; } + return { const_cast(rel), newSize }; + } + } + return absolutePath; + } + + static Juliet::String BuildMarkdownFileName(Juliet::Arena* arena, const char* relativeBaseName, size_t baseLen) + { + char* buf = Juliet::ArenaPushArray(arena, baseLen + 4 JULIET_DEBUG_PARAM("MdFileName")); + for (size_t i = 0; i < baseLen; ++i) + { + char c = relativeBaseName[i]; + if (c == '\\' || c == '/' || c == ':') + { + buf[i] = '_'; + } + else + { + buf[i] = c; + } + } + buf[baseLen] = '.'; buf[baseLen+1] = 'm'; buf[baseLen+2] = 'd'; buf[baseLen+3] = '\0'; + return {buf, baseLen + 3}; + } + + static Juliet::String Trim(const char* start, const char* end) + { + if (end < start) + { + return {}; + } + while (start < end && (*start == ' ' || *start == '\t' || *start == '\n' || *start == '\r')) + { + start++; + } + while (end > start && (*(end - 1) == ' ' || *(end - 1) == '\t' || *(end - 1) == '\n' || *(end - 1) == '\r')) + { + end--; + } + return { const_cast(start), static_cast(end - start) }; + } + + static const char* SkipCommentsAndStrings(const char* p, const char* end) + { + while (p < end) + { + if (*p == '/' && p + 1 < end && *(p + 1) == '/') + { + p += 2; + while (p < end && *p != '\n' && *p != '\r') { p++; } + } + else if (*p == '/' && p + 1 < end && *(p + 1) == '*') + { + p += 2; + while (p < end && !(*p == '*' && p + 1 < end && *(p + 1) == '/')) { p++; } + if (p < end) { p += 2; } + } + else if (*p == '"') + { + p++; + while (p < end && *p != '"') + { + if (*p == '\\') { p += 2; } + else { p++; } + } + if (p < end) { p++; } + } + else if (*p == '\'') + { + p++; + while (p < end && *p != '\'') + { + if (*p == '\\') { p += 2; } + else { p++; } + } + if (p < end) { p++; } + } + else + { + break; + } + } + return p; + } + + static const char* GetNextToken(const char* p, const char* end, Token& token) + { + p = SkipCommentsAndStrings(p, end); + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) + { + p = SkipCommentsAndStrings(p + 1, end); + } + + if (p >= end) + { + token.Start = nullptr; + token.Length = 0; + token.IsIdentifier = false; + return end; + } + + token.Start = p; + + char first = *p; + if (isalpha(first) || first == '_' || first == '~') + { + token.IsIdentifier = true; + p++; + while (p < end && (isalnum(*p) || *p == '_' || *p == ':')) + { + p++; + } + token.Length = static_cast(p - token.Start); + } + else + { + token.IsIdentifier = false; + p++; + token.Length = 1; + } + + return p; + } + + static bool IsForwardDeclaration(const char* p, const char* end) + { + p = SkipCommentsAndStrings(p, end); + while (p < end) + { + char c = *p; + if (c == ';') + { + return true; + } + if (c == '{' || c == ':') + { + return false; + } + p = SkipCommentsAndStrings(p + 1, end); + } + return false; + } + + static const char* FindMatchingParen(const char* p, const char* end) + { + if (p == nullptr) { return nullptr; } + int parenDepth = 1; + p = SkipCommentsAndStrings(p, end); + while (p < end && parenDepth > 0) + { + char c = *p; + if (c == '(') { parenDepth++; } + else if (c == ')') { parenDepth--; } + p = SkipCommentsAndStrings(p + 1, end); + } + return p; + } + + static bool IsKeyword(const Token& tk) + { + if (!tk.IsIdentifier) { return false; } + const char* keywords[] = { "if", "for", "while", "switch", "catch", "template", "return", "throw", "sizeof", "decltype" }; + for (const char* kw : keywords) + { + if (tk.Length == strlen(kw) && strncmp(tk.Start, kw, tk.Length) == 0) + { + return true; + } + } + return false; + } + + static bool IsFunctionFollowup(const char* p, const char* end, bool& opensBody, bool& isConst) + { + if (p == nullptr) { return false; } + p = SkipCommentsAndStrings(p, end); + if (p >= end) { return false; } + + char c = *p; + if (c == ';') { opensBody = false; return true; } + if (c == '{') { opensBody = true; return true; } + if (c == ':') { opensBody = true; return true; } + + Token t; + const char* next = GetNextToken(p, end, t); + if (t.Length > 0) + { + if (t.IsIdentifier) + { + const char* kw[] = { "const", "override", "final", "noexcept", "throw" }; + for (const char* k : kw) + { + if (t.Length == strlen(k) && strncmp(t.Start, k, t.Length) == 0) + { + if (strcmp(k, "const") == 0) { isConst = true; } + return IsFunctionFollowup(next, end, opensBody, isConst); + } + } + } + else if (t.Length == 1 && t.Start[0] == '=') + { + opensBody = false; + return true; + } + } + return false; + } + + struct NamespaceScope + { + char Name[64]; + int OpenBraceDepth; + }; + + struct ClassScope + { + char Name[64]; + int OpenBraceDepth; + }; + + static void GetCurrentNamespace(char* buf, size_t maxLen, NamespaceScope* stack, int count) + { + buf[0] = '\0'; + size_t written = 0; + for (int i = 0; i < count; ++i) + { + if (i > 0) + { + if (written + 2 < maxLen) + { + strcat_s(buf, maxLen, "::"); + written += 2; + } + } + size_t nameLen = strlen(stack[i].Name); + if (written + nameLen < maxLen) + { + strcat_s(buf, maxLen, stack[i].Name); + written += nameLen; + } + } + } + + static void GetCurrentClassContext(char* buf, size_t maxLen, ClassScope* stack, int count) + { + buf[0] = '\0'; + size_t written = 0; + for (int i = 0; i < count; ++i) + { + if (i > 0) + { + if (written + 2 < maxLen) + { + strcat_s(buf, maxLen, "::"); + written += 2; + } + } + size_t nameLen = strlen(stack[i].Name); + if (written + nameLen < maxLen) + { + strcat_s(buf, maxLen, stack[i].Name); + written += nameLen; + } + } + } + + static void ParseSymbolsFromFile( + Juliet::Arena* arena, + const Juliet::String& filePath, + Juliet::VectorArena& outSymbols + ) + { + Assert(arena != nullptr); + + char pathBuf[512]; + size_t copyLen = filePath.Size < 511 ? filePath.Size : 511; + memcpy(pathBuf, filePath.Data, copyLen); + pathBuf[copyLen] = '\0'; + + FILE* srcFile = nullptr; + if (fopen_s(&srcFile, pathBuf, "rb") != 0 || !srcFile) { return; } + + fseek(srcFile, 0, SEEK_END); + long fileSize = ftell(srcFile); + fseek(srcFile, 0, SEEK_SET); + + if (fileSize <= 0) + { + fclose(srcFile); + return; + } + + char* buffer = Juliet::ArenaPushArray(arena, static_cast(fileSize) + 1 JULIET_DEBUG_PARAM("SourceFileBuffer")); + size_t readBytes = fread(buffer, 1, static_cast(fileSize), srcFile); + buffer[readBytes] = '\0'; + fclose(srcFile); + + const char* start = buffer; + const char* end = buffer + readBytes; + const char* cursor = start; + + Token tokenHistory[8] = {}; + + NamespaceScope namespaceStack[16] = {}; + int namespaceStackCount = 0; + + ClassScope classStack[16] = {}; + int classStackCount = 0; + + char pendingNamespace[64] = ""; + char pendingClass[64] = ""; + char pendingClassType[16] = ""; + + int braceDepth = 0; + bool inFunctionBody = false; + int functionStartBraceDepth = -1; + + const char* lastStatementEnd = start; + + Token t; + while (cursor < end) + { + cursor = GetNextToken(cursor, end, t); + if (t.Length == 0) { break; } + + // Shift token history + for (int i = 7; i > 0; --i) + { + tokenHistory[i] = tokenHistory[i - 1]; + } + tokenHistory[0] = t; + + if (t.IsIdentifier) + { + if (strncmp(t.Start, "namespace", t.Length) == 0 && !inFunctionBody) + { + Token nameT; + cursor = GetNextToken(cursor, end, nameT); + if (nameT.Length > 0 && nameT.IsIdentifier) + { + size_t copySize = nameT.Length < 63 ? nameT.Length : 63; + memcpy(pendingNamespace, nameT.Start, copySize); + pendingNamespace[copySize] = '\0'; + } + else + { + strcpy_s(pendingNamespace, "anonymous"); + // backtracking cursor if we read a brace or something else + cursor = nameT.Start; + } + } + else if ((strncmp(t.Start, "class", t.Length) == 0 || + strncmp(t.Start, "struct", t.Length) == 0 || + strncmp(t.Start, "union", t.Length) == 0) && !inFunctionBody) + { + // Check if friend declaration + bool isFriend = false; + if (tokenHistory[1].Length == 6 && strncmp(tokenHistory[1].Start, "friend", 6) == 0) + { + isFriend = true; + } + + if (!isFriend) + { + size_t typeLen = t.Length < 15 ? t.Length : 15; + memcpy(pendingClassType, t.Start, typeLen); + pendingClassType[typeLen] = '\0'; + + Token nameT; + cursor = GetNextToken(cursor, end, nameT); + + // Handle API export macros (e.g. JULIET_API) by skipping them + if (nameT.Length > 0 && nameT.IsIdentifier && + nameT.Length > 4 && strncmp(nameT.Start + nameT.Length - 4, "_API", 4) == 0) + { + cursor = GetNextToken(cursor, end, nameT); + } + + if (nameT.Length > 0 && nameT.IsIdentifier) + { + if (!IsForwardDeclaration(nameT.Start + nameT.Length, end)) + { + size_t copySize = nameT.Length < 63 ? nameT.Length : 63; + memcpy(pendingClass, nameT.Start, copySize); + pendingClass[copySize] = '\0'; + } + } + else + { + strcpy_s(pendingClass, "anonymous"); + cursor = nameT.Start; + } + } + } + else if (strncmp(t.Start, "enum", t.Length) == 0 && !inFunctionBody) + { + Token classT; + const char* nextCursor = GetNextToken(cursor, end, classT); + bool isEnumClass = false; + if (classT.Length == 5 && strncmp(classT.Start, "class", 5) == 0) + { + isEnumClass = true; + cursor = nextCursor; + } + + Token nameT; + cursor = GetNextToken(cursor, end, nameT); + if (nameT.Length > 0 && nameT.IsIdentifier) + { + ParsedSymbol sym = {}; + sym.Name = Juliet::StringCopy(arena, { const_cast(nameT.Start), nameT.Length }); + if (isEnumClass) + { + sym.Type = ConstString("enum class"); + } + else + { + sym.Type = ConstString("enum"); + } + + char nsBuf[128]; + GetCurrentNamespace(nsBuf, sizeof(nsBuf), namespaceStack, namespaceStackCount); + sym.Namespace = Juliet::StringCopy(arena, Juliet::WrapString(nsBuf)); + + char clBuf[128]; + GetCurrentClassContext(clBuf, sizeof(clBuf), classStack, classStackCount); + sym.ClassContext = Juliet::StringCopy(arena, Juliet::WrapString(clBuf)); + + outSymbols.PushBack(sym); + } + } + else if ((strncmp(t.Start, "public", t.Length) == 0 || + strncmp(t.Start, "private", t.Length) == 0 || + strncmp(t.Start, "protected", t.Length) == 0) && !inFunctionBody) + { + // Check for trailing colon + Token colonT; + const char* tempC = GetNextToken(cursor, end, colonT); + if (colonT.Length == 1 && colonT.Start[0] == ':') + { + cursor = tempC; + lastStatementEnd = cursor; + } + } + } + else if (t.Length == 1) + { + char sym = t.Start[0]; + if (sym == '{') + { + braceDepth++; + if (!inFunctionBody) + { + if (pendingNamespace[0] != '\0' && namespaceStackCount < 16) + { + strcpy_s(namespaceStack[namespaceStackCount].Name, pendingNamespace); + namespaceStack[namespaceStackCount].OpenBraceDepth = braceDepth; + namespaceStackCount++; + pendingNamespace[0] = '\0'; + } + else if (pendingClass[0] != '\0' && classStackCount < 16) + { + strcpy_s(classStack[classStackCount].Name, pendingClass); + classStack[classStackCount].OpenBraceDepth = braceDepth; + classStackCount++; + + // Also store the class itself as a symbol! + ParsedSymbol classSym = {}; + classSym.Name = Juliet::StringCopy(arena, Juliet::WrapString(classStack[classStackCount-1].Name)); + classSym.Type = Juliet::StringCopy(arena, Juliet::WrapString(pendingClassType)); + + char nsBuf[128]; + GetCurrentNamespace(nsBuf, sizeof(nsBuf), namespaceStack, namespaceStackCount); + classSym.Namespace = Juliet::StringCopy(arena, Juliet::WrapString(nsBuf)); + + char clBuf[128]; + // Get class context *excluding* the one we just pushed + GetCurrentClassContext(clBuf, sizeof(clBuf), classStack, classStackCount - 1); + classSym.ClassContext = Juliet::StringCopy(arena, Juliet::WrapString(clBuf)); + + outSymbols.PushBack(classSym); + + pendingClass[0] = '\0'; + } + else + { + inFunctionBody = true; + functionStartBraceDepth = braceDepth; + } + } + lastStatementEnd = cursor; + } + else if (sym == '}') + { + if (inFunctionBody) + { + if (braceDepth == functionStartBraceDepth) + { + inFunctionBody = false; + functionStartBraceDepth = -1; + } + } + else + { + if (classStackCount > 0 && classStack[classStackCount - 1].OpenBraceDepth == braceDepth) + { + classStackCount--; + } + else if (namespaceStackCount > 0 && namespaceStack[namespaceStackCount - 1].OpenBraceDepth == braceDepth) + { + namespaceStackCount--; + } + } + braceDepth--; + lastStatementEnd = cursor; + } + else if (sym == ';') + { + pendingNamespace[0] = '\0'; + pendingClass[0] = '\0'; + lastStatementEnd = cursor; + } + else if (sym == '(' && !inFunctionBody) + { + Token funcNameT = tokenHistory[1]; + if (funcNameT.Length > 0 && funcNameT.IsIdentifier && !IsKeyword(funcNameT)) + { + const char* matchingParen = FindMatchingParen(t.Start + 1, end); + + bool opensBody = false; + bool isConst = false; + if (matchingParen != nullptr && IsFunctionFollowup(matchingParen, end, opensBody, isConst)) + { + Juliet::String returnTypeRaw = Trim(lastStatementEnd, funcNameT.Start); + Juliet::String paramsRaw = Trim(t.Start + 1, matchingParen - 1); + + char activeClass[128] = ""; + GetCurrentClassContext(activeClass, sizeof(activeClass), classStack, classStackCount); + + bool hasReturnType = (returnTypeRaw.Size > 0); + bool isConstructorOrDestructor = false; + + if (classStackCount > 0) + { + const char* className = classStack[classStackCount - 1].Name; + size_t cNameLen = strlen(className); + + if (funcNameT.Length == cNameLen && strncmp(funcNameT.Start, className, cNameLen) == 0) + { + isConstructorOrDestructor = true; + } + else if (funcNameT.Length == cNameLen + 1 && funcNameT.Start[0] == '~' && + strncmp(funcNameT.Start + 1, className, cNameLen) == 0) + { + isConstructorOrDestructor = true; + } + } + + if (hasReturnType || isConstructorOrDestructor) + { + ParsedSymbol funcSym = {}; + funcSym.Name = Juliet::StringCopy(arena, { const_cast(funcNameT.Start), funcNameT.Length }); + funcSym.ReturnType = Juliet::StringCopy(arena, returnTypeRaw); + funcSym.Parameters = Juliet::StringCopy(arena, paramsRaw); + funcSym.Type = ConstString("function"); + funcSym.IsConst = isConst; + + char nsBuf[128]; + GetCurrentNamespace(nsBuf, sizeof(nsBuf), namespaceStack, namespaceStackCount); + funcSym.Namespace = Juliet::StringCopy(arena, Juliet::WrapString(nsBuf)); + + funcSym.ClassContext = Juliet::StringCopy(arena, Juliet::WrapString(activeClass)); + + outSymbols.PushBack(funcSym); + } + } + } + } + } + } + } + + static void FormatAndWriteSymbols(StringBuilder& sb, Juliet::Arena* arena, const Juliet::String& filePath) + { + printf("Romeo: FormatAndWriteSymbols start for %.*s\n", static_cast(filePath.Size), CStr(filePath)); + Juliet::VectorArena symbols = {}; + symbols.Create(arena JULIET_DEBUG_ONLY(, "ParsedSymbols")); + + printf("Romeo: Calling ParseSymbolsFromFile...\n"); + ParseSymbolsFromFile(arena, filePath, symbols); + printf("Romeo: ParseSymbolsFromFile returned, symbols count: %zu\n", symbols.Size()); + + if (symbols.Size() == 0) + { + sb.Append("*No symbols detected.*\n"); + return; + } + + // Collect all distinct namespaces + printf("Romeo: Collecting namespaces...\n"); + Juliet::VectorArena namespaces = {}; + namespaces.Create(arena JULIET_DEBUG_ONLY(, "NamespacesList")); + + for (const ParsedSymbol& sym : symbols) + { + bool found = false; + for (const Juliet::String& ns : namespaces) + { + if (Juliet::StringCompare(ns, sym.Namespace) == 0) + { + found = true; + break; + } + } + if (!found) + { + namespaces.PushBack(sym.Namespace); + } + } + printf("Romeo: Namespace collection done.\n"); + + for (const Juliet::String& ns : namespaces) + { + printf("Romeo: Formatting namespace %.*s...\n", static_cast(ns.Size), CStr(ns)); + if (ns.Size > 0) + { + sb.Append("### Namespace `%.*s`\n\n", static_cast(ns.Size), CStr(ns)); + } + else + { + sb.Append("### Global Scope\n\n"); + } + + // Print Structs/Classes/Unions + bool hasTypes = false; + for (const ParsedSymbol& sym : symbols) + { + if (Juliet::StringCompare(sym.Namespace, ns) == 0 && + (Juliet::StringCompare(sym.Type, ConstString("class")) == 0 || + Juliet::StringCompare(sym.Type, ConstString("struct")) == 0 || + Juliet::StringCompare(sym.Type, ConstString("union")) == 0)) + { + if (!hasTypes) + { + sb.Append("#### Classes, Structs & Unions\n"); + hasTypes = true; + } + if (sym.ClassContext.Size > 0) + { + sb.Append("- `%.*s %.*s::%.*s`\n", + static_cast(sym.Type.Size), CStr(sym.Type), + static_cast(sym.ClassContext.Size), CStr(sym.ClassContext), + static_cast(sym.Name.Size), CStr(sym.Name)); + } + else + { + sb.Append("- `%.*s %.*s`\n", + static_cast(sym.Type.Size), CStr(sym.Type), + static_cast(sym.Name.Size), CStr(sym.Name)); + } + } + } + if (hasTypes) { sb.Append("\n"); } + + // Print Enums + bool hasEnums = false; + for (const ParsedSymbol& sym : symbols) + { + if (Juliet::StringCompare(sym.Namespace, ns) == 0 && + (Juliet::StringCompare(sym.Type, ConstString("enum")) == 0 || + Juliet::StringCompare(sym.Type, ConstString("enum class")) == 0)) + { + if (!hasEnums) + { + sb.Append("#### Enums\n"); + hasEnums = true; + } + if (sym.ClassContext.Size > 0) + { + sb.Append("- `%.*s %.*s::%.*s`\n", + static_cast(sym.Type.Size), CStr(sym.Type), + static_cast(sym.ClassContext.Size), CStr(sym.ClassContext), + static_cast(sym.Name.Size), CStr(sym.Name)); + } + else + { + sb.Append("- `%.*s %.*s`\n", + static_cast(sym.Type.Size), CStr(sym.Type), + static_cast(sym.Name.Size), CStr(sym.Name)); + } + } + } + if (hasEnums) { sb.Append("\n"); } + + // Print Functions/Methods + bool hasFuncs = false; + for (const ParsedSymbol& sym : symbols) + { + if (Juliet::StringCompare(sym.Namespace, ns) == 0 && + Juliet::StringCompare(sym.Type, ConstString("function")) == 0) + { + if (!hasFuncs) + { + sb.Append("#### Functions & Methods\n"); + hasFuncs = true; + } + + sb.Append("- `"); + if (sym.ReturnType.Size > 0) + { + sb.Append("%.*s ", static_cast(sym.ReturnType.Size), CStr(sym.ReturnType)); + } + if (sym.ClassContext.Size > 0) + { + sb.Append("%.*s::", static_cast(sym.ClassContext.Size), CStr(sym.ClassContext)); + } + sb.Append("%.*s(%.*s)%s`\n", + static_cast(sym.Name.Size), CStr(sym.Name), + static_cast(sym.Parameters.Size), CStr(sym.Parameters), + sym.IsConst ? " const" : ""); + } + } + if (hasFuncs) { sb.Append("\n"); } + } + } + + void MD_GenerateDocumentation(Database& db, const Juliet::String& workspaceRoot, const Juliet::String& docsDir) + { + EnsureDirectoryExists(CStr(docsDir)); + + char overviewPath[512]; + snprintf(overviewPath, sizeof(overviewPath), "%s\\overview.md", CStr(docsDir)); + + FILE* overviewFile = nullptr; + if (fopen_s(&overviewFile, overviewPath, "w") != 0 || !overviewFile) + { + return; + } + + fprintf(overviewFile, "# Juliet C++ Codebase Overview\n\n"); + fprintf(overviewFile, "This is an auto-generated index of all C++ sources in the workspace.\n\n"); + fprintf(overviewFile, "## Files\n\n"); + + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uint64_t currentTime = (static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + + // Temporary arena for parsing symbols and formatting + Juliet::ArenaParams params{}; + Juliet::Arena* tempArena = Juliet::ArenaAllocate(params JULIET_DEBUG_PARAM("MdGeneratorTempArena")); + Assert(tempArena != nullptr); + + // Separate arena for AI generation (gets cleared after each file to prevent exhaustion) + Juliet::ArenaParams aiParams{}; + Juliet::Arena* aiArena = Juliet::ArenaAllocate(aiParams JULIET_DEBUG_PARAM("AiGeneratorArena")); + Assert(aiArena != nullptr); + + size_t fileIndex = 0; + size_t totalFiles = db.Entries.Size(); + + for (FileEntry& entry : db.Entries) + { + fileIndex++; + const Juliet::String& primaryPath = (entry.HeaderPath.Size > 0) ? entry.HeaderPath : entry.CppPath; + if (primaryPath.Size == 0) + { + continue; + } + + bool hasCpp = (entry.CppPath.Size > 0); + bool isHeaderInSrc = (entry.HeaderPath.Size > 0 && strstr(CStr(entry.HeaderPath), "\\src\\") != nullptr); + if (isHeaderInSrc && !hasCpp) + { + entry.LastMarkdownCreatedTime = currentTime; + continue; + } + + Juliet::String relStr = GetRelativeString(primaryPath, workspaceRoot); + + char baseName[512]; + size_t copyLen = relStr.Size < 511 ? relStr.Size : 511; + memcpy(baseName, relStr.Data, copyLen); + baseName[copyLen] = '\0'; + + size_t baseLen = strlen(baseName); + if (baseLen > 4 && strcmp(baseName + baseLen - 4, ".cpp") == 0) + { + baseName[baseLen - 4] = '\0'; + baseLen -= 4; + } + if (baseLen > 2 && strcmp(baseName + baseLen - 2, ".h") == 0) + { + baseName[baseLen - 2] = '\0'; + baseLen -= 2; + } + + Juliet::String mdFileName = BuildMarkdownFileName(db.DbArena, baseName, baseLen); + + fprintf(overviewFile, "- [%s](%s)\n", baseName, CStr(mdFileName)); + + bool needsUpdate = (entry.LastMarkdownCreatedTime == 0) || + (entry.Description.Size == 0) || + (entry.LastModifiedHeader > entry.LastMarkdownCreatedTime) || + (entry.LastModifiedCpp > entry.LastMarkdownCreatedTime); + + if (needsUpdate) + { + bool aiNeedsUpdate = false; + if (entry.Description.Size == 0) + { + aiNeedsUpdate = true; + } + else if (entry.HeaderPath.Size > 0) + { + if (entry.LastModifiedHeader > entry.LastAIGeneratedTime) + { + aiNeedsUpdate = true; + } + } + else if (entry.CppPath.Size > 0) + { + if (entry.LastModifiedCpp > entry.LastAIGeneratedTime) + { + aiNeedsUpdate = true; + } + } + + if (aiNeedsUpdate) + { + printf("Romeo: [%zu/%zu] Generating AI description for '%s'...\n", + fileIndex, totalFiles, baseName); + + Juliet::ArenaClear(aiArena); + if (AI_GenerateDescription(aiArena, entry)) + { + // Copy the description to the persistent db arena so it survives aiArena clear + entry.Description = Juliet::StringCopy(db.DbArena, entry.Description); + entry.LastAIGeneratedTime = currentTime; + printf("Romeo: [%zu/%zu] Done.\n", fileIndex, totalFiles); + } + else + { + printf("Romeo: [%zu/%zu] AI generation failed.\n", fileIndex, totalFiles); + } + } + + Juliet::ArenaClear(tempArena); + + char mdFilePath[512]; + snprintf(mdFilePath, sizeof(mdFilePath), "%s\\%s", CStr(docsDir), CStr(mdFileName)); + + StringBuilder sb = { tempArena }; + sb.Append("# %s\n\n", baseName); + + sb.Append("## Source Files\n"); + if (entry.HeaderPath.Size > 0) + { + Juliet::String hRel = GetRelativeString(entry.HeaderPath, workspaceRoot); + sb.Append("- Header: `%.*s`\n", static_cast(hRel.Size), hRel.Data); + } + if (entry.CppPath.Size > 0) + { + Juliet::String cRel = GetRelativeString(entry.CppPath, workspaceRoot); + sb.Append("- Source: `%.*s`\n", static_cast(cRel.Size), cRel.Data); + } + sb.Append("\n"); + + sb.Append("## AI Description\n"); + if (entry.Description.Size > 0) + { + sb.Append("%.*s\n", static_cast(entry.Description.Size), entry.Description.Data); + } + else + { + sb.Append("*No AI description generated yet.*\n"); + } + sb.Append("\n"); + + sb.Append("## Symbols\n\n"); + printf("Romeo: [%zu/%zu] Parsing symbols...\n", fileIndex, totalFiles); + if (entry.HeaderPath.Size > 0) + { + FormatAndWriteSymbols(sb, tempArena, entry.HeaderPath); + } + else if (entry.CppPath.Size > 0) + { + FormatAndWriteSymbols(sb, tempArena, entry.CppPath); + } + printf("Romeo: [%zu/%zu] Symbols done.\n", fileIndex, totalFiles); + + // Compare with existing file to decide if we write to disk + Juliet::String existing = ReadFileContent(tempArena, mdFilePath); + Juliet::String newContent = { sb.Buffer, sb.Size }; + if (existing.Size == 0 || Juliet::StringCompare(existing, newContent) != 0) + { + FILE* mdFile = nullptr; + if (fopen_s(&mdFile, mdFilePath, "wb") == 0 && mdFile) + { + fwrite(sb.Buffer, 1, sb.Size, mdFile); + fclose(mdFile); + printf("Romeo: [%zu/%zu] File written/updated.\n", fileIndex, totalFiles); + } + } + else + { + printf("Romeo: [%zu/%zu] File unchanged. No modification made.\n", fileIndex, totalFiles); + } + entry.LastMarkdownCreatedTime = currentTime; + } + } + + Juliet::ArenaRelease(aiArena); + Juliet::ArenaRelease(tempArena); + fclose(overviewFile); + } +} diff --git a/Romeo/src/MarkdownGenerator.h b/Romeo/src/MarkdownGenerator.h new file mode 100644 index 0000000..a023149 --- /dev/null +++ b/Romeo/src/MarkdownGenerator.h @@ -0,0 +1,7 @@ +#pragma once +#include "Database.h" + +namespace Romeo +{ + void MD_GenerateDocumentation(Database& db, const Juliet::String& workspaceRoot, const Juliet::String& docsDir); +} diff --git a/Romeo/src/main.cpp b/Romeo/src/main.cpp new file mode 100644 index 0000000..d9a6534 --- /dev/null +++ b/Romeo/src/main.cpp @@ -0,0 +1,549 @@ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "Database.h" +#include "MarkdownGenerator.h" + +#define ROMEO_TRAY_ICON_ID 1 +#define ROMEO_WM_TRAYICON (WM_USER + 1) + +static Romeo::Database G_Db; +static Juliet::Arena* G_ScratchArena = nullptr; +static Juliet::String G_WorkspaceRoot; +static Juliet::String G_DocsDir; +static Juliet::String G_DbFilePath; + +static Juliet::String GetWorkspaceRoot(Juliet::Arena* arena) +{ + Assert(arena != nullptr); + Juliet::String base = Juliet::GetBasePath(); + + // Copy to arena so we can safely modify it + char* buf = Juliet::ArenaPushArray(arena, base.Size + 1 JULIET_DEBUG_PARAM("WorkspaceRootPath")); + memcpy(buf, base.Data, base.Size); + buf[base.Size] = '\0'; + + size_t count = 0; + for (size_t i = base.Size; i > 0; --i) + { + size_t idx = i - 1; + if (buf[idx] == '\\' || buf[idx] == '/') + { + count++; + if (count == 3) + { + buf[idx] = '\0'; + return { buf, idx }; + } + } + } + return { buf, base.Size }; +} + +static HANDLE G_ExitEvent = INVALID_HANDLE_VALUE; +static std::thread G_MonitorThread; +static bool G_MonitorRunning = false; +static HANDLE G_OllamaProcessHandle = INVALID_HANDLE_VALUE; +static HANDLE G_OllamaJobHandle = nullptr; + +static void MonitorThreadProc() +{ + int wLen = MultiByteToWideChar(CP_UTF8, 0, CStr(G_WorkspaceRoot), -1, nullptr, 0); + if (wLen <= 0) + { + return; + } + + wchar_t* wRoot = new wchar_t[static_cast(wLen + 1)]; + MultiByteToWideChar(CP_UTF8, 0, CStr(G_WorkspaceRoot), -1, wRoot, wLen); + + HANDLE hDir = CreateFileW( + wRoot, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, + nullptr + ); + + delete[] wRoot; + + if (hDir == INVALID_HANDLE_VALUE) + { + return; + } + + HANDLE hEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); + if (hEvent == nullptr) + { + CloseHandle(hDir); + return; + } + + OVERLAPPED overlapped = {}; + overlapped.hEvent = hEvent; + + alignas(FILE_NOTIFY_INFORMATION) BYTE buffer[64 * 1024]; + bool pendingChanges = false; + + BOOL readRes = ReadDirectoryChangesW( + hDir, + buffer, + sizeof(buffer), + TRUE, + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME, + nullptr, + &overlapped, + nullptr + ); + + if (readRes == 0) + { + CloseHandle(hEvent); + CloseHandle(hDir); + return; + } + + HANDLE waitHandles[2] = { G_ExitEvent, hEvent }; + + while (G_MonitorRunning) + { + DWORD timeout = pendingChanges ? 500 : INFINITE; + DWORD waitRes = WaitForMultipleObjects(2, waitHandles, FALSE, timeout); + + if (waitRes == WAIT_OBJECT_0) + { + break; + } + else if (waitRes == WAIT_OBJECT_0 + 1) + { + DWORD bytesTransferred = 0; + if (GetOverlappedResult(hDir, &overlapped, &bytesTransferred, FALSE) != 0) + { + FILE_NOTIFY_INFORMATION* info = reinterpret_cast(buffer); + while (info != nullptr) + { + size_t wCharCount = info->FileNameLength / sizeof(WCHAR); + if (wCharCount > 2) + { + const WCHAR* fName = info->FileName; + if ((fName[wCharCount - 1] == L'h' && fName[wCharCount - 2] == L'.') || + (wCharCount > 4 && fName[wCharCount - 1] == L'p' && fName[wCharCount - 2] == L'p' && fName[wCharCount - 3] == L'c' && fName[wCharCount - 4] == L'.')) + { + bool ignore = false; + for (size_t i = 0; i < wCharCount; ++i) + { + if (i + 12 <= wCharCount && wcsncmp(fName + i, L"Intermediate", 12) == 0) { ignore = true; } + if (i + 3 <= wCharCount && wcsncmp(fName + i, L"bin", 3) == 0) { ignore = true; } + if (i + 8 <= wCharCount && wcsncmp(fName + i, L"External", 8) == 0) { ignore = true; } + if (i + 4 <= wCharCount && wcsncmp(fName + i, L".git", 4) == 0) { ignore = true; } + if (ignore) { break; } + } + + if (!ignore) + { + pendingChanges = true; + } + } + } + + if (info->NextEntryOffset == 0) + { + break; + } + info = reinterpret_cast(reinterpret_cast(info) + info->NextEntryOffset); + } + } + + ResetEvent(hEvent); + ReadDirectoryChangesW( + hDir, + buffer, + sizeof(buffer), + TRUE, + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME, + nullptr, + &overlapped, + nullptr + ); + } + else if (waitRes == WAIT_TIMEOUT) + { + printf("Romeo Monitor: Change detected, updating documentation...\n"); + OutputDebugStringA("Romeo Monitor: Change detected, updating...\n"); + + { + Juliet::LockGuard lock(G_Db.Mutex); + Juliet::ArenaClear(G_ScratchArena); + + Romeo::DB_ScanWorkspace(G_Db, G_ScratchArena, G_WorkspaceRoot); + + bool anyChanged = false; + for (const Romeo::FileEntry& entry : G_Db.Entries) + { + if (entry.LastModifiedHeader > entry.LastMarkdownCreatedTime || + entry.LastModifiedCpp > entry.LastMarkdownCreatedTime) + { + anyChanged = true; + break; + } + } + + if (anyChanged) + { + Romeo::MD_GenerateDocumentation(G_Db, G_WorkspaceRoot, G_DocsDir); + Romeo::DB_Save(G_Db, G_DbFilePath); + } + } + + pendingChanges = false; + } + } + + CancelIo(hDir); + CloseHandle(hEvent); + CloseHandle(hDir); +} + +LRESULT CALLBACK RomeoWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case ROMEO_WM_TRAYICON: + if (LOWORD(lParam) == WM_RBUTTONUP) + { + POINT pt; + GetCursorPos(&pt); + HMENU hMenu = CreatePopupMenu(); + if (hMenu) + { + InsertMenuW(hMenu, 0, MF_BYPOSITION | MF_STRING, 1001, L"Exit"); + SetForegroundWindow(hwnd); + TrackPopupMenu(hMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, nullptr); + DestroyMenu(hMenu); + } + } + else if (LOWORD(lParam) == WM_LBUTTONUP) + { + OutputDebugStringA("Tray icon left-clicked!\n"); + } + return 0; + + case WM_COMMAND: + if (LOWORD(wParam) == 1001) + { + DestroyWindow(hwnd); + } + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +int JulietMain(int argc, wchar_t** argv) +{ + printf("JulietMain Entry!\n"); + + // Single instance mutex check + HANDLE hMutex = CreateMutexW(nullptr, TRUE, L"RomeoSingleInstanceMutex"); + if (hMutex == nullptr || GetLastError() == ERROR_ALREADY_EXISTS) + { + printf("Romeo is already running. Exiting.\n"); + if (hMutex != nullptr) + { + CloseHandle(hMutex); + } + return 0; + } + + // Launch Ollama serve subprocess + { + // Try to find ollama.exe - check common install locations + char ollamaPath[512] = {}; + bool foundOllama = false; + + // 1. Try PATH first via SearchPathA + if (SearchPathA(nullptr, "ollama.exe", nullptr, sizeof(ollamaPath), ollamaPath, nullptr) > 0) + { + foundOllama = true; + } + + // 2. Check %LOCALAPPDATA%\Programs\Ollama\ollama.exe + if (!foundOllama) + { + char localAppData[256] = {}; + DWORD envLen = GetEnvironmentVariableA("LOCALAPPDATA", localAppData, sizeof(localAppData)); + if (envLen > 0 && envLen < sizeof(localAppData)) + { + snprintf(ollamaPath, sizeof(ollamaPath), "%s\\Programs\\Ollama\\ollama.exe", localAppData); + DWORD attribs = GetFileAttributesA(ollamaPath); + if (attribs != INVALID_FILE_ATTRIBUTES) + { + foundOllama = true; + } + } + } + + // 3. Check %USERPROFILE%\AppData\Local\Programs\Ollama\ollama.exe + if (!foundOllama) + { + char userProfile[256] = {}; + DWORD envLen = GetEnvironmentVariableA("USERPROFILE", userProfile, sizeof(userProfile)); + if (envLen > 0 && envLen < sizeof(userProfile)) + { + snprintf(ollamaPath, sizeof(ollamaPath), "%s\\AppData\\Local\\Programs\\Ollama\\ollama.exe", userProfile); + DWORD attribs = GetFileAttributesA(ollamaPath); + if (attribs != INVALID_FILE_ATTRIBUTES) + { + foundOllama = true; + } + } + } + + if (foundOllama) + { + printf("Romeo: Found Ollama at: %s\n", ollamaPath); + + // Create a Job Object so child processes auto-terminate when Romeo exits (even on crash) + G_OllamaJobHandle = CreateJobObjectA(nullptr, nullptr); + if (G_OllamaJobHandle != nullptr) + { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = {}; + jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + SetInformationJobObject(G_OllamaJobHandle, JobObjectExtendedLimitInformation, + &jobInfo, sizeof(jobInfo)); + } + + // Build a mutable command line buffer (CreateProcessA requires mutable string) + char cmdLine[600] = {}; + snprintf(cmdLine, sizeof(cmdLine), "\"%s\" serve", ollamaPath); + + STARTUPINFOA si = {}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi = {}; + BOOL createRes = CreateProcessA( + ollamaPath, + cmdLine, + nullptr, + nullptr, + FALSE, + CREATE_NEW_CONSOLE | CREATE_SUSPENDED, + nullptr, + nullptr, + &si, + &pi + ); + if (createRes != 0) + { + // Assign to job object before resuming so it cannot escape + if (G_OllamaJobHandle != nullptr) + { + AssignProcessToJobObject(G_OllamaJobHandle, pi.hProcess); + } + ResumeThread(pi.hThread); + + // Wait briefly to check if the process crashed immediately + DWORD waitResult = WaitForSingleObject(pi.hProcess, 2000); + if (waitResult == WAIT_TIMEOUT) + { + // Process is still running after 2 seconds - good + G_OllamaProcessHandle = pi.hProcess; + CloseHandle(pi.hThread); + printf("Romeo: Launched local Ollama serve subprocess (PID: %lu).\n", pi.dwProcessId); + } + else + { + // Process exited within 2 seconds - likely crashed or port in use + DWORD exitCode = 0; + GetExitCodeProcess(pi.hProcess, &exitCode); + printf("Romeo: WARNING - Ollama serve process exited immediately (exit code: %lu).\n", exitCode); + printf("Romeo: This may mean Ollama is already running or the port 11434 is in use.\n"); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + } + else + { + DWORD err = GetLastError(); + printf("Romeo: ERROR - Failed to launch Ollama serve (Win32 error: %lu).\n", err); + } + } + else + { + printf("Romeo: WARNING - Could not find ollama.exe. Searched PATH and common install locations.\n"); + printf("Romeo: Please ensure Ollama is installed (https://ollama.com) and available in your PATH.\n"); + printf("Romeo: Continuing without auto-launching Ollama serve...\n"); + } + } + + Juliet::ArenaParams params{}; + Juliet::Arena* mainArena = Juliet::ArenaAllocate(params JULIET_DEBUG_PARAM("RomeoMainArena")); + Assert(mainArena != nullptr); + + G_ScratchArena = Juliet::ArenaAllocate(params JULIET_DEBUG_PARAM("RomeoScratchArena")); + Assert(G_ScratchArena != nullptr); + + printf("Arena Allocated! Pointer: %p\n", static_cast(mainArena)); + + G_WorkspaceRoot = GetWorkspaceRoot(mainArena); + + char docsDirBuf[512]; + snprintf(docsDirBuf, sizeof(docsDirBuf), "%s\\Romeo\\docs", CStr(G_WorkspaceRoot)); + G_DocsDir = Juliet::StringCopy(mainArena, Juliet::WrapString(docsDirBuf)); + + char dbFilePathBuf[512]; + snprintf(dbFilePathBuf, sizeof(dbFilePathBuf), "%s\\Romeo\\docs\\romeo.db", CStr(G_WorkspaceRoot)); + G_DbFilePath = Juliet::StringCopy(mainArena, Juliet::WrapString(dbFilePathBuf)); + + Romeo::DB_Init(G_Db, mainArena); + + printf("DB Init!\n"); + + bool forceRegenerate = false; + if (argc > 1 && (wcscmp(argv[1], L"-force") == 0 || wcscmp(argv[1], L"-f") == 0)) + { + forceRegenerate = true; + } + + { + Juliet::LockGuard lock(G_Db.Mutex); + if (!forceRegenerate) + { + Romeo::DB_Load(G_Db, G_DbFilePath); + } + else + { + printf("Romeo: Force flag detected, clearing cache and rebuilding all documentation...\n"); + } + + Romeo::DB_ScanWorkspace(G_Db, G_ScratchArena, G_WorkspaceRoot); + + if (forceRegenerate) + { + for (Romeo::FileEntry& entry : G_Db.Entries) + { + entry.Description = {}; + entry.LastMarkdownCreatedTime = 0; + entry.LastAIGeneratedTime = 0; + } + } + + Romeo::MD_GenerateDocumentation(G_Db, G_WorkspaceRoot, G_DocsDir); + Romeo::DB_Save(G_Db, G_DbFilePath); + } + + char debugMsg[256]; + snprintf(debugMsg, sizeof(debugMsg), "Romeo scanned workspace. Found %llu C++ files.\n", static_cast(G_Db.Entries.Size())); + printf("%s", debugMsg); + + // Register hidden window class + const wchar_t CLASS_NAME[] = L"RomeoHiddenWindowClass"; + WNDCLASSW wc = {}; + HINSTANCE hInstance = GetModuleHandleW(nullptr); + wc.lpfnWndProc = RomeoWindowProc; + wc.hInstance = hInstance; + wc.lpszClassName = CLASS_NAME; + + RegisterClassW(&wc); + + // Create hidden window to receive messages + HWND hwnd = CreateWindowExW( + 0, + CLASS_NAME, + L"Romeo Tool", + 0, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + nullptr, + nullptr, + hInstance, + nullptr + ); + + if (hwnd == nullptr) + { + CloseHandle(hMutex); + return 0; + } + + G_ExitEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); + if (G_ExitEvent != INVALID_HANDLE_VALUE) + { + G_MonitorRunning = true; + G_MonitorThread = std::thread(MonitorThreadProc); + } + + // Add tray icon + NOTIFYICONDATAW nid = {}; + nid.cbSize = sizeof(NOTIFYICONDATAW); + nid.hWnd = hwnd; + nid.uID = ROMEO_TRAY_ICON_ID; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uCallbackMessage = ROMEO_WM_TRAYICON; + nid.hIcon = LoadIcon(nullptr, IDI_APPLICATION); + wcscpy_s(nid.szTip, L"Romeo Documentation Tool"); + + Shell_NotifyIconW(NIM_ADD, &nid); + + // Message loop + MSG msg = {}; + while (GetMessage(&msg, nullptr, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Cleanup + Shell_NotifyIconW(NIM_DELETE, &nid); + + G_MonitorRunning = false; + if (G_ExitEvent != INVALID_HANDLE_VALUE) + { + SetEvent(G_ExitEvent); + } + if (G_MonitorThread.joinable()) + { + G_MonitorThread.join(); + } + if (G_ExitEvent != INVALID_HANDLE_VALUE) + { + CloseHandle(G_ExitEvent); + G_ExitEvent = INVALID_HANDLE_VALUE; + } + + if (G_OllamaProcessHandle != INVALID_HANDLE_VALUE) + { + TerminateProcess(G_OllamaProcessHandle, 0); + CloseHandle(G_OllamaProcessHandle); + G_OllamaProcessHandle = INVALID_HANDLE_VALUE; + } + if (G_OllamaJobHandle != nullptr) + { + CloseHandle(G_OllamaJobHandle); + G_OllamaJobHandle = nullptr; + } + + Juliet::ArenaRelease(mainArena); + Juliet::ArenaRelease(G_ScratchArena); + + CloseHandle(hMutex); + + return 0; +} diff --git a/fbuild.bff b/fbuild.bff index 507dd41..c379f04 100644 --- a/fbuild.bff +++ b/fbuild.bff @@ -181,6 +181,7 @@ Settings #include "Game/Game.bff" #include "JulietApp/JulietApp.Bff" #include "JulietShaderCompiler/JulietShaderCompiler.Bff" +#include "Romeo/Romeo.bff" // Aliases : All-$Platform$-$Config$ @@ -257,7 +258,7 @@ ForEach( .Config in .Configs ) VSSolution( 'GenerateSolution' ) { .SolutionOutput = 'Juliet.sln' - .SolutionProjects = { 'Juliet', 'Game', 'JulietApp', 'ImGui' } + .SolutionProjects = { 'Juliet', 'Game', 'JulietApp', 'ImGui', 'Romeo' } .SolutionConfigs = .ProjectConfigs .SolutionBuildProject = 'JulietApp' diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..450964d --- /dev/null +++ b/todo.md @@ -0,0 +1,42 @@ +# Juliet Engine: 3D Platformer Todo List + +This TODO list outlines the tasks required to build a simple 3D platformer game in the Juliet Engine. + +## [ ] 1. Gameplay Entities & Transform System (3D Integration) +- [ ] Extend `Game::Entity` in [Entity.h](file:///c:/Dev/Classified/Juliet/Game/Entity/Entity.h) to support 3D transforms (`Vector3 Position`, `Vector3 Scale`, and Euler/Quaternion `Rotation`). +- [ ] Add `bool IsGrounded` to the player/entity state. +- [ ] Link entities to renderable visual mesh IDs (`MeshID` in the graphics system). +- [ ] Define helper function to set/update visual transforms based on entity state. + +## [ ] 2. Physics & 3D Bounding Box (Collision System) +- [ ] Define `AABB` (Axis-Aligned Bounding Box) struct. +- [ ] Implement constant downward gravity acceleration applied to player velocity. +- [ ] Implement AABB vs AABB collision detection. +- [ ] Implement collision resolution: push-back along the shortest axis of separation, resetting vertical velocity when grounded or hitting ceilings, and allowing sliding along walls. + +## [ ] 3. Player Control & Locomotion +- [ ] Calculate camera-relative movement vectors based on player's yaw. +- [ ] Map movement input keys (`W`, `A`, `S`, `D`) to player horizontal velocity with smooth acceleration and friction. +- [ ] Map jump input (`Space`) to apply upward vertical impulse, allowed only when player is grounded. + +## [ ] 4. Third-Person Follow Camera System +- [ ] Implement follow camera logic orbiting the player entity. +- [ ] Handle mouse delta to update camera pitch/yaw orbit angles. +- [ ] Implement view matrix computation targeting the player's position. + +## [ ] 5. Level Setup & Collision Mapping +- [ ] Implement static platform mesh spawner (e.g. ground floor, raised blocks, gaps). +- [ ] Map platform meshes to static colliders in the physics update list. + +## [ ] 6. Game Loop Update Hook +- [ ] Implement an entity update tick `UpdateEntities(float deltaTime)` in [game.cpp](file:///c:/Dev/Classified/Juliet/Game/game.cpp). +- [ ] Integrate input, physics step, collision checks/resolution, and visual sync into the tick. + +## [ ] 7. Gameplay Rules & State Management +- [ ] Define `GameState` struct (Score, Player Life/Health, Game Status). +- [ ] Implement fall-out-of-bounds check (e.g. `Z < -10.0f` resets player to spawn/checkpoint). +- [ ] Implement collectibles (coins) with overlap triggers that update score and deactivate themselves. +- [ ] Implement level-end goal flag/portal trigger. + +## [ ] 8. UI Overlay (HUD) +- [ ] Render a gameplay overlay using ImGui showing score, time elapsed, status, and control instructions.