FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: auto update the aspect ratio of the render texture by Miou-zora · Pull Request #422 · EngineSquared/EngineSquared · GitHub

feat: auto update the aspect ratio of the render texture - #422

Merged
ripel2 merged 20 commits into
mainfrom
auto-update-render-texture-resolution
Jan 10, 2026
Merged

feat: auto update the aspect ratio of the render texture#422
ripel2 merged 20 commits into
mainfrom
auto-update-render-texture-resolution

Conversation

Miou-zora commented Jan 10, 2026
edited by coderabbitai Bot
Loading

Copy link
Copy Markdown
Contributor

Related to no issues.

Fix crash when resizing the window.

Summary by CodeRabbit

  • New Features

    • Window resize event added and automatic resize handling wired into window and rendering setup.
    • Fullscreen toggle exposed on the Window API.
  • Improvements

    • Faster/direct delivery path for event callbacks.
    • Cameras auto-update aspect ratio from render targets after resizes.
    • Window size API uses unsigned dimensions and clearer semantics.
  • Chores

    • Graphics now depend on the event system for resize handling.

✏️ Tip: You can customize this high-level summary in your review settings.

Miou-zora self-assigned this Jan 10, 2026
Miou-zora added the enhancement New feature or request label Jan 10, 2026

coderabbitai Bot commented Jan 10, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Miou-zora has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a0ba1a4 and 6878554.

📒 Files selected for processing (1)
  • src/plugin/event/src/resource/EventManager.hpp
📝 Walkthrough

Walkthrough

Refactors event callbacks to remove Engine::Core parameters and add a DirectCallbackSchedulerTag for direct invocation. Adds window resize events and callbacks, wires resize → surface reconfiguration, introduces SetupResizableRenderTexture, and updates GPUCamera to support render-target-driven aspect ratio updates.

Changes

Cohort / File(s) Summary
Event system API & internals
src/plugin/event/src/resource/EventManager.hpp, src/plugin/event/src/system/EventSystem.hpp, src/plugin/event/src/utils/EventContainer.hpp
Remove Engine::Core& from Trigger/callback signatures; add DirectCallbackSchedulerTag and direct-callback path; change ProcessEvents() to no-arg; update Register/Unregister callback APIs and internal typeID placement.
Event tests
src/plugin/event/tests/EventTest.cpp, src/plugin/physics/tests/PhysicsTest.cpp
Update test callbacks to capture core instead of receiving it; call ProcessEvents<...>() without core; remove some core.RunSystems() calls.
Window: resize event & callbacks
src/plugin/window/src/event/OnResize.hpp, src/plugin/window/src/system/WindowSystem.hpp, src/plugin/window/src/system/WindowSystem.cpp, src/plugin/window/src/resource/Window.hpp, src/plugin/window/src/resource/Window.cpp, src/plugin/window/src/plugin/PluginWindow.cpp, src/plugin/window/xmake.lua
Add Window::Event::OnResize; add SetupWindowCallbacks() to register GLFW framebuffer callback that pushes resize events; change Window::GetSize() to glm::uvec2 GetSize() const; remove SetFramebufferSizeCallback; add PluginEvent dependency and export event headers.
Graphics: resizable render texture & camera updates
src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.hpp, src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.cpp, src/plugin/graphic/src/plugin/PluginGraphic.cpp, src/plugin/graphic/src/Graphic.hpp, src/plugin/graphic/xmake.lua, src/plugin/graphic/src/component/GPUCamera.hpp, src/plugin/graphic/src/system/GPUComponentManagement/OnCameraCreation.cpp, src/plugin/graphic/src/system/preparation/UpdateGPUCameras.cpp, src/plugin/graphic/src/system/initialization/ConfigureSurface.cpp
Add SetupResizableRenderTexture and register it in graphic setup; require Event plugin; add GPUCamera::targetTexture and UpdateAspectRatio(); set camera target to END_RENDER_TEXTURE_ID; UpdateGPUCameras reads texture size to update aspect; ConfigureSurface simplified to use window size.
Build / Example
examples/graphic_material_usage/xmake.lua
Removed inclusion of parent ../../xmake.lua (localize example build).

Sequence Diagram(s)

sequenceDiagram
    participant GLFW as GLFW (framebuffer callback)
    participant Window as Window System
    participant EventMgr as Event Manager
    participant Graphics as Graphic System
    participant Surface as Surface Configurator

    GLFW->>Window: framebuffer size callback(newW,newH)
    Window->>EventMgr: PushEvent(Window::Event::OnResize{newSize})
    EventMgr->>EventMgr: Direct callbacks invoked / queued
    EventMgr->>Graphics: Invoke registered resize callback(newSize)
    Graphics->>Surface: ConfigureSurface(newSize)
    Surface-->>Graphics: surface configured
    Graphics->>Graphics: UpdateGPUCameras -> UpdateAspectRatio(textureSize)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Divengerss
  • ripel2

Poem

🐰
I hopped through headers, bytes, and trees,
Resize events rode on the breeze.
Callbacks straight — no core to hand,
Textures stretch across the land.
Render snug in every frame, hooray! 🎨

🚥 Pre-merge checks | ✅ 2 | ❌ 1 ❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: adding automatic aspect ratio updates for render textures when the window is resized, which is the central change across EventManager refactoring, GPUCamera updates, and window resize event handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Miou-zora requested a review from a team January 10, 2026 17:49
Miou-zora marked this pull request as ready for review January 10, 2026 17:49

coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/plugin/event/src/resource/EventManager.hpp (1)

85-92: Outdated documentation.

The docstring at line 85 states the callback signature is void(Engine::Core&, const TEvent&), but based on the PR changes removing Engine::Core from callbacks, this should be updated to void(const TEvent&).

📝 Proposed fix
-     * @param callback The callback function with signature void(Engine::Core&, const TEvent&).
+     * @param callback The callback function with signature void(const TEvent&).
🤖 Fix all issues with AI agents
In @src/plugin/graphic/src/system/initialization/ConfigureSurface.cpp:
- Around line 14-25: The code must guard against null/empty resources and clamp
surface sizes: check that context.surface and context.surface->capabilities are
non-null and that capabilities->formats is non-empty before using formats[0];
verify context.deviceContext.GetDevice().has_value() (or similar) before calling
value(); ensure surfaceSize.x and surfaceSize.y are positive and clamp them to a
safe unsigned range (e.g., >=1 and capped to a max) before assigning to
config.width/config.height to avoid signed→unsigned wrap; if any check fails,
bail out or return a sensible default/failure path instead of dereferencing
nulls.

In @src/plugin/graphic/src/system/preparation/UpdateGPUCameras.cpp:
- Around line 20-24: The code calls gpuCamera.targetTexture.value() without
confirming the optional contains a value; update the conditional to first check
gpuCamera.targetTexture.has_value() (or use if (gpuCamera.targetTexture &&
textureContainer.Contains(...))) before calling .value(), then use that safe
value to call textureContainer.Contains and textureContainer.Get and pass the
resulting texture size to gpuCamera.UpdateAspectRatio; ensure you reference
gpuCamera.targetTexture, textureContainer.Contains, textureContainer.Get, and
gpuCamera.UpdateAspectRatio in the fixed condition so no
std::bad_optional_access can be thrown.

In @src/plugin/window/src/resource/Window.hpp:
- Line 5: Remove the #include "event/OnResize.hpp" from Window.hpp since
Window's public interface does not reference OnResize, and add an explicit
#include "event/OnResize.hpp" to WindowSystem.cpp near the top with the other
includes so WindowSystem.cpp no longer relies on the transitive dependency
through Window.hpp.
🧹 Nitpick comments (4)
src/plugin/window/src/resource/Window.hpp (1)

80-80: LGTM! Making GetSize() const is appropriate.

Making GetSize() a const method is correct if it only queries GLFW state without modifying the Window object's member variables.

The documentation comment "will update the window size" is slightly ambiguous. Consider clarifying to "will query and return the window size" if that's the intent:

📝 Proposed documentation improvement
 /**
  * @brief Get the window size.
  *
- * This function will update the window size from the GLFW window content area and return it.
+ * This function will query the current window size from the GLFW window content area and return it.
  *
  * @return A vector of integers to store the size of the window.
  */
src/plugin/graphic/src/component/GPUCamera.hpp (1)

32-42: Consider simplifying the aspect ratio calculation.

The logic correctly guards against division by zero. However, the aspect ratio calculation could be slightly simplified.

♻️ Minor refactor for cleaner code
     inline void UpdateAspectRatio(const glm::uvec2 &textureSize)
     {
         if (textureSize.y > 0)
         {
-            aspectRatio = static_cast<float>(textureSize.x) / static_cast<float>(textureSize.y);
+            aspectRatio = static_cast<float>(textureSize.x) / textureSize.y;
         }
         else
         {
             Log::Warn("GPUCamera::UpdateAspectRatio: texture height is zero, cannot update aspect ratio.");
         }
     }

The outer cast is sufficient since the division will promote textureSize.y to float automatically.

src/plugin/event/src/utils/EventContainer.hpp (1)

11-26: Core-less Trigger/Call refactor is consistent and keeps EventContainer focused on dispatch.
The std::any_cast<const TEvent&> + iteration over callbacks is straightforward.

Optional: drop Core include if no longer needed here
 #include "FunctionContainer.hpp"
-#include "core/Core.hpp"
src/plugin/event/src/resource/EventManager.hpp (1)

237-237: Verify the purpose of this mutex.

This mutex is declared but not used anywhere in the current implementation. If it was added for future use or a specific synchronization pattern, consider adding a comment explaining its purpose, or remove it if it's not needed.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e08ffc and c56ef38.

📒 Files selected for processing (22)
  • examples/graphic_material_usage/xmake.lua
  • src/plugin/event/src/resource/EventManager.hpp
  • src/plugin/event/src/system/EventSystem.hpp
  • src/plugin/event/src/utils/EventContainer.hpp
  • src/plugin/event/tests/EventTest.cpp
  • src/plugin/graphic/src/Graphic.hpp
  • src/plugin/graphic/src/component/GPUCamera.hpp
  • src/plugin/graphic/src/plugin/PluginGraphic.cpp
  • src/plugin/graphic/src/system/GPUComponentManagement/OnCameraCreation.cpp
  • src/plugin/graphic/src/system/initialization/ConfigureSurface.cpp
  • src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.cpp
  • src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.hpp
  • src/plugin/graphic/src/system/preparation/UpdateGPUCameras.cpp
  • src/plugin/graphic/xmake.lua
  • src/plugin/physics/tests/PhysicsTest.cpp
  • src/plugin/window/src/event/OnResize.hpp
  • src/plugin/window/src/plugin/PluginWindow.cpp
  • src/plugin/window/src/resource/Window.cpp
  • src/plugin/window/src/resource/Window.hpp
  • src/plugin/window/src/system/WindowSystem.cpp
  • src/plugin/window/src/system/WindowSystem.hpp
  • src/plugin/window/xmake.lua
💤 Files with no reviewable changes (1)
  • examples/graphic_material_usage/xmake.lua
🧰 Additional context used 🧬 Code graph analysis (5) src/plugin/window/src/plugin/PluginWindow.cpp (3)
src/engine/src/plugin/APlugin.hpp (2)
  • RegisterSystems (12-15)
  • RegisterSystems (12-12)
src/plugin/window/src/system/WindowSystem.cpp (4)
  • EnableVSync (13-13)
  • EnableVSync (13-13)
  • SetupWindowCallbacks (54-65)
  • SetupWindowCallbacks (54-54)
src/plugin/window/src/system/WindowSystem.hpp (2)
  • EnableVSync (53-53)
  • SetupWindowCallbacks (135-135)
src/plugin/graphic/src/system/preparation/UpdateGPUCameras.cpp (2)
src/plugin/graphic/src/system/preparation/UpdateGPUCameras.hpp (1)
  • UpdateGPUCameras (7-7)
src/plugin/graphic/src/component/GPUCamera.hpp (2)
  • camera (23-30)
  • camera (23-23)
src/plugin/graphic/src/plugin/PluginGraphic.cpp (6)
src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.cpp (2)
  • SetupResizableRenderTexture (11-17)
  • SetupResizableRenderTexture (11-11)
src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.hpp (1)
  • SetupResizableRenderTexture (7-7)
src/plugin/graphic/src/system/initialization/CreateAmbientLight.hpp (1)
  • CreateAmbientLight (7-7)
src/plugin/graphic/src/system/initialization/CreateAmbientLight.cpp (2)
  • CreateAmbientLight (10-14)
  • CreateAmbientLight (10-10)
src/plugin/graphic/src/system/initialization/CreatePointLights.cpp (2)
  • CreatePointLights (10-14)
  • CreatePointLights (10-10)
src/plugin/graphic/src/system/initialization/CreatePointLights.hpp (1)
  • CreatePointLights (7-7)
src/plugin/event/src/resource/EventManager.hpp (1)
src/plugin/event/src/utils/EventContainer.hpp (3)
  • event (13-13)
  • event (19-26)
  • event (19-19)
src/plugin/event/tests/EventTest.cpp (1)
src/plugin/event/src/utils/EventContainer.hpp (3)
  • event (13-13)
  • event (19-26)
  • event (19-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build and verify binaries (windows-latest)
  • GitHub Check: Run tests (windows-latest)
  • GitHub Check: Run tests (macos-latest)
  • GitHub Check: Run tests (ubuntu-latest)
  • GitHub Check: Build and verify binaries (macos-latest)
  • GitHub Check: Build and verify binaries (ubuntu-latest)
🔇 Additional comments (26)
src/plugin/graphic/xmake.lua (1)

6-6: LGTM! Event plugin integration looks correct.

The addition of the event module dependency enables the graphic plugin to handle window resize events, which is essential for the auto-updating render texture feature.

Also applies to: 33-33

src/plugin/graphic/src/Graphic.hpp (1)

102-102: LGTM! Header include is appropriate.

The new include exposes the resizable render texture setup system, which is central to this PR's functionality.

src/plugin/graphic/src/system/GPUComponentManagement/OnCameraCreation.cpp (2)

11-11: LGTM! Include is necessary for END_RENDER_TEXTURE_ID.

This header provides the constant used to assign the camera's target texture.


24-24: LGTM! Default target texture assignment enables resize handling.

Setting the camera's target texture to END_RENDER_TEXTURE_ID ensures that cameras render into the resizable render texture, allowing automatic aspect ratio updates during window resize events. This is a sensible default for the main rendering pipeline.

src/plugin/window/src/resource/Window.hpp (2)

83-88: LGTM! Documentation clarity is improved.

The updated documentation for SetSize() is clear and well-formatted.


90-93: ToggleFullscreen() implementation is solid.

The method correctly saves windowed state before entering fullscreen mode and restores it when exiting. The implementation includes proper null checking and uses the appropriate GLFW calls to manage window monitor assignment and state transitions.

src/plugin/window/src/plugin/PluginWindow.cpp (1)

15-15: LGTM! Callback registration properly ordered and thread-safe.

SetupWindowCallbacks registered in the Setup phase correctly queues the callback after window creation. The framebuffer size callback safely pushes OnResize events to EventManager via PushEvent(), which is protected by std::scoped_lock on both mutexes for thread safety. Since GLFW callbacks execute on the main thread during PollEvents (PreUpdate phase), no threading issues arise.

src/plugin/window/src/event/OnResize.hpp (1)

12-17: LGTM! Clean event struct design.

The OnResize event structure is well-designed with a clear purpose and minimal surface area. The use of glm::ivec2 for the new size is appropriate for window dimensions.

src/plugin/graphic/src/plugin/PluginGraphic.cpp (2)

5-5: LGTM! Proper event plugin integration.

The addition of Event::Plugin dependency and its corresponding include correctly establishes the plugin dependency chain for event-driven resize handling.

Also applies to: 26-26


53-53: LGTM! Appropriate system ordering.

The SetupResizableRenderTexture system is correctly positioned in the RenderingPipeline::Setup phase, ensuring resize event handling is configured before light initialization.

src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.hpp (1)

1-9: LGTM! Clean header declaration.

The header follows standard conventions with appropriate guards and includes.

src/plugin/window/src/system/WindowSystem.cpp (1)

54-65: The initialization order is already guaranteed by design, and EventManager is thread-safe.

The StoreCoreInWindow function is registered in the RenderingPipeline::Init stage and SetupWindowCallbacks is registered in RenderingPipeline::Setup stage. The Init stage always runs before Setup, so the window user pointer is guaranteed to be set before the callbacks are established. No null pointer guard is needed.

Additionally, EventManager is documented and implemented as a thread-safe class with proper mutex protection (_queueMutex, _callbacksMutex) and scoped locks around all critical operations. GLFW callbacks execute on the main thread anyway, so thread-safety is fully handled.

The code is safe as written.

Likely an incorrect or invalid review comment.

src/plugin/window/src/resource/Window.cpp (1)

23-28: LGTM: Appropriate const correctness.

Adding the const qualifier to GetSize() is correct since it doesn't modify object state.

src/plugin/window/xmake.lua (1)

4-4: LGTM: Event dependency properly integrated.

The build configuration correctly adds the Event plugin dependency and exposes event headers for downstream usage.

Also applies to: 13-13, 24-24

src/plugin/event/src/system/EventSystem.hpp (1)

8-12: LGTM: Event processing refactored to remove core parameter.

The change correctly reflects the removal of the Engine::Core parameter from EventManager::ProcessEvents, aligning with the PR's goal to refactor the event callback system.

src/plugin/graphic/src/component/GPUCamera.hpp (1)

3-3: LGTM: Logger include and targetTexture member.

The Logger include is necessary for the new UpdateAspectRatio method, and the targetTexture member is properly initialized and consistent with other ID members.

Also applies to: 21-21

src/plugin/event/tests/EventTest.cpp (2)

52-60: Multi-scheduler flow looks consistent with core-less Trigger/Call (capture-based access).
Manual ProcessEvents<Update>() / ProcessEvents<FixedTimeUpdate>() sequencing reads clearly.

Also applies to: 64-69


26-35: The integration test is correct as written. RegisterCallback<TestEvent>() uses the default DirectCallbackSchedulerTag, which executes callbacks synchronously within PushEvent() rather than queuing them. The PushEvent implementation immediately calls registered DirectCallbackSchedulerTag callbacks via callback->Call(event) before returning, so EXPECT_EQ(res.value, 42) will pass reliably.

The multi-scheduler test demonstrates the contrast: callbacks registered with explicit schedulers (e.g., Update, FixedTimeUpdate) are queued and require explicit ProcessEvents<TScheduler>() calls to execute.

Likely an incorrect or invalid review comment.

src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.cpp (1)

9-17: Ensure resize callback is safe on dispatch thread.
SetupResizableRenderTexture is registered once during plugin initialization, so callback duplication is not a risk. However, the resize callback will execute directly on whatever thread calls PushEvent(OnResize), and ConfigureSurface() accesses GPU resources that typically require device thread affinity. Verify that OnResize fires on the render/device thread, or consider deferring ConfigureSurface() to the correct scheduler.

Minor cleanup (drop unused param)
-static void OnWindowResize(Engine::Core &core, const glm::ivec2 &newSize) { ConfigureSurface(core); }
+static void OnWindowResize(Engine::Core &core) { ConfigureSurface(core); }
@@
     eventManager.RegisterCallback<Window::Event::OnResize>(
-        [&core](const Window::Event::OnResize &event) { OnWindowResize(core, event.newSize); });
+        [&core](const Window::Event::OnResize &) { OnWindowResize(core); });
src/plugin/physics/tests/PhysicsTest.cpp (1)

