Skip to content

Conversation

kimkulling
Copy link
Owner

@kimkulling kimkulling commented Jun 7, 2025

Summary by CodeRabbit

  • New Features

    • Improved debug text rendering with a new canvas-based approach, providing clearer and more flexible on-screen debug text display.
  • Refactor

    • Simplified and decoupled debug text rendering logic, making it more efficient and easier to maintain.
    • Updated namespace declarations to modern inline style for consistency.
    • Renamed static instance variables for clarity across several components.
    • Enhanced member variable initialization for improved stability.
    • Adjusted utility functions for mesh index handling to improve rendering processes.
    • Modified token retrieval method to provide explicit success feedback.

Copy link

coderabbitai bot commented Jun 7, 2025

"""

Walkthrough

The debug text rendering system in DbgRenderer was refactored to use a CanvasRenderer-based approach instead of mesh-based rendering. The constructor, data structures, and rendering logic were updated to manage debug texts via unique IDs and draw them directly on a canvas, simplifying and decoupling the rendering process.

Changes

File(s) Change Summary
src/Engine/RenderBackend/DbgRenderer.cpp Refactored DbgRenderer to use CanvasRenderer for debug text; updated constructor and methods; removed mesh usage.
src/Engine/RenderBackend/DbgRenderer.h Added CanvasRenderer member; revised DebugText struct to use guid instead of mesh pointer; reordered and initialized members.
src/Engine/App/AppBase.h Initialized member pointer mTimer to nullptr in AppBase class.
src/Engine/App/ServiceProvider.cpp Renamed static member s_instance to sInstance; updated all references; added unused conditional check in setService.
src/Engine/App/ServiceProvider.h Renamed static member s_instance to sInstance; consolidated namespaces; replaced forward declaration with include.
src/Engine/Common/AbstractService.h Removed space in static pointer initialization macro IMPLEMENT_SINGLETON for formatting consistency.
src/Engine/RenderBackend/Mesh/MeshUtilities.h Replaced include guards with #pragma once; merged namespaces; added templated renumberIndices function.
src/Engine/RenderBackend/RenderBackendService.h Reordered include of "Common/glm_common.h" to after "Common/Event.h".
src/Engine/RenderBackend/RenderCommon.h Removed renumberIndices template function; merged namespaces into single declaration.
test/RenderTests/src/*.cpp, *.h (multiple files) Consolidated nested namespaces into single inline namespace declarations; updated closing comments; minor member renames and formatting improvements in some test files.
test/RenderTests/src/SwitchCmdBufferRenderTest.cpp Changed static const to constexpr; added default member initializers; simplified constructor and destructor.
test/RenderTests/src/main.cpp Removed trailing blank lines at file end.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant DbgRenderer
    participant CanvasRenderer

    Client->>DbgRenderer: renderDbgText(x, y, id, text)
    DbgRenderer->>DbgRenderer: Store or update DebugText by id
    DbgRenderer->>CanvasRenderer: drawText(x, y, text)
Loading

Possibly related PRs

Suggested labels

Bug

Poem

In lines of code, a canvas grows,
Where once were meshes, text now flows.
With guids and pointers set anew,
Debugging's clearer, simpler too!
A rabbit hops with glee to see—
The canvas brings more clarity!
🐇✨
"""

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
src/Engine/RenderBackend/DbgRenderer.cpp (1)

193-196: ⚠️ Potential issue

Update clear() to handle canvas-based resources.

The clear method still references the old mesh-based system. It should clean up the debug text array instead.

Replace with:

 void DbgRenderer::clear() {
-    delete mDebugMesh;
-    mDebugMesh = nullptr;
+    for (size_t i = 0; i < mDebugTextMeshes.size(); ++i) {
+        delete mDebugTextMeshes[i];
+    }
+    mDebugTextMeshes.clear();
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 164aaa9 and 1c7354f.

📒 Files selected for processing (2)
  • src/Engine/RenderBackend/DbgRenderer.cpp (3 hunks)
  • src/Engine/RenderBackend/DbgRenderer.h (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: Analyze (cpp)
🔇 Additional comments (4)
src/Engine/RenderBackend/DbgRenderer.h (3)

28-28: LGTM! Necessary include for canvas-based rendering.

The addition of the CanvasRenderer header is appropriate for the new canvas-based debug text rendering approach.


61-63: Good refactoring to use guid-based identification.

Replacing the mesh pointer with a guid identifier is a cleaner approach for managing debug text entries.


65-69: Proper member initialization.

Good practice to explicitly initialize all pointers to nullptr.

src/Engine/RenderBackend/DbgRenderer.cpp (1)

198-225: Verify if addLine() is still needed with the canvas-based approach.

This method still uses mDebugMesh from the old mesh-based system. If line rendering should also move to canvas-based rendering, this method needs refactoring. If it's intentionally kept mesh-based, ensure mDebugMesh is properly managed.

Please clarify whether line rendering should remain mesh-based or if this is legacy code that should be removed/refactored.

Comment on lines +51 to +52
mCanvasRenderer = new CanvasRenderer(2, x, y, w, h);
mCanvasRenderer->create();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check after allocation and ensure proper cleanup.

The CanvasRenderer allocation should be checked for success, and it needs to be properly deleted in the destructor to prevent memory leaks.

Consider adding:

 mCanvasRenderer = new CanvasRenderer(2, x, y, w, h);
+if (mCanvasRenderer != nullptr) {
     mCanvasRenderer->create();
+}

Also, update the destructor:

 DbgRenderer::~DbgRenderer() {
     clear();
+    delete mCanvasRenderer;
+    mCanvasRenderer = nullptr;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mCanvasRenderer = new CanvasRenderer(2, x, y, w, h);
mCanvasRenderer->create();
// In the constructor (around lines 51–52)
mCanvasRenderer = new CanvasRenderer(2, x, y, w, h);
if (mCanvasRenderer != nullptr) {
mCanvasRenderer->create();
}
// In the destructor
DbgRenderer::~DbgRenderer() {
clear();
delete mCanvasRenderer;
mCanvasRenderer = nullptr;
}
🤖 Prompt for AI Agents
In src/Engine/RenderBackend/DbgRenderer.cpp around lines 51 to 52, add a null
check after allocating mCanvasRenderer to ensure it was successfully created
before calling create(). Additionally, update the class destructor to properly
delete mCanvasRenderer to prevent memory leaks by releasing the allocated
memory.

i32 w = settings->getInt(Settings::WinWidth);
i32 h = settings->getInt(Settings::WinHeight);

mCanvasRenderer = new CanvasRenderer(2, x, y, w, h);

Check warning

Code scanning / CodeQL

Resource not released in destructor Warning

Resource mCanvasRenderer is acquired by class DbgRenderer but not released anywhere in this class.

Copilot Autofix

AI 4 months ago

To fix the issue, the DbgRenderer destructor should release the mCanvasRenderer resource by calling delete on it. This ensures that the resource is properly cleaned up when the DbgRenderer instance is destroyed. Additionally, the clear() function should be reviewed to ensure it does not redundantly release mCanvasRenderer. If clear() already handles this, the destructor should not duplicate the cleanup.

Steps:

  1. Add a delete mCanvasRenderer; statement in the destructor of DbgRenderer.
  2. Ensure that mCanvasRenderer is set to nullptr after deletion to avoid dangling pointers.
  3. Review the clear() function to confirm it does not already release mCanvasRenderer.

Suggested changeset 1
src/Engine/RenderBackend/DbgRenderer.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Engine/RenderBackend/DbgRenderer.cpp b/src/Engine/RenderBackend/DbgRenderer.cpp
--- a/src/Engine/RenderBackend/DbgRenderer.cpp
+++ b/src/Engine/RenderBackend/DbgRenderer.cpp
@@ -56,2 +56,6 @@
     clear();
+    if (mCanvasRenderer != nullptr) {
+        delete mCanvasRenderer;
+        mCanvasRenderer = nullptr;
+    }
 }
EOF
@@ -56,2 +56,6 @@
clear();
if (mCanvasRenderer != nullptr) {
delete mCanvasRenderer;
mCanvasRenderer = nullptr;
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (2)
src/Engine/App/AppBase.h (1)

197-200: ⚠️ Potential issue

Getter/field type mismatch: CanvasRenderer* vs IRenderPath*

getCanvasRenderer() advertises a RenderBackend::CanvasRenderer *, yet the backing member is declared as
RenderBackend::IRenderPath *mCanvasRenderer. Unless every call site casts, this violates the contract and can
trigger UB.

-RenderBackend::IRenderPath *mCanvasRenderer = nullptr;
+RenderBackend::CanvasRenderer *mCanvasRenderer = nullptr;

Synchronise the types (or the getter’s return type) and adjust includes as needed.

Also applies to: 264-265

src/Engine/RenderBackend/RenderCommon.h (1)

37-57: ⚠️ Potential issue

Namespace switch breaks all RenderBackend::… qualified references inside the same scope

Changing to the C++-17 nested-namespace syntax is fine, but every reference that still starts with RenderBackend:: is now interpreted as
OSRE::RenderBackend::RenderBackend::…, which does not exist and will cause compilation errors (e.g. the MeshArray alias on l.49, UiVertexCache,
UiIndexCache, etc.).

-using MeshArray = cppcore::TArray<RenderBackend::Mesh*>;
+using MeshArray = cppcore::TArray<Mesh*>;

Please drop the redundant qualifier (or use the fully-qualified ::OSRE::RenderBackend::Mesh) everywhere inside this namespace.
Search/replace of RenderBackend:: within the file will catch several more occurrences.

🧹 Nitpick comments (2)
src/Engine/App/ServiceProvider.cpp (2)

34-40: Singleton creation is not thread-safe

create() uses a raw static pointer without synchronisation. If two threads call create() concurrently during engine start-up, a double allocation or interleaved write to sInstance is possible.

Consider the usual Meyers singleton:

static ServiceProvider &instance() {
    static ServiceProvider instance;
    return instance;
}

or protect the allocation with std::call_once / a mutex.


42-46: Potential double-delete if destroy() is raced

destroy() deletes sInstance without synchronisation. A second thread could reach destroy() after the first one has already set sInstance to nullptr but before the delete, resulting in UB, or delete twice in other race patterns. Align the lifetime management with the thread-safe singleton suggestion above or guard with a mutex.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c7354f and 2516b9d.

📒 Files selected for processing (25)
  • src/Engine/App/AppBase.h (1 hunks)
  • src/Engine/App/ServiceProvider.cpp (1 hunks)
  • src/Engine/App/ServiceProvider.h (2 hunks)
  • src/Engine/Common/AbstractService.h (1 hunks)
  • src/Engine/RenderBackend/DbgRenderer.cpp (4 hunks)
  • src/Engine/RenderBackend/DbgRenderer.h (2 hunks)
  • src/Engine/RenderBackend/Mesh/MeshUtilities.h (2 hunks)
  • src/Engine/RenderBackend/RenderBackendService.h (1 hunks)
  • src/Engine/RenderBackend/RenderCommon.h (2 hunks)
  • test/RenderTests/src/AABBDbgRenderTest.cpp (2 hunks)
  • test/RenderTests/src/AbstractRenderTest.cpp (2 hunks)
  • test/RenderTests/src/AbstractRenderTest.h (2 hunks)
  • test/RenderTests/src/AbstractTestFixure.h (2 hunks)
  • test/RenderTests/src/BaseTriangleRenderTest.cpp (2 hunks)
  • test/RenderTests/src/DbgFontRenderTest.cpp (3 hunks)
  • test/RenderTests/src/GeoInstanceRenderTest.cpp (2 hunks)
  • test/RenderTests/src/GeoModelMatrixRenderTest.cpp (2 hunks)
  • test/RenderTests/src/PrimTypeRenderTest.cpp (2 hunks)
  • test/RenderTests/src/RenderBufferAccessTest.cpp (2 hunks)
  • test/RenderTests/src/RenderTargetRenderTest.cpp (2 hunks)
  • test/RenderTests/src/RenderTestSuite.cpp (4 hunks)
  • test/RenderTests/src/RenderTestSuite.h (3 hunks)
  • test/RenderTests/src/StaticTextRenderTest.cpp (2 hunks)
  • test/RenderTests/src/SwitchCmdBufferRenderTest.cpp (3 hunks)
  • test/RenderTests/src/main.cpp (0 hunks)
💤 Files with no reviewable changes (1)
  • test/RenderTests/src/main.cpp
✅ Files skipped from review due to trivial changes (17)
  • src/Engine/RenderBackend/RenderBackendService.h
  • test/RenderTests/src/GeoModelMatrixRenderTest.cpp
  • test/RenderTests/src/PrimTypeRenderTest.cpp
  • test/RenderTests/src/AbstractRenderTest.h
  • test/RenderTests/src/AbstractRenderTest.cpp
  • src/Engine/Common/AbstractService.h
  • test/RenderTests/src/StaticTextRenderTest.cpp
  • test/RenderTests/src/AABBDbgRenderTest.cpp
  • test/RenderTests/src/RenderTargetRenderTest.cpp
  • test/RenderTests/src/BaseTriangleRenderTest.cpp
  • src/Engine/App/ServiceProvider.h
  • test/RenderTests/src/GeoInstanceRenderTest.cpp
  • test/RenderTests/src/RenderTestSuite.h
  • test/RenderTests/src/AbstractTestFixure.h
  • test/RenderTests/src/RenderBufferAccessTest.cpp
  • test/RenderTests/src/DbgFontRenderTest.cpp
  • test/RenderTests/src/RenderTestSuite.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Engine/RenderBackend/DbgRenderer.h
  • src/Engine/RenderBackend/DbgRenderer.cpp
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/Engine/App/ServiceProvider.cpp (3)
src/Engine/Platform/PlatformInterface.cpp (1)
  • sInstance (44-44)
src/Engine/App/ServiceProvider.h (3)
  • create (47-47)
  • destroy (48-48)
  • setService (49-49)
src/Engine/App/AssetRegistry.cpp (2)
  • destroy (45-52)
  • destroy (45-45)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Analyze (cpp)
  • GitHub Check: build
🔇 Additional comments (7)
src/Engine/App/ServiceProvider.cpp (1)

32-32: Renaming to sInstance improves consistency

Matches the rest of the codebase (AssetRegistry, PlatformInterface, etc.). 👍

src/Engine/App/AppBase.h (1)

256-256: 👍 Null-initialisation of mTimer improves safety

Explicitly initialising the raw pointer prevents undefined behaviour when the member is accessed before assignment.
No further issues with this single-line change.

test/RenderTests/src/SwitchCmdBufferRenderTest.cpp (1)

36-36: C++17 nested-namespace syntax requires C++17 compile flags

namespace OSRE::RenderTest { is only valid from C++17 onward.
Ensure your build system passes -std=c++17 (or higher) for every translation unit in this target.

src/Engine/RenderBackend/RenderCommon.h (1)

1078-1080: Closing brace unchanged – LGTM

No further issues with the namespace close.

src/Engine/RenderBackend/Mesh/MeshUtilities.h (3)

23-24: Good call replacing include guards with #pragma once

Reduces boiler-plate and is supported by all major compilers we target.


30-32: Namespace style consistent with the rest of the refactor

No concerns here; just remember that all fully-qualified references must be adjusted in the implementation files too.


166-166: Namespace close – LGTM

Comment on lines +50 to 53
if (type == ServiceType::Invalid || type == ServiceType::Count) {
}
sInstance->mServiceArray[static_cast<size_t>(type)] = service;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Guard against Invalid / Count to avoid out-of-bounds access

The check for ServiceType::Invalid and ServiceType::Count is empty, but the code immediately writes to mServiceArray with the (still) invalid index.
ServiceType::Count is one-past-the-end, so this is an off-by-one write; Invalid is typically 0 or -1, also unintended. Both will corrupt memory in release builds and trip assertions in debug builds.

-    if (type == ServiceType::Invalid || type == ServiceType::Count) {
-    }
-    sInstance->mServiceArray[static_cast<size_t>(type)] = service;
+    if (type == ServiceType::Invalid || type == ServiceType::Count) {
+        OSRE_ASSERT(false, "Attempt to register an invalid service type");
+        return;
+    }
+
+    OSRE_ASSERT(nullptr != sInstance, "ServiceProvider::create() was never called");
+    sInstance->mServiceArray[static_cast<size_t>(type)] = service;

Fail-fast (assert / early-return) prevents undefined behaviour and makes misuse obvious in tests.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (type == ServiceType::Invalid || type == ServiceType::Count) {
}
sInstance->mServiceArray[static_cast<size_t>(type)] = service;
}
if (type == ServiceType::Invalid || type == ServiceType::Count) {
OSRE_ASSERT(false, "Attempt to register an invalid service type");
return;
}
OSRE_ASSERT(nullptr != sInstance, "ServiceProvider::create() was never called");
sInstance->mServiceArray[static_cast<size_t>(type)] = service;
}
🤖 Prompt for AI Agents
In src/Engine/App/ServiceProvider.cpp around lines 50 to 53, the code checks for
ServiceType::Invalid and ServiceType::Count but does not prevent using these
invalid indices to write to mServiceArray, causing potential out-of-bounds
memory corruption. Fix this by adding a fail-fast mechanism such as an assertion
or an early return inside the if block to prevent writing to mServiceArray when
type is Invalid or Count, ensuring no invalid index is used.

Comment on lines +84 to 87
static constexpr ui32 NumPoints = 1000;
Mesh *m_pointMesh = nullptr;
ParticleEmitter *m_particeGen = nullptr;

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Memory-ownership & naming clean-up

  1. m_pointMesh is declared but never initialised or destroyed – potential leak or dead code.
  2. m_particeGen is misspelled; consider m_particleEmitter for clarity.

Suggested fix:

-static constexpr ui32 NumPoints = 1000;
-Mesh *m_pointMesh = nullptr;
-ParticleEmitter *m_particeGen = nullptr;
+static constexpr ui32 NumPoints = 1000;
+ParticleEmitter *m_particleEmitter = nullptr;

And in destructor / usages:

-    delete m_particeGen;
+    delete m_particleEmitter;

If m_pointMesh truly isn’t needed, drop it entirely; otherwise allocate & free it consistently.

Also applies to: 94-97

🤖 Prompt for AI Agents
In test/RenderTests/src/SwitchCmdBufferRenderTest.cpp around lines 84 to 87 and
94 to 97, the member variable m_pointMesh is declared but never initialized or
deleted, risking a memory leak or dead code; also, m_particeGen is misspelled
and should be renamed to m_particleEmitter for clarity. Fix this by either
removing m_pointMesh if unused or properly allocating it in the constructor and
deleting it in the destructor. Rename m_particeGen to m_particleEmitter
consistently throughout the code, including all declarations, initializations,
and usages.

Comment on lines 153 to 164
/// @brief Will renumber the indices.
/// @tparam T Index type
/// @param dc The draw command container
/// @param offset Offset as renumbering parameter
template <class T>
inline void renumberIndices(const DrawCmd &dc, T offset) {
if (offset > 0) {
for (size_t j = 0; j < dc.NumIndices; ++j) {
dc.Indices[j] += static_cast<ui16>(offset);
}
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

renumberIndices mutates through a const ref and truncates the offset

  1. The function takes const DrawCmd& yet mutates the pointed-to index array – that’s misleading.
  2. offset is cast to ui16; an offset ≥ 65 536 will silently overflow.
  3. No guard against dc.Indices == nullptr or dc.NumIndices == 0.
-template <class T>
-inline void renumberIndices(const DrawCmd &dc, T offset) {
-    if (offset > 0) {
-        for (size_t j = 0; j < dc.NumIndices; ++j) {
-            dc.Indices[j] += static_cast<ui16>(offset);
-        }
-    }
+template <class TOffset>
+inline void renumberIndices(DrawCmd &dc, TOffset offset) {
+    using IndexT = std::remove_pointer_t<decltype(dc.Indices)>;
+
+    if (dc.Indices == nullptr || dc.NumIndices == 0 || offset == 0) {
+        return;
+    }
+    for (size_t j = 0; j < dc.NumIndices; ++j) {
+        dc.Indices[j] =
+            static_cast<IndexT>(dc.Indices[j] + static_cast<IndexT>(offset));
+    }
 }

This keeps the intent intact, avoids UB and prevents truncation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// @brief Will renumber the indices.
/// @tparam T Index type
/// @param dc The draw command container
/// @param offset Offset as renumbering parameter
template <class T>
inline void renumberIndices(const DrawCmd &dc, T offset) {
if (offset > 0) {
for (size_t j = 0; j < dc.NumIndices; ++j) {
dc.Indices[j] += static_cast<ui16>(offset);
}
}
}
/// @brief Will renumber the indices.
/// @tparam T Index type
/// @param dc The draw command container
/// @param offset Offset as renumbering parameter
template <class TOffset>
inline void renumberIndices(DrawCmd &dc, TOffset offset) {
using IndexT = std::remove_pointer_t<decltype(dc.Indices)>;
if (dc.Indices == nullptr || dc.NumIndices == 0 || offset == 0) {
return;
}
for (size_t j = 0; j < dc.NumIndices; ++j) {
dc.Indices[j] =
static_cast<IndexT>(dc.Indices[j] + static_cast<IndexT>(offset));
}
}
🤖 Prompt for AI Agents
In src/Engine/RenderBackend/Mesh/MeshUtilities.h around lines 153 to 164, the
renumberIndices function incorrectly takes a const reference to DrawCmd but
modifies its Indices array, which is misleading and unsafe. Change the parameter
to a non-const reference to allow mutation. Remove the cast of offset to ui16 to
prevent silent overflow for large values. Add checks to ensure dc.Indices is not
nullptr and dc.NumIndices is greater than zero before the loop to avoid
undefined behavior.

Comment on lines +50 to +51
if (type == ServiceType::Invalid || type == ServiceType::Count) {
}

Check notice

Code scanning / CodeQL

Futile conditional Note

If-statement with an empty then-branch and no else-branch.

Copilot Autofix

AI 4 months ago

To fix the issue, we need to address the empty then-branch of the if statement. There are two possible approaches:

  1. Remove the if statement entirely if the condition is unnecessary and does not affect the program's functionality.
  2. Implement the then-branch to handle the case when the condition is true. For example, we could log an error, throw an exception, or return early to indicate that invalid service types are not allowed.

In this case, the best approach is to implement the then-branch to handle invalid service types explicitly. This ensures the code is robust and avoids potential misuse of the setService method. We will add a logging statement or throw an exception to indicate that the provided service type is invalid.

Suggested changeset 1
src/Engine/App/ServiceProvider.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Engine/App/ServiceProvider.cpp b/src/Engine/App/ServiceProvider.cpp
--- a/src/Engine/App/ServiceProvider.cpp
+++ b/src/Engine/App/ServiceProvider.cpp
@@ -23,2 +23,3 @@
 #include "App/ServiceProvider.h"
+#include <stdexcept>
 #include "RenderBackend/RenderBackendService.h"
@@ -50,2 +51,3 @@
     if (type == ServiceType::Invalid || type == ServiceType::Count) {
+        throw std::invalid_argument("Invalid service type provided.");
     }
EOF
@@ -23,2 +23,3 @@
#include "App/ServiceProvider.h"
#include <stdexcept>
#include "RenderBackend/RenderBackendService.h"
@@ -50,2 +51,3 @@
if (type == ServiceType::Invalid || type == ServiceType::Count) {
throw std::invalid_argument("Invalid service type provided.");
}
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +50 to +51
if (type == ServiceType::Invalid || type == ServiceType::Count) {
}

Check notice

Code scanning / CodeQL

Empty branch of conditional Note

Empty block without comment.

Copilot Autofix

AI 4 months ago

To fix the issue, we need to address the empty block on line 51. The best approach depends on the intent of the code:

  1. If the condition type == ServiceType::Invalid || type == ServiceType::Count is not supposed to trigger any action, we should add a comment explaining why the block is intentionally left empty.
  2. If the condition is meant to handle a specific case, we should implement the appropriate logic within the block.

In this case, we will assume the first option (no action is required for the condition) and add a comment to clarify the intent. This ensures the code is more maintainable and avoids confusion for future developers.

Suggested changeset 1
src/Engine/App/ServiceProvider.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Engine/App/ServiceProvider.cpp b/src/Engine/App/ServiceProvider.cpp
--- a/src/Engine/App/ServiceProvider.cpp
+++ b/src/Engine/App/ServiceProvider.cpp
@@ -50,2 +50,3 @@
     if (type == ServiceType::Invalid || type == ServiceType::Count) {
+        // No action required for invalid or count service types.
     }
EOF
@@ -50,2 +50,3 @@
if (type == ServiceType::Invalid || type == ServiceType::Count) {
// No action required for invalid or count service types.
}
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (5)
src/Engine/RenderBackend/2D/CanvasRenderer.cpp (5)

216-224: ⚠️ Potential issue

Potential 16-bit overflow in renumberIndices call

renumberIndices casts the offset to ui16. Because numVertices is a running total that can easily exceed 65 535, any larger value will silently wrap, yielding corrupt indices when the cumulative vertex count grows beyond the 16-bit range.

-renumberIndices(dc.Indices, dc.NumIndices, numVertices);
+// use a wider temporary and guard against overflow
+if (numVertices > std::numeric_limits<ui16>::max()) {
+    osre_error(Tag, "Vertex count exceeds 16-bit index range – update Mesh/IBO strategy");
+}
+renumberIndices(dc.Indices, dc.NumIndices, static_cast<ui16>(numVertices));

Consider switching the index type to 32 bit or splitting large batches.


320-324: ⚠️ Potential issue

Off-by-one: element Indices[1] is never written

Indices[0] is assigned twice, leaving Indices[1] uninitialised (UB) and the line will not be rendered.

-dc->Indices[0] = 0;
-dc->Indices[0] = 1;
+dc->Indices[0] = 0;
+dc->Indices[1] = 1;

365-376: 🛠️ Refactor suggestion

Broken edge-line index list in unfilled triangle

The three assignments below overwrite earlier values, producing {0,1,1,2,1,0} instead of the expected {0,1,1,2,2,0}.

-        dc->Indices[1] = 1;
-        dc->Indices[2] = 2;
-
-        dc->Indices[2] = 1;
-        dc->Indices[0] = 2;
+        dc->Indices[0] = 0;
+        dc->Indices[1] = 1;
+        dc->Indices[2] = 1;
+        dc->Indices[3] = 2;
+        dc->Indices[4] = 2;
+        dc->Indices[5] = 0;

This yields the correct six indices for the three triangle edges.


139-146: ⚠️ Potential issue

Incorrect deallocation path for objects from TPoolAllocator

alloc() acquires memory from sAllocator, but dealloc() calls delete, which is undefined behaviour for pool-managed memory and leaks the nested Vertices/Indices buffers.

-    delete cmd;
+    // Release nested buffers first …
+    delete[] cmd->Vertices;
+    delete[] cmd->Indices;
+    // … then return the object to the pool
+    sAllocator.free(cmd);

Add a matching free/reset function in TPoolAllocator if it doesn’t exist.


222-224: 🛠️ Refactor suggestion

setLastIndex uses number of indices, not highest index value

setLastIndex(lastIndex + dc.NumIndices) increments by the count of indices, not by the offset applied (numVertices). This desynchronises the mesh’s internal cursor.

- mMesh->setLastIndex(lastIndex + static_cast<ui16>(dc.NumIndices));
+ mMesh->setLastIndex(lastIndex + static_cast<ui16>(dc.NumVertices));

Apply the same fix to the font path below.

🧹 Nitpick comments (1)
src/Engine/Common/Tokenizer.h (1)

60-64: Comment wording is unclear and slightly misleading

@return true, if any else false. does not convey the intent.
Consider re-phrasing to something like:

/// @return true if a token exists at the given index, false otherwise.

Pure documentation change (no functional impact).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2516b9d and f43f4a6.

📒 Files selected for processing (13)
  • samples/00_HelloWorld/HelloWorld.cpp (2 hunks)
  • src/Editor/src/RenderView/MainRenderView.cpp (1 hunks)
  • src/Engine/App/AppBase.cpp (1 hunks)
  • src/Engine/App/CameraComponent.cpp (1 hunks)
  • src/Engine/Common/Tokenizer.cpp (1 hunks)
  • src/Engine/Common/Tokenizer.h (1 hunks)
  • src/Engine/Platform/win32/Win32DbgLogStream.h (2 hunks)
  • src/Engine/RenderBackend/2D/CanvasRenderer.cpp (2 hunks)
  • src/Engine/RenderBackend/Mesh/MeshUtilities.h (2 hunks)
  • src/Engine/RenderBackend/MeshProcessor.h (2 hunks)
  • src/Engine/RenderBackend/OGLRenderer/OGLRenderBackend.cpp (1 hunks)
  • src/Engine/RenderBackend/Pipeline.cpp (3 hunks)
  • src/Engine/RenderBackend/Pipeline.h (3 hunks)
✅ Files skipped from review due to trivial changes (8)
  • src/Engine/App/CameraComponent.cpp
  • src/Engine/RenderBackend/MeshProcessor.h
  • src/Engine/RenderBackend/OGLRenderer/OGLRenderBackend.cpp
  • src/Engine/RenderBackend/Pipeline.cpp
  • samples/00_HelloWorld/HelloWorld.cpp
  • src/Editor/src/RenderView/MainRenderView.cpp
  • src/Engine/RenderBackend/Pipeline.h
  • src/Engine/Platform/win32/Win32DbgLogStream.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Engine/RenderBackend/Mesh/MeshUtilities.h
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/Engine/Common/Tokenizer.cpp (1)
src/Engine/Common/Tokenizer.h (1)
  • getTokenAt (63-63)
src/Engine/RenderBackend/2D/CanvasRenderer.cpp (1)
src/Engine/RenderBackend/Mesh/MeshUtilities.h (2)
  • renumberIndices (159-165)
  • renumberIndices (159-159)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: Analyze (cpp)

Comment on lines +33 to 41
bool Tokenizer::getTokenAt(size_t index, String &token) const {
if ( m_Token.isEmpty() || m_Token.size() >= index ) {
token = "";
return false;
}

return m_Token[ index ];
token = m_Token[index];
return true;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Off-by-one / reversed bound check causes the method to always report failure

m_Token.size() >= index is true for every valid index (e.g. size = 10, index = 3 ⇒ 10 ≥ 3), so the function exits early and returns false almost all the time.
The comparison has to be index >= m_Token.size().

-    if ( m_Token.isEmpty() || m_Token.size() >= index ) {        
-        token = "";
-        return false;
-    }
+    if (m_Token.isEmpty() || index >= m_Token.size()) {
+        token = "";
+        return false;
+    }

Without this fix every caller will assume the token was not found, breaking all parsing that relies on the tokenizer.
Please patch before merging.

🤖 Prompt for AI Agents
In src/Engine/Common/Tokenizer.cpp between lines 33 and 41, the condition
checking if the index is out of bounds is reversed, causing the function to
incorrectly return false for valid indices. Change the condition from
'm_Token.size() >= index' to 'index >= m_Token.size()' to correctly detect
out-of-range indices and allow valid tokens to be returned properly.

Comment on lines +399 to +403
for (size_t i = 0; i < mScenes.size(); ++i) {
// delete mScenes[i];
}
mScenes.clear();

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Scene objects are leaked – heap-allocated Scene instances are never deleted

onCreate() allocates scenes with new Scene(...) and pushes raw pointers into mScenes.
In onDestroy() the loop merely iterates but the delete statement is commented out, so every scene (and its GPU/stateful resources) survives application shutdown.

Minimal corrective patch:

-    for (size_t i = 0; i < mScenes.size(); ++i) {
-     //   delete mScenes[i];
-    }
+    for (Scene *scene : mScenes) {
+        delete scene;
+    }

Preferable long-term fix: change mScenes to Vector<std::unique_ptr<Scene>> (or std::vector) to enforce RAII and avoid manual memory management altogether.

Please confirm that no other subsystem assumes ownership of these pointers before applying.


🏁 Script executed:

#!/bin/bash
set -e
echo "=== mScenes Declaration in AppBase.cpp ==="
rg -n "mScenes" -n src/Engine/App/AppBase.cpp || true

echo "=== mScenes Declaration in AppBase.h ==="
rg -n "mScenes" -n src/Engine/App/AppBase.h || true

echo "=== Scene Allocations in onCreate methods ==="
rg -n "new Scene" -n src/Engine/App || true

echo "=== Any delete calls for mScenes elements ==="
rg -n "delete.*mScenes" -n src || true
rg -n "delete.*Scene" -n src/Engine/App/AppBase.cpp || true

Length of output: 1227


Delete heap-allocated Scene objects in onDestroy to avoid memory leaks

In src/Engine/App/AppBase.cpp (lines 399–403), the loop that should delete each Scene* is commented out, so every Scene allocated in onCreate() survives shutdown:

-    for (size_t i = 0; i < mScenes.size(); ++i) {
-     //   delete mScenes[i];
-    }
-    mScenes.clear();
+    for (Scene* scene : mScenes) {
+        delete scene;
+    }
+    mScenes.clear();

• Long-term, change
cppcore::TArray<Scene*> mScenes;
cppcore::TArray<std::unique_ptr<Scene>> mScenes;
(or std::vector<std::unique_ptr<Scene>>) to enforce RAII and remove manual deletes.
• Confirm no other subsystem takes ownership of these raw pointers before merging.
• Bonus: check src/Engine/App/AssimpWrapper.cpp:183 (mAssetContext.mWorld = new Scene("scene");) to ensure it’s deleted appropriately.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (size_t i = 0; i < mScenes.size(); ++i) {
// delete mScenes[i];
}
mScenes.clear();
// in AppBase.cpp, onDestroy (around lines 399–403):
- for (size_t i = 0; i < mScenes.size(); ++i) {
- // delete mScenes[i];
- }
- mScenes.clear();
+ for (Scene* scene : mScenes) {
+ delete scene;
+ }
+ mScenes.clear();
🤖 Prompt for AI Agents
In src/Engine/App/AppBase.cpp around lines 399 to 403, the loop intended to
delete heap-allocated Scene objects is commented out, causing memory leaks. To
fix this, uncomment the delete statement inside the loop to properly delete each
Scene pointer before clearing mScenes. For a long-term fix, refactor mScenes to
hold std::unique_ptr<Scene> instead of raw pointers to automate memory
management, but first verify no other code assumes ownership of these raw
pointers.

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
B Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

mKeyboardEvListener = nullptr;

for (size_t i = 0; i < mScenes.size(); ++i) {
// delete mScenes[i];

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

Copilot Autofix

AI 4 months ago

To fix the problem, we need to either remove the commented-out code or reinstate it if it is still relevant. In this case, the line delete mScenes[i]; appears to be part of a cleanup process in the onDestroy method, where other dynamically allocated resources are being deleted. If this line is necessary for proper cleanup, it should be reinstated. Otherwise, it should be removed entirely to avoid confusion.

Given the context of the onDestroy method, it seems likely that the line was commented out by mistake or during debugging. Reinstating the line ensures that the mScenes objects are properly deleted, preventing potential memory leaks.

Suggested changeset 1
src/Engine/App/AppBase.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Engine/App/AppBase.cpp b/src/Engine/App/AppBase.cpp
--- a/src/Engine/App/AppBase.cpp
+++ b/src/Engine/App/AppBase.cpp
@@ -399,3 +399,3 @@
     for (size_t i = 0; i < mScenes.size(); ++i) {
-     //   delete mScenes[i];
+        delete mScenes[i];
     }
EOF
@@ -399,3 +399,3 @@
for (size_t i = 0; i < mScenes.size(); ++i) {
// delete mScenes[i];
delete mScenes[i];
}
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants