Skip to content

Fix stale texture unit cache in WebGL multi-texture mode - #762

Draft
samme with Copilot wants to merge 5 commits into
masterfrom
fix/multitexture
Draft

samme with Copilot wants to merge 5 commits into
masterfrom
fix/multitexture

Conversation

Copilot AI commented Oct 24, 2026 •

Copy link
Copy Markdown

In multi-texture (MT) mode the static PIXI.WebGLRenderer.textureArray cache records which texture is bound to each texture unit. Several code paths rebound units without updating it, and WebGLSpriteBatch.renderBatch bound the first texture of a batch on whichever unit was active, so sprites drew with the wrong texture. Only one texture per batch was uploaded in MT mode, so late-loaded or changed canvas textures rendered blank or stale.

Cache correctness

  • Add PIXI.WebGLRenderer.invalidateTextureUnit(unit?).
  • updateTexture / updateCompressedTexture now record the bound texture in the cache.
  • renderBatch no longer binds the first texture on the active unit in MT mode (non-MT path unchanged).
  • Invalidate the cache where other code rebinds units: initContext, PixiShader (initMultitexShader, initSampler2D, syncUniforms), Rope, Creature, WebGLFilterManager push/pop, FilterTexture._CreateFramebuffer.

Upload gap

  • WebGLSpriteBatch.render, renderTilingSprite (new bindMultiTexture helper) and WebGLFastSpriteBatch.renderSprite/render now flush and updateTexture dirty base textures; sprites whose texture isn't loaded yet are skipped.

Filters

  • pushFilter now unbinds unit 0 after creating/resizing the filter texture. Previously the old renderBatch bind hid a feedback loop (filter target left bound while rendered to), which Chrome reported as GL errors once the bind was removed.

Testing

  • Test page with 24 prioritized textures (more than fit without unit conflicts), Rope, filtered sprite, tilesprite, sprite batch, late-loaded texture and changing Text, with temporary instrumentation (not committed) comparing the cache to actual GL bindings. Before: ~92k stale-unit hits in 2.5 s and mostly black sprites. After: 0 hits and everything renders. Non-MT mode renders correctly.
  • eslint src and npm run build-test pass.

Known, not addressed here

  • Context loss/restore via WEBGL_lose_context doesn't recover, before or after this change (shaders/buffers from the old context are reused; Cache.clearGLTextures doesn't mark textures dirty).
  • In MT mode Rope doesn't set aTextureIndex for the strip shader, so it can sample the wrong unit.

Keep PIXI.WebGLRenderer.textureArray in sync with texture unit bindings,
stop binding the batch's first texture on the active unit, and upload
dirty or late-loaded textures before drawing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

Copilot AI 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.

🟡 Changes recommended

Texture-binding errors remain during TileSprite uploads, first-use shader initialization, and RenderTexture resizing.

3 open issues

Review effort: Balanced

gl.activeTexture(gl.TEXTURE0 + texture.textureIndex);

gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]);
PIXI.WebGLRenderer.textureArray[texture.textureIndex] = texture;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Flushed the sprite batch before the eager TileSprite texture upload in 6cb50ea.

Comment thread src/pixi/renderers/webgl/utils/FilterTexture.js Outdated
}
}
else
else if (!PIXI._enableMultiTextureToggle)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Restored the cached texture bindings after first-use multi-texture shader initialization in 6cb50ea.

Co-authored-by: samme <435681+samme@users.noreply.github.com>
Copilot AI requested a review from samme October 24, 2026 05:22
@samme
samme requested a balanced review from Copilot October 24, 2026 05:23

Copilot AI 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.

🟡 Changes recommended

Removing the batch rebind leaves a RenderTexture feedback loop that can cause WebGL to reject draws using prioritized source textures.

3 open issues
1 resolved since last review

Review effort: Balanced

}
}
else
else if (!PIXI._enableMultiTextureToggle)
samme and others added 3 commits October 24, 2026 17:14
… and RenderTexture resize

- TileSprite: flush before uploading the tiling texture, and upload it on the
  unit the vertices sample.
- PixiShader.initMultitexShader: restore existing unit bindings after the
  dummy-texture hack instead of clobbering them mid-render.
- FilterTexture: restore the previous binding after creating or resizing.
- Treat base textures without a GL texture as needing an upload.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI 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.

🟡 Changes recommended

The shared texture cache can still cause incorrect sampling across renderers now that the draw-time binding is removed.

4 open issues

Review effort: Balanced

}
}
else
else if (!PIXI._enableMultiTextureToggle)

This branch has not been deployed

No deployments
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.

3 participants