Bug 235478
Summary: | WebGL enabling Blend causes high performance drop | ||
---|---|---|---|
Product: | WebKit | Reporter: | Reinis <muiznieks.reinis> |
Component: | WebGL | Assignee: | Nobody <webkit-unassigned> |
Status: | NEW | ||
Severity: | Normal | CC: | dino, johncunningham, kbr, kkinnunen, kpiddington, webkit-bug-importer |
Priority: | P2 | Keywords: | InRadar |
Version: | Safari Technology Preview | ||
Hardware: | All | ||
OS: | All | ||
Bug Depends on: | |||
Bug Blocks: | 231180 |
Reinis
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 :/
Attachments | ||
---|---|---|
Add attachment proposed patch, testcase, etc. |
Alexey Proskuryakov
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?
Reinis
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.
Reinis
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?
Reinis
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.
Kenneth Russell
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.
Radar WebKit Bug Importer
<rdar://problem/88224455>