Skip to content
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

Require rgb renderable webgl1 #3364

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion sdk/tests/conformance/textures/misc/00_test_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ copy-tex-image-and-sub-image-2d.html
--min-version 1.0.2 copy-tex-image-2d-formats.html
--min-version 1.0.4 copy-tex-image-crash.html
--min-version 1.0.4 copytexsubimage2d-large-partial-copy-corruption.html
--min-version 1.0.4 copytexsubimage2d-subrects.html
--min-version 1.0.4 copytexsubimage2d-subrects.html
--min-version 1.0.4 cube-incomplete-fbo.html
--min-version 1.0.4 cube-map-uploads-out-of-order.html
--min-version 1.0.3 default-texture.html
--min-version 1.0.4 exif-orientation.html
--min-version 1.0.4 format-filterable-renderable.html
--min-version 1.0.2 --max-version 1.9.9 gl-get-tex-parameter.html
gl-pixelstorei.html
gl-teximage.html
Expand Down
378 changes: 378 additions & 0 deletions sdk/tests/conformance/textures/misc/format-filterable-renderable.html
Original file line number Diff line number Diff line change
@@ -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: true , unpack: ['RGB', 'UNSIGNED_BYTE'] },
RGBA4 : { filter: true, render: true , unpack: ['RGBA', 'UNSIGNED_SHORT_4_4_4_4'] },
RGB5_A1: { filter: true, render: true , unpack: ['RGBA', 'UNSIGNED_SHORT_5_5_5_1'] },
RGB565 : { filter: true, render: true , 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>
Loading