Source code
Revision control
Copy as Markdown
Other Tools
<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ensure sampling-feedback detection can allow certain immutable texture uses</title>
<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
<script src="../../../js/js-test-pre.js"></script>
<script src="../../../js/webgl-test-utils.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
"use strict";
const wtu = WebGLTestUtils;
description();
const gl = wtu.create3DContext(undefined, undefined, 2);
function* range(a, b) {
a.required;
if (b === undefined) {
b = a;
a = 0;
}
for (let i = a; i < b; i += 1) {
yield i;
}
}
const VS = `\
void main() {
gl_PointSize = 1.0;
}
`;
const FS = `\
uniform sampler2D u_tex0;
void main() {
gl_FragColor = texture2D(u_tex0, vec2(0));
}
`;
const prog = wtu.loadProgram(gl, VS, FS);
gl.useProgram(prog);
(() => {
const MIPS = 3;
const SIZE = 10;
const immutTex = gl.createTexture();
immutTex.name = "immutTex";
immutTex.immutable = true;
gl.bindTexture(gl.TEXTURE_2D, immutTex);
gl.texStorage2D(gl.TEXTURE_2D, MIPS, gl.RGBA8, SIZE, SIZE);
const mutTex = gl.createTexture();
mutTex.name = "mutTex";
mutTex.immutable = false;
gl.bindTexture(gl.TEXTURE_2D, mutTex);
for (const mip of range(MIPS)) {
const size = SIZE >> mip;
gl.texImage2D(gl.TEXTURE_2D, mip, gl.RGBA8, size, size, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
}
const MAG_FILTERS = [
'LINEAR',
'NEAREST',
];
const MIN_FILTERS = [
['LINEAR',false],
['LINEAR_MIPMAP_LINEAR',true],
['LINEAR_MIPMAP_NEAREST',true],
['NEAREST',false],
['NEAREST_MIPMAP_LINEAR',true],
['NEAREST_MIPMAP_NEAREST',true],
];
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
debug(`
mips: ${MIPS}: [0,${MIPS-1}] (inclusive)
size: ${SIZE}`);
const texs = [
immutTex,
mutTex,
];
for (const tex of texs) {
debug(`\
immutable: ${tex.immutable}`);
gl.bindTexture(gl.TEXTURE_2D, tex);
for (const level_prime_base of range(MIPS+1)) { // `level_base` in GLES
// ES 3.0.6 p150
let _level_base = level_prime_base;
if (tex.immutable) {
_level_base = Math.min(_level_base, MIPS-1);
}
const level_base = _level_base;
for (let _level_prime_max of range(level_prime_base-1, MIPS+2)) { // `q` in GLES
if (_level_prime_max < 0) continue;
if (_level_prime_max == MIPS+1) {
_level_prime_max = 10000; // This is the default, after all!
}
const level_prime_max = _level_prime_max;
// ES 3.0.6 p150
let _level_max = level_prime_max;
if (tex.immutable) {
_level_max = Math.min(Math.max(level_base, level_prime_max), MIPS-1);
}
const level_max = _level_max;
const p = Math.floor(Math.log2(SIZE)) + level_base;
const q = Math.min(p, level_max);
debug(`\
level_prime_base/max: [${level_prime_base}, ${level_prime_max}] (inclusive)
level_base/max: [${level_base}, ${level_max}] (inclusive)
q: ${q}`);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL,
level_prime_base);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL,
level_prime_max);
const mipComplete = (q <= MIPS-1);
for (const [minFilter,useMips] of MIN_FILTERS) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
gl[minFilter]);
// ES3.0 p211
const srcMaxSampledMip = (useMips ? q : level_base);
// ES3.0 p160-161
const textureComplete = (srcMaxSampledMip <= MIPS-1) &&
(level_base <= level_max);
for (const magFilter of MAG_FILTERS) {
debug(`\
min: ${minFilter}, mag: ${magFilter}`);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,
gl[magFilter]);
for (const dstMip of range(0,MIPS+1)) {
debug(`
mip: ${dstMip}`);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, tex, dstMip);
// -
// ES3.0 p213-214
let fbComplete = true;
// * "The width and height of `image` are non-zero"
fbComplete &= (0 <= dstMip && dstMip <= MIPS-1);
if (!tex.immutable) { // "...does not name an immutable-format texture..."
// * "...the value of [level] must be in the range `[level_base, q]`"
fbComplete &= (level_base <= dstMip && dstMip <= q);
// * "...the value of [level] is not `level_base`, then the texture must be mipmap complete"
if (dstMip != level_base) {
fbComplete &= mipComplete;
}
}
// -
let expectError = 0;
let expectStatus = gl.FRAMEBUFFER_COMPLETE;
// ES3.0 p211
let samplingFeedback = (level_base <= dstMip && dstMip <= srcMaxSampledMip);
if (!textureComplete) {
// Incomplete textures are safe
samplingFeedback = false;
}
if (samplingFeedback) {
expectError = gl.INVALID_OPERATION;
}
if (!fbComplete) {
expectStatus = gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
expectError = gl.INVALID_FRAMEBUFFER_OPERATION;
}
// -
wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER,
expectStatus, `{immutable: ${tex.immutable}, level_prime_base/max: [${level_prime_base}, ${level_prime_max}], minFilter: ${minFilter}, dest: ${dstMip}}`);
gl.drawArrays(gl.POINTS, 0, 1);
wtu.glErrorShouldBe(gl, expectError, "after draw with texture");
}
}
}
}
}
}
})();
var successfullyParsed = true;
</script>
<script src="../../../js/js-test-post.js"></script>
</body>
</html>