jnm2
jnm2

Reputation: 8354

Is there a way to cache slow WebGL shader compilation if the shader never changes?

I have a WebGL shader which compiles perceptually immediately (Chrome on Windows 7) when I have this:

void main(void) {
    if (antialias_level == 1)
        gl_FragColor = NewtonIteration((gl_FragCoord.xy + offset) / zoom);
    else if (antialias_level == 2)
        gl_FragColor = (NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.25, -0.25)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.25, 0.25)) / zoom)) * 0.5;
}

But it takes a very long time (~10 sec) to compile this:

void main(void) {
    if (antialias_level == 1)
        gl_FragColor = NewtonIteration((gl_FragCoord.xy + offset) / zoom);
    else if (antialias_level == 2)
        gl_FragColor = (NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.25, -0.25)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.25, 0.25)) / zoom)) * 0.5;
    else if (antialias_level == 4)
        gl_FragColor = (NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.25, -0.25)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.25, 0.25)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.25, -0.25)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.25, 0.25)) / zoom)) * 0.25;
    else if (antialias_level == 9)\
        gl_FragColor = (NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.33, -0.33)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.0, -0.33)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.33, -0.33)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.33, 0.0)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.0, 0.0)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.33, 0.0)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.33, 0.33)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.0, 0.33)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.33, 0.33)) / zoom)) * 0.111111111;
    else if (antialias_level == 16)\
        gl_FragColor = (NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.375, -0.375)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.125, -0.375)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.125, -0.375)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.375, -0.375)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.375, -0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.125, -0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.125, -0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.375, -0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.375, 0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.125, 0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.125, 0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.375, 0.125)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.375, 0.375)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(-0.125, 0.375)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.125, 0.375)) / zoom) + NewtonIteration((gl_FragCoord.xy + offset + vec2(0.375, 0.375)) / zoom)) * 0.0625;
}

Is there a way to cache the result of the WebGL compilation to binary and load that instead, or would that not help? I'm assuming the long delay has to do with translating the shader from OpenGL to DirectX.

(Here's a live example.)

Upvotes: 1

Views: 1499

Answers (2)

user128511
user128511

Reputation:

No, there is no way to precompile a shader. It would be a huge security hole.

On the other hand, it is likely both browsers will start caching shaders in the future for you under the hood

Here's the issue for Chrome http://code.google.com/p/chromium/issues/detail?id=88572

I'm not sure if there is a similar one for Firefox or other browsers.

Upvotes: 2

Kevin Reid
Kevin Reid

Reputation: 43753

I am not aware of any way to precompile shaders for WebGL; there would probably be many portability and security issues with providing such a feature.

Is antialias_level a user-chosen parameter that is constant for the entire scene? You would probably be better off compiling a shader for the current chosen level rather than all possible ones; this may be more efficient at runtime as well as compiling faster.

A simple way to customize a shader while still separating your shader source from your JS code is to prepend "#define ANTIALIAS_LEVEL " + level before compiling, and in your shader source use #if to choose the appropriate case. However, since your main() code is very systematic, it might be worthwhile just generating it algorithmically.

Upvotes: 3

Related Questions