Skip to content

GLEmulation: fix texture-load merging when a texture matrix is in use - #28061

Open
TheTomBritton wants to merge 1 commit into
emscripten-core:mainfrom
TheTomBritton:glemu-texload-sentinel
Open

TheTomBritton wants to merge 1 commit into
emscripten-core:mainfrom
TheTomBritton:glemu-texload-sentinel

Conversation

@TheTomBritton

Copy link
Copy Markdown

Fixes #27946.

The duplicate-texture-load merge in genPassLines extracted sample expressions with a lazy regex that stopped at the first ), so with a texture matrix active it captured a truncated texture2D(u_texUnit0, (u_textureMatrix0 * v_texCoord0 and the GL_COMBINE shader failed to compile. This is @juj's sentinel diff from the issue, verbatim: genTexUnitSampleExpr appends /*endload*/ and the regex matches up to it.

Adds test_gl_texenv_combine_texmatrix: two GL_COMBINE setups that use the texture more than once (two loads on a single line, and INTERPOLATE), drawn with a texture matrix set, checked texel by texel. Fails before the change (shader compile error), passes after it on Chrome/ANGLE (Metal and SwiftShader). The other LEGACY_GL_EMULATION browser tests pass locally.

Note: this and #27947's PR both touch genTexUnitSampleExpr; whichever lands second needs a one-line rebase (the texture2DProj return also wants the /*endload*/ sentinel).

🤖 Generated with Claude Code

https://claude.ai/code/session_01AFhyBALrPm8CSkyHzQizDH

CTexUnit.genPassLines merges repeated texture loads into one variable,
finding them with /(texture.*?\(.*?\))/g. With a texture matrix in use the
generated sample expression is

  texture2D(u_texUnit0, (u_textureMatrix0 * v_texCoord0).xy)

and the non-greedy match stops at the `)` that closes the matrix product,
so any GL_COMBINE environment that uses the texture more than once was
rewritten into invalid GLSL and nothing was drawn. (The regex used after
the first merge was greedy instead, and could run to the last `)` on a
line.)

Mark the end of every generated texture load with a /*endload*/ comment
and match up to it, as suggested in emscripten-core#27946. The comments left behind
after merging are ordinary GLSL comments.

Adds a browser test that draws with two GL_COMBINE environments (one with
two loads on a single line, one with GL_INTERPOLATE loading the texture on
two lines) while a texture matrix is set, and checks the colours. It fails
before this change (the shader does not compile) and passes after it.

Fixes emscripten-core#27946

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UR3bCLyLBQiGtevGJx2ARt

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request addresses an issue in LEGACY_GL_EMULATION where invalid fixed-function fragment shaders were generated when a texture matrix was in use and a texture was referenced multiple times in GL_COMBINE environments. The fix appends an /*endload*/ sentinel to texture load expressions and updates the regex to match it, preventing premature matching at internal parentheses. A regression test has also been added. The review feedback highlights a potential issue where the non-greedy regex could backtrack across multiple texture calls if a sentinel is missing, and suggests a more robust regex pattern using negative lookaheads to prevent incorrect merging.

Comment thread src/lib/libglemu.js

var texLoadLines = '';
var texLoadRegex = /(texture.*?\(.*?\))/g;
var texLoadRegex = /(texture.*?\(.*?\))\/\*endload\*\//g;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

If any texture load expression (such as texture2DProj, as noted in the PR description) does not have the /*endload*/ sentinel, the non-greedy .*? in the regex /(texture.*?\(.*?\))\/\*endload\*\//g can backtrack and match across multiple texture calls to find the next available /*endload*/. This would incorrectly merge multiple distinct texture calls into a single invalid expression, leading to shader compilation failures. Using (?:(?!texture).)*? instead of .*? ensures that the match cannot cross another texture keyword, making the regex robust against missing sentinels or other occurrences of the word texture on the same line.

        var texLoadRegex = /(texture(?:(?!texture).)*?\((?:(?!texture).)*?\))\/\*endload\*\//g;

Comment thread src/lib/libglemu.js
lines = lines.split(texLoadExpr).join(texLoadVar);
// Reset regex search, since we modified the string.
texLoadRegex = /(texture.*\(.*\))/g;
texLoadRegex = /(texture.*?\(.*?\))\/\*endload\*\//g;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

If any texture load expression (such as texture2DProj, as noted in the PR description) does not have the /*endload*/ sentinel, the non-greedy .*? in the regex /(texture.*?\(.*?\))\/\*endload\*\//g can backtrack and match across multiple texture calls to find the next available /*endload*/. This would incorrectly merge multiple distinct texture calls into a single invalid expression, leading to shader compilation failures. Using (?:(?!texture).)*? instead of .*? ensures that the match cannot cross another texture keyword, making the regex robust against missing sentinels or other occurrences of the word texture on the same line.

            texLoadRegex = /(texture(?:(?!texture).)*?\((?:(?!texture).)*?\))\/\*endload\*\//g;

@sbc100
sbc100 requested a review from juj October 23, 2026 19:41
@sbc100

sbc100 commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Feel free to take Gemini's comments with a grain of salt. We are currently experimenting with various review agents.

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.

LEGACY_GL_EMULATION: duplicate-texture-load regex truncates sample expressions when a texture matrix is active (invalid GLSL for COMBINE shaders)

2 participants