Skip to content

Update from task 8f5f5615-9e0f-40cb-8b38-ecc911acf640#20

Open
tronpis wants to merge 1 commit into
farmengine-architecture-overview-9e516from
ecs-and-memory-bugs-cf640
Open

Update from task 8f5f5615-9e0f-40cb-8b38-ecc911acf640#20
tronpis wants to merge 1 commit into
farmengine-architecture-overview-9e516from
ecs-and-memory-bugs-cf640

Conversation

@tronpis
Copy link
Copy Markdown
Owner

@tronpis tronpis commented Apr 1, 2026

This PR was created by qwen-chat coder for task 8f5f5615-9e0f-40cb-8b38-ecc911acf640.

- Fix ECS container type mismatch by changing components to std::vector<std::vector<std::unique_ptr<Component>>>, add entity bounds checks in add/get/removeComponent, fix getComponentTypeIndex to use componentTypeToIndex map with overflow guard
- Fix MemoryManager deallocate to find records by pointer, reallocate to copy data using realloc and update tracked size, add ptr field to AllocationHeader
- Add Input.h missing includes (unordered_map, array, glm), initialize Input.cpp arrays with correct sizes, add bounds checks in setKeyState/setButtonState
- Fix terrain.frag.glsl by adding missing CameraData uniform block, fix MeshRenderer.h duplicate drawCalls member, update Engine.h with FarmEngine namespace alias, clean up .gitignore formatting
Copy link
Copy Markdown
Contributor

@amazon-q-developer amazon-q-developer Bot left a comment

Choose a reason for hiding this comment

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

The PR successfully addresses ECS and memory management bugs by adding pointer tracking to the memory manager and refactoring component storage from map-based to vector-based indexing. The changes correctly implement allocation tracking and component access patterns.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements a new input system for keyboard, mouse, and gamepad, refactors the ECS to use vector-based component storage, and enhances the MemoryManager to track allocations by pointer. Key feedback includes addressing performance bottlenecks in memory deallocation and ECS component indexing, fixing a logic error in mouse delta calculations that causes lost input, and restoring standard build directories to the .gitignore file.

I am having trouble creating individual review comments. Click here to see my feedback.

FarmEngine/core/memory/MemoryManager.cpp (32-35)

high

The use of std::find_if on a std::vector for tracking allocations results in $O(N)$ complexity for deallocations. In a system with many allocations, this will lead to severe performance degradation. It is highly recommended to use a std::unordered_map<void*, AllocationHeader> to achieve $O(1)$ lookup performance.

FarmEngine/core/memory/MemoryManager.cpp (54-57)

high

Similar to deallocate, the reallocate function also performs an $O(N)$ search to find the existing allocation. This should be optimized using a hash map for pointer-to-header lookups to avoid performance issues as the number of allocations grows.

FarmEngine/platform/input/Input.cpp (136-147)

high

The mouse delta calculation in setPosition is flawed because it overwrites the delta value instead of accumulating it. If multiple mouse movement events occur within a single frame, only the last movement's delta is preserved. A more robust approach is to store the current position and calculate the frame-to-frame delta once per frame in processFrame.

void Mouse::setPosition(double x, double y) {
    posX = x;
    posY = y;
    
    if (motionCallback) {
        motionCallback(x, y);
    }
}

FarmEngine/platform/input/Input.cpp (158-168)

high

The processFrame method should be responsible for calculating the mouse delta based on the movement since the last frame. The current implementation simply resets the delta to zero, which, combined with the issues in setPosition, leads to incorrect or lost input data.

void Mouse::processFrame() {
    // Reset just pressed/released states
    std::fill(justPressed.begin(), justPressed.end(), false);
    std::fill(justReleased.begin(), justReleased.end(), false);
    
    // Update previous state
    previousButtons = currentButtons;
    
    // Calculate delta and update previous position
    delta.x = static_cast<float>(posX - prevX);
    delta.y = static_cast<float>(posY - prevY);
    prevX = posX;
    prevY = posY;
}

.gitignore (10-12)

medium

The removal of standard build directories like build/ and dist/ from the .gitignore file is likely a regression. These directories are commonly used for build artifacts and should remain ignored to prevent them from being accidentally committed.

FarmEngine/core/ecs/ECS.h (129-144)

medium

Using std::unordered_map with std::type_index in getComponentTypeIndex introduces a performance bottleneck in a hot code path. Since this function is called frequently during component access, the map lookup and RTTI overhead can be significant. Consider using a static variable within a template to assign unique IDs at compile/link time, which would provide $O(1)$ access without map lookups.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Fix critical ECS, memory manager, and input system bugs with proper bounds checking

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Fix ECS container type mismatch: changed components from map to vector of vectors with proper
  bounds checking
• Fix MemoryManager allocation tracking: added ptr field to AllocationHeader, implemented proper
  pointer-based deallocation and reallocation
• Implement Input system: added complete Input.cpp with keyboard, mouse, gamepad, and input manager
  implementations with bounds checks
• Fix shader and renderer issues: added missing CameraData uniform block in terrain shader, resolved
  duplicate drawCalls member in MeshRenderer
• Add missing includes and namespace alias: updated Input.h with required headers, added FarmEngine
  namespace alias in Engine.h
Diagram
flowchart LR
  ECS["ECS System<br/>Vector-based components<br/>with bounds checks"]
  Memory["MemoryManager<br/>Pointer tracking<br/>in AllocationHeader"]
  Input["Input System<br/>Keyboard/Mouse/Gamepad<br/>with array storage"]
  Renderer["Renderer Fixes<br/>CameraData uniform<br/>drawCalls deduplication"]
  
  ECS -- "Fixed container type" --> Fix1["Proper entity validation"]
  Memory -- "Added ptr field" --> Fix2["Pointer-based lookup"]
  Input -- "Implemented" --> Fix3["Bounds-checked arrays"]
  Renderer -- "Added uniforms" --> Fix4["Shader compatibility"]
Loading

Grey Divider

File Changes

1. FarmEngine/core/ecs/ECS.cpp 🐞 Bug fix +2/-2

Fix ECS component container type mismatch

FarmEngine/core/ecs/ECS.cpp


2. FarmEngine/core/ecs/ECS.h 🐞 Bug fix +32/-14

Change components to vector-based storage with validation

FarmEngine/core/ecs/ECS.h


3. FarmEngine/core/memory/MemoryManager.h 🐞 Bug fix +1/-0

Add ptr field to AllocationHeader struct

FarmEngine/core/memory/MemoryManager.h


View more (9)
4. FarmEngine/core/memory/MemoryManager.cpp 🐞 Bug fix +18/-12

Fix allocation tracking and reallocation logic

FarmEngine/core/memory/MemoryManager.cpp


5. FarmEngine/platform/input/Input.h ✨ Enhancement +11/-11

Add missing includes and convert to array storage

FarmEngine/platform/input/Input.h


6. FarmEngine/platform/input/Input.cpp ✨ Enhancement +317/-0

Implement complete input system with bounds checks

FarmEngine/platform/input/Input.cpp


7. FarmEngine/assets/shaders/terrain.frag.glsl 🐞 Bug fix +5/-1

Add missing CameraData uniform block

FarmEngine/assets/shaders/terrain.frag.glsl


8. FarmEngine/renderer/3d/MeshRenderer.h 🐞 Bug fix +3/-3

Fix duplicate drawCalls member variable

FarmEngine/renderer/3d/MeshRenderer.h


9. FarmEngine/core/Engine.h ✨ Enhancement +4/-0

Add FarmEngine namespace alias for compatibility

FarmEngine/core/Engine.h


10. FarmEngine/renderer/2d/SpriteRenderer.h 🐞 Bug fix +1/-0

Add missing glm header include

FarmEngine/renderer/2d/SpriteRenderer.h


11. FarmEngine/renderer/passes/RenderPasses.h 🐞 Bug fix +2/-0

Add missing memory and unordered_map includes

FarmEngine/renderer/passes/RenderPasses.h


12. FarmEngine/tools/inspector/Inspector.h 🐞 Bug fix +2/-0

Add missing functional and unordered_map includes

FarmEngine/tools/inspector/Inspector.h


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Apr 1, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Broken namespace alias 🐞 Bug ≡ Correctness
Description
FarmEngine/core/Engine.h introduces namespace EngineNS = FarmEngine; inside namespace farm but
FarmEngine is not declared at that point, so any translation unit that includes Engine.h first
will fail to compile. FarmEngine/core/Engine.cpp includes core/Engine.h as its first include,
making this failure immediate for the core target.
Code

FarmEngine/core/Engine.h[R10-12]

+// Alias for FarmEngine namespace to maintain backward compatibility
+namespace EngineNS = FarmEngine;
+
Evidence
Engine.h creates an alias to FarmEngine before that namespace is declared in that translation
unit; Engine.cpp includes Engine.h first, so there is no earlier declaration available to make the
alias valid.

FarmEngine/core/Engine.h[8-16]
FarmEngine/core/Engine.cpp[1-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Engine.h` defines `namespace EngineNS = FarmEngine;` but `FarmEngine` is not declared in that header before the alias. This breaks compilation when `Engine.h` is the first include in a translation unit (it is in `Engine.cpp`).

### Issue Context
C++ namespace aliases require the target namespace to be declared before the alias.

### Fix Focus Areas
- FarmEngine/core/Engine.h[8-16]
- FarmEngine/core/Engine.cpp[1-9]

### Suggested fix
Choose one of:
1) Add a forward declaration before the alias:
```cpp
namespace FarmEngine {}
namespace farm {
 namespace EngineNS = ::FarmEngine;
 ...
}
```
2) If you intend a global alias (not nested under `farm`), move it outside `namespace farm`:
```cpp
namespace FarmEngine {}
namespace EngineNS = FarmEngine;
namespace farm { ... }
```
Also consider using `::FarmEngine` in the alias to avoid accidental lookup issues.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Terrain UBO layout mismatch 🐞 Bug ≡ Correctness
Description
terrain.frag.glsl binds CameraData at binding 0 with only vec3 cameraPosition, but
terrain.vert.glsl binding 0 UBO starts with three mat4 fields, so the fragment shader will read
incorrect bytes for cameraPosition and compute an invalid view direction. This will produce
incorrect lighting/specular results (and is extremely hard to debug because descriptor binding types
still appear compatible).
Code

FarmEngine/assets/shaders/terrain.frag.glsl[R16-18]

+layout(binding = 0) uniform CameraData {
+    vec3 cameraPosition;
+} cameraData;
Evidence
The vertex shader’s binding=0 UBO layout places cameraPosition after model/view/projection,
while the fragment shader’s binding=0 block reads cameraPosition at offset 0 of the same binding.
With a single descriptor bound at binding 0, the fragment shader will not access the intended camera
position field.

FarmEngine/assets/shaders/terrain.frag.glsl[11-23]
FarmEngine/assets/shaders/terrain.frag.glsl[49-53]
FarmEngine/assets/shaders/terrain.vert.glsl[14-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The terrain vertex and fragment shaders both use descriptor `binding = 0`, but they declare incompatible UBO layouts. The fragment shader reads `cameraPosition` from offset 0, while the vertex shader’s binding=0 buffer begins with `mat4 model/view/projection`.

### Issue Context
In Vulkan, `binding = 0` refers to the same descriptor binding across shader stages. Even if both are UBOs, differing struct layouts means the fragment stage will interpret the bytes incorrectly.

### Fix Focus Areas
- FarmEngine/assets/shaders/terrain.frag.glsl[11-23]
- FarmEngine/assets/shaders/terrain.vert.glsl[14-20]
- FarmEngine/assets/shaders/terrain.frag.glsl[49-53]

### Suggested fix
Make the fragment shader binding=0 UBO match the vertex shader layout, e.g.:
```glsl
layout(binding = 0) uniform UniformBuffer {
   mat4 model;
   mat4 view;
   mat4 projection;
   vec3 cameraPosition;
   float time;
} ubo;

// ...
vec3 viewDir = normalize(ubo.cameraPosition - fragPosition);
```
Alternatively, if you truly want a separate camera-only UBO, give it a different binding number and ensure the descriptor set layout and buffer binding are updated accordingly.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing stdexcept include 🐞 Bug ≡ Correctness
Description
ECS.h now throws std::out_of_range and std::overflow_error in header-defined templates, but
does not include <stdexcept>, causing compilation errors when these templates are instantiated.
This is directly introduced by the new validation/overflow checks.
Code

FarmEngine/core/ecs/ECS.h[R71-73]

+    if (entity >= entities.size() || !entities[entity].active) {
+        throw std::out_of_range("Invalid or inactive entity ID");
+    }
Evidence
The header’s include list does not provide declarations for
std::out_of_range/std::overflow_error, yet the template bodies in the header reference them;
this fails at compile time in any TU that instantiates these templates.

FarmEngine/core/ecs/ECS.h[1-9]
FarmEngine/core/ecs/ECS.h[68-74]
FarmEngine/core/ecs/ECS.h[128-144]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`FarmEngine/core/ecs/ECS.h` throws `std::out_of_range` and `std::overflow_error` in template implementations, but the header does not include `<stdexcept>`. This will break compilation when templates are instantiated.

### Issue Context
Because the throwing code is in the header (templates), every translation unit that instantiates these methods needs the proper declarations available via includes.

### Fix Focus Areas
- FarmEngine/core/ecs/ECS.h[1-9]
- FarmEngine/core/ecs/ECS.h[68-74]
- FarmEngine/core/ecs/ECS.h[128-144]

### Suggested fix
Add the missing include near the top:
```cpp
#include <stdexcept>
```
(Keep existing includes as-is.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread FarmEngine/core/Engine.h
Comment on lines +10 to +12
// Alias for FarmEngine namespace to maintain backward compatibility
namespace EngineNS = FarmEngine;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Broken namespace alias 🐞 Bug ≡ Correctness

FarmEngine/core/Engine.h introduces namespace EngineNS = FarmEngine; inside namespace farm but
FarmEngine is not declared at that point, so any translation unit that includes Engine.h first
will fail to compile. FarmEngine/core/Engine.cpp includes core/Engine.h as its first include,
making this failure immediate for the core target.
Agent Prompt
### Issue description
`Engine.h` defines `namespace EngineNS = FarmEngine;` but `FarmEngine` is not declared in that header before the alias. This breaks compilation when `Engine.h` is the first include in a translation unit (it is in `Engine.cpp`).

### Issue Context
C++ namespace aliases require the target namespace to be declared before the alias.

### Fix Focus Areas
- FarmEngine/core/Engine.h[8-16]
- FarmEngine/core/Engine.cpp[1-9]

### Suggested fix
Choose one of:
1) Add a forward declaration before the alias:
```cpp
namespace FarmEngine {}
namespace farm {
  namespace EngineNS = ::FarmEngine;
  ...
}
```
2) If you intend a global alias (not nested under `farm`), move it outside `namespace farm`:
```cpp
namespace FarmEngine {}
namespace EngineNS = FarmEngine;
namespace farm { ... }
```
Also consider using `::FarmEngine` in the alias to avoid accidental lookup issues.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +16 to +18
layout(binding = 0) uniform CameraData {
vec3 cameraPosition;
} cameraData;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Terrain ubo layout mismatch 🐞 Bug ≡ Correctness

terrain.frag.glsl binds CameraData at binding 0 with only vec3 cameraPosition, but
terrain.vert.glsl binding 0 UBO starts with three mat4 fields, so the fragment shader will read
incorrect bytes for cameraPosition and compute an invalid view direction. This will produce
incorrect lighting/specular results (and is extremely hard to debug because descriptor binding types
still appear compatible).
Agent Prompt
### Issue description
The terrain vertex and fragment shaders both use descriptor `binding = 0`, but they declare incompatible UBO layouts. The fragment shader reads `cameraPosition` from offset 0, while the vertex shader’s binding=0 buffer begins with `mat4 model/view/projection`.

### Issue Context
In Vulkan, `binding = 0` refers to the same descriptor binding across shader stages. Even if both are UBOs, differing struct layouts means the fragment stage will interpret the bytes incorrectly.

### Fix Focus Areas
- FarmEngine/assets/shaders/terrain.frag.glsl[11-23]
- FarmEngine/assets/shaders/terrain.vert.glsl[14-20]
- FarmEngine/assets/shaders/terrain.frag.glsl[49-53]

### Suggested fix
Make the fragment shader binding=0 UBO match the vertex shader layout, e.g.:
```glsl
layout(binding = 0) uniform UniformBuffer {
    mat4 model;
    mat4 view;
    mat4 projection;
    vec3 cameraPosition;
    float time;
} ubo;

// ...
vec3 viewDir = normalize(ubo.cameraPosition - fragPosition);
```
Alternatively, if you truly want a separate camera-only UBO, give it a different binding number and ensure the descriptor set layout and buffer binding are updated accordingly.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread FarmEngine/core/ecs/ECS.h
Comment on lines +71 to +73
if (entity >= entities.size() || !entities[entity].active) {
throw std::out_of_range("Invalid or inactive entity ID");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Missing stdexcept include 🐞 Bug ≡ Correctness

ECS.h now throws std::out_of_range and std::overflow_error in header-defined templates, but
does not include <stdexcept>, causing compilation errors when these templates are instantiated.
This is directly introduced by the new validation/overflow checks.
Agent Prompt
### Issue description
`FarmEngine/core/ecs/ECS.h` throws `std::out_of_range` and `std::overflow_error` in template implementations, but the header does not include `<stdexcept>`. This will break compilation when templates are instantiated.

### Issue Context
Because the throwing code is in the header (templates), every translation unit that instantiates these methods needs the proper declarations available via includes.

### Fix Focus Areas
- FarmEngine/core/ecs/ECS.h[1-9]
- FarmEngine/core/ecs/ECS.h[68-74]
- FarmEngine/core/ecs/ECS.h[128-144]

### Suggested fix
Add the missing include near the top:
```cpp
#include <stdexcept>
```
(Keep existing includes as-is.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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