Skip to content

Fix splat overlay rendering performance#833

Merged
slimbuck merged 1 commit intoplaycanvas:mainfrom
slimbuck:fix
Mar 12, 2026
Merged

Fix splat overlay rendering performance#833
slimbuck merged 1 commit intoplaycanvas:mainfrom
slimbuck:fix

Conversation

@slimbuck
Copy link
Member

Summary

  • Fix the splat overlay creating and leaking a new WebGL VAO on every frame by adding a dummy 1-vertex instancing VB with SEMANTIC_POSITION, which makes the engine cache the VAO on the vertex buffer
  • Remove per-frame material.update() call from onPreRender() — only setParameter() is needed for uniform changes; update() was clearing shader variant caches every frame due to ShaderMaterial._dirtyShader being permanently true
  • Remove gl_FragDepth and vZW varying from the overlay shader — depthWrite is false so the written depth was unused, and gl_FragDepth disables Early-Z optimization

Details

The overlay mesh uses attributeless PRIMITIVE_POINTS rendering via gl_VertexID (0 vertex buffers). The engine's setBuffers only caches VAOs for the single-VB path (vertexBuffers.length === 1); the 0-VB case falls through to createVertexArray which has useCache = vertexBuffers.length > 1, so a brand new WebGL VAO was created and leaked every frame. On macOS with ANGLE (WebGL-to-Metal), this caused significant overhead from repeated Metal pipeline state lookups.

The workaround adds a dummy 1-vertex VB with format.instancing = true (sets vertexAttribDivisor(loc, 1)) so only 1 vertex is needed regardless of draw count. This also resolves the "No vertex attribute is mapped to location 0" Safari compatibility warning.

A proper engine fix (caching an empty VAO for the 0-VB case in setBuffers) will follow separately.

@slimbuck slimbuck requested review from a team and Copilot March 12, 2026 12:10
@slimbuck slimbuck self-assigned this Mar 12, 2026
@slimbuck slimbuck added the bug Something isn't working label Mar 12, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR targets a performance leak in the splat overlay rendering path by ensuring the engine can reuse/caches the WebGL VAO for the overlay draw and by removing unnecessary per-frame shader/material work.

Changes:

  • Add a dummy 1-vertex VertexBuffer to the overlay Mesh so PlayCanvas hits the VAO caching path (avoiding per-frame VAO creation/leak).
  • Remove per-frame material.update() from SplatOverlay.onPreRender() and rely on setParameter() for uniform updates.
  • Simplify the overlay shader by removing the vZW varying and gl_FragDepth write.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/splat-overlay.ts Adds a dummy vertex buffer to enable VAO caching and removes per-frame material.update() calls.
src/shaders/splat-overlay-shader.ts Removes custom fragment depth output and an unused varying to reduce shader overhead.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@slimbuck slimbuck merged commit 0442cc9 into playcanvas:main Mar 12, 2026
6 checks passed
@slimbuck slimbuck deleted the fix branch March 12, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants