Skip to content

feat: add render2D#1364

Open
wumaolinmaoan wants to merge 11 commits intofeat/2.9from
feat/render-2d
Open

feat: add render2D#1364
wumaolinmaoan wants to merge 11 commits intofeat/2.9from
feat/render-2d

Conversation

@wumaolinmaoan
Copy link
Contributor

@wumaolinmaoan wumaolinmaoan commented Jan 8, 2026

Summary by CodeRabbit

  • New Features

    • Render2D: GPU-backed 2D renderer with begin/end workflow, transform stack, drawLines/drawLine/drawBezier/drawRectangle/fillRectangle, per-vertex color, and resource disposal.
    • Added lineTo to shape path APIs for simpler chained path construction.
  • Chores

    • Reorganized shape utilities onto the core math export surface and removed their re-exports from the plugin barrel; public component APIs remain unchanged.

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

@wumaolinmaoan wumaolinmaoan requested a review from yiiqii January 8, 2026 09:08
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Re-exported shape modules from plugins into math, added GraphicsPath.lineTo and ShapePath.lineTo, updated shape-component imports and shader paths to point at math, and introduced Render2D: a new 2D renderer with batched geometry, transform stack, and begin/end lifecycle.

Changes

Cohort / File(s) Summary
Barrel exports (math ↔ plugins)
packages/effects-core/src/math/index.ts, packages/effects-core/src/plugins/index.ts
Moved shape-related re-exports from plugins into math (added multiple export * from './shape/...' in math and removed them from plugins).
Component import updates
packages/effects-core/src/components/shape-component.ts
Switched internal imports and shader source paths from ../plugins/... to ../math/...; public API unchanged.
Shape primitives & path APIs
packages/effects-core/src/math/shape/graphics-path.ts, packages/effects-core/src/math/shape/shape-path.ts, packages/effects-core/src/math/shape/*
Added GraphicsPath.lineTo(x, y) and ShapePath.lineTo(x, y) handling; command dispatch updated to call new method and avoid duplicate consecutive points.
Render subsystem
packages/effects-core/src/render/render-2D.ts, packages/effects-core/src/render/index.ts
Added exported Render2D class and re-export in render/index.ts. Render2D provides batched Geometry/Material, vertex/color/index buffers, a Matrix3 transform stack, begin/end lifecycle, drawing primitives (lines, beziers, rectangles, fills), and dispose().

Sequence Diagram(s)

sequenceDiagram
    participant App as Client
    participant R2D as Render2D
    participant GP as GraphicsPath/buildLine
    participant Engine as Engine
    participant GPU as GPU

    App->>R2D: new Render2D(engine)
    App->>R2D: begin()
    App->>R2D: pushTransform(transform)
    App->>R2D: drawLine(p1, p2, color, thickness)
    R2D->>GP: buildLine(path, thickness)
    GP-->>R2D: vertices & indices
    R2D->>R2D: apply transform & color, append to buffers
    App->>R2D: end()
    R2D->>Engine: upload buffers, set MatrixVP uniform
    Engine->>GPU: drawGeometry(...)
    GPU-->>Engine: rendered
    Engine-->>R2D: render complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through barrels, tidy and bright,

moved lines to math and gave paths new bite.
Render2D stacks transforms, batches with glee,
paints rects and strokes for all to see.
A twitch of whiskers — code set free!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add render2D' accurately reflects the primary change: introducing a new Render2D class for 2D rendering capabilities. The title is concise, specific, and clearly conveys the main feature being added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/render-2d

Tip

🧪 Unit Test Generation v2 is now available!

We have significantly improved our unit test generation capabilities.

To enable: Add this to your .coderabbit.yaml configuration:

reviews:
  finishing_touches:
    unit_tests:
      enabled: true

Try it out by using the @coderabbitai generate unit tests command on your code files or under ✨ Finishing Touches on the walkthrough!

Have feedback? Share your thoughts on our Discord thread!


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

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

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In @packages/effects-core/src/render/render-2D.ts:
- Around line 93-98: The begin() method resets indices, vertices, transformStack
and currentTransform but forgets to clear the colors buffer, causing stale
colors to leak between frames; update the begin() implementation (the begin
method that sets this.indices, this.vertices, this.transformStack,
this.currentTransform) to also clear the colors array (e.g., set
this.colors.length = 0 or otherwise reset the colors buffer) so colors are reset
at the start of each render batch.
- Line 158: The slice uses the wrong stride multiplier: new
Float32Array(this.vertices.slice(0, this.currentVertexCount * 3)) assumes 3
components per vertex but the attribute aPos is 2-component; update the slice
length to use * 2 (i.e. this.currentVertexCount * 2) or otherwise derive the end
index from the vertex component count so verticesView uses exactly
currentVertexCount * 2 floats; update the creation site (the verticesView
assignment) and ensure any other code relying on that slice uses the same
2-component stride (references: this.vertices, this.currentVertexCount,
verticesView, aPos).
🧹 Nitpick comments (4)
packages/effects-core/src/render/render-2D.ts (4)

57-63: Remove unused aUV attribute.

The aUV attribute is defined but never used in the shader (lines 72-85). This adds unnecessary memory overhead.

♻️ Proposed fix
         aColor: {
           type: glContext.FLOAT,
           size: 4,
           data: new Float32Array([
             1, 1, 1, 1,
             1, 1, 1, 1,
             1, 1, 1, 1,
             1, 1, 1, 1,
           ]),
         },
-        aUV: {
-          size: 2,
-          offset: 0,
-          releasable: true,
-          type: glContext.FLOAT,
-          data: new Float32Array([0, 1, 0, 0, 1, 1, 1, 0]),
-        },
       },

140-148: Consider using English for warning messages.

The warning message is in Chinese. For consistency with typical open-source conventions and broader accessibility, consider using English.

♻️ Proposed fix
   popTransform (): void {
     if (this.transformStack.length === 0) {
-      console.warn('Render2D: 变换栈为空,无法弹出');
+      console.warn('Render2D: Transform stack is empty, cannot pop');

       return;
     }

177-181: Consider using English for warning messages.

For consistency with typical open-source conventions.

♻️ Proposed fix
     if (!points || points.length < 2) {
-      console.warn('drawLines: 至少需要2个点');
+      console.warn('drawLines: At least 2 points are required');

       return;
     }

190-195: Consider adding a defensive check for shapePrimitives.

Direct access to shapePrimitives[0] could throw if the array is unexpectedly empty. While this should not happen with valid path construction, a guard would improve robustness.

♻️ Proposed fix
     const buildPoints: number[] = [];
-    const shape = this.graphicsPath.shapePath.shapePrimitives[0].shape;
+    const shapePrimitives = this.graphicsPath.shapePath.shapePrimitives;
+
+    if (!shapePrimitives.length) {
+      console.warn('drawLines: Failed to build shape primitives');
+
+      return;
+    }
+    const shape = shapePrimitives[0].shape;
     const indexOffset = this.indices.length;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 044369b and 4c26413.

⛔ Files ignored due to path filters (2)
  • packages/effects-core/src/math/shape/shaders/shape.frag.glsl is excluded by !**/*.glsl
  • packages/effects-core/src/math/shape/shaders/shape.vert.glsl is excluded by !**/*.glsl
📒 Files selected for processing (19)
  • packages/effects-core/src/components/shape-component.ts
  • packages/effects-core/src/math/index.ts
  • packages/effects-core/src/math/shape/build-adaptive-bezier.ts
  • packages/effects-core/src/math/shape/build-line.ts
  • packages/effects-core/src/math/shape/ellipse.ts
  • packages/effects-core/src/math/shape/graphics-path.ts
  • packages/effects-core/src/math/shape/point-data.ts
  • packages/effects-core/src/math/shape/point-like.ts
  • packages/effects-core/src/math/shape/point.ts
  • packages/effects-core/src/math/shape/poly-star.ts
  • packages/effects-core/src/math/shape/polygon.ts
  • packages/effects-core/src/math/shape/rectangle.ts
  • packages/effects-core/src/math/shape/shape-path.ts
  • packages/effects-core/src/math/shape/shape-primitive.ts
  • packages/effects-core/src/math/shape/triangle.ts
  • packages/effects-core/src/math/shape/triangulate.ts
  • packages/effects-core/src/plugins/index.ts
  • packages/effects-core/src/render/index.ts
  • packages/effects-core/src/render/render-2D.ts
💤 Files with no reviewable changes (1)
  • packages/effects-core/src/plugins/index.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-05T07:50:26.317Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:135-219
Timestamp: 2025-08-05T07:50:26.317Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the mesh subdivision logic (lines 147-219) within the collectSpriteComponents method is temporary debug code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
  • packages/effects-core/src/components/shape-component.ts
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects-core/src/components/shape-component.ts
🧬 Code graph analysis (2)
packages/effects-core/src/render/render-2D.ts (3)
packages/effects-core/src/math/shape/graphics-path.ts (1)
  • GraphicsPath (8-146)
plugin-packages/model/src/runtime/math.ts (4)
  • Matrix3 (13-13)
  • Matrix4 (14-14)
  • Vector2 (10-10)
  • Color (15-15)
packages/effects-core/src/math/shape/build-line.ts (1)
  • buildLine (197-550)
packages/effects-core/src/math/shape/shape-path.ts (1)
packages/effects-core/src/math/shape/polygon.ts (1)
  • Polygon (11-235)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
packages/effects-core/src/math/shape/graphics-path.ts (1)

56-68: LGTM!

The lineTo method implementation is correct and follows the established pattern used by other path methods like bezierCurveTo and moveTo. It properly marks the path as dirty and returns this for method chaining.

packages/effects-core/src/math/index.ts (1)

4-10: LGTM!

The new exports appropriately expand the public API surface to include shape-related modules, aligning with the module reorganization described in the PR objectives.

packages/effects-core/src/render/index.ts (1)

12-12: LGTM!

The export of the new render-2D module appropriately extends the render API surface, consistent with the introduction of the Render2D utility class.

packages/effects-core/src/math/shape/shape-path.ts (2)

43-47: LGTM!

The lineTo case handling correctly wires up the new instruction type to the corresponding method implementation.


110-129: LGTM!

The lineTo implementation is correct and follows the established pattern:

  • Uses ensurePoly() to guarantee a polygon exists with at least one point (initialized to 0,0 if needed)
  • Safely accesses the last point coordinates
  • Optimizes by skipping duplicate points
  • Maintains method chaining
packages/effects-core/src/components/shape-component.ts (1)

9-9: LGTM!

The import reorganization correctly relocates shape-related modules from ../plugins to ../math, aligning with the module restructuring described in the PR objectives. The changes are consistent with the new exports added in math/index.ts.

Also applies to: 13-14, 20-21

packages/effects-core/src/render/render-2D.ts (4)

1-11: LGTM!

Imports are well-organized and all appear to be used within the class.


13-32: LGTM!

Class members are well-structured with appropriate types for 2D rendering state management.


121-126: LGTM!

The transform application correctly handles the Matrix3 column-major layout for 2D affine transformations.


226-228: LGTM!

Clean wrapper method that correctly delegates to drawLines.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In @packages/effects-core/src/render/render-2D.ts:
- Line 144: The slice for building verticesView is using an incorrect
multiplier; since currentVertexCount was computed as vertices.length / 2 for 2D
(x,y) vertices, change the slice multiplier from * 3 to * 2 so verticesView =
new Float32Array(this.vertices.slice(0, this.currentVertexCount * 2)) — update
the reference in the code that constructs verticesView and ensure it uses
this.currentVertexCount * 2 with this.vertices to avoid including garbage or
wrong-length data.
- Around line 93-97: In begin() the colors buffer isn't cleared which can leave
stale color entries when a new batch has fewer vertices; update the begin()
method to reset the colors array (e.g., set this.colors.length = 0) alongside
this.indices.length = 0 and this.vertices.length = 0 so colors is emptied before
reuse; locate the begin() function that also resets transformStack and
currentTransform (Matrix3.fromIdentity()) and add the colors reset there.
🧹 Nitpick comments (1)
packages/effects-core/src/render/render-2D.ts (1)

215-218: Redundant moveTo before rect.

The moveTo(x, y) call on line 216 appears unnecessary since rect() defines its own closed rectangular path. Consider removing it for clarity.

Proposed simplification
   fillRectangle (x: number, y: number, width: number, height: number, color: Color = new Color(1, 1, 1, 1)): void {
     this.graphicsPath.clear();
-    this.graphicsPath.moveTo(x, y);
-
     this.graphicsPath.rect(x, y, width, height, 0);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c26413 and f52b6f7.

📒 Files selected for processing (1)
  • packages/effects-core/src/render/render-2D.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-05T07:50:26.317Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:135-219
Timestamp: 2025-08-05T07:50:26.317Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the mesh subdivision logic (lines 147-219) within the collectSpriteComponents method is temporary debug code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
🧬 Code graph analysis (1)
packages/effects-core/src/render/render-2D.ts (3)
packages/effects-core/src/math/shape/graphics-path.ts (1)
  • GraphicsPath (8-146)
packages/effects-core/src/gl/index.ts (1)
  • glContext (8-8)
packages/effects-core/src/math/shape/build-line.ts (1)
  • buildLine (197-550)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript-typescript)

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In @packages/effects-core/src/render/render-2D.ts:
- Around line 210-216: drawRectangle accesses
this.graphicsPath.shapePath.shapePrimitives[0] without verifying the array is
non-empty which can throw; add a bounds check before calling buildShapeLine
(similar to the fix in drawLines): ensure this.graphicsPath.shapePath and its
shapePrimitives array exist and have length > 0, then grab the primitive (e.g.,
const prim = this.graphicsPath.shapePath.shapePrimitives[0]) and call
buildShapeLine(prim.shape, color, thickness, true); otherwise return early (or
log/throw as appropriate).
- Around line 174-189: In drawLines, avoid accessing
this.graphicsPath.shapePath.shape.shapePrimitives[0] without checking it exists;
add a guard after building the graphicsPath (e.g., after the loop and before
calling buildShapeLine) that verifies this.graphicsPath.shapePath and its shape
and shapePrimitives are defined and that shapePrimitives.length > 0, and if not
log a warning and return (or handle the fallback), then call buildShapeLine with
the validated primitive; reference the drawLines method, this.graphicsPath,
shapePath.shape.shapePrimitives, and buildShapeLine when applying the check.
- Around line 226-232: In fillRectangle, avoid directly indexing
graphicsPath.shapePath.shapePrimitives[0]; first check that
this.graphicsPath.shapePath.shapePrimitives exists and has length > 0 (similar
to drawLines/drawRectangle), and only then call buildShape with that shape; if
the array is empty, bail out or handle the error path consistently (e.g., return
early or log) so buildShape is never invoked with undefined.
🧹 Nitpick comments (1)
packages/effects-core/src/render/render-2D.ts (1)

67-73: Consider removing unused aUV attribute.

The aUV attribute is defined in the default geometry but is never referenced in the shader (lines 82-95) or updated in the end() method. This creates unnecessary GPU memory overhead.

♻️ Remove unused attribute
-        aUV: {
-          size: 2,
-          offset: 0,
-          releasable: true,
-          type: glContext.FLOAT,
-          data: new Float32Array([0, 1, 0, 0, 1, 1, 1, 0]),
-        },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00885c7 and 44382b3.

📒 Files selected for processing (1)
  • packages/effects-core/src/render/render-2D.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-05T07:50:26.317Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:135-219
Timestamp: 2025-08-05T07:50:26.317Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the mesh subdivision logic (lines 147-219) within the collectSpriteComponents method is temporary debug code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
🧬 Code graph analysis (1)
packages/effects-core/src/render/render-2D.ts (4)
packages/effects-core/src/math/shape/graphics-path.ts (1)
  • GraphicsPath (8-146)
packages/effects-core/src/math/shape/build-line.ts (2)
  • StrokeAttributes (171-183)
  • buildLine (197-550)
plugin-packages/model/src/runtime/math.ts (4)
  • Matrix3 (13-13)
  • Matrix4 (14-14)
  • Vector2 (10-10)
  • Color (15-15)
packages/effects-core/src/gl/index.ts (1)
  • glContext (8-8)
🔇 Additional comments (1)
packages/effects-core/src/render/render-2D.ts (1)

130-133: These transform stack methods appear to be unused dead code.

The pushTransform and popTransform methods are defined but never called anywhere in the codebase. If they're not needed, consider removing them. If they're intended for future use, add a comment explaining the purpose.

Note: The use of premultiply is consistent with transform composition patterns elsewhere in the codebase (e.g., in hit test calculations with sizeMatrix.premultiply(worldMatrix)), where it correctly applies local transforms before parent/world transforms.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In @packages/effects-core/src/render/render-2D.ts:
- Around line 183-187: The code mutates the caller's points array by calling
points.pop(); instead, detect closure as you do, then work with a non-mutating
copy—e.g., create a local variable (pts or localPoints) set to points.slice(0,
-1) when closed, otherwise to points—and use that local variable for subsequent
processing in the render-2D logic (replace usages of points with the new local
variable) so the original input array is not modified.
- Around line 158-165: The index buffer currently uses a Uint16Array
(indicesArray) which overflows past 65535; update the code that builds indices
(this.indices) to detect the maximum index value and, if it exceeds 65535,
allocate a Uint32Array for indicesArray and ensure the geometry's index buffer
is created/updated to accept 32-bit indices (use this.geometry.setIndexData with
the Uint32Array or call the geometry API to set the index type accordingly);
keep using Float32Array for vertices/colors and continue to call
this.geometry.setAttributeData('aPos', verticesArray),
this.geometry.setAttributeData('aColor', colorsArray) and
this.geometry.setDrawCount(this.currentIndexCount) as before, but add the guard
that switches to 32-bit indices when needed to avoid overflow.
- Around line 233-239: The fillRectangle method currently calls
this.graphicsPath.moveTo(x, y) before this.graphicsPath.rect(...), which is
redundant because rect creates its own Rectangle primitive; remove the
unnecessary this.graphicsPath.moveTo(x, y) call from fillRectangle so it simply
clears the path, calls this.graphicsPath.rect(x, y, width, height, 0), then
invokes this.buildShape(...) on the created rectangle primitive (referencing
fillRectangle, graphicsPath.rect, buildShape and
graphicsPath.shapePath.shapePath.shapePrimitives).
🧹 Nitpick comments (3)
packages/effects-core/src/render/render-2D.ts (3)

138-148: Consider using English for warning messages for consistency.

The warning message on line 142 is in Chinese. For international developer accessibility and consistency with typical logging practices, consider using English:

-      console.warn('Render2D: 变换栈为空,无法弹出');
+      console.warn('Render2D: Transform stack is empty, cannot pop');

176-181: Consider English for warning message.

Similar to the earlier comment, use English for consistency:

-      console.warn('drawLines: 至少需要2个点');
+      console.warn('drawLines: At least 2 points are required');

176-176: Default parameter allocation on each call may impact performance.

Creating new Color(1, 1, 1, 1) as a default parameter value allocates a new object on every call where the color is not specified. For a frequently-called rendering API, this could create GC pressure.

Consider using a shared constant:

// At module or class level
const DEFAULT_WHITE = new Color(1, 1, 1, 1);
// Or use Object.freeze(new Color(1, 1, 1, 1)) if immutability is needed

// Then in method signatures:
drawLines(points: Vector2[], color: Color = DEFAULT_WHITE, thickness: number = 1.0): void

Based on learnings about avoiding allocations for GC overhead in this codebase.

Also applies to: 206-206, 218-218, 233-233

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44382b3 and 1fb86bb.

📒 Files selected for processing (1)
  • packages/effects-core/src/render/render-2D.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-05T07:50:26.317Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:135-219
Timestamp: 2025-08-05T07:50:26.317Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the mesh subdivision logic (lines 147-219) within the collectSpriteComponents method is temporary debug code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
🧬 Code graph analysis (1)
packages/effects-core/src/render/render-2D.ts (3)
packages/effects-core/src/math/shape/graphics-path.ts (1)
  • GraphicsPath (8-146)
packages/effects-core/src/math/shape/build-line.ts (2)
  • StrokeAttributes (171-183)
  • buildLine (197-550)
packages/effects-core/src/gl/index.ts (1)
  • glContext (8-8)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
packages/effects-core/src/render/render-2D.ts (3)

44-98: Constructor and shader setup looks good.

The geometry initialization with position, color, and UV attributes, along with the simple vertex/fragment shaders for 2D colored rendering, is well-structured for the intended use case.


100-124: Orthographic projection setup is correct.

The begin() method properly resets batch state and computes an orthographic projection matrix that maps screen coordinates to NDC space.


283-295: Transform application is correct.

The applyTransform method correctly applies the Matrix3 affine transformation using the standard 2D transform formula with column-major matrix elements.

Copy link
Contributor

@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: 4

🤖 Fix all issues with AI agents
In @packages/effects-core/src/render/render-2D.ts:
- Line 196: The code calls
this.buildShapeLine(this.graphicsPath.shapePath.shapePrimitives[0].shape, ...)
without checking the array; add a defensive guard that verifies
this.graphicsPath.shapePath.shapePrimitives exists and has length > 0 before
accessing [0] (e.g., return or skip drawing when empty), and use the first
primitive only after that check to avoid runtime errors in buildShapeLine.
- Around line 233-238: fillRectangle currently assumes
this.graphicsPath.shapePath.shapePrimitives[0] exists which can throw if the
primitives array is empty; before calling buildShape, check that
this.graphicsPath.shapePath and its shapePrimitives array exist and have length
> 0, and only then call
this.buildShape(this.graphicsPath.shapePath.shapePrimitives[0].shape, color);
otherwise bail out or create/populate a default primitive so the method safely
no-ops without throwing.
- Around line 218-223: In drawRectangle, you’re accessing
this.graphicsPath.shapePath.shapePrimitives[0] without verifying it exists (same
issue as drawLines); add a defensive check that this.graphicsPath,
this.graphicsPath.shapePath, and this.graphicsPath.shapePath.shapePrimitives
exist and have length > 0 before using [0], and if not, return early (or create
a primitive) so buildShapeLine is not called with undefined.
- Around line 183-187: The closed check currently mutates the caller's points
array and uses strict equality for floats; change it to use an epsilon
comparison (e.g., Math.abs(a - b) < EPS) when comparing points[0].x/y to
points[points.length - 1].x/y, and avoid calling points.pop(); instead compute a
local array (e.g., const pts = closed ? points.slice(0, -1) : points) or
otherwise work on a shallow copy so the original points array is not modified;
keep the boolean name closed (or compute it from the epsilon test) and use the
new local pts for subsequent logic.
🧹 Nitpick comments (1)
packages/effects-core/src/render/render-2D.ts (1)

44-78: Consider removing placeholder geometry data and unused aUV attribute.

The initial quad data (lines 50-77) is never rendered since end() overwrites it. Additionally, the aUV attribute is defined but unused in the shader.

♻️ Simplify initialization
   constructor (private engine: Engine) {
     this.geometry = Geometry.create(this.engine, {
       attributes: {
         aPos: {
           type: glContext.FLOAT,
           size: 2,
-          data: new Float32Array([
-            -0.5, 0.5,  //左上
-            -0.5, -0.5, //左下
-            0.5, 0.5, //右上
-            0.5, -0.5, //右下
-          ]),
+          data: new Float32Array(0),
         },
         aColor: {
           type: glContext.FLOAT,
           size: 4,
-          data: new Float32Array([
-            1, 1, 1, 1,
-            1, 1, 1, 1,
-            1, 1, 1, 1,
-            1, 1, 1, 1,
-          ]),
+          data: new Float32Array(0),
         },
-        aUV: {
-          size: 2,
-          offset: 0,
-          releasable: true,
-          type: glContext.FLOAT,
-          data: new Float32Array([0, 1, 0, 0, 1, 1, 1, 0]),
-        },
       },
-      indices: { data: new Uint16Array([0, 1, 2, 2, 1, 3]), releasable: true },
+      indices: { data: new Uint16Array(0), releasable: true },
       mode: glContext.TRIANGLES,
-      drawCount: 6,
+      drawCount: 0,
     });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fb86bb and 26fda04.

📒 Files selected for processing (1)
  • packages/effects-core/src/render/render-2D.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-05T07:50:26.317Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:135-219
Timestamp: 2025-08-05T07:50:26.317Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the mesh subdivision logic (lines 147-219) within the collectSpriteComponents method is temporary debug code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
🧬 Code graph analysis (1)
packages/effects-core/src/render/render-2D.ts (3)
packages/effects-core/src/math/shape/graphics-path.ts (1)
  • GraphicsPath (8-146)
packages/effects-core/src/math/shape/build-line.ts (1)
  • StrokeAttributes (171-183)
packages/effects-core/src/gl/index.ts (1)
  • glContext (8-8)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
packages/effects-core/src/render/render-2D.ts (5)

103-124: LGTM!

The orthographic projection matrix correctly maps screen coordinates to NDC space, and buffer clearing is properly implemented.


130-148: LGTM!

The transform stack implementation is correct. The use of premultiply ensures transforms compose in the expected order (new transforms apply before existing ones).


153-168: LGTM!

The batch flush logic correctly uploads accumulated geometry data and issues the draw call.


240-243: LGTM!

The cleanup logic properly disposes of geometry and material resources.


256-266: The buildLine parameter order is correct. All 9 parameters on line 263 match the function signature exactly:

  • buildPointspoints
  • this.lineStylelineStyle
  • falseflipAlignment
  • closedclosed
  • this.verticesvertices
  • 2_verticesStride
  • vertexOffset_verticesOffset
  • this.indicesindices
  • indexOffset_indicesOffset

No issues found.

Copy link
Contributor

@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: 4

🤖 Fix all issues with AI agents
In @packages/effects-core/src/render/render-2D.ts:
- Around line 103-124: The begin() method builds a projectionMatrix using
2/width and 2/height which will yield Infinity when
this.engine.renderer.getWidth() or getHeight() is 0 and also assumes (0,0) is
bottom-left; change begin() to guard by reading width/height via
this.engine.renderer.getWidth()/getHeight(), if either is <= 0 then either
early-return/reset to a safe default (e.g., width = Math.max(1, width), height =
Math.max(1, height)) or postpone setting the matrix until valid sizes exist, and
make the vertical origin explicit (flip Y by negating the 2/height or adjust the
translation term) so the coordinate origin expectation is consistent; update the
matrix you set via this.material.setMatrix('effects_MatrixVP', projectionMatrix)
accordingly and ensure Matrix4 construction uses the corrected width/height and
origin convention.
- Around line 176-197: drawLines currently mutates the caller-provided points
array via points.pop() and uses exact equality to detect closure; change it to
avoid mutating the input by working on a shallow copy (e.g., const pts =
points.slice() or [...points]) and, if a closure needs removing for rendering,
remove the duplicate from the copy only; replace exact equality with an epsilon
check (e.g., Math.abs(a - b) < EPS) or add an optional boolean parameter
(closed?: boolean) so callers can explicitly indicate closure; update references
in drawLines (graphicsPath.moveTo/lineTo and buildShapeLine) to use the local
copy and ensure EPS is a small constant (e.g., 1e-6) or respect the explicit
closed param.
- Around line 285-311: The applyTransform method uses hardcoded indices into
this.currentTransform.elements assuming a column-major 3x3 layout (used as
m[0],m[3],m[6] and m[1],m[4],m[7]); add a short clarifying comment above
applyTransform that documents the Matrix3 element ordering (e.g., column-major:
[m0 m3 m6; m1 m4 m7; m2 m5 m8]) so future readers know why those indices are
used, and while editing also check if @galacean/effects-math v1.1.0 exposes a
Matrix3.transformPoint or similar helper and, if so, replace the manual indexing
in applyTransform with that helper for clarity and maintainability.
🧹 Nitpick comments (2)
packages/effects-core/src/render/render-2D.ts (2)

108-148: Avoid avoidable GC in transform stack (clone() + reassigning a new array).

begin() resets this.transformStack = [] and pushTransform() clones a Matrix3 per push—this can get allocation-heavy in UI-like usage. Consider reusing the array (length = 0) and using a small object pool / element-copy approach for matrices. (Based on learnings, avoid clone-heavy patterns when possible.)

Possible direction (minimal change)
-    this.transformStack = [];
+    this.transformStack.length = 0;

196-255: Avoid assuming shapePrimitives[0] always exists; centralize the “get first shape” logic.

Multiple methods dereference this.graphicsPath.shapePath.shapePrimitives[0].shape. If GraphicsPath ever changes to produce multiple primitives (or none on edge cases), this becomes fragile. Consider a helper that validates existence and errors/warns once.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26fda04 and 1cac93b.

📒 Files selected for processing (1)
  • packages/effects-core/src/render/render-2D.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-05T07:50:26.317Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:135-219
Timestamp: 2025-08-05T07:50:26.317Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the mesh subdivision logic (lines 147-219) within the collectSpriteComponents method is temporary debug code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
🧬 Code graph analysis (1)
packages/effects-core/src/render/render-2D.ts (4)
packages/effects-core/src/math/shape/graphics-path.ts (1)
  • GraphicsPath (8-146)
packages/effects-core/src/math/shape/build-line.ts (2)
  • StrokeAttributes (171-183)
  • buildLine (197-550)
plugin-packages/model/src/runtime/math.ts (4)
  • Matrix3 (13-13)
  • Matrix4 (14-14)
  • Vector2 (10-10)
  • Color (15-15)
packages/effects-core/src/gl/index.ts (1)
  • glContext (8-8)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
packages/effects-core/src/render/render-2D.ts (2)

262-283: No geometry corruption risk with current offset calculations.

The offset parameters are correctly used. In ShapePrimitive.triangulate(), the verticesOffset is properly treated as a vertex count: polygon.ts line 162 shows vertices[verticesOffset * 2 + i] = triangles[i], which multiplies the offset by 2 to convert to float indices. The calculation vertexOffset = this.vertices.length / 2 correctly provides a vertex count. For buildLine(), the offset parameter is passed but never actually used by the function—it always appends to the end of the arrays via push() operations. No correctness issue exists here.

Likely an incorrect or invalid review comment.


44-98: No action required - shader compatibility is handled correctly by the framework.

The engine includes an automatic shader transpilation layer that converts GLSL ES 1.00 syntax (attribute, varying, gl_FragColor) to GLSL ES 3.00 at compile time based on the WebGL context level (this.engine.gpuCapability.level). This happens in ShaderFactory.genFinalShaderCode() and requires no manual specification of glslVersion in the shader source.

Additionally, the WebGL binding layer (gl-program.ts) only binds attributes that are active in the compiled program (via ACTIVE_ATTRIBUTES). The unused aUV attribute in the geometry will be safely ignored during binding—no compilation or runtime errors will occur.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In @packages/effects-core/src/render/render-2D.ts:
- Around line 183-187: The code currently pops the last element from the
caller-provided points array when closed is true, mutating the input; instead,
avoid side effects by operating on a local copy or by adjusting loop bounds:
create a local array (e.g., const pts = points.slice()) and use pts for
subsequent processing, or keep points intact and where loops/process use
points.length, use an effective length of points.length - 1 when closed; update
all subsequent references in the surrounding function (the closed check and any
downstream iteration) to use the non-mutating local variable or adjusted length.
🧹 Nitpick comments (3)
packages/effects-core/src/render/render-2D.ts (3)

158-160: Potential index overflow with Uint16Array and GC pressure from repeated allocations.

  1. Uint16Array limits indices to 65535. Complex scenes exceeding this will silently overflow.
  2. Allocating new typed arrays every end() call can cause GC pressure in high-frequency rendering.

Consider:

  • Adding a check/warning when vertex count exceeds 65535, or switching to Uint32Array for larger batches.
  • Reusing pre-allocated typed arrays that grow as needed.

262-274: Variable shadowing reduces clarity.

The parameter shape: ShapePath is shadowed by const shape = shapePrimitive.shape on line 264. This can cause confusion and potential bugs if the outer parameter needs to be referenced. Same issue exists in buildShapeLine at line 278.

♻️ Suggested rename
-  private buildShape (shape: ShapePath, color: Color) {
-    for (const shapePrimitive of shape.shapePrimitives) {
-      const shape = shapePrimitive.shape;
+  private buildShape (shapePath: ShapePath, color: Color) {
+    for (const shapePrimitive of shapePath.shapePrimitives) {
+      const shape = shapePrimitive.shape;

Apply the same pattern to buildShapeLine.


142-142: Consider using English for warning messages.

Warning messages like 'Render2D: 变换栈为空,无法弹出' (line 142) and 'drawLines: 至少需要2个点' (line 178) are in Chinese. For consistency and broader maintainability, consider using English: e.g., 'Render2D: Transform stack is empty, cannot pop'.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cac93b and 7773c4d.

📒 Files selected for processing (1)
  • packages/effects-core/src/render/render-2D.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-05T07:50:26.317Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:135-219
Timestamp: 2025-08-05T07:50:26.317Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the mesh subdivision logic (lines 147-219) within the collectSpriteComponents method is temporary debug code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2024-10-18T09:15:08.038Z
Learnt from: wumaolinmaoan
Repo: galacean/effects-runtime PR: 691
File: packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts:39-42
Timestamp: 2024-10-18T09:15:08.038Z
Learning: In `packages/effects-core/src/plugins/timeline/playables/color-property-mixer-playable.ts`, when accumulating color components, avoid using `clone()` methods because it can cause additional garbage collection overhead.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
📚 Learning: 2025-08-05T07:51:18.728Z
Learnt from: ChengYi996
Repo: galacean/effects-runtime PR: 1118
File: packages/effects-core/src/components/ffd-component.ts:162-164
Timestamp: 2025-08-05T07:51:18.728Z
Learning: In packages/effects-core/src/components/ffd-component.ts, the //ts-expect-error usage for accessing siblingComponent.splits (around lines 162-164) is temporary debugging code that will be properly implemented later, as confirmed by ChengYi996.

Applied to files:

  • packages/effects-core/src/render/render-2D.ts
🧬 Code graph analysis (1)
packages/effects-core/src/render/render-2D.ts (6)
packages/effects-core/src/math/shape/graphics-path.ts (1)
  • GraphicsPath (8-146)
packages/effects-core/src/math/shape/build-line.ts (2)
  • StrokeAttributes (171-183)
  • buildLine (197-550)
plugin-packages/model/src/runtime/math.ts (4)
  • Matrix3 (13-13)
  • Matrix4 (14-14)
  • Vector2 (10-10)
  • Color (15-15)
packages/effects-core/src/gl/index.ts (1)
  • glContext (8-8)
scripts/print-changelog/index.js (1)
  • i (25-25)
packages/effects-core/src/math/shape/shape-path.ts (1)
  • ShapePath (14-249)
🔇 Additional comments (2)
packages/effects-core/src/render/render-2D.ts (2)

257-260: LGTM!

Resource disposal correctly releases both geometry and material.


312-317: The transform formula is correct. The code correctly applies a 2D affine transformation using @galacean/effects-math's column-major Matrix3 layout: x' = m[0]*x + m[3]*y + m[6] and y' = m[1]*x + m[4]*y + m[7] properly map to the standard 2D transformation matrix elements.

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.

1 participant