66-80: No data race concern. Collision events are buffered in worker threads by ContactListenerImpl and flushed on the main thread via ProcessBufferedEvents(). The RegisterCallback() with default DirectCallbackSchedulerTag executes callbacks synchronously on the calling thread (main thread), so collisionAdded and collisionRemoved are safely written and read only on the main thread.

src/plugin/event/src/resource/EventManager.hpp (6)

24-26: LGTM!

The private tag struct pattern is an appropriate way to distinguish direct callback scheduling from other scheduler types.


55-60: LGTM!

The move constructor correctly initializes all mutexes and acquires locks on the source object's mutexes before moving the data members.


67-78: LGTM!

The move assignment operator correctly handles self-assignment and uses std::scoped_lock with all six mutexes, which employs a deadlock avoidance algorithm.


139-171: LGTM!

Good thread-safety pattern: copying the queue under lock and then processing events without holding the lock prevents potential deadlocks from callbacks that might interact with the EventManager.


183-201: LGTM!

The default scheduler change is consistent with RegisterCallback, and the implementation properly validates the callback existence before deletion.


204-222: LGTM!

Clean implementation with proper mutex usage and efficient try_emplace for container creation.

coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/plugin/window/src/resource/Window.hpp (1)

99-107: Fix type inconsistency in GetMousePosition().

Line 103 assigns the result of GetSize() (which now returns glm::uvec2) to a glm::ivec2 variable. While implicit conversion works, this creates a type inconsistency.

🔧 Proposed fix
     inline glm::vec2 GetMousePosition()
     {
         double x = 0;
         double y = 0;
-        glm::ivec2 windowSize = GetSize();
+        glm::uvec2 windowSize = GetSize();
         glfwGetCursorPos(_window, &x, &y);
         y = windowSize.y - y;
         return {x, y};
     }
src/plugin/event/src/resource/EventManager.hpp (3)

102-124: Don’t invoke callbacks while holding _queueMutex/_callbacksMutex (deadlock/reentrancy + latency).
On Line 105 and Lines 119-122, PushEvent holds both mutexes and runs user code. If a callback calls back into EventManager (register/unregister/push), you can deadlock immediately; even without reentrancy, long callbacks will stall all event traffic.

Also, direct events are currently queued in the for loop and then immediately dispatched (Lines 107-113 + 115-123), which can lead to double-delivery if the direct queue ever gets processed (or if future refactors expose it).

