-
Notifications
You must be signed in to change notification settings - Fork 670
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
Add test of manual mipmap chain generation in WebGL 2.0 #3614
Comments
Is there something unique to TexStorage2D here? It seems like manually creating mip levels with |
Not really. It's only known that |
I thought this
and this
are equivalent except for the fact that first one is immutable. |
I have my own test here https://jsgist.org/?src=137493160103a6bc85bff8d47eb50045 It's definitely failing using
|
Oh, I had a wrong setting, they both work. Arguably neither should have worked before though and the error message still seems wrong. |
For details, here's the current code Instead of gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD, 0);
...
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD, 100); I had gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
...
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 100); It's not clear to me which one should work or if both should work but, with it using //import 'https://greggman.github.io/webgl-lint/webgl-lint.js';
import * as twgl from 'https://twgljs.org/dist/5.x/twgl-full.module.js';
function main(useTexStorage) {
log(useTexStorage ? 'texStorage2D' : 'texImage2D');
const gl = document.createElement('canvas').getContext('webgl2');
if (!gl) {
return alert("need WebGL2");
}
document.body.appendChild(gl.canvas);
const vs = `#version 300 es
void main() {
gl_PointSize = 300.0;
gl_Position = vec4(0, 0, 0, 1);
}
`;
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// make a 2x2 texture with 2 mip levels
if (useTexStorage) {
gl.texStorage2D(gl.TEXTURE_2D, 2, gl.RGBA8, 2, 2);
} else {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texImage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
}
// fill mip level 0 with red
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([
255, 0, 0, 255,
255, 0, 0, 255,
255, 0, 0, 255,
255, 0, 0, 255,
]));
// fill mip level 1 with yellow
gl.texSubImage2D(gl.TEXTURE_2D, 1, 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([
255, 255, 0, 255,
]));
// bind mip level 1 to a framebuffer
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 1);
console.log(twgl.glEnumToString(gl, gl.checkFramebufferStatus(gl.FRAMEBUFFER)));
checkError(gl);
// render mip level 0 to mip level 1, swapping red and blue
const fs = `#version 300 es
precision mediump float;
uniform highp sampler2D tex;
out vec4 fragColor;
void main() {
fragColor = texture(tex, vec2(0)).bgra;
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
gl.viewport(0, 0, 1, 1);
// set to use only first mip level (so no feedback loop).
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD, 0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// draw a single point
gl.useProgram(program);
gl.drawArrays(gl.POINTS, 0, 1);
checkError(gl);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD, 100);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);
// render the texture showing both mips.
const fs2 = `#version 300 es
precision mediump float;
uniform sampler2D tex;
out vec4 outColor;
void main() {
outColor = texture(tex, gl_PointCoord.xy, mod(floor(gl_FragCoord.x / 16.0), 2.0) * 1000.0);
}
`;
const prg2 = twgl.createProgram(gl, [vs, fs2]);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(prg2);
gl.drawArrays(gl.POINTS, 0, 1);
checkError(gl);
}
main(true);
main(false);
function checkError(gl) {
const err = gl.getError();
if (err) {
console.error(twgl.glEnumToString(gl, err));
}
}
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = args.join(' ');
document.body.appendChild(elem);
} |
Just for my own curiosity I checked in OpenGL on mac. Either MAX_LEVEL and MAX_LOD work there (of course GL doesn't check for feedback IIRC). |
Immutable and non-immutable textures are treated differently regarding framebuffer attachment completeness (OpenGL ES 3.0, Section 4.4.4.1):
|
I'm confused. The text about is only about non-immutable formats, where as AFAICT, the point of the text above is effectively about the fact that non-immutable formats can have non-homogenious levels. Each level can be a different internal format and can have any random size. Vs immutable-formats where this is never true. With immutable formats all levels are already guaranteed to be the same format and all levels are guaranteed to have the correct size for their level. All it's basically saying is that the range of mips being used has to follow the normal rules (they must all be the same format and all the correct size for a mip-chain within the currently defined range. TEXTURE_BASE_LEVEL and TEXTURE_MAX_LEVEL defines the current range). So, validation is the same after that. Effectively, check that the range of mips from TEXTURE_BASE_LEVEL to TEXTURE_MAX_LEVEL follows the same rules as an immutable format texture. Otherwise, everything else is the same. Am I missing something? |
It is valid to create an immutable texture and then use a level outside of the base-max range as a framebuffer attachment. It is not valid to do the same with non-immutable textures. |
Got it, thanks! |
I believe #3221 was my attempt to exhaustively test this kind of thing, but it would be great to add anything I missed! |
After the updates from https://anglebug.com/4690 , ANGLE guarantees support for allocating a texture in WebGL 2.0 via
TexStorage2D
and manually generating mipmaps by iteratively rendering to level N+1 while sampling at level N. (Specifically, that this is not considered a rendering feedback loop.)A test that this works has been added to ANGLE's test suite, but it should be ported to JavaScript and added to the WebGL 2.0 conformance test suite to guarantee that this functionality works across browsers.
The text was updated successfully, but these errors were encountered: