Skip to content

Batch Bunnymark sprites with public raylib instancing - #10

Merged
frostney merged 2 commits into
mainfrom
codex/bulk-sprite-submission
Jul 24, 2026
Merged

frostney merged 2 commits into
mainfrom
codex/bulk-sprite-submission

Conversation

@frostney

@frostney frostney commented Jul 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • replace Bunnymark's per-sprite DrawTextureV crossings with one public-raylib DrawMeshInstanced submission wrapped by BeginShaderMode/EndShaderMode
  • keep motion at JavaScript double precision while storing exact byte tints and the C Matrix instance layout in compact typed arrays
  • preserve texture, tint, alpha, insertion order, and 2D blending behavior, with a direct DrawTextureV fallback if the desktop shader cannot be created
  • add focused state tests, deterministic direct-versus-instanced visual checks in Linux CI, and controlled benchmark evidence

No companion C/native bridge is included, and this does not change the GocciaScript runtime, generated binding surface, or WASM work.

Why

Raylib already batches GPU quads internally, but the Bunnymark example still crossed GocciaScript's FFI boundary once per sprite per frame. The existing Raylib 6 bindings already expose DrawMeshInstanced, shader loading, mesh updates, and material APIs, so the example can submit the whole sprite set through public Raylib calls.

Performance

Five alternating-order pairs used the production GocciaScript import-binding fast path at 3b19fdbe, seed 1234, 10,000 sprites, an uncapped target, and 25 frames per run:

  • origin/main direct path: 1925.2 ms / 12.99 FPS mean
  • instanced path: 824.0 ms / 30.35 FPS mean
  • result: 57.2% less measured frame time and 2.34x FPS

The complete method, individual trials, and tradeoffs are recorded in benchmarks/bunnymark-instancing.md.

Validation

  • npm run generate
  • npm test - interpreter and bytecode, 10 tests / 90 assertions each
  • npm run check:generated
  • npm run test:types
  • npm run test:abi - all 35 native layouts
  • npm run test:ffi
  • npm run pack:check - exact 22-file allowlist
  • npm run test:bunnymark:visual - valid, byte-identical direct/instanced PNG captures on macOS
  • full tests/linux/Dockerfile gate on Linux/arm64 with Xvfb/Mesa, including byte-identical visual proof and existing DOOM smoke
  • test-instrumented origin/main screenshot also byte-matches the macOS instanced image
  • git diff --check

Review status

Current automated findings were independently evaluated. Capture integrity validation is included in 650cf2f; the packed Matrix shader mapping was revalidated against Raylib's MatrixToFloatV implementation and retained.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Bunnymark implementation now uses compact typed-array state and an optional custom-shader instanced renderer. Runtime configuration supports deterministic benchmarks, screenshots, reporting, and draw-path selection. New tests compare direct and instanced screenshots byte-for-byte, with benchmark documentation and CI integration.

Changes

Bunnymark instanced rendering

Layer / File(s) Summary
Compact bunny state model
examples/lib/bunny-state.ts, tests/unit/bunny-state.test.ts
Adds packed bunny state, transform layout constants, spawning and movement updates, with unit coverage for layout and boundary behavior.
Instanced mesh renderer
examples/lib/bunny-instancing.ts, benchmarks/bunnymark-instancing.md, README.md
Adds custom GLSL mesh instancing, resource lifecycle helpers, performance measurements, rendering-equivalence results, constraints, and fallback documentation.
Configurable Bunnymark runtime
examples/lib/run-bunnymark.ts, tests/fixtures/bunnymark-benchmark.ts
Routes simulation and drawing through shared state, supports direct or instanced rendering, and adds deterministic configuration, screenshots, FPS logging, summaries, and cleanup.
Visual validation and packaging
tests/fixtures/bunnymark-visual.ts, tests/fixtures/bunnymark-*-visual.ts, tests/integration/bunnymark-visual.test.mjs, .github/workflows/ci.yml, tests/linux/Dockerfile, package.json, scripts/validate-package.mjs, tests/unit/syntax.test.ts
Adds visual fixtures and byte-identical screenshot assertions, then wires the test into package validation and Linux CI execution.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VisualTest
  participant Fixture
  participant runBunnymark
  participant Renderer
  participant PNGFiles
  VisualTest->>Fixture: run direct and instanced fixtures
  Fixture->>runBunnymark: configure draw_path and capture settings
  runBunnymark->>Renderer: render selected Bunnymark path
  Renderer-->>runBunnymark: capture configured frame
  runBunnymark->>PNGFiles: write screenshot
  VisualTest->>PNGFiles: read direct and instanced PNGs
  VisualTest->>VisualTest: compare bytes and compute SHA-256
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: batching Bunnymark sprites with public Raylib instancing.
Description check ✅ Passed The description is directly related to the changeset and matches the Bunnymark instancing, tests, and benchmark updates.

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

@frostney
frostney marked this pull request as ready for review July 24, 2026 13:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/integration/bunnymark-visual.test.mjs (1)

40-49: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the shader-failure fallback path.

These cases validate successful direct and instanced rendering only. Add a controlled fixture or runtime switch that forces instanced shader creation to fail, then assert that the requested instanced run reports draw_path=direct and produces the same screenshot as the direct reference.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/bunnymark-visual.test.mjs` around lines 40 - 49, The visual
integration test currently covers only successful direct and instanced
rendering. Extend the fixture or runtime configuration used by
bunnymark-instanced-visual to deliberately fail instanced shader creation, then
verify the requested instanced execution reports draw_path=direct and its
screenshot matches the direct reference, while preserving the existing
successful comparison.
examples/lib/bunny-instancing.ts (1)

73-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Non-obvious mesh construction could use a short comment.

Building the quad via GenMeshHeightmap + full UpdateMeshBuffer overwrite works but isn't self-explanatory; a one-line comment on why this API is (ab)used to obtain a ready 6-vertex mesh would help future maintainers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/lib/bunny-instancing.ts` around lines 73 - 80, Add a concise comment
immediately before the GenMeshHeightmap call explaining that it is used to
create a ready 6-vertex quad mesh whose vertex and texture-coordinate buffers
are fully overwritten by the subsequent UpdateMeshBuffer calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/lib/bunny-instancing.ts`:
- Around line 21-33: The vertex shader in examples/lib/bunny-instancing.ts lines
21-33 must extract packed transform data correctly: update fragColor to use the
.x component of each transform column and compute position from the .w
components of columns 0 and 1. Then re-run the direct-versus-instanced
visual-equivalence test and update or re-confirm the reported SHA-256 checksums
in benchmarks/bunnymark-instancing.md lines 41-57.

In `@tests/integration/bunnymark-visual.test.mjs`:
- Around line 43-49: Validate the buffers returned by readFile for directPath
and instancedPath before the assert.deepEqual comparison, ensuring each is a
non-empty, valid PNG capture. Reject either capture when validation fails, then
preserve the existing pixel-buffer equality assertion for valid files.

---

Nitpick comments:
In `@examples/lib/bunny-instancing.ts`:
- Around line 73-80: Add a concise comment immediately before the
GenMeshHeightmap call explaining that it is used to create a ready 6-vertex quad
mesh whose vertex and texture-coordinate buffers are fully overwritten by the
subsequent UpdateMeshBuffer calls.

In `@tests/integration/bunnymark-visual.test.mjs`:
- Around line 40-49: The visual integration test currently covers only
successful direct and instanced rendering. Extend the fixture or runtime
configuration used by bunnymark-instanced-visual to deliberately fail instanced
shader creation, then verify the requested instanced execution reports
draw_path=direct and its screenshot matches the direct reference, while
preserving the existing successful comparison.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c653a78-efaa-427e-b7ed-bc63d2958b8e

📥 Commits

Reviewing files that changed from the base of the PR and between ff978c1 and 978aaed.

📒 Files selected for processing (16)
  • .github/workflows/ci.yml
  • README.md
  • benchmarks/bunnymark-instancing.md
  • examples/lib/bunny-instancing.ts
  • examples/lib/bunny-state.ts
  • examples/lib/run-bunnymark.ts
  • package.json
  • scripts/validate-package.mjs
  • tests/fixtures/bunnymark-benchmark.ts
  • tests/fixtures/bunnymark-direct-visual.ts
  • tests/fixtures/bunnymark-instanced-visual.ts
  • tests/fixtures/bunnymark-visual.ts
  • tests/integration/bunnymark-visual.test.mjs
  • tests/linux/Dockerfile
  • tests/unit/bunny-state.test.ts
  • tests/unit/syntax.test.ts

Comment thread examples/lib/bunny-instancing.ts
Comment thread tests/integration/bunnymark-visual.test.mjs
@frostney
frostney merged commit 70a0453 into main Jul 24, 2026
3 checks passed
@frostney
frostney deleted the codex/bulk-sprite-submission branch July 24, 2026 19:15
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