Proposed fix (avoid reentrancy deadlocks + avoid double-queueing direct)
 template <typename TEvent> void PushEvent(const TEvent &event)
 {
     EventTypeID typeID = _GetId<TEvent>();
-    std::scoped_lock lock(_queueMutex, _callbacksMutex);
+    const auto directSchedulerID = std::type_index(typeid(DirectCallbackSchedulerTag));
+    std::shared_ptr<Utils::IEventContainer> directContainer;
+
+    {
+        std::scoped_lock lock(_queueMutex, _callbacksMutex);

-    for (const auto &[schedulerID, callbacks] : _eventCallbacks)
-    {
-        if (callbacks.contains(typeID))
-        {
-            _eventQueue[schedulerID].push({typeID, event});
-        }
-    }
-
-    auto &directEventCallbacks = _eventCallbacks[std::type_index(typeid(DirectCallbackSchedulerTag))];
-    if (directEventCallbacks.contains(typeID))
-    {
-        auto container = std::static_pointer_cast<Utils::EventContainer<TEvent>>(directEventCallbacks[typeID]);
-        for (auto &callback : container->GetFunctions())
-        {
-            callback->Call(event);
-        }
-    }
+        for (const auto &[schedulerID, callbacks] : _eventCallbacks)
+        {
+            if (schedulerID == directSchedulerID)
+                continue; // direct is handled immediately
+
+            if (callbacks.contains(typeID))
+            {
+                _eventQueue[schedulerID].push({typeID, event});
+            }
+        }
+
+        // Resolve direct callbacks without inserting empty scheduler buckets
+        if (auto schedIt = _eventCallbacks.find(directSchedulerID); schedIt != _eventCallbacks.end())
+        {
+            if (auto typeIt = schedIt->second.find(typeID); typeIt != schedIt->second.end())
+            {
+                directContainer = typeIt->second;
+            }
+        }
+    }
+
+    // Call outside locks to avoid deadlocks and long critical sections
+    if (directContainer)
+    {
+        directContainer->Trigger(std::any(event));
+    }
 }

179-197: Update UnregisterCallback docs: default scheduler is no longer Update.
Line 176 says “defaults to Update”, but the template default is DirectCallbackSchedulerTag (Line 179).


84-91: Default scheduler changed from deferred (Update) to synchronous (DirectCallbackSchedulerTag).

Callbacks registered without an explicit TScheduler parameter now execute synchronously inside PushEvent instead of being queued for deferred processing. Call sites in examples/physics_usage/src/main.cpp and src/plugin/graphic/src/system/initialization/SetupResizableRenderTexture.cpp that rely on the previous deferred dispatch semantics will be affected. Verify these integrations still behave correctly with synchronous execution, especially regarding thread safety and timing of event handlers.

Note: UnregisterCallback documentation (line 176) incorrectly states "defaults to Update" but actually defaults to DirectCallbackSchedulerTag.

🤖 Fix all issues with AI agents
In @src/plugin/window/src/resource/Window.cpp:
- Around line 23-29: GetSize() currently calls glfwGetWindowSize on _window
without checking for null and can cause undefined behavior; add the same null
check pattern used in Destroy() and ToggleFullscreen(): if _window is null, log
an error (or handle consistently with those methods) and return a safe default
glm::uvec2(0); otherwise call glfwGetWindowSize and return the size. Ensure you
reference the Window::GetSize method and the _window member when implementing
the guard so behavior matches the other methods.
🧹 Nitpick comments (2)
src/plugin/window/src/resource/Window.hpp (1)

72-79: Consider updating documentation to reflect the new return type.

The documentation at line 77 still refers to "A vector of integers" but the return type is now glm::uvec2 (unsigned integers).

📝 Proposed documentation update
     /**
      * @brief Get the window size.
      *
      * This function will update the window size from the GLFW window content area and return it.
      *
-     * @return A vector of integers to store the size of the window.
+     * @return A vector of unsigned integers representing the window's width and height.
      */
     glm::uvec2 GetSize() const;
src/plugin/event/src/resource/EventManager.hpp (1)

126-167: Docs/signature drift in ProcessEvents: remove stale core docs and prefer ProcessEvents() over ProcessEvents(void).
The comment block still references Engine::Core& (Line 133-134), but the function no longer takes it. Also, ProcessEvents(void) is non-idiomatic in C++.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c56ef38 and 416f84b.

📒 Files selected for processing (4)
  • src/plugin/event/src/resource/EventManager.hpp
  • src/plugin/window/src/resource/Window.cpp
  • src/plugin/window/src/resource/Window.hpp
  • src/plugin/window/src/system/WindowSystem.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/plugin/window/src/system/WindowSystem.cpp
🧰 Additional context used 🧬 Code graph analysis (2) src/plugin/window/src/resource/Window.cpp (1)
src/plugin/window/src/resource/Window.hpp (1)
  • _window (59-59)
src/plugin/event/src/resource/EventManager.hpp (1)
src/plugin/event/src/utils/EventContainer.hpp (3)
  • event (13-13)
  • event (19-26)
  • event (19-19)
🔇 Additional comments (3)
src/plugin/window/src/resource/Window.cpp (1)

23-23: Good addition of const correctness.

Marking GetSize() as const is appropriate since it doesn't modify the window state.

src/plugin/event/src/resource/EventManager.hpp (2)

24-26: Private DirectCallbackSchedulerTag is a good encapsulation point.
Keeping the tag private prevents external code from depending on the “direct scheduler” type.


199-218: typeID computed before locking is a nice cleanup.
Keeps type lookup consistent and reduces the locked region slightly.

coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @src/plugin/event/src/resource/EventManager.hpp:
- Around line 115-124: The callbacks are being invoked while holding _queueMutex
and _callbacksMutex (via the directEventCallbacks path), risking deadlocks if
callbacks call PushEvent/RegisterCallback/UnregisterCallback; change
EventManager's handling so you copy or move the
std::shared_ptr<Utils::EventContainer<TEvent>> (or its vector of callbacks) out
of _eventCallbacks[typeID] while holding the locks, then release both
_queueMutex and _callbacksMutex before iterating and calling
callback->Call(event) (follow the same lock-release-then-invoke pattern used in
ProcessEvents); ensure you access _eventCallbacks with the same keys
(std::type_index(typeid(DirectCallbackSchedulerTag)) and typeID) and preserve
the container type (Utils::EventContainer<TEvent>) when extracting it.
- Line 88: The template default for TScheduler was changed to
DirectCallbackSchedulerTag which makes callbacks run immediately and breaks
existing callers; restore the original behavior by reverting the template
declaration to use Update as the default TScheduler (e.g., template<typename
TEvent, typename TScheduler = Engine::Scheduler::Update, typename TCallBack>)
and update the documentation comment on UnregisterCallback to reflect the true
default, and if you intentionally keep the new immediate behavior instead,
update all call sites that relied on the old deferred semantics to explicitly
pass Engine::Scheduler::Update and adjust callers in graphics/physics/examples
accordingly.
🧹 Nitpick comments (1)
src/plugin/event/src/resource/EventManager.hpp (1)

183-183: Minor: Variable declarations moved to function start.

The typeID declarations are now at the beginning of their respective functions. Since _GetId<TEvent>() has no side effects, this is purely organizational with no behavioral change.

Also applies to: 204-204

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 416f84b and ee90c32.

📒 Files selected for processing (1)
  • src/plugin/event/src/resource/EventManager.hpp
🧰 Additional context used 🧬 Code graph analysis (1) src/plugin/event/src/resource/EventManager.hpp (1)
src/plugin/event/src/utils/EventContainer.hpp (3)
  • event (13-13)
  • event (19-26)
  • event (19-19)
🔇 Additional comments (3)
src/plugin/event/src/resource/EventManager.hpp (3)

24-25: LGTM: Tag type for immediate callback dispatch.

The empty tag struct is an appropriate pattern for distinguishing direct callbacks from scheduled ones at compile time.


43-47: LGTM: Thread-safe cleanup prevents destruction-time crashes.

The explicit clearing of event callbacks and queue under combined locks properly addresses the window resize crash mentioned in the PR objectives by ensuring clean teardown.


136-136: LGTM: Callback signature simplified by removing Engine::Core dependency.

The removal of the Engine::Core parameter streamlines the event callback interface and aligns with the refactored callback signature void(const TEvent&). Note that ProcessEvents correctly invokes callbacks after releasing locks (line 165 is outside the lock scope that ends at line 161), preventing the deadlock issue present in PushEvent.

Also applies to: 165-165

coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/plugin/event/src/resource/EventManager.hpp (3)

104-126: PushEvent direct-path has 3 correctness hazards (unbounded queue growth, deadlock, unsafe cast).

  1. If direct callbacks exist, the loop enqueues into _eventQueue[DirectCallbackSchedulerTag], but that scheduler can’t be processed externally (private tag) ⇒ queue growth without draining.
  2. Direct callbacks are executed while holding _queueMutex and _callbacksMutex ⇒ easy deadlock if a callback calls RegisterCallback/UnregisterCallback/PushEvent.
  3. static_pointer_cast<Utils::EventContainer<TEvent>> relies on hash_code() key correctness; on any mismatch/collision this becomes UB.
