From e81041fa2ba546e88e2186d42b373013a08c57a0 Mon Sep 17 00:00:00 2001 From: OusmBlueNinja <89956790+OusmBlueNinja@users.noreply.github.com> Date: Fri, 18 Apr 2025 17:23:59 -0500 Subject: [PATCH] Adde Profiling and user Setting saving --- imgui.ini | 72 ++- remake.yaml | 1 + remake/build.log | 13 +- src/assets/lua/slow.lua | 16 + src/assets/scenes/lighting_test_2.cene | 22 +- src/assets/shaders/sprite.frag | 36 +- src/assets/shaders/sprite.vert | 27 +- src/assets/shaders/unlit.frag | 13 +- src/assets/shaders/unlit.vert | 25 +- src/src/Components/ScriptComponent.cpp | 35 +- src/src/Components/SpriteComponent.cpp | 2 + src/src/Engine.cpp | 593 +++++++++++++++++++------ src/src/Engine.h | 14 +- src/src/Entitys/Object.cpp | 122 +++-- src/src/Entitys/Object.h | 19 +- src/src/Renderer.cpp | 58 ++- src/src/Renderer.h | 70 +-- src/src/utils/EngineConfig.cpp | 69 ++- src/src/utils/EngineConfig.h | 12 +- src/src/utils/Profiler.cpp | 107 +++++ src/src/utils/Profiler.h | 94 ++++ 21 files changed, 1101 insertions(+), 319 deletions(-) create mode 100644 src/assets/lua/slow.lua create mode 100644 src/src/utils/Profiler.cpp create mode 100644 src/src/utils/Profiler.h diff --git a/imgui.ini b/imgui.ini index 287ef1a..bc880ac 100644 --- a/imgui.ini +++ b/imgui.ini @@ -10,24 +10,24 @@ Collapsed=1 [Window][WindowOverViewport_11111111] Pos=0,19 -Size=1920,1158 +Size=1280,701 Collapsed=0 [Window][Inspector] -Pos=1530,19 -Size=390,835 +Pos=890,19 +Size=390,292 Collapsed=0 -DockId=0x00000005,0 +DockId=0x0000000B,0 [Window][Scene Tree] Pos=0,19 -Size=263,1158 +Size=376,366 Collapsed=0 -DockId=0x00000001,0 +DockId=0x00000009,0 [Window][Viewport] -Pos=265,19 -Size=1263,786 +Pos=378,19 +Size=510,218 Collapsed=0 DockId=0x00000007,0 @@ -36,16 +36,16 @@ Size=1280,19 Collapsed=0 [Window][Performance Info] -Pos=1530,856 -Size=390,321 +Pos=890,526 +Size=390,194 Collapsed=0 DockId=0x00000006,0 [Window][Console] -Pos=265,807 -Size=1263,370 +Pos=0,387 +Size=376,333 Collapsed=0 -DockId=0x00000008,0 +DockId=0x0000000A,0 [Window][Tilemap Editor] Pos=265,19 @@ -53,14 +53,48 @@ Size=1263,674 Collapsed=0 DockId=0x00000007,1 +[Window][Profiler] +Pos=378,239 +Size=510,481 +Collapsed=0 +DockId=0x00000008,0 + +[Window][Profiler Timeline] +Pos=265,69 +Size=623,651 +Collapsed=0 +DockId=0x00000008,1 + +[Window][Profiler (Unity Style)] +Pos=265,430 +Size=623,290 +Collapsed=0 +DockId=0x00000008,1 + +[Window][Profiler Timeline View] +Pos=265,526 +Size=1263,651 +Collapsed=0 +DockId=0x00000008,1 + +[Window][Color Correction] +Pos=890,313 +Size=390,211 +Collapsed=0 +DockId=0x0000000C,0 + [Docking][Data] -DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=X +DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X DockNode ID=0x00000003 Parent=0x11111111 SizeRef=888,1158 Split=X - DockNode ID=0x00000001 Parent=0x00000003 SizeRef=263,701 HiddenTabBar=1 Selected=0x12EF0F59 - DockNode ID=0x00000002 Parent=0x00000003 SizeRef=623,701 Split=Y Selected=0xC450F867 - DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,329 CentralNode=1 Selected=0xC450F867 - DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,370 Selected=0xEA83D666 + DockNode ID=0x00000001 Parent=0x00000003 SizeRef=376,701 Split=Y Selected=0x12EF0F59 + DockNode ID=0x00000009 Parent=0x00000001 SizeRef=376,605 HiddenTabBar=1 Selected=0x12EF0F59 + DockNode ID=0x0000000A Parent=0x00000001 SizeRef=376,551 HiddenTabBar=1 Selected=0xEA83D666 + DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1150,701 Split=Y Selected=0xC450F867 + DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,675 CentralNode=1 Selected=0xC450F867 + DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,481 Selected=0x9B5D3198 DockNode ID=0x00000004 Parent=0x11111111 SizeRef=390,1158 Split=Y Selected=0x36DC96AB - DockNode ID=0x00000005 Parent=0x00000004 SizeRef=407,835 HiddenTabBar=1 Selected=0x36DC96AB + DockNode ID=0x00000005 Parent=0x00000004 SizeRef=407,835 Split=Y Selected=0x36DC96AB + DockNode ID=0x0000000B Parent=0x00000005 SizeRef=390,483 Selected=0x36DC96AB + DockNode ID=0x0000000C Parent=0x00000005 SizeRef=390,350 Selected=0xA873C17F DockNode ID=0x00000006 Parent=0x00000004 SizeRef=407,321 HiddenTabBar=1 Selected=0x3FC1A724 diff --git a/remake.yaml b/remake.yaml index cf8ab15..ce5d9f3 100644 --- a/remake.yaml +++ b/remake.yaml @@ -39,6 +39,7 @@ cxx: g++ cxxflags: - -std=c++20 - -Wall + - -g # Auto-detect libraries and headers auto_libs: diff --git a/remake/build.log b/remake/build.log index 683d9c2..63a2f52 100644 --- a/remake/build.log +++ b/remake/build.log @@ -1,3 +1,10 @@ -[COMPILE] g++ -std=c++20 -Wall -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -IC:\msys64\mingw64\lib\libyaml-cpp.a -Isrc\vendor\imgui -MMD -MP -c src\src\Components\ScriptComponent.cpp -o src\build\Components\ScriptComponent.o -[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\Entitys\Object.o src\build\utils\EngineConfig.o src\build\utils\ExceptionHandler.o src\build\utils\FileDialog.o src\build\utils\GameObjectsList.o src\build\utils\Logging.o src\build\utils\Shader.o src\build\utils\UID.o src\build\utils\utils.o src\build\imgui\imgui.o src\build\imgui\imgui_demo.o src\build\imgui\imgui_draw.o src\build\imgui\imgui_impl_glfw.o src\build\imgui\imgui_impl_opengl3.o src\build\imgui\imgui_tables.o src\build\imgui\imgui_widgets.o src\build\lapi.o src\build\lauxlib.o src\build\lbaselib.o src\build\lcode.o src\build\lcorolib.o src\build\lctype.o src\build\ldblib.o src\build\ldebug.o src\build\ldo.o src\build\ldump.o src\build\lfunc.o src\build\lgc.o src\build\linit.o src\build\liolib.o src\build\llex.o src\build\lmathlib.o src\build\lmem.o src\build\loadlib.o src\build\lobject.o src\build\lopcodes.o src\build\loslib.o src\build\lparser.o src\build\lstate.o src\build\lstring.o src\build\lstrlib.o src\build\ltable.o src\build\ltablib.o src\build\ltm.o src\build\lua.o src\build\luac.o src\build\lundump.o src\build\lutf8lib.o src\build\lvm.o src\build\lzio.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto -[RUN] Executed app.exe successfully. +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Engine.cpp -o src\build\Engine.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\main.cpp -o src\build\main.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Renderer.cpp -o src\build\Renderer.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\CameraComponent.cpp -o src\build\Components\CameraComponent.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\LightComponent.cpp -o src\build\Components\LightComponent.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\ScriptComponent.cpp -o src\build\Components\ScriptComponent.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\SpriteComponent.cpp -o src\build\Components\SpriteComponent.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\TextComonent.cpp -o src\build\Components\TextComonent.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\TilemapComponent.cpp -o src\build\Components\TilemapComponent.o +[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Entitys\Object.cpp -o src\build\Entitys\Object.o diff --git a/src/assets/lua/slow.lua b/src/assets/lua/slow.lua new file mode 100644 index 0000000..6d8c6bb --- /dev/null +++ b/src/assets/lua/slow.lua @@ -0,0 +1,16 @@ +Engine.DebugLua(true) + +local function sleep (a) + local sec = tonumber(os.clock() + a); + while (os.clock() < sec) do + end +end + + +function OnInit() + +end + +function OnUpdate(dt) + sleep(0.001) +end diff --git a/src/assets/scenes/lighting_test_2.cene b/src/assets/scenes/lighting_test_2.cene index e41fdf6..b408763 100644 --- a/src/assets/scenes/lighting_test_2.cene +++ b/src/assets/scenes/lighting_test_2.cene @@ -1,12 +1,13 @@ engine_version: 0.1.0 scene_name: lighting_test_2 -scene_hash: a0ae96d593e1990a6f3944184fd6595738629af0dcb2d4cdd83011a91aaa738c +scene_hash: 8f8260f09292105727f7f8afe890565a9feeaa92a1ca6d7fa55790a5019c67d7 format_version: 1 objects: - name: Tiles uid: f5e01f7892874a67b662633650b41dbd id: 3 position: [0, 0] + rotation: 0 layer: 0 visable: true components: [] @@ -15,6 +16,7 @@ objects: uid: 7dc3bbf8affb4844ae3801f03857b904 id: 4 position: [0, 0] + rotation: 0 layer: 0 visable: true components: @@ -27,6 +29,7 @@ objects: uid: 13d8988343354e3c8a1f51c03ed40cda id: 5 position: [1024, 0] + rotation: 0 layer: 0 visable: true components: @@ -39,6 +42,7 @@ objects: uid: cff28abe7e3b455ab9b756acc84cd2d7 id: 6 position: [0, 1024] + rotation: 0 layer: 0 visable: true components: @@ -51,6 +55,7 @@ objects: uid: 98967eb30e5b429b992766d8062b7c17 id: 7 position: [1024, 1024] + rotation: 0 layer: 0 visable: true components: @@ -62,7 +67,8 @@ objects: - name: Logo uid: c4ce6f16dfb347b0ae0ac67f5881b243 id: 8 - position: [2048, 0] + position: [2104, 56] + rotation: 0 layer: 0 visable: true components: @@ -74,7 +80,8 @@ objects: - name: Carbooon Fobar uid: 5ea269572751401da6d86519d3513b7d id: 9 - position: [2559.30005, 1562] + position: [2048, 3070] + rotation: 0 layer: 1 visable: true components: @@ -86,7 +93,8 @@ objects: - name: Mud uid: a36b71937ba349bd8e6414f75be9ee16 id: 10 - position: [0, 2561.80005] + position: [0, 3070] + rotation: 0 layer: 0 visable: true components: @@ -99,6 +107,7 @@ objects: uid: 051b338a725a4076ad53ad8fa00c5f4e id: 12 position: [-556, 951] + rotation: 0 layer: 0 visable: true components: [] @@ -107,6 +116,7 @@ objects: uid: 6afde2dd47aa4557b6afb1a607c99dc8 id: 13 position: [512, 1024] + rotation: 0 layer: 2 visable: true components: @@ -128,6 +138,7 @@ objects: uid: 0f950d76d24b4dc18f54cab2c3aaaf9a id: 14 position: [1024, 512] + rotation: 0 layer: 2 visable: true components: @@ -149,6 +160,7 @@ objects: uid: 09f722f51c7c4b0f98de3a0a16d127c4 id: 15 position: [250, 250] + rotation: 0 layer: 2 visable: true components: @@ -170,6 +182,7 @@ objects: uid: d4fb425522d84a8cbbd7d1415bcd93df id: 16 position: [500, 500] + rotation: 0 layer: 0 visable: true components: @@ -187,6 +200,7 @@ objects: uid: 895c655f3dda4aec9f2a354c1276c53e id: 14 position: [0, 0] + rotation: 0 layer: 0 visable: true components: diff --git a/src/assets/shaders/sprite.frag b/src/assets/shaders/sprite.frag index 75af612..8ce3527 100644 --- a/src/assets/shaders/sprite.frag +++ b/src/assets/shaders/sprite.frag @@ -6,6 +6,11 @@ out vec4 FragColor; uniform sampler2D uTex; uniform sampler2D uNormalMap; +uniform float uRotation; + +uniform float uBrightness; +uniform float uSaturation; +uniform float uGamma; #define MAX_LIGHTS 512 @@ -17,16 +22,22 @@ uniform float uLightRadius[MAX_LIGHTS]; void main() { - vec4 texColor = texture(uTex, vUV); + // Rotate UV 90° clockwise + vec2 rotatedUV = vec2(vUV.y, 1.0 - vUV.x); + + vec4 texColor = texture(uTex, rotatedUV); if (texColor.a < 0.1) discard; - // unpack normal map and convert from [0,1] to [-1,1] - vec3 n = texture(uNormalMap, vUV).rgb * 2.0 - 1.0; - // invert the green channel for OpenGL + vec3 n = texture(uNormalMap, rotatedUV).rgb * 2.0 - 1.0; n.y = -n.y; - vec3 normal = normalize(n); + float c = cos(uRotation); + float s = sin(uRotation); + mat2 rot = mat2(c, -s, s, c); + n.xy = rot * n.xy; + + vec3 normal = normalize(n); vec3 finalLight = vec3(0.0); for (int i = 0; i < uLightCount; ++i) @@ -37,9 +48,8 @@ void main() if (dist < uLightRadius[i]) { vec2 lightDir2D = normalize(lightVec); - vec3 lightDir = normalize(vec3(lightDir2D, 1.0)); // pseudo-3D - - float attenuation = 1.0 - dist / uLightRadius[i]; + vec3 lightDir = normalize(vec3(lightDir2D, 1.0)); + float attenuation = smoothstep(uLightRadius[i], 0.0, dist); float diff = max(dot(normal, lightDir), 0.0); vec3 light = uLightColor[i] * diff * attenuation * uLightIntensity[i]; finalLight += light; @@ -47,5 +57,15 @@ void main() } vec3 result = texColor.rgb * finalLight; + + result *= uBrightness; + + float gray = dot(result, vec3(0.299, 0.587, 0.114)); + result = mix(vec3(gray), result, uSaturation); + + result = pow(result, vec3(1.0 / uGamma)); + + result = clamp(result, 0.0, 1.0); + FragColor = vec4(result, texColor.a); } diff --git a/src/assets/shaders/sprite.vert b/src/assets/shaders/sprite.vert index 39ec639..b462092 100644 --- a/src/assets/shaders/sprite.vert +++ b/src/assets/shaders/sprite.vert @@ -1,7 +1,7 @@ -#version 430 core +#version 330 core -layout(location = 0) in vec2 aPos; -layout(location = 1) in vec2 aUV; +layout (location = 0) in vec2 aPos; +layout (location = 1) in vec2 aUV; out vec2 vUV; out vec2 vFragScreenPos; @@ -9,17 +9,22 @@ out vec2 vFragScreenPos; uniform vec2 uPos; uniform vec2 uSize; uniform vec2 uScreen; +uniform float uRotation;//r void main() { + vec2 centered = aPos - vec2(0.5); + + float s = cos(uRotation); + float c = sin(uRotation); + mat2 rot = mat2(c, -s, s, c); + vec2 rotated = rot * (centered * uSize); + + vec2 finalPos = uPos + rotated; + vUV = aUV; + vFragScreenPos = finalPos; - vec2 worldPos = uPos + aPos * uSize; - - vFragScreenPos = worldPos; - - vec2 ndc = (worldPos / uScreen) * 2.0 - 1.0; - ndc.y *= -1.0; - - gl_Position = vec4(ndc, 0.0, 1.0); + vec2 ndc = finalPos / uScreen * 2.0 - 1.0; + gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0); // Y-flip } diff --git a/src/assets/shaders/unlit.frag b/src/assets/shaders/unlit.frag index c577230..321d3e2 100644 --- a/src/assets/shaders/unlit.frag +++ b/src/assets/shaders/unlit.frag @@ -1,10 +1,15 @@ -#version 430 core +#version 330 core in vec2 vUV; out vec4 FragColor; + uniform sampler2D uTex; -void main() { - vec4 color = texture(uTex, vUV); - + +void main() +{ + + vec2 rotatedUV = vec2(vUV.y, 1.0 - vUV.x); + + vec4 color = texture(uTex, rotatedUV); if (color.a < 0.01) discard; diff --git a/src/assets/shaders/unlit.vert b/src/assets/shaders/unlit.vert index b493d31..e9ed802 100644 --- a/src/assets/shaders/unlit.vert +++ b/src/assets/shaders/unlit.vert @@ -1,13 +1,28 @@ -#version 430 core +#version 330 core layout (location = 0) in vec2 aPos; layout (location = 1) in vec2 aUV; + out vec2 vUV; +out vec2 vFragScreenPos; + uniform vec2 uPos; uniform vec2 uSize; uniform vec2 uScreen; -void main() { - vec2 worldPos = aPos * uSize + uPos; +uniform float uRotation; // radians + +void main() +{ + vec2 centered = aPos - vec2(0.5); + float s = cos(uRotation); + float c = sin(uRotation); + mat2 rot = mat2(c, -s, s, c); + vec2 rotated = rot * (centered * uSize); + + vec2 finalPos = uPos + rotated; + + vFragScreenPos = finalPos; vUV = aUV; - vec2 ndc = (worldPos / uScreen) * 2.0 - 1.0; - gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0); + + vec2 ndc = finalPos / uScreen * 2.0 - 1.0; + gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0); // Y flip for OpenGL } diff --git a/src/src/Components/ScriptComponent.cpp b/src/src/Components/ScriptComponent.cpp index a028496..d1c1d0e 100644 --- a/src/src/Components/ScriptComponent.cpp +++ b/src/src/Components/ScriptComponent.cpp @@ -10,6 +10,8 @@ #include "../Components/TextComponent.h" #include "../Components/TilemapComponent.h" +#include "../utils/Profiler.h" + #include #include #include @@ -74,6 +76,8 @@ static int Lua_DebugLua(lua_State *L) // Component resolver static Component *GetComponentByName(Object *obj, const std::string &type) { + PROFILE_SCOPE("LUA_GetComponentByName"); + if (type == "SpriteComponent") return obj->GetComponent().get(); if (type == "CameraComponent") @@ -92,6 +96,8 @@ static Component *GetComponentByName(Object *obj, const std::string &type) // Object:GetComponent("Type") static int Lua_Object_GetComponent(lua_State *L) { + PROFILE_SCOPE("Lua_Object_GetComponent"); + auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT); const char *type = luaL_checkstring(L, 2); @@ -106,6 +112,8 @@ static int Lua_Object_GetComponent(lua_State *L) // Object:GetPosition() static int Lua_Object_GetPosition(lua_State *L) { + PROFILE_SCOPE("Lua_Object_GetPosition"); + auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT); glm::vec2 pos = wrapper->obj->GetLocalPosition(); @@ -120,6 +128,8 @@ static int Lua_Object_GetPosition(lua_State *L) // Object:SetPosition(Vector2) static int Lua_Object_SetPosition(lua_State *L) { + PROFILE_SCOPE("Lua_Object_SetPosition"); + auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT); auto *vec = (LuaVector2 *)luaL_checkudata(L, 2, LUA_VECTOR2_MT); wrapper->obj->SetLocalPosition({vec->x, vec->y}); @@ -129,6 +139,8 @@ static int Lua_Object_SetPosition(lua_State *L) // __index for Object static int Lua_Object_Index(lua_State *L) { + PROFILE_SCOPE("Lua_Object_Index"); + const char *key = luaL_checkstring(L, 2); lua_getfield(L, lua_upvalueindex(1), key); return 1; @@ -163,6 +175,8 @@ static void PushObject(lua_State *L, Object *obj) // Engine.GetObjectByTag(name) static int Lua_GetObjectByTag(lua_State *L) { + PROFILE_SCOPE("Lua_GetObjectByTag"); + const char *name = luaL_checkstring(L, 1); for (const auto &obj : objects) { @@ -180,16 +194,22 @@ static int Lua_GetObjectByTag(lua_State *L) // Vector2(x, y) static int Lua_Vector2_New(lua_State *L) { - LuaVector2 *vec = (LuaVector2 *)lua_newuserdata(L, sizeof(LuaVector2)); - vec->x = (float)luaL_optnumber(L, 1, 0); - vec->y = (float)luaL_optnumber(L, 2, 0); - luaL_getmetatable(L, LUA_VECTOR2_MT); - lua_setmetatable(L, -2); + PROFILE_SCOPE("Lua_Vector2_New"); + + LuaVector2 *vec = static_cast(lua_newuserdata(L, sizeof(LuaVector2))); + + int nargs = lua_gettop(L); + vec->x = nargs >= 1 ? (float)lua_tonumber(L, 1) : 0.0f; + vec->y = nargs >= 2 ? (float)lua_tonumber(L, 2) : 0.0f; + + luaL_setmetatable(L, LUA_VECTOR2_MT); return 1; } static int Lua_Vector2_Index(lua_State *L) { + PROFILE_SCOPE("Lua_Vector2_Index"); + auto *vec = (LuaVector2 *)luaL_checkudata(L, 1, LUA_VECTOR2_MT); const char *key = luaL_checkstring(L, 2); if (strcmp(key, "x") == 0) @@ -202,6 +222,8 @@ static int Lua_Vector2_Index(lua_State *L) } static int Lua_Vector2_NewIndex(lua_State *L) { + PROFILE_SCOPE("Lua_Vector2_NewIndex"); + auto *vec = (LuaVector2 *)luaL_checkudata(L, 1, LUA_VECTOR2_MT); const char *key = luaL_checkstring(L, 2); float value = (float)luaL_checknumber(L, 3); @@ -214,6 +236,7 @@ static int Lua_Vector2_NewIndex(lua_State *L) void RegisterVector2Type(lua_State *L) { + luaL_newmetatable(L, LUA_VECTOR2_MT); lua_pushcfunction(L, Lua_Vector2_Index); lua_setfield(L, -2, "__index"); @@ -286,6 +309,8 @@ void ScriptComponent::ReloadScript() void ScriptComponent::OnUpdate(float dt) { + PROFILE_SCOPE("ScriptComponent::OnUpdate"); + if (!L) return; lua_getglobal(L, "OnUpdate"); diff --git a/src/src/Components/SpriteComponent.cpp b/src/src/Components/SpriteComponent.cpp index 6b5d37f..3bdb1d5 100644 --- a/src/src/Components/SpriteComponent.cpp +++ b/src/src/Components/SpriteComponent.cpp @@ -8,6 +8,8 @@ #include "../utils/utils.h" + + struct ImageCacheEntry { unsigned int textureID; glm::vec2 size; diff --git a/src/src/Engine.cpp b/src/src/Engine.cpp index a06758a..bfb159e 100644 --- a/src/src/Engine.cpp +++ b/src/src/Engine.cpp @@ -12,6 +12,7 @@ #include "utils/EngineConfig.h" #include "utils/GameObjectsList.h" +#include "utils/Profiler.h" #include #include @@ -24,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -40,14 +45,15 @@ static glm::vec2 cameraPos = {0, 0}; static float cameraZoom = 1.0f; static ImVec2 lastMousePos = {}; -static const std::string tempScenePath = "__tmp_scene.yaml"; +float g_fps = 0.0f; -static float g_fps = 0.0f; +static const std::string tempScenePath = "__tmp_scene.yaml"; GLFWwindow *window = nullptr; Engine::Engine() { + Init(); } @@ -56,46 +62,251 @@ Engine::~Engine() Shutdown(); } +void DrawProfilerTimelineBars(const ProfileNode &node, + int depth, + float baseOffsetX, + float frameDuration, + ImVec2 origin) +{ + constexpr float rowHeight = 24.0f; + constexpr float textPadding = 5.0f; + + // compute bar rect + float startRel = node.visualStartMs / frameDuration; + float durRel = node.visualDurationMs / frameDuration; + float x = origin.x + startRel * baseOffsetX; + float width = std::fmax(1.0f, durRel * baseOffsetX); + float y = origin.y + depth * rowHeight; + + ImVec2 barMin(x, y); + ImVec2 barMax(x + width, y + rowHeight - 4.0f); + + // draw the bar + size_t hash = std::hash{}(node.name); + ImU32 color = ImColor::HSV((hash % 1000) / 1000.0f, 0.6f, 0.85f); + ImDrawList *draw = ImGui::GetWindowDrawList(); + draw->AddRectFilled(barMin, barMax, color, 3.0f); + draw->AddRect(barMin, barMax, IM_COL32_BLACK, 3.0f); + + // how much room we have for text + float availW = width - textPadding * 2; + const char *textToDraw = nullptr; + + // 1) try the short "XX.XX ms" + char timeBuf[16]; + int tn = std::snprintf(timeBuf, sizeof(timeBuf), "%.2f ms", node.visualDurationMs); + if (tn > 0) + { + ImVec2 sz = ImGui::CalcTextSize(timeBuf); + if (sz.x <= availW) + { + textToDraw = timeBuf; + } + } + + // 2) only if the short fits, see if full fits + if (textToDraw == timeBuf) + { + char fullBuf[64]; + int fn = std::snprintf(fullBuf, sizeof(fullBuf), + "%s (%.2f ms)", + node.name.c_str(), + node.visualDurationMs); + if (fn > 0) + { + ImVec2 fsz = ImGui::CalcTextSize(fullBuf); + if (fsz.x <= availW) + { + textToDraw = fullBuf; + } + } + } + + // 3) draw if we decided on something + if (textToDraw) + { + draw->AddText( + ImVec2(barMin.x + textPadding, + barMin.y), + IM_COL32_WHITE, + textToDraw); + } + + // tooltip + ImVec2 mouse = ImGui::GetMousePos(); + if (mouse.x >= barMin.x && mouse.x <= barMax.x && + mouse.y >= barMin.y && mouse.y <= barMax.y) + { + ImGui::SetTooltip( + "%s\nCurrent: %.3f ms\nSmoothed: %.3f ms", + node.name.c_str(), + node.durationMs, + node.visualDurationMs); + } + + // recurse + for (auto &child : node.children) + DrawProfilerTimelineBars(child, + depth + 1, + baseOffsetX, + frameDuration, + origin); +} + +static ProfileNode GetAveragedFrameCopy(const ProfileNode &src) +{ + ProfileNode averaged = src; + averaged.visualStartMs = src.startMs; + averaged.visualDurationMs = src.durationMs; + + averaged.children.clear(); + for (const auto &child : src.children) + averaged.children.push_back(GetAveragedFrameCopy(child)); + + return averaged; +} + +void ShowProfilerTimeline() +{ + + static ProfileNode cachedAveragedFrame; + static double lastUpdateTime = 0.0; + static double updateInterval = 0.25; + static bool freezeView = false; + + const double now = ImGui::GetTime(); + const ProfileNode *latest = profiler.GetLatestFrame(); + + ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar); + + // --- Controls --- + if (g_engineConfig.settings.profile_enabled) + { + + ImGui::Checkbox("Freeze View", &freezeView); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100); + float interval = static_cast(updateInterval); + if (ImGui::DragFloat("Update Interval", &interval, 0.05f, 0.05f, 5.0f)) + updateInterval = interval; + + updateInterval = std::clamp(updateInterval, 0.05, 5.0); + + if (!freezeView && latest && (now - lastUpdateTime) >= updateInterval) + { + cachedAveragedFrame = GetAveragedFrameCopy(*latest); + lastUpdateTime = now; + } + + if (cachedAveragedFrame.durationMs <= 0.0f) + { + ImGui::Text("Waiting for profiler data..."); + ImGui::End(); + return; + } + + const float rowHeight = 24.0f; + const float timelineHeight = 400.0f; + float frameDuration = cachedAveragedFrame.visualDurationMs; + float timelineWidth = ImGui::GetContentRegionAvail().x * 0.95f; + + ImGui::BeginChild("TimelineScroll", ImVec2(0, timelineHeight)); + ImVec2 origin = ImGui::GetCursorScreenPos(); + + DrawProfilerTimelineBars(cachedAveragedFrame, 0, timelineWidth, frameDuration, origin); + + ImGui::Dummy(ImVec2(timelineWidth, 32 * rowHeight)); + ImGui::EndChild(); + + ImGui::Text("Frame Duration (Avg): %.2f ms | View Update Rate: %.2fs", frameDuration, updateInterval); + } + else + { + ImGui::Text("Profiling Disabled."); + } + ImGui::End(); +} + void Engine::ShowDebugOverlay(float deltaTime) { + // === Performance Window === static std::vector fpsHistory; - static const int maxHistory = 100; - static float timer = 0; + static const int maxFpsHistory = 100; + static float fpsTimer = 0.0f; float fps = 1.0f / deltaTime; g_fps = fps; - if (fpsHistory.size() >= maxHistory) + if (fpsHistory.size() >= maxFpsHistory) fpsHistory.erase(fpsHistory.begin()); - if (timer >= 0.05) + if (fpsTimer >= 0.05f) { - timer = 0; + fpsTimer = 0; fpsHistory.push_back(fps); } - timer += deltaTime; + fpsTimer += deltaTime; - ImGui::Begin("Performance Info", nullptr, - ImGuiWindowFlags_NoDecoration); + ImGui::Begin("Performance Info", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize); ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), "Performance"); ImGui::Separator(); ImGui::Text("FPS: %.1f", fps); ImGui::Text("Delta Time: %.4f s", deltaTime); ImGui::Text("Frame Time: %.2f ms", deltaTime * 1000.0f); - ImGui::PlotLines("##FPS", fpsHistory.data(), fpsHistory.size(), 0, "FPS History", 0.0f, 144.0f, ImVec2(-1, 50)); + + if (!fpsHistory.empty()) + { + float maxFps = *std::max_element(fpsHistory.begin(), fpsHistory.end()); + maxFps = std::max(maxFps * 1.1f, 60.0f); + + ImGui::PlotLines("##FPS", fpsHistory.data(), fpsHistory.size(), 0, "FPS History", + 0.0f, maxFps, ImVec2(-1, 50)); + } + else + { + ImGui::Text("FPS data unavailable"); + } ImGui::Spacing(); ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "Renderer"); ImGui::Separator(); - ImGui::Text("Draw Calls: %d", Renderer::GetDrawCallCount()); - ImGui::Text("Scene Lights: %d/%d", Renderer::GetLightsCount(), g_engineConfig.gl_maxLight); - + ImGui::Text("Draw Calls: %d", Renderer::GetDrawCallCount()); + ImGui::Text("Lua OnUpdate Calls: %d", m_OnUpdateCalls); + ImGui::Text("Scene Lights: %d / %d", Renderer::GetLightsCount(), g_engineConfig.gl_maxLight); ImGui::Text("Reserved Draws: %d", m_Reserved_draws); - - ImGui::Text("Camera Zoom: %.2f", cameraZoom); - ImGui::Text("Camera Pos: (%.1f, %.1f)", cameraPos.x, cameraPos.y); + ImGui::Text("Camera Zoom: %.2f", cameraZoom); + ImGui::Text("Camera Pos: (%.1f, %.1f)", cameraPos.x, cameraPos.y); ImGui::End(); } +void ShowColorCorrectionWindow() +{ + PROFILE_ENGINE_SCOPE("Engine::ShowColorCorrectionWindow"); + + ColorCorrection* cc = Renderer::GetColorCorrection(); + if (!cc) + return; + + ImGui::Begin("Color Correction"); + + + bool changed = false; + changed |= ImGui::SliderFloat("Brightness", &cc->brightness, 0.0f, 2.0f, "%.2f"); + changed |= ImGui::SliderFloat("Saturation", &cc->saturation, 0.0f, 2.0f, "%.2f"); + changed |= ImGui::SliderFloat("Gamma", &cc->gamma, 0.1f, 4.0f, "%.2f"); + + ImGui::Spacing(); + + if (ImGui::Button("Reset to Default")) + { + *cc = ColorCorrection(); + } + ImGui::SameLine(); + + ImGui::End(); +} + + + void Engine::Init() { glfwInit(); @@ -108,6 +319,8 @@ void Engine::Init() glfwSwapInterval(0); // No VSync glewInit(); + g_engineConfig.LoadFromFile(); + IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO &io = ImGui::GetIO(); @@ -123,10 +336,16 @@ void Engine::Init() objects.push_back(obj); selected = obj; + Logger::LogVerbose("Resverving Objects"); + + m_toDraw.reserve(1024); + m_scriptUpdates.reserve(256); + m_collectStack.reserve(1024); Logger::LogInfo("Initialized Engine"); } void DrawInspectorUI(std::shared_ptr selected) { + PROFILE_ENGINE_SCOPE("Engine::DrawInspectorUI"); ImGui::Begin("Inspector"); if (!selected) @@ -167,6 +386,10 @@ void DrawInspectorUI(std::shared_ptr selected) if (ImGui::DragFloat2("Position", &pos.x, 0.1f)) selected->SetLocalPosition(pos); + float rotation = selected->GetLocalRotation(); + if (ImGui::DragFloat("Rotation", &rotation, 1.0f)) + selected->SetLocalRotation(rotation); + int layer = selected->layer; if (ImGui::InputInt("Layer", &layer)) selected->layer = layer; @@ -342,24 +565,109 @@ void DrawInspectorUI(std::shared_ptr selected) } } +// Add this method to Engine: +void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom) +{ + m_toDraw.clear(); + m_scriptUpdates.clear(); + m_collectStack.clear(); + + for (auto &root : objects) + if (!root->GetParent()) + m_collectStack.push_back(root); + + while (!m_collectStack.empty()) + { + auto obj = m_collectStack.back(); + m_collectStack.pop_back(); + + if (!obj->GetVisable()) + continue; + m_toDraw.push_back(obj.get()); + + if (auto light = obj->GetComponent()) + { + + glm::vec2 world = obj->GetWorldPosition(); + glm::vec2 screen = (world - camPos) * camZoom + glm::vec2(Renderer::GetSize()) * 0.5f; + Renderer::AddLight(screen, + light->GetColor(), + light->GetIntensity(), + light->GetRadius() * camZoom); + } + + if (playing) + { + if (auto script = obj->GetComponent()) + m_scriptUpdates.push_back(script.get()); + } + + for (auto &child : obj->GetChildren()) + m_collectStack.push_back(child); + } +} + void Engine::Run() { while (!glfwWindowShouldClose(window)) { + if (g_engineConfig.settings.profile_editor) + { + profiler.BeginFrame(); + } + + profiler.BeginEngineSection("glfwPollEvents"); + glfwPollEvents(); + profiler.EndEngineSection(); + + profiler.BeginEngineSection("NewFrame"); + + profiler.BeginEngineSection("ImGui_ImplOpenGL3_NewFrame"); ImGui_ImplOpenGL3_NewFrame(); + + profiler.EndEngineSection(); + + profiler.BeginEngineSection("ImGui_ImplGlfw_NewFrame"); + ImGui_ImplGlfw_NewFrame(); + + profiler.EndEngineSection(); + + profiler.BeginEngineSection("ImGui::NewFrame"); + ImGui::NewFrame(); + + profiler.EndEngineSection(); + + profiler.BeginEngineSection("ImGui::DockSpaceOverViewport"); + ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()->ID); + profiler.EndEngineSection(); + + profiler.BeginEngineSection("glfwGetTime"); + float currentTime = glfwGetTime(); static float lastTime = currentTime; float deltaTime = currentTime - lastTime; lastTime = currentTime; + profiler.EndEngineSection(); + + profiler.EndEngineSection(); + + profiler.BeginEngineSection("Engine::ShowDebugOverlay"); ShowDebugOverlay(deltaTime); + profiler.EndEngineSection(); + + profiler.BeginEngineSection("Logger::Draw"); + Logger::Draw(); + profiler.EndEngineSection(); + + profiler.BeginEngineSection("BeginMainMenuBar"); if (ImGui::BeginMainMenuBar()) { @@ -402,9 +710,24 @@ void Engine::Run() if (ImGui::BeginMenu("Options")) { - // simple checkbox never closes the menu when you click it - ImGui::Checkbox("Enable Lighting", &g_engineConfig.lighting_enabled); - ImGui::Checkbox("Enable Gizmos", &g_engineConfig.settings.draw_gizmos); + + if (ImGui::BeginMenu("Settings")) + { + ImGui::Checkbox("Enable Lighting", &g_engineConfig.settings.lighting_enabled); + ImGui::Checkbox("Enable Gizmos", &g_engineConfig.settings.draw_gizmos); + ImGui::Checkbox("Profiling", &g_engineConfig.settings.profile_enabled); + if (g_engineConfig.settings.profile_enabled) + { + ImGui::Checkbox("Profile Engine", &g_engineConfig.settings.profile_editor); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Windows")) + { + ImGui::Checkbox("Color Correction", &g_engineConfig.settings.show_color_correction_window); + ImGui::EndMenu(); + } ImGui::EndMenu(); } @@ -426,44 +749,53 @@ void Engine::Run() ImGui::EndMainMenuBar(); } + profiler.EndEngineSection(); + + if (g_engineConfig.settings.show_color_correction_window) + ShowColorCorrectionWindow(); - ImGui::Begin("Scene Tree"); - if (ImGui::BeginPopupContextWindow("SceneTreeContext", ImGuiPopupFlags_MouseButtonRight)) { - if (ImGui::MenuItem("Create New")) + PROFILE_ENGINE_SCOPE("Engine::DrawSceneTree"); + + ImGui::Begin("Scene Tree"); + + if (ImGui::BeginPopupContextWindow("SceneTreeContext", ImGuiPopupFlags_MouseButtonRight)) { - auto obj = std::make_shared("NewObject"); - objects.push_back(obj); - selected = obj; - ImGui::OpenPopup("RenameObject"); + if (ImGui::MenuItem("Create New")) + { + auto obj = std::make_shared("NewObject"); + objects.push_back(obj); + selected = obj; + ImGui::OpenPopup("RenameObject"); + } + ImGui::Separator(); + if (ImGui::MenuItem("Create New Sprite")) + { + auto obj = std::make_shared("NewSprite"); + obj->AddComponent(); + objects.push_back(obj); + selected = obj; + ImGui::OpenPopup("RenameObject"); + } + if (ImGui::MenuItem("Create New Light")) + { + auto obj = std::make_shared("NewSprite"); + obj->AddComponent(); + objects.push_back(obj); + selected = obj; + ImGui::OpenPopup("RenameObject"); + } + ImGui::EndPopup(); } - ImGui::Separator(); - if (ImGui::MenuItem("Create New Sprite")) - { - auto obj = std::make_shared("NewSprite"); - obj->AddComponent(); - objects.push_back(obj); - selected = obj; - ImGui::OpenPopup("RenameObject"); - } - if (ImGui::MenuItem("Create New Light")) - { - auto obj = std::make_shared("NewSprite"); - obj->AddComponent(); - objects.push_back(obj); - selected = obj; - ImGui::OpenPopup("RenameObject"); - } - ImGui::EndPopup(); + + for (auto &obj : objects) + if (!obj->GetParent()) // Only draw root nodes + DrawObjectNode(obj); + + ImGui::End(); } - for (auto &obj : objects) - if (!obj->GetParent()) // Only draw root nodes - DrawObjectNode(obj); - - ImGui::End(); - DrawInspectorUI(selected); // Viewport @@ -506,95 +838,92 @@ void Engine::Run() } } + // Resize + profiler.BeginEngineSection("Renderer::Resize"); Renderer::Resize((int)size.x, (int)size.y); + profiler.EndEngineSection(); + + // Begin + profiler.BeginEngineSection("Renderer::Begin"); Renderer::Begin(); + profiler.EndEngineSection(); - std::vector> toDraw; - std::vector scriptUpdates; - - toDraw.reserve(m_Reserved_draws); - m_Reserved_draws = 0; // Reset - - Renderer::DrawEditorGrid(cameraPos, cameraZoom); - - // clear arrays each frame… - toDraw.clear(); - scriptUpdates.clear(); + // Reserve (no profiling) + m_toDraw.reserve(m_Reserved_draws); m_Reserved_draws = 0; - // recursive collector now skips invisible objects (and their children) - std::function &)> collect = - [&](const std::shared_ptr &obj) + // Draw Editor Grid + profiler.BeginEngineSection("Draw Editor Grid"); + Renderer::DrawEditorGrid(cameraPos, cameraZoom); + profiler.EndEngineSection(); + + // Clear temporary arrays + m_toDraw.clear(); + m_scriptUpdates.clear(); + m_Reserved_draws = 0; + + // Collect Objects + profiler.BeginEngineSection("Collect Objects"); + collectObjects(playing, cameraPos, cameraZoom); + profiler.EndEngineSection(); + + // Sort Objects + profiler.BeginEngineSection("Sort Objects"); + if (m_toDraw.size() > 1) { - // if this object isn’t visible, skip it and its children - if (!obj->GetVisable()) - return; + std::sort(m_toDraw.begin(), m_toDraw.end(), + [](auto const &a, auto const &b) + { + if (a->layer != b->layer) + return a->layer < b->layer; + return a->GetWorldPosition().y < b->GetWorldPosition().y; + }); + } + profiler.EndEngineSection(); - // still visible → collect for drawing - toDraw.push_back(obj); - m_Reserved_draws += 1; + if (!g_engineConfig.settings.profile_editor) + profiler.BeginFrame(); - // Collect lights - if (auto light = obj->GetComponent()) - { - glm::vec2 world = obj->GetWorldPosition(); - glm::vec2 screen = (world - cameraPos) * cameraZoom + glm::vec2(Renderer::GetSize().x * 0.5f, - Renderer::GetSize().y * 0.5f); - Renderer::AddLight(screen, - light->GetColor(), - light->GetIntensity(), - light->GetRadius() * cameraZoom); + m_OnUpdateCalls = 0; - Renderer::DrawGizmoCircle( - world, - light->GetRadius(), - 64, // segments - light->GetColor(), // circle color - cameraPos, - cameraZoom); - } - - // Collect scripts - if (playing) - { - if (auto script = obj->GetComponent()) - scriptUpdates.push_back(script.get()); - } - - // recurse into children - for (const auto &child : obj->GetChildren()) - collect(child); - }; - - // Traverse only root objects - for (const auto &obj : objects) - if (!obj->GetParent()) - collect(obj); - - // Sort drawables by layer then Y - std::sort(toDraw.begin(), toDraw.end(), [](auto const &a, auto const &b) - { - if (a->layer != b->layer) - return a->layer < b->layer; - return a->GetWorldPosition().y < b->GetWorldPosition().y; }); - - // Run script updates - for (auto *script : scriptUpdates) + profiler.BeginSection("Script Updates"); + for (auto *script : m_scriptUpdates) + { + profiler.BeginSection("Script: " + script->GetOwner()->GetName()); script->OnUpdate(deltaTime); + m_OnUpdateCalls++; + profiler.EndSection(); + } + profiler.EndSection(); - // Draw all sprites - for (const auto &obj : toDraw) + profiler.BeginSection("Render"); + for (auto *obj : m_toDraw) { - if (auto sprite = obj->GetComponent()) + if (auto spritePtr = obj->GetComponent()) { - glm::vec2 worldPos = obj->GetWorldPosition(); - Renderer::DrawSprite(sprite.get(), worldPos, cameraZoom, cameraPos); + profiler.BeginSection("Draw Sprite: " + obj->GetName()); + Renderer::DrawSprite(spritePtr.get(), + obj->GetWorldPosition(), + cameraZoom, + cameraPos); + profiler.EndSection(); } } + profiler.EndSection(); + // Finish frame profiling + if (!g_engineConfig.settings.profile_editor) + profiler.EndFrame(); + + // End renderer + profiler.BeginEngineSection("Renderer::End"); Renderer::End(); + profiler.EndEngineSection(); + + // Display render target texture GLuint texID = Renderer::GetRenderTexture(); ImGui::Image((ImTextureID)(uintptr_t)texID, size, ImVec2(0, 1), ImVec2(1, 0)); + ImGui::End(); if (ImGui::BeginPopup("RenameObject")) @@ -627,7 +956,13 @@ void Engine::Run() ImGui::EndPopup(); } + if (g_engineConfig.settings.profile_editor) + { + profiler.EndFrame(); // Finish frame + } // ImGui render + ShowProfilerTimeline(); + ImGui::Render(); int w, h; glfwGetFramebufferSize(window, &w, &h); @@ -656,6 +991,8 @@ void Engine::Run() void Engine::DrawObjectNode(const std::shared_ptr &obj) { + + PROFILE_ENGINE_SCOPE("Engine::DrawObjectNode"); ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth | (obj == selected ? ImGuiTreeNodeFlags_Selected : 0); @@ -813,7 +1150,6 @@ void Engine::LoadScene(const std::string &path) Logger::LogInfo("[LoadScene] Loaded scene: %s", root["scene_name"].as().c_str()); } - void Engine::LoadState() { if (savedStateYAML.empty()) @@ -827,14 +1163,14 @@ void Engine::LoadState() { objectArray = YAML::Load(savedStateYAML); } - catch (const std::exception& e) + catch (const std::exception &e) { Logger::LogError("[LoadState] Failed to parse saved scene: %s", e.what()); return; } objects.clear(); - for (const auto& node : objectArray) + for (const auto &node : objectArray) { auto obj = std::make_shared("[DefaultObject]"); obj->Load(node); @@ -849,7 +1185,7 @@ void Engine::SaveState() YAML::Emitter sceneData; sceneData << YAML::BeginSeq; - for (const auto& obj : objects) + for (const auto &obj : objects) obj->Save(sceneData); sceneData << YAML::EndSeq; @@ -857,12 +1193,10 @@ void Engine::SaveState() Logger::LogVerbose("[SaveState] Scene serialized (%zu bytes)", savedStateYAML.size()); } - - - - void Engine::Shutdown() { + g_engineConfig.SaveToFile(); + ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); @@ -870,4 +1204,5 @@ void Engine::Shutdown() glfwTerminate(); std::filesystem::remove(tempScenePath); + } diff --git a/src/src/Engine.h b/src/src/Engine.h index f0f5383..f490369 100644 --- a/src/src/Engine.h +++ b/src/src/Engine.h @@ -1,9 +1,13 @@ #pragma once + #include #include #include +#include // ← for std::vector<> +#include // ← for glm::vec2 class Object; +class ScriptComponent; class Engine { @@ -16,13 +20,21 @@ public: private: void Init(); void Shutdown(); - void DrawObjectNode(const std::shared_ptr &obj); // make sure this matches Engine.cpp + void DrawObjectNode(const std::shared_ptr &obj); void SaveScene(const std::string &path); void LoadScene(const std::string &path); void ShowDebugOverlay(float deltaTime); + void collectObjects(bool playing, const glm::vec2& camPos, float camZoom); + void SaveState(); void LoadState(); int m_Reserved_draws; + + std::vector m_toDraw; + std::vector m_scriptUpdates; + int m_OnUpdateCalls; + + std::vector> m_collectStack; }; diff --git a/src/src/Entitys/Object.cpp b/src/src/Entitys/Object.cpp index 17d088c..e6334a6 100644 --- a/src/src/Entitys/Object.cpp +++ b/src/src/Entitys/Object.cpp @@ -13,7 +13,7 @@ #include Object::Object(const std::string &name) - : name(name), localPosition(0.0f, 0.0f), uid(), visable(true) {} + : name(name), localPosition(0.0f, 0.0f), localRotationDeg(0.0f), uid(), visable(true) {} Object::~Object() {} @@ -45,6 +45,41 @@ glm::vec2 Object::GetLocalPosition() const return localPosition; } +void Object::SetLocalPosition(glm::vec2 pos) +{ + localPosition = pos; +} + + +glm::vec2 Object::GetWorldPosition() const +{ + if (parent) + { + float parentRotation = glm::radians(parent->GetWorldRotation()); + glm::vec2 rotated = glm::rotate(localPosition, parentRotation); + return parent->GetWorldPosition() + rotated; + } + return localPosition; +} + + +float Object::GetLocalRotation() const +{ + return localRotationDeg; +} + +void Object::SetLocalRotation(float deg) +{ + localRotationDeg = deg; +} + +float Object::GetWorldRotation() const +{ + if (parent) + return parent->GetWorldRotation() + localRotationDeg; + return localRotationDeg; +} + bool Object::GetVisable() const { return visable; @@ -55,21 +90,6 @@ void Object::SetVisable(bool state) visable = state; } - -void Object::SetLocalPosition(glm::vec2 pos) -{ - localPosition = pos; -} - -glm::vec2 Object::GetWorldPosition() const -{ - if (parent) - return parent->GetWorldPosition() + localPosition; - return localPosition; -} - - - const std::string &Object::GetName() const { return name; } void Object::SetName(const std::string &n) { name = n; } std::vector> &Object::GetChildren() { return children; } @@ -79,16 +99,15 @@ void Object::Save(YAML::Emitter &out) const { Logger::LogVerbose("[LoadScene] Saving Object: [%s, %d]", name.c_str(), uid.id); - out << YAML::BeginMap; out << YAML::Key << "name" << YAML::Value << name; out << YAML::Key << "uid" << YAML::Value << uid.uuid; out << YAML::Key << "id" << YAML::Value << uid.id; - out << YAML::Key << "position" << YAML::Value << YAML::Flow << YAML::BeginSeq << localPosition.x << localPosition.y << YAML::EndSeq; + out << YAML::Key << "position" << YAML::Flow << YAML::BeginSeq << localPosition.x << localPosition.y << YAML::EndSeq; + out << YAML::Key << "rotation" << YAML::Value << localRotationDeg; out << YAML::Key << "layer" << YAML::Value << layer; out << YAML::Key << "visable" << YAML::Value << visable; - out << YAML::Key << "components" << YAML::Value << YAML::BeginSeq; for (const auto &comp : components) { @@ -108,71 +127,36 @@ void Object::Save(YAML::Emitter &out) const void Object::Load(const YAML::Node &node) { name = node["name"].as(); - - if (node["uid"]) - uid.uuid = node["uid"].as(); - else - uid.uuid = GenerateUUID(); - - if (node["id"]) - uid.id = node["id"].as(); + uid.uuid = node["uid"] ? node["uid"].as() : GenerateUUID(); + uid.id = node["id"] ? node["id"].as() : 0; auto pos = node["position"]; if (pos && pos.IsSequence() && pos.size() == 2) - { - localPosition.x = pos[0].as(); - localPosition.y = pos[1].as(); - } + localPosition = {pos[0].as(), pos[1].as()}; - if (node["layer"]) - layer = node["layer"].as(); - - if (node["visable"]) - visable = node["visable"].as(); - + localRotationDeg = node["rotation"] ? node["rotation"].as() : 0.0f; + layer = node["layer"] ? node["layer"].as() : 0; + visable = node["visable"] ? node["visable"].as() : true; Logger::LogVerbose("[LoadScene] Loading Object: [%s, %d]", name.c_str(), uid.id); + components.clear(); if (node["components"]) { for (const auto &compNode : node["components"]) { std::string type = compNode["type"].as(); - Logger::LogVerbose("[LoadScene] Createing Component: %s", type.c_str()); - if (type == "SpriteComponent") - { - auto comp = AddComponent(); - comp->Load(compNode); - } - else if (type == "CameraComponent") - { - auto comp = AddComponent(); - comp->Load(compNode); - } - else if (type == "LightComponent") - { - auto comp = AddComponent(); - comp->Load(compNode); - } - else if (type == "TilemapComponent") - { - auto comp = AddComponent(); - comp->Load(compNode); - } - else if (type == "TextComponent") - { - auto comp = AddComponent(); - comp->Load(compNode); - } - else if (type == "ScriptComponent") - { - auto comp = AddComponent(); - comp->Load(compNode); - } + if (type == "SpriteComponent") AddComponent()->Load(compNode); + else if (type == "CameraComponent") AddComponent()->Load(compNode); + else if (type == "LightComponent") AddComponent()->Load(compNode); + else if (type == "TilemapComponent") AddComponent()->Load(compNode); + else if (type == "TextComponent") AddComponent()->Load(compNode); + else if (type == "ScriptComponent") AddComponent()->Load(compNode); } } + children.clear(); if (node["children"]) { for (const auto &childNode : node["children"]) @@ -182,4 +166,4 @@ void Object::Load(const YAML::Node &node) AddChild(child); } } -} +} \ No newline at end of file diff --git a/src/src/Entitys/Object.h b/src/src/Entitys/Object.h index 209e253..5346182 100644 --- a/src/src/Entitys/Object.h +++ b/src/src/Entitys/Object.h @@ -3,7 +3,10 @@ #include #include #include +#define GLM_ENABLE_EXPERIMENTAL #include +#include + #include #include "../utils/UID.h" @@ -22,6 +25,10 @@ public: void SetLocalPosition(glm::vec2 pos); glm::vec2 GetWorldPosition() const; + float GetLocalRotation() const; + void SetLocalRotation(float deg); + float GetWorldRotation() const; + void SetParent(Object *parent); Object *GetParent() const; void AddChild(std::shared_ptr child); @@ -31,9 +38,6 @@ public: bool GetVisable() const; void SetVisable(bool state); - - - template std::shared_ptr GetComponent() const; template @@ -48,9 +52,10 @@ public: int layer = 0; private: - bool visable; + bool visable = true; std::string name; - glm::vec2 localPosition; + glm::vec2 localPosition{0.0f}; + float localRotationDeg = 0.0f; // Rotation in degrees Object *parent = nullptr; std::vector> children; std::vector> components; @@ -60,10 +65,8 @@ template std::shared_ptr Object::GetComponent() const { for (const auto &comp : components) - { if (auto casted = std::dynamic_pointer_cast(comp)) return casted; - } return nullptr; } @@ -74,7 +77,7 @@ std::shared_ptr Object::AddComponent() if (existing) return existing; - std::shared_ptr component = std::make_shared(this); + auto component = std::make_shared(this); components.push_back(component); return component; } diff --git a/src/src/Renderer.cpp b/src/src/Renderer.cpp index 0f6925f..c2f257e 100644 --- a/src/src/Renderer.cpp +++ b/src/src/Renderer.cpp @@ -4,6 +4,8 @@ #include "utils/Logging.h" #include "utils/EngineConfig.h" #include "utils/utils.h" +#include "utils/Profiler.h" +#include "Entitys/Object.h" #include "stb_image.h" @@ -25,6 +27,8 @@ int Renderer::width = 1280; int Renderer::height = 720; int Renderer::s_DrawCalls = 0; int Renderer::s_LightsCount = 0; +std::unique_ptr Renderer::s_ColorCorrection = nullptr; + std::vector Renderer::s_Lights; @@ -83,6 +87,9 @@ void Renderer::Init() // Load unlit shader unlitShader.LoadFromFile("src/assets/shaders/unlit.vert", "src/assets/shaders/unlit.frag"); + SetColorCorrection(std::make_unique()); + + // Create a 1x1 flat normal map (RGB: 128,128,255) unsigned char flatNormal[3] = {128, 128, 255}; glGenTextures(1, &defaultNormalMap); @@ -96,6 +103,7 @@ void Renderer::Init() void Renderer::Resize(int w, int h) { + if (w == width && h == height) return; width = w; @@ -111,14 +119,26 @@ void Renderer::Resize(int w, int h) void Renderer::Begin() { - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glViewport(0, 0, width, height); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - s_DrawCalls = 0; - ClearLights(); + + { + PROFILE_ENGINE_SCOPE("glBindFramebuffer"); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + } + { + glViewport(0, 0, width, height); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + { + + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + { + + s_DrawCalls = 0; + ClearLights(); + } } void Renderer::End() @@ -128,12 +148,16 @@ void Renderer::End() void Renderer::ClearLights() { + PROFILE_ENGINE_SCOPE("Renderer::ClearLights"); + s_Lights.clear(); s_LightsCount = 0; } void Renderer::AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius) { + PROFILE_ENGINE_SCOPE("Engine::AddLight"); + if (s_Lights.size() >= g_engineConfig.gl_maxLight) return; s_Lights.push_back({screenPos, color, intensity, radius}); @@ -142,6 +166,8 @@ void Renderer::AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, floa void Renderer::DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos, float zoom, const glm::vec2 &cameraPos) { + PROFILE_ENGINE_SCOPE("Renderer::DrawTilemap"); + if (!tilemap || tilemap->GetAtlasPath().empty()) return; @@ -209,26 +235,32 @@ void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float z Shader *shader = &unlitShader; bool useLighting = false; - if (g_engineConfig.lighting_enabled && sprite->GetRenderType() == SpriteComponent::RenderType::Lit) + if (g_engineConfig.settings.lighting_enabled && sprite->GetRenderType() == SpriteComponent::RenderType::Lit) { shader = &spriteShader; useLighting = true; } + shader->Use(); glm::vec2 size = sprite->GetSize(); glm::vec2 screenPos = (pos - CameraPos) * zoom + glm::vec2(width, height) * 0.5f - (size * zoom * 0.5f); + float rotationDeg = sprite->GetOwner()->GetWorldRotation(); shader->SetVec2("uPos", screenPos); shader->SetVec2("uSize", size * zoom); shader->SetVec2("uScreen", glm::vec2(width, height)); + shader->SetFloat("uRotation", glm::radians(rotationDeg)); shader->SetInt("uTex", 0); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, sprite->GetTextureID()); if (useLighting) { + s_ColorCorrection->Upload(*shader); + shader->SetInt("uLightCount", static_cast(s_Lights.size())); for (size_t i = 0; i < s_Lights.size(); ++i) { @@ -369,4 +401,12 @@ GLuint Renderer::GetRenderTexture() glm::ivec2 Renderer::GetSize() { return {width, height}; +} +void Renderer::SetColorCorrection(std::unique_ptr correction) +{ + s_ColorCorrection = std::move(correction); +} +ColorCorrection *Renderer::GetColorCorrection() +{ + return s_ColorCorrection.get(); } \ No newline at end of file diff --git a/src/src/Renderer.h b/src/src/Renderer.h index 44dcfc3..bb48863 100644 --- a/src/src/Renderer.h +++ b/src/src/Renderer.h @@ -6,63 +6,61 @@ #include "Components/TilemapComponent.h" #include "Components/SpriteComponent.h" #include "utils/EngineConfig.h" +#include "utils/Shader.h" +#include "utils/Profiler.h" -struct Light { + +struct ColorCorrection { + float brightness = 1.0f; + float saturation = 1.0f; + float gamma = 1.0f; + + + void Upload(Shader& shader) const { + shader.SetFloat("uBrightness", brightness); + shader.SetFloat("uSaturation", saturation); + shader.SetFloat("uGamma", gamma); + } +}; + + +struct Light +{ glm::vec2 screenPos; glm::vec3 color; float intensity; float radius; }; -class Renderer { +class Renderer +{ public: static void Init(); static void Resize(int w, int h); static void Begin(); static void End(); - static void DrawSprite(SpriteComponent* sprite, const glm::vec2& pos, float zoom, glm::vec2& CameraPos); - static void DrawTilemap(TilemapComponent* tilemap, const glm::vec2& worldPos, float zoom, const glm::vec2& cameraPos); + static void DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float zoom, glm::vec2 &CameraPos); + static void DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos, float zoom, const glm::vec2 &cameraPos); - static void AddLight(const glm::vec2& screenPos, const glm::vec3& color, float intensity, float radius); + static void AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius); static void ClearLights(); - static void DrawEditorGrid(const glm::vec2& cameraPos, float zoom); + static void DrawEditorGrid(const glm::vec2 &cameraPos, float zoom); - // —— New gizmo functions —— - // Draws a colored line between two world‑space points. - static void DrawGizmoLine( - const glm::vec2& worldStart, - const glm::vec2& worldEnd, - const glm::vec3& color, - const glm::vec2& cameraPos, - float zoom - ); - - // Draws a colored axis‑aligned rectangle in world space. - static void DrawGizmoRect( - const glm::vec2& worldPos, - const glm::vec2& size, - const glm::vec3& color, - const glm::vec2& cameraPos, - float zoom - ); - - // Draws a colored circle (approximated by segments) in world space. - static void DrawGizmoCircle( - const glm::vec2& worldCenter, - float radius, - int segments, - const glm::vec3& color, - const glm::vec2& cameraPos, - float zoom - ); + static void DrawGizmoLine(const glm::vec2 &worldStart, const glm::vec2 &worldEnd, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom); + static void DrawGizmoRect(const glm::vec2 &worldPos, const glm::vec2 &size, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom); + static void DrawGizmoCircle(const glm::vec2 &worldCenter, float radius, int segments, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom); static GLuint GetRenderTexture(); static glm::ivec2 GetSize(); static int GetDrawCallCount(); static int GetLightsCount(); + static void SetColorCorrection(std::unique_ptr correction); + + static ColorCorrection *GetColorCorrection(); + private: static std::vector s_Lights; static GLuint fbo, textureColorBuffer, rbo; @@ -73,5 +71,7 @@ private: static GLuint shader, quadVAO, quadVBO; static void InitQuad(); - static GLuint LoadShader(const char* vertexSrc, const char* fragmentSrc); + static GLuint LoadShader(const char *vertexSrc, const char *fragmentSrc); + + static std::unique_ptr s_ColorCorrection; }; diff --git a/src/src/utils/EngineConfig.cpp b/src/src/utils/EngineConfig.cpp index 0d8866d..80bacbe 100644 --- a/src/src/utils/EngineConfig.cpp +++ b/src/src/utils/EngineConfig.cpp @@ -1,14 +1,71 @@ -// EngineConfig.cpp #include "EngineConfig.h" +#include +#include +#include +#include +#include // SHGetFolderPathA +#include "Logging.h" -EngineConfig g_engineConfig { - .lighting_enabled = true, +EngineConfig g_engineConfig{ .version = "0.1.0", .gl_version = "430", .gl_maxLight = 512, - .settings{ + .settings = { .draw_gizmos = true, - }, - + .profile_editor = false, + .profile_enabled = true, + .show_color_correction_window = false, + .lighting_enabled = true + } }; + +static std::filesystem::path GetUserSettingsPath() { + char userPath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0, userPath))) { + std::filesystem::path path = std::filesystem::path(userPath) / ".CreateEngine" / ".user_settings.yaml"; + std::filesystem::create_directories(path.parent_path()); + Logger::LogVerbose("Settings Path: %s", path.string().c_str()); + + return path; + } + return {}; +} + +void EngineConfig::SaveToFile() { + Logger::LogVerbose("Saving User Settings"); + + YAML::Emitter out; + out << YAML::BeginMap; + + out << YAML::Key << "draw_gizmos" << YAML::Value << settings.draw_gizmos; + out << YAML::Key << "profile_editor" << YAML::Value << settings.profile_editor; + out << YAML::Key << "profile_enabled" << YAML::Value << settings.profile_enabled; + out << YAML::Key << "show_color_correction_window" << YAML::Value << settings.show_color_correction_window; + out << YAML::Key << "lighting_enabled" << YAML::Value << settings.lighting_enabled; + + out << YAML::EndMap; + + std::ofstream fout(GetUserSettingsPath()); + fout << out.c_str(); +} + +void EngineConfig::LoadFromFile() { + Logger::LogVerbose("Loading User Settings"); + + auto path = GetUserSettingsPath(); + if (!std::filesystem::exists(path)) return; + + YAML::Node root = YAML::LoadFile(path.string()); + + if (root["draw_gizmos"]) + settings.draw_gizmos = root["draw_gizmos"].as(); + if (root["profile_editor"]) + settings.profile_editor = root["profile_editor"].as(); + if (root["profile_enabled"]) + settings.profile_enabled = root["profile_enabled"].as(); + if (root["show_color_correction_window"]) + settings.show_color_correction_window = root["show_color_correction_window"].as(); + if (root["lighting_enabled"]) + settings.lighting_enabled = root["lighting_enabled"].as(); +} diff --git a/src/src/utils/EngineConfig.h b/src/src/utils/EngineConfig.h index fd06044..e220312 100644 --- a/src/src/utils/EngineConfig.h +++ b/src/src/utils/EngineConfig.h @@ -1,17 +1,23 @@ #pragma once #include -struct UserSettings{ - bool draw_gizmos; +struct UserSettings { + bool draw_gizmos; + bool profile_editor; + bool profile_enabled; + bool show_color_correction_window; + bool lighting_enabled; }; struct EngineConfig { - bool lighting_enabled; std::string version; std::string gl_version; int gl_maxLight; UserSettings settings; + + void SaveToFile(); + void LoadFromFile(); }; extern EngineConfig g_engineConfig; diff --git a/src/src/utils/Profiler.cpp b/src/src/utils/Profiler.cpp new file mode 100644 index 0000000..8fd973b --- /dev/null +++ b/src/src/utils/Profiler.cpp @@ -0,0 +1,107 @@ +#include "Profiler.h" +#include + +extern float g_fps; +extern EngineConfig g_engineConfig; + +inline double Lerp(double a, double b, double t) { + return a + (b - a) * t; +} + +static void SmoothVisualDurations(ProfileNode& node, double blendFactor) { + node.visualStartMs = Lerp(node.visualStartMs, node.startMs, blendFactor); + node.visualDurationMs = Lerp(node.visualDurationMs, node.durationMs, blendFactor); + for (auto& child : node.children) + SmoothVisualDurations(child, blendFactor); +} + +void HierarchicalProfiler::BeginFrame() { + if (!g_engineConfig.settings.profile_enabled) + return; + + root.startMs = 0.0; + root.durationMs = 0.0; + root.visualStartMs = 0.0; + root.visualDurationMs = 0.0; + root.children.clear(); + + currentStack.clear(); + sectionStartTimes.clear(); + + currentStack.reserve(ProfilesLastFrame); + sectionStartTimes.reserve(ProfilesLastFrame); + + startTime = Clock::now(); + currentStack.push_back(&root); + + ProfilesLastFrame = 0; +} + +void HierarchicalProfiler::BeginSection(const std::string& name) { + if (!g_engineConfig.settings.profile_enabled) + return; + + auto now = Clock::now(); + double timeSinceStart = std::chrono::duration(now - startTime).count(); + + ProfileNode& parent = *currentStack.back(); + parent.children.emplace_back(name, timeSinceStart); + currentStack.push_back(&parent.children.back()); + + sectionStartTimes.push_back(now); + ProfilesLastFrame++; +} + +void HierarchicalProfiler::EndSection() { + if (!g_engineConfig.settings.profile_enabled) + return; + + if (currentStack.size() <= 1 || sectionStartTimes.empty()) + return; + + auto now = Clock::now(); + ProfileNode& node = *currentStack.back(); + double duration = std::chrono::duration(now - sectionStartTimes.back()).count(); + + node.durationMs = duration; + + currentStack.pop_back(); + sectionStartTimes.pop_back(); +} + +void HierarchicalProfiler::BeginEngineSection(const std::string& name) { + if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor) + BeginSection(name); +} + +void HierarchicalProfiler::EndEngineSection() { + if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor) + EndSection(); +} + +void HierarchicalProfiler::EndFrame() { + if (!g_engineConfig.settings.profile_enabled) + return; + + root.durationMs = std::chrono::duration(Clock::now() - startTime).count(); + + if (frameHistory.empty()) + root.visualDurationMs = root.durationMs; + + double blendFactor = 1.0 - std::pow(0.01, 1.0 / g_fps); + SmoothVisualDurations(root, blendFactor); + + frameHistory.push_back(root); + if (frameHistory.size() > maxFrames) + frameHistory.erase(frameHistory.begin()); +} + +const std::vector& HierarchicalProfiler::GetFrames() const { + return frameHistory; +} + +const ProfileNode* HierarchicalProfiler::GetLatestFrame() const { + return frameHistory.empty() ? nullptr : &frameHistory.back(); +} + +HierarchicalProfiler profiler; diff --git a/src/src/utils/Profiler.h b/src/src/utils/Profiler.h new file mode 100644 index 0000000..b003b55 --- /dev/null +++ b/src/src/utils/Profiler.h @@ -0,0 +1,94 @@ +#pragma once +#include +#include +#include +#include "EngineConfig.h" + +struct ProfileNode +{ + std::string name; + double startMs = 0.0; + double durationMs = 0.0; + double visualStartMs = 0.0; + double visualDurationMs = 0.0; + std::vector children; + + + + ProfileNode() = default; + + + ProfileNode(const std::string &n, double start) + : name(n), startMs(start) + { + children.reserve(8); + } +}; + +class HierarchicalProfiler +{ +public: + void BeginFrame(); + void EndFrame(); + + void BeginSection(const std::string &name); + void EndSection(); + + void BeginEngineSection(const std::string &name); + void EndEngineSection(); + + const std::vector &GetFrames() const; + const ProfileNode *GetLatestFrame() const; + +private: + using Clock = std::chrono::high_resolution_clock; + Clock::time_point startTime; + std::vector sectionStartTimes; + std::vector currentStack; + ProfileNode root{"Frame", 0.0}; + + std::vector frameHistory; + static constexpr size_t maxFrames = 30; + + size_t ProfilesLastFrame = 128; +}; + +extern HierarchicalProfiler profiler; + +// RAII Scoped Profiling (Zero-overhead when disabled) +struct ScopedProfile +{ + ScopedProfile(const std::string &name) + { + if (g_engineConfig.settings.profile_enabled) + profiler.BeginSection(name); + } + ~ScopedProfile() + { + if (g_engineConfig.settings.profile_enabled) + profiler.EndSection(); + } +}; + +struct ScopedEngineProfile +{ + ScopedEngineProfile(const std::string &name) + { + if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor) + profiler.BeginSection(name); + } + ~ScopedEngineProfile() + { + if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor) + profiler.EndSection(); + } +}; + +#define CONCAT_IMPL(a, b) a##b +#define CONCAT(a, b) CONCAT_IMPL(a, b) + +#define PROFILE_SCOPE(label) \ + ScopedProfile CONCAT(_scopedProfile_, __LINE__)(label) + +#define PROFILE_ENGINE_SCOPE(label) \ + ScopedEngineProfile CONCAT(_scopedEngineProfile_, __LINE__)(label)