Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support non-integer loop start and ends when using pragma unroll_loop_* #28020

Open
sguimmara opened this issue Mar 28, 2024 · 8 comments
Open

Comments

@sguimmara
Copy link
Contributor

Description

Currently, the pragma only supports integer values in its regexp:

/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g

This makes it impossible to unroll loops with statically defined values, such as:

#define TEXTURE_COUNT 3

sampler2D textures[TEXTURE_COUNT];

#pragma unroll_loop_start
for ( int i = 0; i < TEXTURE_COUNT; i++ ) {
   vec4 color = texture2D(textures[i], vec2(0, 0));
}
#pragma unroll_loop_end

This would be useful to access samplers in an array, as GLSL forbids the user of dynamic indices to access samplers.

In my situation, I cannot use texture arrays as the various textures in the sampler array do not have the same sizes.

Solution

Currently, in WebGLProgram.js, the unrolling is done right after various preprocessing, such as includes:

vertexShader = resolveIncludes( vertexShader );
vertexShader = replaceLightNums( vertexShader, parameters );
vertexShader = replaceClippingPlaneNums( vertexShader, parameters );

fragmentShader = resolveIncludes( fragmentShader );
fragmentShader = replaceLightNums( fragmentShader, parameters );
fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );

vertexShader = unrollLoops( vertexShader );
fragmentShader = unrollLoops( fragmentShader );

We could make unrollLoops aware of the defines in the material, and if the loop end/start are identifiers rather than integers, lookup the indentifiers in the defines of the material to access the actual value:

vertexShader = unrollLoops( vertexShader , defines );
fragmentShader = unrollLoops( fragmentShader, defines );

Then, we slightly modify loopReplace to capture the defines parameter:

function unrollLoops( string, defines ) {
	
	function loopReplacer( match, start, end, snippet ) {

		let string = '';
	
		const loopStart = defines[ start ] ?? parseInt( start );
		const loopEnd = defines [ end ] ??  parseInt( end );
	
		for ( let i = loopStart; i < loopEnd; i ++ ) {
	
			string += snippet
				.replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' )
				.replace( /UNROLLED_LOOP_INDEX/g, i );
	
		}
	
		return string;
	
	}

	return string.replace( unrollLoopPattern, loopReplacer );

}

Alternatives

I also considered adding hooks to the preprocessing of the shader (à la onBeforeCompile), so that the user could perform the replacement of the loop-related defines by actual integers:

  • onBeforeUnrollLoops()
  • onBeforeIncludes()

but it feels a bit too heavy.

Additional context

No response

@gkjohnson
Copy link
Collaborator

This has been discussed previously in #20305

@sguimmara
Copy link
Contributor Author

@gkjohnson My bad I missed this PR. I guess then that my proposal is refused as it complexifies the unroling mechanism.

Since we don't want to complexify the unrolling mechanism, the only way I see is by providing a hook that is executed just before the unrolling, and that let the user change the shader code directly.

@gkjohnson
Copy link
Collaborator

the only way I see is by providing a hook that is executed just before the unrolling, and that let the user change the shader code directly.

As mentioned in the other issue, this can be done with the onBeforeCompile callback.

@sguimmara
Copy link
Contributor Author

@gkjohnson Unfortunately, this does not work if the unrollable loop is in an included chunk. It does work if I inline the chunk though.

@mrdoob
Copy link
Owner

mrdoob commented Mar 29, 2024

Are these unroll loops still necessary?
They sure made a difference on iPads 10 years ago but I wonder what the is situation now.

@mrdoob
Copy link
Owner

mrdoob commented Mar 29, 2024

This would be useful to access samplers in an array, as GLSL forbids the user of dynamic indices to access samplers.

Oh, I see...

@mrdoob
Copy link
Owner

mrdoob commented Mar 29, 2024

@sguimmara Have you tried using the new WebGPURenderer? It comes with a WebGL fallback.

@sguimmara
Copy link
Contributor Author

@mrdoob yes, the dynamic index limitation of WebGL is extremely irritating and prevents a lot of cool features. For example I have to recompile the shader every time the number of iterations in the loop changes (luckily it does not change too often).

Have you tried using the new WebGPURenderer? It comes with a WebGL fallback.

No, but I'm very interested in WebGPU as it might help with performance.

For info, we use three.js to develop Giro3D, a visualization library for geospatial data. This library is all about data streaming, dynamic updates, GPU picking, and WebGL shows its age for this use case.

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

No branches or pull requests

3 participants