Proposed fix (skip queuing direct scheduler; don’t insert with operator[]; invoke outside locks; avoid static cast)
 template <typename TEvent> void PushEvent(const TEvent &event)
 {
-    EventTypeID typeID = _GetId<TEvent>();
-    std::scoped_lock lock(_queueMutex, _callbacksMutex);
+    const EventTypeID typeID = _GetId<TEvent>();
+    const auto directSchedulerID = std::type_index(typeid(DirectCallbackSchedulerTag));
+    std::shared_ptr<Utils::IEventContainer> directContainer;
 
-    for (const auto &[schedulerID, callbacks] : _eventCallbacks)
-    {
-        if (callbacks.contains(typeID))
-        {
-            _eventQueue[schedulerID].push({typeID, event});
-        }
-    }
-
-    auto &directEventCallbacks = _eventCallbacks[std::type_index(typeid(DirectCallbackSchedulerTag))];
-    if (directEventCallbacks.contains(typeID))
-    {
-        auto container = std::static_pointer_cast<Utils::EventContainer<TEvent>>(directEventCallbacks[typeID]);
-        for (auto &callback : container->GetFunctions())
-        {
-            callback->Call(event);
-        }
-    }
+    {
+        std::scoped_lock lock(_queueMutex, _callbacksMutex);
+
+        for (const auto &[schedulerID, callbacks] : _eventCallbacks)
+        {
+            if (schedulerID == directSchedulerID)
+            {
+                continue; // direct callbacks are invoked immediately (not queued)
+            }
+            if (callbacks.contains(typeID))
+            {
+                _eventQueue[schedulerID].push({typeID, event});
+            }
+        }
+
+        if (auto itSched = _eventCallbacks.find(directSchedulerID); itSched != _eventCallbacks.end())
+        {
+            if (auto itCb = itSched->second.find(typeID); itCb != itSched->second.end())
+            {
+                directContainer = itCb->second; // keep container alive after unlocking
+            }
+        }
+    }
+
+    if (directContainer)
+    {
+        std::any anyEvent = event;
+        directContainer->Trigger(anyEvent);
+    }
 }

181-199: UnregisterCallback + direct-callback execution currently deadlocks with the new direct-path.

As implemented in PushEvent, direct callbacks run while _callbacksMutex is held; if any callback calls UnregisterCallback(...), it will try to re-lock _callbacksMutex and deadlock. Fixing PushEvent to invoke outside locks (see earlier comment) should address this.


81-93: API signature change unaddressed in examples: callback no longer receives Engine::Core& parameter.

The new callback signature is void(const TEvent&) (requiring only the event), while examples/physics_usage/src/main.cpp line 98 still defines OnCollisionAdded(Engine::Core &core, const Physics::Event::CollisionAddedEvent &event). This will cause a compilation error. The default scheduler has also changed to DirectCallbackSchedulerTag. Update all downstream callbacks to match the new single-parameter signature.

🤖 Fix all issues with AI agents
In @src/plugin/event/src/resource/EventManager.hpp:
- Around line 24-26: Defaulting TScheduler to DirectCallbackSchedulerTag changes
semantics so RegisterCallback<TEvent>(...) now invokes callbacks inline in
PushEvent rather than during the scheduler phase; revert the template default
for TScheduler to the prior non-direct scheduler (or remove the default so
callers must opt-in) to preserve existing thread-affinity and reentrancy
behavior, update EventManager's template declaration (the TScheduler default)
accordingly, then audit all RegisterCallback<TEvent> call sites for places that
rely on scheduler-phase execution and add explicit DirectCallbackSchedulerTag
where inline execution is intended; run/adjust unit tests that depend on
scheduler-phase callback ordering.
🧹 Nitpick comments (1)
src/plugin/event/src/resource/EventManager.hpp (1)

137-169: ProcessEvents(void) is a bit unidiomatic, and Trigger concurrency needs a quick sanity check.

  • Prefer ProcessEvents() (C++ style) unless there’s a strong convention otherwise.
  • Trigger(event) happens outside _callbacksMutex (which is good for avoiding lock-held callbacks), but assumes Utils::EventContainer<TEvent> is safe against concurrent AddFunction/DeleteFunction while triggering. Please confirm the container provides its own synchronization or copy-on-write semantics.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee90c32 and a0ba1a4.

📒 Files selected for processing (1)
  • src/plugin/event/src/resource/EventManager.hpp
🧰 Additional context used 🧬 Code graph analysis (1) src/plugin/event/src/resource/EventManager.hpp (1)
src/plugin/event/src/utils/EventContainer.hpp (3)
  • event (13-13)
  • event (19-26)
  • event (19-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Run tests (windows-latest)
  • GitHub Check: Run tests (macos-latest)
  • GitHub Check: Build and verify binaries (windows-latest)
  • GitHub Check: Build and verify binaries (macos-latest)
  • GitHub Check: Build and verify binaries (ubuntu-latest)
  • GitHub Check: Run tests (ubuntu-latest)
🔇 Additional comments (2)
src/plugin/event/src/resource/EventManager.hpp (2)

43-48: Destructor lock+clear is fine, but it doesn’t make destruction “thread-safe”.

Clearing under std::scoped_lock is OK, but if other threads can still call into EventManager during teardown, that’s UB regardless. Worth ensuring lifecycle guarantees elsewhere.


201-220: typeID hoist in _RegisterCallbackImpl is a nice cleanup.

Moving typeID earlier improves readability and keeps the type lookup consistent.

Copy link
Copy Markdown

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL