Bug 235478 - WebGL enabling Blend causes high performance drop
Summary: WebGL enabling Blend causes high performance drop
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebGL (show other bugs)
Version: Safari Technology Preview
Hardware: All All
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks: anglemetalregr
  Show dependency treegraph
 
Reported: 2022-01-22 07:16 PST by Reinis
Modified: 2022-03-01 02:22 PST (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Reinis 2022-01-22 07:16:52 PST
I develop a game using WebGL2. If I enable `gl.enable(gl.BLEND);` it causes the framerate to go down from 30 to 4. (Macbook air M1, but the same is for my iPad and iPhone as well)

In my scene, I have around 60k vertices (Updated every frame) + I use blend to overlay the screen with texture.


```
........

        // Bind uniforms
        gl.uniformBlockBinding(this.glProgram, this.uniBlockMain, 0);
        gl.uniform1i(this.uniTextures, 1); // texture sampler array is bound to texture1
        gl.uniform2fv(this.uniTextureOffsets, this.textureOffsets, 0);

        // We just allow the GL to do face culling. Note this requires the priority renderer
        // to have logic to disregard culled faces in the priority depth testing.
        gl.enable(gl.CULL_FACE);

        // Enable blending for alpha
        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

        // Draw buffers
        gl.bindVertexArray(this.vaoHandle);

        gl.enableVertexAttribArray(0);
        gl.bindBuffer(gl.ARRAY_BUFFER, this.tmpVertexBuffer.buffer);
        gl.vertexAttribIPointer(0, 4, gl.INT, 0, 0);

        gl.enableVertexAttribArray(1);
        gl.bindBuffer(gl.ARRAY_BUFFER, this.tmpUvBuffer.buffer);
        gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0);

        gl.drawArrays(gl.TRIANGLES, 0, this.vertexBuffer.length() / 4);

        // gl.disable(gl.BLEND);
        gl.disable(gl.CULL_FACE);

        gl.useProgram(null);

        this.vertexBuffer.clear();
        this.uvBuffer.clear();

        this.drawUi(canvasWidth, canvasHeight);

        gl.flush();

.....
```

````
    private drawUi(canvasWidth: number, canvasHeight: number) {
        const gl = this.gl;

        gl.enable(gl.BLEND);
		gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
		gl.bindTexture(gl.TEXTURE_2D, this.interfaceTexture);

		gl.useProgram(this.glUiProgram);
		gl.uniform2i(this.uniTexSourceDimensions, canvasWidth, canvasHeight);

        gl.viewport(0, 0, canvasWidth, canvasHeight);

        gl.uniform2i(this.uniTexTargetDimensions, canvasWidth, canvasHeight);
		
		// Texture on UI
		gl.bindVertexArray(this.vaoUiHandle);
		gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);

		gl.disable(gl.BLEND);
    }

```

Running on chrome I get stable 60fps :/
Comment 1 Alexey Proskuryakov 2022-01-23 17:27:15 PST
This seems like two issues: frame rate being 2x too low without blend, and 15x too low with it.

Could you please provide a complete test case that can be viewed in a browser?
Comment 2 Reinis 2022-01-24 01:47:07 PST
I have a working demo of my project here https://play.rsps.app/ 
Let me know if this is enough to check, or if I should create a smaller test case.
Comment 3 Reinis 2022-01-24 02:52:44 PST
I have made a bit of a breakthrough. Issue happens only if I render textures in the scene. Otherwise, it is solid 50 fps

```

export const fragmentShader = `#version 300 es

    precision highp float;
    precision highp int;
    precision highp sampler2DArray;

    uniform sampler2DArray textures;
    uniform vec2 textureOffsets[128];
    uniform float brightness;
    uniform float smoothBanding;
    uniform vec4 fogColor;
    uniform int colorBlindMode;
    uniform float textureLightMode;

    in vec4 Color;
    centroid in float fHsl;
    flat in int textureId;
    in vec2 fUv;
    in float fogAmount;

    out vec4 FragColor;

    ${hslToRgb}

    void main()	{
        vec4 c;

        // if (textureId > 0) {
        //     int textureIdx = textureId - 1;

        //     vec2 animatedUv = fUv + textureOffsets[textureIdx];

        //     vec4 textureColor = texture(textures, vec3(animatedUv, float(textureIdx)));

        //     vec4 textureColorBrightness = pow(textureColor, vec4(brightness, brightness, brightness, 1.0f));

        //     vec3 mul = 1.0 - (Color.rgb);
        //     c = textureColorBrightness * vec4(mul, 1.f);
        // } else {
            c = Color;
        // }

        vec3 mixedColor = mix(c.rgb, fogColor.rgb, fogAmount);
        FragColor = vec4(mixedColor, c.a);

    }
`;
```

This is my fragment shader I have commented part and it works smooth.

```
export const initTextureArray = (gl: WebGL2RenderingContext): WebGLTexture | null => {
    if (!allTexturesLoaded()) {
        return null;
    }

    const textures = Rasterizer3D.textures;

    const textureArray = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D_ARRAY, textureArray);
    gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 8, gl.RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length);

    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

    gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);

    // Set brightness to 1.0d to upload unmodified textures to GPU
    const save = Rasterizer3D.brightness;
    Rasterizer3D.setBrightness(1.0);

    updateTextures(gl);

    Rasterizer3D.setBrightness(save);

    gl.activeTexture(gl.TEXTURE1);
    gl.bindTexture(gl.TEXTURE_2D_ARRAY, textureArray);
    gl.generateMipmap(gl.TEXTURE_2D_ARRAY);
    gl.activeTexture(gl.TEXTURE0);

    return textureArray;
};
```

Code responsible for loading `uniform sampler2DArray textures;`

I did notice this bug https://bugs.webkit.org/show_bug.cgi?id=223322 I wonder if it is related. Maybe my textures change internal pixel format that safari doesn't support well?
Comment 4 Reinis 2022-01-24 03:21:12 PST
The 30 FPS limit I noticed before was because the console was open. Didn't know before that it affects it.

Seems like I found the issue..

* Causes lag
```
int textureIdx = textureId - 1;
vec2 animatedUv = fUv + textureOffsets[textureIdx];
```

* Causes lag
```
int textureIdx = textureId - 1;
if (textureIdx > 127) {
    c = Color;
    return;
}
vec2 animatedUv = fUv + textureOffsets[textureIdx];
```

* Doesn't lag
```
int textureIdx = textureId - 1;
vec2 animatedUv = fUv + textureOffsets[1];
```

* Doesn't lag
```
int textureIdx = 1;
vec2 animatedUv = fUv + textureOffsets[textureIdx];
```


* Doesn't lag
```
int textureIdx = textureId - 1;
vec2 animatedUv = fUv;
```

I did a bit more experiments, but it seems that using `flat in int textureId;` as an index in `uniform vec2 textureOffsets[128];` causes lag.
Comment 5 Kenneth Russell 2022-01-24 11:01:09 PST
The use of flat interpolation will probably trigger a code path where indices will be rewritten every frame. This is a known area for future optimization in the Metal backend.

Please try to create a smaller test case. This report is not easily investigated without one. Thanks.
Comment 6 Radar WebKit Bug Importer 2022-01-29 07:17:18 PST
<rdar://problem/88224455>