blob: bedf51a96ae51371252e9ba94e90c27452b17a64 [file] [log] [blame]
/*
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.
*/
function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) {
var wtu = WebGLTestUtils;
var tiu = TexImageUtils;
var gl = null;
var successfullyParsed = false;
var imageData = null;
var blackColor = [0, 0, 0];
var originalPixels = (function() {
// (red|green|blue|cyan)(opaque|transparent)
var ro = [255, 0, 0, 255]; var rt = [255, 0, 0, 0];
var go = [0, 255, 0, 255]; var gt = [0, 255, 0, 0];
var bo = [0, 0, 255, 255]; var bt = [0, 0, 255, 0];
var co = [0, 255, 255, 255]; var ct = [0, 255, 255, 0];
return [ro, rt, go, gt,
ro, rt, go, gt,
bo, bt, co, ct,
bo, bt, co, ct];
})();
function init()
{
description('Verify texImage3D and texSubImage3D code paths taking ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')');
// Set the default context version while still allowing the webglVersion URL query string to override it.
wtu.setDefault3DContextVersion(defaultContextVersion);
gl = wtu.create3DContext("example");
if (!prologue(gl)) {
finishTest();
return;
}
gl.clearColor(0,0,0,1);
gl.clearDepth(1);
gl.disable(gl.BLEND);
var canvas2d = document.getElementById("texcanvas");
var context2d = canvas2d.getContext("2d");
imageData = context2d.createImageData(4, 4);
var data = imageData.data;
for (var i = 0; i < originalPixels.length; i++) {
data.set(originalPixels[i], 4 * i);
}
runTest();
}
function runOneIteration(useTexSubImage3D, flipY, premultiplyAlpha, bindingTarget,
depth, sourceSubRectangle, rTexCoord, program)
{
var expected = simulate(flipY, premultiplyAlpha, depth, sourceSubRectangle, rTexCoord);
var sourceSubRectangleString = '';
if (sourceSubRectangle) {
sourceSubRectangleString = ', sourceSubRectangle=' + sourceSubRectangle;
sourceSubRectangleString += ', rTexCoord=' + rTexCoord;
}
debug('');
debug('Testing ' + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') +
' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha +
', bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') +
sourceSubRectangleString);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Enable writes to the RGBA channels
gl.colorMask(1, 1, 1, 0);
var texture = gl.createTexture();
// Bind the texture to texture unit 0
gl.bindTexture(bindingTarget, texture);
// Set up texture parameters
gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
// Set up pixel store parameters
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
var uploadWidth = imageData.width;
var uploadHeight = imageData.height;
if (sourceSubRectangle) {
gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]);
gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]);
uploadWidth = sourceSubRectangle[2];
uploadHeight = sourceSubRectangle[3];
}
// Upload the image into the texture
if (useTexSubImage3D) {
// Initialize the texture to black first
gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0,
gl[pixelFormat], gl[pixelType], null);
gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, uploadWidth, uploadHeight, depth,
gl[pixelFormat], gl[pixelType], imageData);
} else {
gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0,
gl[pixelFormat], gl[pixelType], imageData);
}
gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0);
gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture upload");
var tl = expected[0][0];
var tr = expected[0][1];
var bl = expected[1][0];
var br = expected[1][1];
var rCoordLocation = gl.getUniformLocation(program, 'uRCoord');
if (!rCoordLocation) {
testFailed("Shader incorrectly set up; couldn't find uRCoord uniform");
return;
}
gl.uniform1f(rCoordLocation, rTexCoord);
// Draw the triangles
wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]);
var width = gl.canvas.width;
var halfWidth = Math.floor(width / 2);
var height = gl.canvas.height;
var halfHeight = Math.floor(height / 2);
var top = 0;
var bottom = height - halfHeight;
var left = 0;
var right = width - halfWidth;
debug("Checking pixel values");
debug("Expecting: " + expected);
var expectedH = expected.length;
var expectedW = expected[0].length;
var texelH = Math.floor(gl.canvas.height / expectedH);
var texelW = Math.floor(gl.canvas.width / expectedW);
// For each entry of the expected[][] array, check the appropriate
// canvas rectangle for correctness.
for (var row = 0; row < expectedH; row++) {
var y = row * texelH;
for (var col = 0; col < expectedW; col++) {
var x = col * texelW;
var val = expected[row][col];
wtu.checkCanvasRect(gl, x, y, texelW, texelH, val, "should be " + val);
}
}
}
function runTest()
{
var program = tiu.setupTexturedQuadWith3D(gl, internalFormat);
runTestOnBindingTarget(gl.TEXTURE_3D, program);
program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat);
runTestOnBindingTarget(gl.TEXTURE_2D_ARRAY, program);
debug("");
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
finishTest();
}
function simulate(flipY, premultiplyAlpha, depth, sourceSubRectangle, rTexCoord) {
var ro = [255, 0, 0]; var rt = premultiplyAlpha ? [0, 0, 0] : [255, 0, 0];
var go = [0, 255, 0]; var gt = premultiplyAlpha ? [0, 0, 0] : [0, 255, 0];
var bo = [0, 0, 255]; var bt = premultiplyAlpha ? [0, 0, 0] : [0, 0, 255];
var co = [0, 255, 255]; var ct = premultiplyAlpha ? [0, 0, 0] : [0, 255, 255];
var expected = [[ro, rt, go, gt],
[ro, rt, go, gt],
[bo, bt, co, ct],
[bo, bt, co, ct]];
switch (gl[pixelFormat]) {
case gl.RED:
case gl.RED_INTEGER:
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 4; col++) {
expected[row][col][1] = 0; // zero the green channel
}
}
// fall-through
case gl.RG:
case gl.RG_INTEGER:
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 4; col++) {
expected[row][col][2] = 0; // zero the blue channel
}
}
break;
default:
break;
}
if (flipY) {
expected.reverse();
}
if (sourceSubRectangle) {
let expected2 = [];
for (var row = 0; row < sourceSubRectangle[3]; row++) {
expected2[row] = [];
for (var col = 0; col < sourceSubRectangle[2]; col++) {
expected2[row][col] =
expected[sourceSubRectangle[1] + row + rTexCoord * sourceSubRectangle[3]][sourceSubRectangle[0] + col];
}
}
expected = expected2;
}
return expected;
}
function runTestOnBindingTarget(bindingTarget, program) {
var rects = [
undefined,
[0, 0, 2, 2],
[2, 0, 2, 2],
];
var dbg = false; // Set to true for debug output images
if (dbg) {
(function() {
debug("");
debug("Original ImageData (transparent pixels appear black):");
var cvs = document.createElement("canvas");
cvs.width = 4;
cvs.height = 4;
cvs.style.width = "32px";
cvs.style.height = "32px";
cvs.style.imageRendering = "pixelated";
cvs.style.background = "#000";
var ctx = cvs.getContext("2d");
ctx.putImageData(imageData, 0, 0);
var output = document.getElementById("console");
output.appendChild(cvs);
})();
}
for (const sub of [false, true]) {
for (const flipY of [false, true]) {
for (const premul of [false, true]) {
for (let irect = 0; irect < rects.length; irect++) {
var rect = rects[irect];
let depth = rect ? 2 : 1;
for (let rTexCoord = 0; rTexCoord < depth; rTexCoord++) {
// TODO: add tests for UNPACK_IMAGE_HEIGHT.
runOneIteration(sub, flipY, premul, bindingTarget,
depth, rect, rTexCoord, program);
if (dbg) {
debug("Actual:");
var img = document.createElement("img");
img.src = gl.canvas.toDataURL("image/png");
var output = document.getElementById("console");
output.appendChild(img);
}
}
}
}
}
}
}
return init;
}