diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/format-filterable-renderable.html')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/format-filterable-renderable.html | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/format-filterable-renderable.html b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/format-filterable-renderable.html new file mode 100644 index 0000000000..df7694b821 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/format-filterable-renderable.html @@ -0,0 +1,378 @@ +<!-- +Copyright (c) 2021 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"> +<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(); +gl.canvas.width = gl.canvas.height = 1; + +function makeTexImage(format, unpackFormat, unpackType, data) { + data = data || null; + + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texImage2D(gl.TEXTURE_2D, 0, gl[format], 2, 2, 0, + gl[unpackFormat], gl[unpackType], null); + return tex; +} + +const DUMMY_COLOR = makeTexImage('RGBA', 'RGBA', 'UNSIGNED_BYTE'); + +function makeProgram(gl, vsSrc, fsSrc) { + function makeShader(prog, type, src) { + const shader = gl.createShader(gl[type]); + gl.shaderSource(shader, src.trim()); + gl.compileShader(shader); + gl.attachShader(prog, shader); + gl.deleteShader(shader); + }; + + const prog = gl.createProgram(); + makeShader(prog, 'VERTEX_SHADER', vsSrc); + makeShader(prog, 'FRAGMENT_SHADER', fsSrc); + gl.linkProgram(prog); + + if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { + throw 'Program linking failed' + fsSrc; + } + return prog; +} + +const TEX_FILTER_PROG_T = (version, samplerT) => makeProgram(gl, `\ + ${version} + void main() { + gl_PointSize = 1.0; + gl_Position = vec4(0.5,0.5,0,1); + }`,`\ + ${version} + precision mediump float; + uniform ${samplerT} u_tex0; + #if __VERSION__ == 300 + out vec4 o_FragColor; + #else + #define o_FragColor gl_FragColor + #define texture texture2D + #endif + void main() { + o_FragColor = vec4(texture(u_tex0, vec2(0.8))); + }`); +const TEX_FILTER_PROG_BY_TYPEISH = { + 'float': TEX_FILTER_PROG_T('', 'sampler2D'), +}; +if (wtu.isWebGL2(gl)) { + TEX_FILTER_PROG_BY_TYPEISH['int'] = + TEX_FILTER_PROG_T('#version 300 es', 'highp isampler2D'); + TEX_FILTER_PROG_BY_TYPEISH['uint'] = + TEX_FILTER_PROG_T('#version 300 es', 'highp usampler2D'); +} + +function runPixelProgram(gl, prog) { + gl.useProgram(prog); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.POINTS, 0, 1); + const bytes = new Uint8Array(4); + gl.readPixels(0,0,1,1,gl.RGBA, gl.UNSIGNED_BYTE, bytes); + return [].map.call(bytes, x => x/255.0); +} + +// GLES 2.0.25 p63 +const FORMAT_INFO_WEBGL1 = { + RGBA8 : { filter: true, render: true , unpack: ['RGBA', 'UNSIGNED_BYTE'] }, + RGB8 : { filter: true, render: undefined, unpack: ['RGB', 'UNSIGNED_BYTE'] }, + RGBA4 : { filter: true, render: undefined, unpack: ['RGBA', 'UNSIGNED_SHORT_4_4_4_4'] }, + RGB5_A1: { filter: true, render: undefined, unpack: ['RGBA', 'UNSIGNED_SHORT_5_5_5_1'] }, + RGB565 : { filter: true, render: undefined, unpack: ['RGB', 'UNSIGNED_SHORT_5_6_5'] }, + LA8 : { filter: true, render: false , unpack: ['LUMINANCE_ALPHA', 'UNSIGNED_BYTE'] }, + L8 : { filter: true, render: false , unpack: ['LUMINANCE', 'UNSIGNED_BYTE'] }, + A8 : { filter: true, render: false , unpack: ['ALPHA', 'UNSIGNED_BYTE'] }, +}; + +// GLES 3.0.6 p130-132 +const FORMAT_INFO_WEBGL2 = { + R8 : { render: true , filter: true , unpack: ['RED', 'UNSIGNED_BYTE'] }, + R8_SNORM : { render: false, filter: true , unpack: ['RED', 'BYTE'] }, + RG8 : { render: true , filter: true , unpack: ['RG', 'UNSIGNED_BYTE'] }, + RG8_SNORM : { render: false, filter: true , unpack: ['RG', 'BYTE'] }, + RGB8 : { render: true , filter: true , unpack: ['RGB', 'UNSIGNED_BYTE'] }, + RGB8_SNORM : { render: false, filter: true , unpack: ['RGB', 'BYTE'] }, + RGB565 : { render: true , filter: true , unpack: ['RGB', 'UNSIGNED_SHORT_5_6_5'] }, + RGBA4 : { render: true , filter: true , unpack: ['RGBA', 'UNSIGNED_SHORT_4_4_4_4'] }, + RGB5_A1 : { render: true , filter: true , unpack: ['RGBA', 'UNSIGNED_SHORT_5_5_5_1'] }, + RGBA8 : { render: true , filter: true , unpack: ['RGBA', 'UNSIGNED_BYTE'] }, + RGBA8_SNORM : { render: false, filter: true , unpack: ['RGBA', 'BYTE'] }, + RGB10_A2 : { render: true , filter: true , unpack: ['RGBA', 'UNSIGNED_INT_10_10_10_2'] }, + RGB10_A2UI : { render: true , filter: false, unpack: ['RGBA', 'UNSIGNED_INT_10_10_10_2'] }, + SRGB8 : { render: false, filter: true , unpack: ['RGB', 'UNSIGNED_BYTE'] }, + SRGB8_ALPHA8 : { render: true , filter: true , unpack: ['RGBA', 'UNSIGNED_BYTE'] }, + R16F : { render: false, filter: true , unpack: ['RED', 'FLOAT'] }, + RG16F : { render: false, filter: true , unpack: ['RG', 'FLOAT'] }, + RGB16F : { render: false, filter: true , unpack: ['RGB', 'FLOAT'] }, + RGBA16F : { render: false, filter: true , unpack: ['RGBA', 'FLOAT'] }, + R32F : { render: false, filter: false, unpack: ['RED', 'FLOAT'] }, + RG32F : { render: false, filter: false, unpack: ['RG', 'FLOAT'] }, + RGB32F : { render: false, filter: false, unpack: ['RGB', 'FLOAT'] }, + RGBA32F : { render: false, filter: false, unpack: ['RGBA', 'FLOAT'] }, + R11F_G11F_B10F: { render: false, filter: true , unpack: ['RGB', 'FLOAT'] }, + RGB9_E5 : { render: false, filter: true , unpack: ['RGB', 'FLOAT'] }, + R8I : { render: true , filter: false, unpack: ['RED', 'BYTE'] }, + R8UI : { render: true , filter: false, unpack: ['RED', 'UNSIGNED_BYTE'] }, + R16I : { render: true , filter: false, unpack: ['RED', 'BYTE'] }, + R16UI : { render: true , filter: false, unpack: ['RED', 'UNSIGNED_BYTE'] }, + R32I : { render: true , filter: false, unpack: ['RED', 'BYTE'] }, + R32UI : { render: true , filter: false, unpack: ['RED', 'UNSIGNED_BYTE'] }, + RG8I : { render: true , filter: false, unpack: ['RG', 'BYTE'] }, + RG8UI : { render: true , filter: false, unpack: ['RG', 'UNSIGNED_BYTE'] }, + RG16I : { render: true , filter: false, unpack: ['RG', 'SHORT'] }, + RG16UI : { render: true , filter: false, unpack: ['RG', 'UNSIGNED_SHORT'] }, + RG32I : { render: true , filter: false, unpack: ['RG', 'INT'] }, + RG32UI : { render: true , filter: false, unpack: ['RG', 'UNSIGNED_INT'] }, + RGB8I : { render: false, filter: false, unpack: ['RGB', 'BYTE'] }, + RGB8UI : { render: false, filter: false, unpack: ['RGB', 'UNSIGNED_BYTE'] }, + RGB16I : { render: false, filter: false, unpack: ['RGB', 'SHORT'] }, + RGB16UI : { render: false, filter: false, unpack: ['RGB', 'UNSIGNED_SHORT'] }, + RGB32I : { render: false, filter: false, unpack: ['RGB', 'INT'] }, + RGB32UI : { render: false, filter: false, unpack: ['RGB', 'UNSIGNED_INT'] }, + RGBA8I : { render: true , filter: false, unpack: ['RGBA', 'BYTE'] }, + RGBA8UI : { render: true , filter: false, unpack: ['RGBA', 'UNSIGNED_BYTE'] }, + RGBA16I : { render: true , filter: false, unpack: ['RGBA', 'SHORT'] }, + RGBA16UI : { render: true , filter: false, unpack: ['RGBA', 'UNSIGNED_SHORT'] }, + RGBA32I : { render: true , filter: false, unpack: ['RGBA', 'INT'] }, + RGBA32UI : { render: true , filter: false, unpack: ['RGBA', 'UNSIGNED_INT'] }, + + DEPTH_COMPONENT16: { render: 'DEPTH_ATTACHMENT', filter: false }, + DEPTH_COMPONENT24: { render: 'DEPTH_ATTACHMENT', filter: false }, + DEPTH_COMPONENT32F: { render: 'DEPTH_ATTACHMENT', filter: false }, + DEPTH24_STENCIL8: { render: 'DEPTH_STENCIL_ATTACHMENT', filter: false }, + DEPTH32F_STENCIL8: { render: 'DEPTH_STENCIL_ATTACHMENT', filter: false }, +}; + +const ONE_BY_TYPE = { + 'BYTE': (1<<7)-1, + 'UNSIGNED_BYTE': (1<<8)-1, + 'SHORT': (1<<15)-1, + 'UNSIGNED_SHORT': (1<<16)-1, + 'INT': (1<<31)-1, + 'UNSIGNED_INT': Math.pow(2,32)-1, + 'FLOAT': 1, +}; + +const ABV_BY_TYPE = { + 'BYTE': Int8Array, + 'UNSIGNED_BYTE': Uint8Array, + 'SHORT': Int16Array, + 'UNSIGNED_SHORT': Uint16Array, + 'INT': Int32Array, + 'UNSIGNED_INT': Uint32Array, + 'FLOAT': Float32Array, +}; + +function pushBitsUnorm(prev, bitCount, floatVal) { + let ret = prev << bitCount; + ret |= floatVal * ((1 << bitCount)-1); + return ret; +} + +const CHANNELS_BY_FORMAT = { + 'RED': 1, + 'LUMINANCE': 1, + 'ALPHA': 1, + 'LUMINANCE_ALPHA': 2, + 'RG': 2, + 'RGB': 3, + 'RGBA': 4, +}; + +function throwv(val) { + throw val; +} + +function pixelDataForUnpack(format, type, floatVal) { + switch (type) { + case 'UNSIGNED_SHORT_5_6_5': { + let bits = 0; + bits = pushBitsUnorm(bits, 5, floatVal); + bits = pushBitsUnorm(bits, 6, floatVal); + bits = pushBitsUnorm(bits, 5, floatVal); + return new Uint16Array([bits]); + } + case 'UNSIGNED_SHORT_4_4_4_4': { + let bits = 0; + bits = pushBitsUnorm(bits, 4, floatVal); + bits = pushBitsUnorm(bits, 4, floatVal); + bits = pushBitsUnorm(bits, 4, floatVal); + bits = pushBitsUnorm(bits, 4, floatVal); + return new Uint16Array([bits]); + } + case 'UNSIGNED_SHORT_5_5_5_1': { + let bits = 0; + bits = pushBitsUnorm(bits, 5, floatVal); + bits = pushBitsUnorm(bits, 5, floatVal); + bits = pushBitsUnorm(bits, 5, floatVal); + bits = pushBitsUnorm(bits, 1, floatVal); // Ok, silly for 1 bit here. + return new Uint16Array([bits]); + } + case 'UNSIGNED_INT_10_10_10_2': { + let bits = 0; + bits = pushBitsUnorm(bits, 10, floatVal); + bits = pushBitsUnorm(bits, 10, floatVal); + bits = pushBitsUnorm(bits, 10, floatVal); + bits = pushBitsUnorm(bits, 2, floatVal); // 2 bits isn't much more useful + return new Uint32Array([bits]); + } + } + + const channels = CHANNELS_BY_FORMAT[format] || throwv(format); + const one = ONE_BY_TYPE[type] || throwv('240', type); + const abvType = ABV_BY_TYPE[type] || throwv('241', type); + + const val = floatVal * one; + const arr = []; + for (const i of range(channels)) { + arr.push(val); + } + return new abvType(arr); +} + +function expect(name, was, expected) { + let text = `${name} was ${was}`; + const cond = was == expected; + if (!cond) { + text += `, but expected ${expected}`; + } + expectTrue(cond, text); +} + +function toTypeish(sizedFormat) { + if (sizedFormat.endsWith('UI')) { + return 'int'; + } else if (sizedFormat.endsWith('I')) { + return 'uint'; + } + return 'float'; +} + +call(async () => { + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + + let formatList = FORMAT_INFO_WEBGL1; + if (wtu.isWebGL2(gl)) { + formatList = FORMAT_INFO_WEBGL2; + } + for (const [sizedFormat, info] of Object.entries(formatList)) { + await wtu.dispatchPromise(); + debug(``); + debug(`${sizedFormat}: ${JSON.stringify(info)}`); + + const typeish = toTypeish(sizedFormat); + + // |---|---| + // | 0 | 1 | + // |---|---| + // 0| 0 | 0 | + // |---|---| + // 0 + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + if (gl.texStorage2D) { + gl.texStorage2D(gl.TEXTURE_2D, 1, gl[sizedFormat], 2, 2); + } else { + gl.texImage2D(gl.TEXTURE_2D, 0, gl[info.unpack[0]], + 2, 2, 0, gl[info.unpack[0]], gl[info.unpack[1]], null); + } + + if (info.unpack) { + const one = pixelDataForUnpack(...info.unpack, 1.0); + const data = new one.constructor(one.length*4); + data.set(one, one.length*3); + gl.texSubImage2D(gl.TEXTURE_2D, 0, 0,0, + 2,2, gl[info.unpack[0]], gl[info.unpack[1]], data); + } else { + info.render || throwv(`${sizedFormat} without unpack or render`); + } + + // - + // color-renderable test + + { + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + let attach = info.render || true; + const isColor = (attach === true); + if (isColor) { + attach = 'COLOR_ATTACHMENT0'; + } else { + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, DUMMY_COLOR, 0); + } + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl[attach], + gl.TEXTURE_2D, tex, 0); + const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + const wasRenderable = (status == gl.FRAMEBUFFER_COMPLETE); + if (info.render === undefined) { + debug(`Non-normative: color-renderable was ${wasRenderable}`); + } else { + expect('color-renderable', wasRenderable, !!info.render); + } + if (wasRenderable) { + gl.clearColor(0,0,0,0); + gl.clearDepth(0); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.enable(gl.SCISSOR_TEST); + gl.scissor(1,1,1,1); + gl.clearColor(1,1,1,1); + gl.clearDepth(1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.disable(gl.SCISSOR_TEST); + } + gl.deleteFramebuffer(fb); + if (!wasRenderable && !info.unpack) { + testFailed('No unpack provided and !wasRenderable, skipping filtering subtest...'); + continue; + } + } + + // - + // filterable test + + const prog = TEX_FILTER_PROG_BY_TYPEISH[typeish]; + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.clearColor(0,0,0,0); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.viewport(0,0,1,1); + const v = runPixelProgram(gl, prog); + if (sizedFormat != 'A8') { + v[3] = 0; // Incomplete no-alpha formats put 1 in alpha. + } + const wasFilterable = v.some(x => !!x); + expect('filterable', wasFilterable, info.filter); + } + + finishTest(); +}); + +</script> +</body> +</html> |