/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES Utilities * ------------------------------------------------ * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ 'use strict'; goog.provide('functional.gles3.es3fTextureFilteringTests'); goog.require('framework.common.tcuImageCompare'); goog.require('framework.common.tcuLogImage'); goog.require('framework.common.tcuPixelFormat'); goog.require('framework.common.tcuRGBA'); goog.require('framework.common.tcuSurface'); goog.require('framework.common.tcuTestCase'); goog.require('framework.common.tcuTexLookupVerifier'); goog.require('framework.common.tcuTexture'); goog.require('framework.common.tcuTextureUtil'); goog.require('framework.delibs.debase.deMath'); goog.require('framework.delibs.debase.deRandom'); goog.require('framework.delibs.debase.deString'); goog.require('framework.opengl.gluShaderUtil'); goog.require('framework.opengl.gluTexture'); goog.require('framework.opengl.gluTextureUtil'); goog.require('functional.gles3.es3fFboTestUtil'); goog.require('modules.shared.glsTextureTestUtil'); goog.scope(function() { var es3fTextureFilteringTests = functional.gles3.es3fTextureFilteringTests; var es3fFboTestUtil = functional.gles3.es3fFboTestUtil; var gluShaderUtil = framework.opengl.gluShaderUtil; var gluTextureUtil = framework.opengl.gluTextureUtil; var tcuImageCompare = framework.common.tcuImageCompare; var tcuLogImage = framework.common.tcuLogImage; var tcuPixelFormat = framework.common.tcuPixelFormat; var tcuRGBA = framework.common.tcuRGBA; var tcuTestCase = framework.common.tcuTestCase; var tcuTexLookupVerifier = framework.common.tcuTexLookupVerifier; var tcuSurface = framework.common.tcuSurface; var tcuTexture = framework.common.tcuTexture; var tcuTextureUtil = framework.common.tcuTextureUtil; var deMath = framework.delibs.debase.deMath; var deString = framework.delibs.debase.deString; var deRandom = framework.delibs.debase.deRandom; var gluTexture = framework.opengl.gluTexture; var glsTextureTestUtil = modules.shared.glsTextureTestUtil; /** @type {WebGL2RenderingContext} */ var gl; es3fTextureFilteringTests.version = gluShaderUtil.getGLSLVersionString(gluShaderUtil.GLSLVersion.V300_ES); let canvasWH = 256; let viewportWH = 64; if (tcuTestCase.isQuickMode()) { canvasWH = 64; viewportWH = 32; } const TEX2D_VIEWPORT_WIDTH = viewportWH; const TEX2D_VIEWPORT_HEIGHT = viewportWH; const TEX2D_MIN_VIEWPORT_WIDTH = viewportWH; const TEX2D_MIN_VIEWPORT_HEIGHT = viewportWH; const TEX3D_VIEWPORT_WIDTH = viewportWH; const TEX3D_VIEWPORT_HEIGHT = viewportWH; const TEX3D_MIN_VIEWPORT_WIDTH = viewportWH; const TEX3D_MIN_VIEWPORT_HEIGHT = viewportWH; /** * @constructor * @extends {tcuTestCase.DeqpTest} */ es3fTextureFilteringTests.TextureFilteringTests = function() { tcuTestCase.DeqpTest.call(this, 'filtering', 'Texture Filtering Tests'); }; es3fTextureFilteringTests.TextureFilteringTests.prototype = Object.create(tcuTestCase.DeqpTest.prototype); es3fTextureFilteringTests.TextureFilteringTests.prototype.constructor = es3fTextureFilteringTests.TextureFilteringTests; /** * @constructor * @extends {tcuTestCase.DeqpTest} * @param {string} name * @param {string} desc * @param {number} minFilter * @param {number} magFilter * @param {number} wrapS * @param {number} wrapT * @param {number} internalFormat * @param {number} width * @param {number} height */ es3fTextureFilteringTests.Texture2DFilteringCase = function( name, desc, minFilter, magFilter, wrapS, wrapT, internalFormat, width, height ) { tcuTestCase.DeqpTest.call(this, name, desc); this.m_minFilter = minFilter; this.m_magFilter = magFilter; this.m_wrapS = wrapS; this.m_wrapT = wrapT; this.m_internalFormat = internalFormat; this.m_width = width; this.m_height = height; /** @type {glsTextureTestUtil.TextureRenderer} */ this.m_renderer = new glsTextureTestUtil.TextureRenderer( es3fTextureFilteringTests.version, gluShaderUtil.precision.PRECISION_HIGHP ); this.m_caseNdx = 0; /** @type {Array} */ this.m_textures = []; this.m_cases = []; }; es3fTextureFilteringTests.Texture2DFilteringCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype); es3fTextureFilteringTests.Texture2DFilteringCase.prototype.constructor = es3fTextureFilteringTests.Texture2DFilteringCase; /** * @constructor * @param {gluTexture.Texture2D} tex_ * @param {Array} minCoord_ * @param {Array} maxCoord_ */ es3fTextureFilteringTests.Texture2DFilteringCase.FilterCase = function( tex_, minCoord_, maxCoord_ ) { this.texture = tex_; this.minCoord = minCoord_; this.maxCoord = maxCoord_; }; /** @typedef {{texNdx: number, lodX: number, * lodY: number, oX: number, oY: number}} */ es3fTextureFilteringTests.Cases; /** * init */ es3fTextureFilteringTests.Texture2DFilteringCase.prototype.init = function() { try { // Create 2 textures. for (var ndx = 0; ndx < 2; ndx++) this.m_textures.push( gluTexture.texture2DFromInternalFormat( gl, this.m_internalFormat, this.m_width, this.m_height ) ); var mipmaps = true; var numLevels = mipmaps ? deMath.logToFloor( Math.max(this.m_width, this.m_height) ) + 1 : 1; /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo( this.m_textures[0].getRefTexture().getFormat() ); /** @type {Array} */ var cBias = fmtInfo.valueMin; /** @type {Array} */ var cScale = deMath.subtract( fmtInfo.valueMax, fmtInfo.valueMin ); // Fill first gradient texture. for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { /** @type {Array} */ var gMin = deMath.add( deMath.multiply([0.0, 0.0, 0.0, 1.0], cScale), cBias ); /** @type {Array} */ var gMax = deMath.add( deMath.multiply([1.0, 1.0, 1.0, 0.0], cScale), cBias ); this.m_textures[0].getRefTexture().allocLevel(levelNdx); tcuTextureUtil.fillWithComponentGradients( this.m_textures[0].getRefTexture().getLevel(levelNdx), gMin, gMax ); } // Fill second with grid texture. for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { /** @type {number} */ var step = 0x00ffffff / numLevels; /** @type {number} */ var rgb = step * levelNdx; /** @type {number} */ var colorA = deMath.binaryOp( 0xff000000, rgb, deMath.BinaryOp.OR ); /** @type {number} */ var colorB = deMath.binaryOp( 0xff000000, deMath.binaryNot(rgb), deMath.BinaryOp.OR ); this.m_textures[1].getRefTexture().allocLevel(levelNdx); tcuTextureUtil.fillWithGrid( this.m_textures[1].getRefTexture().getLevel(levelNdx), 4, deMath.add(deMath.multiply( tcuRGBA.newRGBAFromValue(colorA).toVec(), cScale), cBias ), deMath.add(deMath.multiply( tcuRGBA.newRGBAFromValue(colorB).toVec(), cScale), cBias ) ); } // Upload. for (var i = 0; i < this.m_textures.length; i++) this.m_textures[i].upload(); // Compute cases. /** @type {Array} */ var cases = [{ texNdx: 0, lodX: 1.6, lodY: 2.9, oX: -1.0, oY: -2.7 }, { texNdx: 0, lodX: -2.0, lodY: -1.35, oX: -0.2, oY: 0.7 }, { texNdx: 1, lodX: 0.14, lodY: 0.275, oX: -1.5, oY: -1.1 }, { texNdx: 1, lodX: -0.92, lodY: -2.64, oX: 0.4, oY: -0.1 } ]; var viewportW = Math.min( TEX2D_VIEWPORT_WIDTH, gl.canvas.width ); var viewportH = Math.min( TEX2D_VIEWPORT_HEIGHT, gl.canvas.height ); for (var caseNdx = 0; caseNdx < cases.length; caseNdx++) { /** @type {number} */ var texNdx = deMath.clamp( cases[caseNdx].texNdx, 0, this.m_textures.length - 1 ); /** @type {number} */ var lodX = cases[caseNdx].lodX; /** @type {number} */ var lodY = cases[caseNdx].lodY; /** @type {number} */ var oX = cases[caseNdx].oX; /** @type {number} */ var oY = cases[caseNdx].oY; /** @type {number} */ var sX = Math.exp(lodX * Math.log(2)) * viewportW / this.m_textures[texNdx].getRefTexture().getWidth(); /** @type {number} */ var sY = Math.exp(lodY * Math.log(2)) * viewportH / this.m_textures[texNdx].getRefTexture().getHeight(); this.m_cases.push( new es3fTextureFilteringTests.Texture2DFilteringCase.FilterCase( this.m_textures[texNdx], [oX, oY], [oX + sX, oY + sY] ) ); } this.m_caseNdx = 0; } catch (e) { // Clean up to save memory. this.deinit(); throw e; } }; /** * deinit */ es3fTextureFilteringTests.Texture2DFilteringCase.prototype.deinit = function() { while (this.m_textures.length > 0) { gl.deleteTexture(this.m_textures[0].getGLTexture()); this.m_textures.splice(0, 1); } }; /** * @return {tcuTestCase.IterateResult} */ es3fTextureFilteringTests.Texture2DFilteringCase.prototype.iterate = function() { /** @type {glsTextureTestUtil.RandomViewport} */ var viewport = new glsTextureTestUtil.RandomViewport( gl.canvas, TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT, deMath.binaryOp( deString.deStringHash(this.fullName()), deMath.deMathHash(this.m_caseNdx), deMath.BinaryOp.XOR ) ); /** @type {tcuTexture.TextureFormat} */ var texFmt = this.m_textures[0].getRefTexture().getFormat(); /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo(texFmt); var curCase = this.m_cases[this.m_caseNdx]; bufferedLogToConsole('Test ' + this.m_caseNdx); var refParams = new glsTextureTestUtil.ReferenceParams( glsTextureTestUtil.textureType.TEXTURETYPE_2D ); var rendered = new tcuSurface.Surface(viewport.width, viewport.height); var texCoord = [0, 0]; if (viewport.width < TEX2D_MIN_VIEWPORT_WIDTH || viewport.height < TEX2D_MIN_VIEWPORT_HEIGHT) throw new Error('Too small render target'); // Setup params for reference. refParams.sampler = gluTextureUtil.mapGLSamplerWrapST( this.m_wrapS, this.m_wrapT, this.m_minFilter, this.m_magFilter ); refParams.samplerType = glsTextureTestUtil.getSamplerType(texFmt); refParams.lodMode = glsTextureTestUtil.lodMode.EXACT; refParams.colorBias = fmtInfo.lookupBias; refParams.colorScale = fmtInfo.lookupScale; // Compute texture coordinates. bufferedLogToConsole( 'Texture coordinates: ' + curCase.minCoord + ' -> ' + curCase.maxCoord ); texCoord = glsTextureTestUtil.computeQuadTexCoord2D( curCase.minCoord, curCase.maxCoord ); gl.bindTexture(gl.TEXTURE_2D, curCase.texture.getGLTexture()); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.m_minFilter ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.m_magFilter ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.m_wrapS); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.m_wrapT); gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); this.m_renderer.renderQuad(0, texCoord, refParams); rendered.readViewport( gl, [viewport.x, viewport.y, viewport.width, viewport.height] ); /** @type {boolean} */ var isNearestOnly = this.m_minFilter == gl.NEAREST && this.m_magFilter == gl.NEAREST; /** @type {tcuPixelFormat.PixelFormat} */ var pixelFormat = tcuPixelFormat.PixelFormatFromContext(gl); //(iVec4) var colorBits = deMath.max( deMath.addScalar( glsTextureTestUtil.getBitsVec(pixelFormat), // 1 inaccurate bit if nearest only, 2 otherwise -1 * (isNearestOnly ? 1 : 2) ), [0, 0, 0, 0] ); /** @type {tcuTexLookupVerifier.LodPrecision} */ var lodPrecision = new tcuTexLookupVerifier.LodPrecision(); /** @type {tcuTexLookupVerifier.LookupPrecision} */ var lookupPrecision = new tcuTexLookupVerifier.LookupPrecision(); lodPrecision.derivateBits = 18; lodPrecision.lodBits = 6; lookupPrecision.colorThreshold = deMath.divide( tcuTexLookupVerifier.computeFixedPointThreshold(colorBits), refParams.colorScale ); lookupPrecision.coordBits = [20, 20, 0]; lookupPrecision.uvwBits = [7, 7, 0]; lookupPrecision.colorMask = glsTextureTestUtil.getCompareMask(pixelFormat); var isHighQuality = glsTextureTestUtil.verifyTexture2DResult( rendered.getAccess(), curCase.texture.getRefTexture(), texCoord, refParams, lookupPrecision, lodPrecision, pixelFormat ); if (!isHighQuality) { // Evaluate against lower precision requirements. lodPrecision.lodBits = 4; lookupPrecision.uvwBits = [4, 4, 0]; bufferedLogToConsole('Warning: Verification against high ' + 'precision requirements failed, trying with lower ' + 'requirements.' ); var isOk = glsTextureTestUtil.verifyTexture2DResult( rendered.getAccess(), curCase.texture.getRefTexture(), texCoord, refParams, lookupPrecision, lodPrecision, pixelFormat ); if (!isOk) { bufferedLogToConsole( 'ERROR: Verification against low ' + 'precision requirements failed, failing test case.' ); testFailedOptions('Image verification failed', false); //In JS version, one mistake and you're out return tcuTestCase.IterateResult.STOP; } else checkMessage( false, 'Low-quality filtering result in iteration no. ' + this.m_caseNdx ); } this.m_caseNdx += 1; if (this.m_caseNdx < this.m_cases.length) return tcuTestCase.IterateResult.CONTINUE; testPassed('Verified'); return tcuTestCase.IterateResult.STOP; }; /** * @constructor * @extends {tcuTestCase.DeqpTest} * @param {string} name * @param {string} desc * @param {number} minFilter * @param {number} magFilter * @param {number} wrapS * @param {number} wrapT * @param {boolean} onlySampleFaceInterior * @param {number} internalFormat * @param {number} width * @param {number} height */ es3fTextureFilteringTests.TextureCubeFilteringCase = function( name, desc, minFilter, magFilter, wrapS, wrapT, onlySampleFaceInterior, internalFormat, width, height ) { tcuTestCase.DeqpTest.call(this, name, desc); this.m_minFilter = minFilter; this.m_magFilter = magFilter; this.m_wrapS = wrapS; this.m_wrapT = wrapT; /** @type {boolean}*/ this.m_onlySampleFaceInterior = onlySampleFaceInterior; this.m_internalFormat = internalFormat; this.m_width = width; this.m_height = height; /** @type {glsTextureTestUtil.TextureRenderer} */ this.m_renderer = new glsTextureTestUtil.TextureRenderer( es3fTextureFilteringTests.version, gluShaderUtil.precision.PRECISION_HIGHP ); this.m_caseNdx = 0; /** @type {Array} */ this.m_textures = []; /** @type {Array} */ this.m_cases = []; }; /** * @constructor * @param {gluTexture.TextureCube} tex_ * @param {Array} bottomLeft_ * @param {Array} topRight_ */ es3fTextureFilteringTests.TextureCubeFilteringCase.FilterCase = function( tex_, bottomLeft_, topRight_ ) { this.texture = tex_; this.bottomLeft = bottomLeft_; this.topRight = topRight_; }; es3fTextureFilteringTests.TextureCubeFilteringCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype); es3fTextureFilteringTests.TextureCubeFilteringCase.prototype.constructor = es3fTextureFilteringTests.TextureCubeFilteringCase; /** * init */ es3fTextureFilteringTests.TextureCubeFilteringCase.prototype.init = function() { try { assertMsgOptions( this.m_width == this.m_height, 'Texture has to be squared', false, true ); for (var ndx = 0; ndx < 2; ndx++) this.m_textures.push(gluTexture.cubeFromInternalFormat( gl, this.m_internalFormat, this.m_width )); var numLevels = deMath.logToFloor( Math.max(this.m_width, this.m_height) ) + 1; /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo( this.m_textures[0].getRefTexture().getFormat() ); /** @type {Array} */ var cBias = fmtInfo.valueMin; /** @type {Array} */ var cScale = deMath.subtract( fmtInfo.valueMax, fmtInfo.valueMin ); // Fill first with gradient texture. /** @type {Array>>} * (array of 4 component vectors) */ var gradients = [ [ // negative x [0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0, 0.0] ], [ // positive x [0.5, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0, 0.0] ], [ // negative y [0.0, 0.5, 0.0, 1.0], [1.0, 1.0, 1.0, 0.0] ], [ // positive y [0.0, 0.0, 0.5, 1.0], [1.0, 1.0, 1.0, 0.0] ], [ // negative z [0.0, 0.0, 0.0, 0.5], [1.0, 1.0, 1.0, 1.0] ], [ // positive z [0.5, 0.5, 0.5, 1.0], [1.0, 1.0, 1.0, 0.0] ] ]; for (var face = 0; face < Object.keys(tcuTexture.CubeFace).length; face++) { for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { this.m_textures[0].getRefTexture().allocLevel( face, levelNdx ); tcuTextureUtil.fillWithComponentGradients( this.m_textures[0].getRefTexture().getLevelFace( levelNdx, face ), deMath.add(deMath.multiply( gradients[face][0], cScale ), cBias), deMath.add(deMath.multiply( gradients[face][1], cScale ), cBias) ); } } // Fill second with grid texture. for (var face = 0; face < Object.keys(tcuTexture.CubeFace).length; face++) { for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { var step = 0x00ffffff / ( numLevels * Object.keys(tcuTexture.CubeFace).length ); var rgb = step * levelNdx * face; /** @type {number} */ var colorA = deMath.binaryOp( 0xff000000, rgb, deMath.BinaryOp.OR ); /** @type {number} */ var colorB = deMath.binaryOp( 0xff000000, deMath.binaryNot(rgb), deMath.BinaryOp.OR ); this.m_textures[1].getRefTexture().allocLevel( face, levelNdx ); tcuTextureUtil.fillWithGrid( this.m_textures[1].getRefTexture().getLevelFace( levelNdx, face ), 4, deMath.add( deMath.multiply( tcuRGBA.newRGBAFromValue(colorA).toVec(), cScale ), cBias ), deMath.add( deMath.multiply( tcuRGBA.newRGBAFromValue(colorB).toVec(), cScale ), cBias ) ); } } // Upload. for (var i = 0; i < this.m_textures.length; i++) this.m_textures[i].upload(); // Compute cases /** @type {gluTexture.TextureCube} */ var tex0 = this.m_textures[0]; /** @type {gluTexture.TextureCube} */ var tex1 = this.m_textures.length > 1 ? this.m_textures[1] : tex0; if (this.m_onlySampleFaceInterior) { // minification this.m_cases.push(new es3fTextureFilteringTests. TextureCubeFilteringCase.FilterCase( tex0, [-0.8, -0.8], [0.8, 0.8] )); // magnification this.m_cases.push(new es3fTextureFilteringTests. TextureCubeFilteringCase.FilterCase( tex0, [0.5, 0.65], [0.8, 0.8] )); // minification this.m_cases.push(new es3fTextureFilteringTests. TextureCubeFilteringCase.FilterCase( tex1, [-0.8, -0.8], [0.8, 0.8] )); // magnification this.m_cases.push(new es3fTextureFilteringTests. TextureCubeFilteringCase.FilterCase( tex1, [0.2, 0.2], [0.6, 0.5] )); } else { // minification if (gl.getParameter(gl.SAMPLES) == 0) this.m_cases.push( new es3fTextureFilteringTests.TextureCubeFilteringCase. FilterCase( tex0, [-1.25, -1.2], [1.2, 1.25] ) ); // minification - w/ tweak to avoid hitting triangle // edges with face switchpoint. else this.m_cases.push( new es3fTextureFilteringTests.TextureCubeFilteringCase. FilterCase( tex0, [-1.19, -1.3], [1.1, 1.35] ) ); // magnification this.m_cases.push( new es3fTextureFilteringTests.TextureCubeFilteringCase. FilterCase( tex0, [0.8, 0.8], [1.25, 1.20] ) ); // minification this.m_cases.push( new es3fTextureFilteringTests.TextureCubeFilteringCase. FilterCase( tex1, [-1.19, -1.3], [1.1, 1.35] ) ); // magnification this.m_cases.push( new es3fTextureFilteringTests.TextureCubeFilteringCase. FilterCase( tex1, [-1.2, -1.1], [-0.8, -0.8] ) ); } this.m_caseNdx = 0; } catch (e) { // Clean up to save memory. this.deinit(); throw e; } }; /** * deinit */ es3fTextureFilteringTests.TextureCubeFilteringCase.prototype.deinit = function() { while (this.m_textures.length > 0) { gl.deleteTexture(this.m_textures[0].getGLTexture()); this.m_textures.splice(0, 1); } }; /** * @param {tcuTexture.CubeFace} face * @return {string} */ es3fTextureFilteringTests.getFaceDesc = function(face) { switch (face) { case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X: return '-X'; case tcuTexture.CubeFace.CUBEFACE_POSITIVE_X: return '+X'; case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y: return '-Y'; case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y: return '+Y'; case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z: return '-Z'; case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z: return '+Z'; default: throw new Error('Invalid cube face specified'); } }; /** * @return {tcuTestCase.IterateResult} */ es3fTextureFilteringTests.TextureCubeFilteringCase.prototype.iterate = function() { var viewportSize = 28; /** @type {glsTextureTestUtil.RandomViewport} */ var viewport = new glsTextureTestUtil.RandomViewport( gl.canvas, viewportSize, viewportSize, deMath.binaryOp( deString.deStringHash(this.fullName()), deMath.deMathHash(this.m_caseNdx), deMath.BinaryOp.XOR ) ); bufferedLogToConsole('Test' + this.m_caseNdx); /** @type {es3fTextureFilteringTests. * TextureCubeFilteringCase.FilterCase} */ var curCase = this.m_cases[this.m_caseNdx]; /** @type {tcuTexture.TextureFormat} */ var texFmt = curCase.texture.getRefTexture().getFormat(); /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo(texFmt); /** @type {glsTextureTestUtil.ReferenceParams} */ var sampleParams = new glsTextureTestUtil.ReferenceParams( glsTextureTestUtil.textureType.TEXTURETYPE_CUBE ); if (viewport.width < viewportSize || viewport.height < viewportSize) throw new Error('Too small render target'); // Setup texture gl.bindTexture(gl.TEXTURE_CUBE_MAP, curCase.texture.getGLTexture()); gl.texParameteri( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, this.m_minFilter ); gl.texParameteri( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, this.m_magFilter ); gl.texParameteri( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, this.m_wrapS ); gl.texParameteri( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, this.m_wrapT ); // Other state gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); // Params for reference computation. sampleParams.sampler = gluTextureUtil.mapGLSamplerWrapST( gl.CLAMP_TO_EDGE, gl.CLAMP_TO_EDGE, this.m_minFilter, this.m_magFilter ); sampleParams.sampler.seamlessCubeMap = true; sampleParams.samplerType = glsTextureTestUtil.getSamplerType(texFmt); sampleParams.colorBias = fmtInfo.lookupBias; sampleParams.colorScale = fmtInfo.lookupScale; sampleParams.lodMode = glsTextureTestUtil.lodMode.EXACT; bufferedLogToConsole( 'Coordinates: ' + curCase.bottomLeft + ' -> ' + curCase.topRight ); for (var faceNdx = 0; faceNdx < Object.keys(tcuTexture.CubeFace).length; faceNdx++) { var face = /** @type {tcuTexture.CubeFace} */ (faceNdx); /** @type {tcuSurface.Surface} */ var result = new tcuSurface.Surface( viewport.width, viewport.height ); /** @type {Array} */ var texCoord; texCoord = glsTextureTestUtil.computeQuadTexCoordCubeFace( face, curCase.bottomLeft, curCase.topRight ); bufferedLogToConsole( 'Face ' + es3fTextureFilteringTests.getFaceDesc(face) ); // \todo Log texture coordinates. this.m_renderer.renderQuad(0, texCoord, sampleParams); result.readViewport( gl, [viewport.x, viewport.y, viewport.width, viewport.height] ); /** @type {boolean} */ var isNearestOnly = this.m_minFilter == gl.NEAREST && this.m_magFilter == gl.NEAREST; /** @type {tcuPixelFormat.PixelFormat} */ var pixelFormat = tcuPixelFormat.PixelFormatFromContext(gl); //(iVec4) var colorBits = deMath.max( deMath.addScalar( glsTextureTestUtil.getBitsVec(pixelFormat), // 1 inaccurate bit if nearest only, 2 otherwise -1 * (isNearestOnly ? 1 : 2) ), [0, 0, 0, 0] ); /** @type {tcuTexLookupVerifier.LodPrecision} */ var lodPrecision = new tcuTexLookupVerifier.LodPrecision(); /** @type {tcuTexLookupVerifier.LookupPrecision} */ var lookupPrecision = new tcuTexLookupVerifier.LookupPrecision(); lodPrecision.derivateBits = 10; lodPrecision.lodBits = 5; lookupPrecision.colorThreshold = deMath.divide( tcuTexLookupVerifier.computeFixedPointThreshold(colorBits), sampleParams.colorScale ); lookupPrecision.coordBits = [10, 10, 10]; lookupPrecision.uvwBits = [6, 6, 0]; lookupPrecision.colorMask = glsTextureTestUtil.getCompareMask(pixelFormat); var isHighQuality = glsTextureTestUtil.verifyTextureCubeResult( result.getAccess(), curCase.texture.getRefTexture(), texCoord, sampleParams, lookupPrecision, lodPrecision, pixelFormat ); if (!isHighQuality) { // Evaluate against lower precision requirements. lodPrecision.lodBits = 2; lookupPrecision.uvwBits = [3, 3, 0]; bufferedLogToConsole('Warning: Verification against high ' + 'precision requirements failed, trying with lower ' + 'requirements.'); var isOk = glsTextureTestUtil.verifyTextureCubeResult( result.getAccess(), curCase.texture.getRefTexture(), texCoord, sampleParams, lookupPrecision, lodPrecision, pixelFormat ); if (!isOk) { bufferedLogToConsole('ERROR: Verification against low' + 'precision requirements failed, failing test case.'); testFailedOptions('Image verification failed', false); //In JS version, one mistake and you're out return tcuTestCase.IterateResult.STOP; } else checkMessage( false, 'Low-quality filtering result in iteration no. ' + this.m_caseNdx ); } } this.m_caseNdx += 1; if (this.m_caseNdx < this.m_cases.length) return tcuTestCase.IterateResult.CONTINUE; testPassed('Verified'); return tcuTestCase.IterateResult.STOP; }; // 2D array filtering /** * @constructor * @extends {tcuTestCase.DeqpTest} * @param {string} name * @param {string} desc * @param {number} minFilter * @param {number} magFilter * @param {number} wrapS * @param {number} wrapT * @param {number} internalFormat * @param {number} width * @param {number} height * @param {number} numLayers */ es3fTextureFilteringTests.Texture2DArrayFilteringCase = function( name, desc, minFilter, magFilter, wrapS, wrapT, internalFormat, width, height, numLayers ) { tcuTestCase.DeqpTest.call(this, name, desc); this.m_minFilter = minFilter; this.m_magFilter = magFilter; this.m_wrapS = wrapS; this.m_wrapT = wrapT; this.m_internalFormat = internalFormat; this.m_width = width; this.m_height = height; this.m_numLayers = numLayers; this.m_gradientTex = null; this.m_gridTex = null; /** @type {glsTextureTestUtil.TextureRenderer} */ this.m_renderer = new glsTextureTestUtil.TextureRenderer( es3fTextureFilteringTests.version, gluShaderUtil.precision.PRECISION_HIGHP ); this.m_textures = []; this.m_caseNdx = 0; this.m_cases = []; }; es3fTextureFilteringTests.Texture2DArrayFilteringCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype); es3fTextureFilteringTests.Texture2DArrayFilteringCase.prototype. constructor = es3fTextureFilteringTests.Texture2DArrayFilteringCase; /** * @constructor * @param {gluTexture.Texture2DArray} tex_ * @param {Array} lod_ * @param {Array} offset_ * @param {Array} layerRange_ */ es3fTextureFilteringTests.Texture2DArrayFilteringCase.FilterCase = function( tex_, lod_, offset_, layerRange_ ) { this.texture = tex_; this.lod = lod_; this.offset = offset_; this.layerRange = layerRange_; }; /* * init */ es3fTextureFilteringTests.Texture2DArrayFilteringCase.prototype.init = function() { try { /** @type {tcuTexture.TextureFormat} */ var texFmt = gluTextureUtil.mapGLInternalFormat( this.m_internalFormat ); /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo(texFmt); var cScale = deMath.subtract( fmtInfo.valueMax, fmtInfo.valueMin ); var cBias = fmtInfo.valueMin; var numLevels = deMath.logToFloor( Math.max(this.m_width, this.m_height) ) + 1; // Create textures. this.m_gradientTex = gluTexture.texture2DArrayFromInternalFormat( gl, this.m_internalFormat, this.m_width, this.m_height, this.m_numLayers ); this.m_gridTex = gluTexture.texture2DArrayFromInternalFormat( gl, this.m_internalFormat, this.m_width, this.m_height, this.m_numLayers ); var levelSwz = [ [0, 1, 2, 3], [2, 1, 3, 0], [3, 0, 1, 2], [1, 3, 2, 0] ]; // Fill first gradient texture // (gradient direction varies between layers). for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { this.m_gradientTex.getRefTexture().allocLevel(levelNdx); var levelBuf = this.m_gradientTex.getRefTexture().getLevel(levelNdx); for (var layerNdx = 0; layerNdx < this.m_numLayers; layerNdx++) { var swz = levelSwz[layerNdx % levelSwz.length]; var gMin = deMath.add(deMath.multiply(deMath.swizzle( [0.0, 0.0, 0.0, 1.0], [swz[0], swz[1], swz[2], swz[3]] ), cScale), cBias); var gMax = deMath.add(deMath.multiply(deMath.swizzle( [1.0, 1.0, 1.0, 0.0], [swz[0], swz[1], swz[2], swz[3]] ), cScale), cBias); tcuTextureUtil.fillWithComponentGradients2D( tcuTextureUtil.getSubregion( levelBuf, 0, 0, layerNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1 ), gMin, gMax ); } } // Fill second with grid texture (each layer has unique colors). for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { this.m_gridTex.getRefTexture().allocLevel(levelNdx); /** @type {tcuTexture.PixelBufferAccess} */ var levelBuf = this.m_gridTex.getRefTexture().getLevel(levelNdx); for ( var layerNdx = 0; layerNdx < this.m_numLayers; layerNdx++) { var step = 0x00ffffff / (numLevels * this.m_numLayers - 1); var rgb = step * (levelNdx + layerNdx * numLevels); /** @type {number} */ var colorA = deMath.binaryOp( 0xff000000, rgb, deMath.BinaryOp.OR ); /** @type {number} */ var colorB = deMath.binaryOp( 0xff000000, deMath.binaryNot(rgb), deMath.BinaryOp.OR ); tcuTextureUtil.fillWithGrid( tcuTextureUtil.getSubregion( levelBuf, 0, 0, layerNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1 ), 4, deMath.add( deMath.multiply( tcuRGBA.newRGBAFromValue(colorA).toVec(), cScale ), cBias ), deMath.add( deMath.multiply( tcuRGBA.newRGBAFromValue(colorB).toVec(), cScale ), cBias ) ); } } // Upload. this.m_gradientTex.upload(); this.m_gridTex.upload(); // Test cases this.m_cases.push( new es3fTextureFilteringTests. Texture2DArrayFilteringCase.FilterCase( this.m_gradientTex, [1.5, 2.8], [-1.0, -2.7], [-0.5, this.m_numLayers + 0.5] ) ); this.m_cases.push( new es3fTextureFilteringTests. Texture2DArrayFilteringCase.FilterCase( this.m_gridTex, [0.2, 0.175], [-2.0, -3.7], [-0.5, this.m_numLayers + 0.5] ) ); this.m_cases.push( new es3fTextureFilteringTests. Texture2DArrayFilteringCase.FilterCase( this.m_gridTex, [-0.8, -2.3], [0.2, -0.1], [this.m_numLayers + 0.5, -0.5] ) ); // Level rounding - only in single-sample configs as // multisample configs may produce smooth transition at the middle. if (gl.getParameter(gl.SAMPLES) == 0) this.m_cases.push( new es3fTextureFilteringTests. Texture2DArrayFilteringCase.FilterCase( this.m_gradientTex, [-2.0, -1.5], [-0.1, 0.9], [1.50001, 1.49999] ) ); this.m_caseNdx = 0; } catch (e) { // Clean up to save memory. this.deinit(); throw e; } }; /** * deinit */ es3fTextureFilteringTests.Texture2DArrayFilteringCase.prototype.deinit = function() { if (this.m_gradientTex) gl.deleteTexture(this.m_gradientTex.getGLTexture()); if (this.m_gridTex) gl.deleteTexture(this.m_gridTex.getGLTexture()); this.m_gradientTex = null; this.m_gridTex = null; }; /** * iterate * @return {tcuTestCase.IterateResult} */ es3fTextureFilteringTests.Texture2DArrayFilteringCase.prototype.iterate = function() { /** @type {glsTextureTestUtil.RandomViewport} */ var viewport = new glsTextureTestUtil.RandomViewport( gl.canvas, TEX3D_VIEWPORT_WIDTH, TEX3D_VIEWPORT_HEIGHT, deMath.binaryOp( deString.deStringHash(this.fullName()), deMath.deMathHash(this.m_caseNdx), deMath.BinaryOp.XOR ) ); /** @type {es3fTextureFilteringTests.Texture2DArrayFilteringCase. * FilterCase} */ var curCase = this.m_cases[this.m_caseNdx]; /** @type {tcuTexture.TextureFormat} */ var texFmt = curCase.texture.getRefTexture().getFormat(); /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo(texFmt); bufferedLogToConsole('Test' + this.m_caseNdx); /** @type {glsTextureTestUtil.ReferenceParams} */ var refParams = new glsTextureTestUtil.ReferenceParams( glsTextureTestUtil.textureType.TEXTURETYPE_2D_ARRAY ); /** @type {tcuSurface.Surface} */ var rendered = new tcuSurface.Surface(viewport.width, viewport.height); if (viewport.width < TEX3D_MIN_VIEWPORT_WIDTH || viewport.height < TEX3D_MIN_VIEWPORT_HEIGHT) throw new Error('Too small render target'); // Setup params for reference. refParams.sampler = gluTextureUtil.mapGLSampler( this.m_wrapS, this.m_wrapT, this.m_wrapT, this.m_minFilter, this.m_magFilter ); refParams.samplerType = glsTextureTestUtil.getSamplerType(texFmt); refParams.lodMode = glsTextureTestUtil.lodMode.EXACT; refParams.colorBias = fmtInfo.lookupBias; refParams.colorScale = fmtInfo.lookupScale; // Compute texture coordinates. bufferedLogToConsole( 'Approximate lod per axis = ' + curCase.lod + ', offset = ' + curCase.offset ); /** @type {number} */ var lodX = curCase.lod[0]; /** @type {number} */ var lodY = curCase.lod[1]; /** @type {number} */ var oX = curCase.offset[0]; /** @type {number} */ var oY = curCase.offset[1]; /** @type {number} */ var sX = Math.pow(2, lodX) * viewport.width / this.m_gradientTex.getRefTexture().getWidth(); /** @type {number} */ var sY = Math.pow(2, lodY) * viewport.height / this.m_gradientTex.getRefTexture().getHeight(); /** @type {number} */ var l0 = curCase.layerRange[0]; /** @type {number} */ var l1 = curCase.layerRange[1]; /** @type {Array}*/ var texCoord = [ oX, oY, l0, oX, oY + sY, l0 * 0.5 + l1 * 0.5, oX + sX, oY, l0 * 0.5 + l1 * 0.5, oX + sX, oY + sY, l1 ]; gl.bindTexture(gl.TEXTURE_2D_ARRAY, curCase.texture.getGLTexture()); gl.texParameteri( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, this.m_minFilter ); gl.texParameteri( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, this.m_magFilter ); gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, this.m_wrapS); gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, this.m_wrapT); gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); this.m_renderer.renderQuad( 0, texCoord, refParams ); rendered.readViewport( gl, [viewport.x, viewport.y, viewport.width, viewport.height] ); /** @type {boolean} */ var isNearestOnly = this.m_minFilter == gl.NEAREST && this.m_magFilter == gl.NEAREST; /** @type {tcuPixelFormat.PixelFormat} */ var pixelFormat = tcuPixelFormat.PixelFormatFromContext(gl); //(iVec4) var colorBits = deMath.max( deMath.addScalar( glsTextureTestUtil.getBitsVec(pixelFormat), // 1 inaccurate bit if nearest only, 2 otherwise -1 * (isNearestOnly ? 1 : 2) ), [0, 0, 0, 0] ); /** @type {tcuTexLookupVerifier.LodPrecision} */ var lodPrecision = new tcuTexLookupVerifier.LodPrecision(); /** @type {tcuTexLookupVerifier.LookupPrecision} */ var lookupPrecision = new tcuTexLookupVerifier.LookupPrecision(); lodPrecision.derivateBits = 18; lodPrecision.lodBits = 6; lookupPrecision.colorThreshold = deMath.divide( tcuTexLookupVerifier.computeFixedPointThreshold(colorBits), refParams.colorScale ); lookupPrecision.coordBits = [20, 20, 20]; lookupPrecision.uvwBits = [7, 7, 0]; lookupPrecision.colorMask = glsTextureTestUtil.getCompareMask(pixelFormat); var isHighQuality = glsTextureTestUtil.verifyTexture2DArrayResult( rendered.getAccess(), curCase.texture.getRefTexture().getView(), texCoord, refParams, lookupPrecision, lodPrecision, pixelFormat); if (!isHighQuality) { // Evaluate against lower precision requirements. lodPrecision.lodBits = 3; lookupPrecision.uvwBits = [3, 3, 0]; bufferedLogToConsole( 'Warning: Verification against high ' + 'precision requirements failed, ' + 'trying with lower requirements.' ); var isOk = glsTextureTestUtil.verifyTexture2DArrayResult( rendered.getAccess(), curCase.texture.getRefTexture().getView(), texCoord, refParams, lookupPrecision, lodPrecision, pixelFormat ); if (!isOk) { bufferedLogToConsole( 'ERROR: Verification against low precision requirements ' + 'failed, failing test case.' ); testFailedOptions('Image verification failed', false); //In JS version, one mistake and you're out return tcuTestCase.IterateResult.STOP; } else checkMessage( false, 'Low-quality filtering result in iteration no. ' + this.m_caseNdx ); } this.m_caseNdx += 1; if (this.m_caseNdx < this.m_cases.length) return tcuTestCase.IterateResult.CONTINUE; testPassed('Verified'); return tcuTestCase.IterateResult.STOP; }; // 3D filtering /** * @constructor * @extends {tcuTestCase.DeqpTest} * @param {string} name * @param {string} desc * @param {number} minFilter * @param {number} magFilter * @param {number} wrapS * @param {number} wrapT * @param {number} wrapR * @param {number} internalFormat * @param {number} width * @param {number} height * @param {number} depth */ es3fTextureFilteringTests.Texture3DFilteringCase = function( name, desc, minFilter, magFilter, wrapS, wrapT, wrapR, internalFormat, width, height, depth ) { tcuTestCase.DeqpTest.call(this, name, desc); this.m_minFilter = minFilter; this.m_magFilter = magFilter; this.m_wrapS = wrapS; this.m_wrapT = wrapT; this.m_wrapR = wrapR; this.m_internalFormat = internalFormat; this.m_width = width; this.m_height = height; this.m_depth = depth; this.m_gradientTex = null; this.m_gridTex = null; /** @type {glsTextureTestUtil.TextureRenderer} */ this.m_renderer = new glsTextureTestUtil.TextureRenderer( es3fTextureFilteringTests.version, gluShaderUtil.precision.PRECISION_HIGHP ); this.m_caseNdx = 0; this.m_cases = []; }; es3fTextureFilteringTests.Texture3DFilteringCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype); es3fTextureFilteringTests.Texture3DFilteringCase.prototype.constructor = es3fTextureFilteringTests.Texture3DFilteringCase; /** * @constructor * @param {gluTexture.Texture3D} tex_ * @param {Array} lod_ * @param {Array} offset_ */ es3fTextureFilteringTests.Texture3DFilteringCase.FilterCase = function( tex_, lod_, offset_ ) { this.texture = tex_; this.lod = lod_; this.offset = offset_; }; /** * init */ es3fTextureFilteringTests.Texture3DFilteringCase.prototype.init = function( ) { try { /** @type {tcuTexture.TextureFormat} */ var texFmt = gluTextureUtil.mapGLInternalFormat(this.m_internalFormat); /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo(texFmt); var cScale = deMath.subtract( fmtInfo.valueMax, fmtInfo.valueMin ); var cBias = fmtInfo.valueMin; var numLevels = deMath.logToFloor( Math.max(Math.max(this.m_width, this.m_height), this.m_depth) ) + 1; // Create textures. this.m_gradientTex = gluTexture.texture3DFromInternalFormat( gl, this.m_internalFormat, this.m_width, this.m_height, this.m_depth ); this.m_gridTex = gluTexture.texture3DFromInternalFormat( gl, this.m_internalFormat, this.m_width, this.m_height, this.m_depth ); // Fill first gradient texture. for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { var gMin = deMath.add( deMath.multiply([0.0, 0.0, 0.0, 1.0], cScale), cBias ); var gMax = deMath.add( deMath.multiply([1.0, 1.0, 1.0, 0.0], cScale), cBias ); this.m_gradientTex.getRefTexture().allocLevel(levelNdx); tcuTextureUtil.fillWithComponentGradients( this.m_gradientTex.getRefTexture().getLevel(levelNdx), gMin, gMax ); } // Fill second with grid texture. for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { /** @type {number} */ var step = 0x00ffffff / numLevels; /** @type {number} */ var rgb = step * levelNdx; /** @type {number} */ var colorA = deMath.binaryOp( 0xff000000, rgb, deMath.BinaryOp.OR ); /** @type {number} */ var colorB = deMath.binaryOp( 0xff000000, deMath.binaryNot(rgb), deMath.BinaryOp.OR ); this.m_gridTex.getRefTexture().allocLevel(levelNdx); tcuTextureUtil.fillWithGrid( this.m_gridTex.getRefTexture().getLevel(levelNdx), 4, deMath.add( deMath.multiply( tcuRGBA.newRGBAFromValue(colorA).toVec(), cScale ), cBias ), deMath.add( deMath.multiply( tcuRGBA.newRGBAFromValue(colorB).toVec(), cScale ), cBias ) ); } // Upload. this.m_gradientTex.upload(); this.m_gridTex.upload(); // Test cases this.m_cases.push( new es3fTextureFilteringTests.Texture3DFilteringCase.FilterCase( this.m_gradientTex, [1.5, 2.8, 1.0], [-1.0, -2.7, -2.275] ) ); this.m_cases.push( new es3fTextureFilteringTests.Texture3DFilteringCase.FilterCase( this.m_gradientTex, [-2.0, -1.5, -1.8], [-0.1, 0.9, -0.25] ) ); this.m_cases.push( new es3fTextureFilteringTests.Texture3DFilteringCase.FilterCase( this.m_gridTex, [0.2, 0.175, 0.3], [-2.0, -3.7, -1.825] ) ); this.m_cases.push( new es3fTextureFilteringTests.Texture3DFilteringCase.FilterCase( this.m_gridTex, [-0.8, -2.3, -2.5], [0.2, -0.1, 1.325] ) ); this.m_caseNdx = 0; } catch (e) { // Clean up to save memory. this.deinit(); throw e; } }; /** * deinit */ es3fTextureFilteringTests.Texture3DFilteringCase.prototype.deinit = function() { if (this.m_gradientTex) gl.deleteTexture(this.m_gradientTex.getGLTexture()); if (this.m_gridTex) gl.deleteTexture(this.m_gridTex.getGLTexture()); this.m_gradientTex = null; this.m_gridTex = null; }; /** * @return {tcuTestCase.IterateResult} */ es3fTextureFilteringTests.Texture3DFilteringCase.prototype.iterate = function() { /** @type {glsTextureTestUtil.RandomViewport} */ var viewport = new glsTextureTestUtil.RandomViewport( gl.canvas, TEX3D_VIEWPORT_WIDTH, TEX3D_VIEWPORT_HEIGHT, deMath.binaryOp( deString.deStringHash(this.fullName()), deMath.deMathHash(this.m_caseNdx), deMath.BinaryOp.XOR ) ); /** @type {es3fTextureFilteringTests.Texture3DFilteringCase.FilterCase} */ var curCase = this.m_cases[this.m_caseNdx]; /** @type {tcuTexture.TextureFormat} */ var texFmt = curCase.texture.getRefTexture().getFormat(); /** @type {tcuTextureUtil.TextureFormatInfo} */ var fmtInfo = tcuTextureUtil.getTextureFormatInfo(texFmt); bufferedLogToConsole('Test' + this.m_caseNdx); /** @type {glsTextureTestUtil.ReferenceParams} */ var refParams = new glsTextureTestUtil.ReferenceParams( glsTextureTestUtil.textureType.TEXTURETYPE_3D ); /** @type {tcuSurface.Surface} */ var rendered = new tcuSurface.Surface(viewport.width, viewport.height); /** @type {Array}*/ var texCoord = []; if (viewport.width < TEX3D_MIN_VIEWPORT_WIDTH || viewport.height < TEX3D_MIN_VIEWPORT_HEIGHT) throw new Error('Too small render target'); // Setup params for reference. refParams.sampler = gluTextureUtil.mapGLSampler( this.m_wrapS, this.m_wrapT, this.m_wrapR, this.m_minFilter, this.m_magFilter ); // Setup params for reference. refParams.samplerType = glsTextureTestUtil.getSamplerType(texFmt); refParams.lodMode = glsTextureTestUtil.lodMode.EXACT; refParams.colorBias = fmtInfo.lookupBias; refParams.colorScale = fmtInfo.lookupScale; // Compute texture coordinates. bufferedLogToConsole('Approximate lod per axis = ' + curCase.lod + ', offset = ' + curCase.offset); /** @type {number} */ var lodX = curCase.lod[0]; /** @type {number} */ var lodY = curCase.lod[1]; /** @type {number} */ var lodZ = curCase.lod[2]; /** @type {number} */ var oX = curCase.offset[0]; /** @type {number} */ var oY = curCase.offset[1]; /** @type {number} */ var oZ = curCase.offset[2]; /** @type {number} */ var sX = Math.pow(2, lodX) * viewport.width / this.m_gradientTex.getRefTexture().getWidth(); /** @type {number} */ var sY = Math.pow(2, lodY) * viewport.height / this.m_gradientTex.getRefTexture().getHeight(); /** @type {number} */ var sZ = Math.pow(2, lodZ) * Math.max(viewport.width, viewport.height) / this.m_gradientTex.getRefTexture().getDepth(); texCoord[0] = oX; texCoord[1] = oY; texCoord[2] = oZ; texCoord[3] = oX; texCoord[4] = oY + sY; texCoord[5] = oZ + sZ * 0.5; texCoord[6] = oX + sX; texCoord[7] = oY; texCoord[8] = oZ + sZ * 0.5; texCoord[9] = oX + sX; texCoord[10] = oY + sY; texCoord[11] = oZ + sZ; gl.bindTexture(gl.TEXTURE_3D, curCase.texture.getGLTexture()); gl.texParameteri( gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, this.m_minFilter ); gl.texParameteri( gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, this.m_magFilter ); gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_S, this.m_wrapS); gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_T, this.m_wrapT); gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, this.m_wrapR); gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); this.m_renderer.renderQuad(0, texCoord, refParams); rendered.readViewport( gl, [viewport.x, viewport.y, viewport.width, viewport.height] ); var isNearestOnly = this.m_minFilter == gl.NEAREST && this.m_magFilter == gl.NEAREST; /** @type {tcuPixelFormat.PixelFormat} */ var pixelFormat = tcuPixelFormat.PixelFormatFromContext(gl); //(iVec4) var colorBits = deMath.max( deMath.addScalar( glsTextureTestUtil.getBitsVec(pixelFormat), // 1 inaccurate bit if nearest only, 2 otherwise -1 * (isNearestOnly ? 1 : 2) ), [0, 0, 0, 0] ); /** @type {tcuTexLookupVerifier.LodPrecision} */ var lodPrecision = new tcuTexLookupVerifier.LodPrecision(); /** @type {tcuTexLookupVerifier.LookupPrecision} */ var lookupPrecision = new tcuTexLookupVerifier.LookupPrecision(); lodPrecision.derivateBits = 18; lodPrecision.lodBits = 6; lookupPrecision.colorThreshold = deMath.divide( tcuTexLookupVerifier.computeFixedPointThreshold(colorBits), refParams.colorScale ); lookupPrecision.coordBits = [20, 20, 20]; lookupPrecision.uvwBits = [7, 7, 7]; lookupPrecision.colorMask = glsTextureTestUtil.getCompareMask(pixelFormat); var isHighQuality = glsTextureTestUtil.verifyTexture3DResult( rendered.getAccess(), curCase.texture.getRefTexture(), texCoord, refParams, lookupPrecision, lodPrecision, pixelFormat ); if (!isHighQuality) { // Evaluate against lower precision requirements. lodPrecision.lodBits = 4; lookupPrecision.uvwBits = [4, 4, 4]; bufferedLogToConsole( 'Warning: Verification against high precision ' + 'requirements failed, trying with lower requirements.' ); var isOk = glsTextureTestUtil.verifyTexture3DResult( rendered.getAccess(), curCase.texture.getRefTexture(), texCoord, refParams, lookupPrecision, lodPrecision, pixelFormat ); if (!isOk) { bufferedLogToConsole('ERROR: Verification against low ' + 'precision requirements failed, failing test case.' ); testFailedOptions('Image verification failed', false); //In JS version, one mistake and you're out return tcuTestCase.IterateResult.STOP; } else checkMessage( false, 'Low-quality filtering result in iteration no. ' + this.m_caseNdx ); } this.m_caseNdx += 1; if (this.m_caseNdx < this.m_cases.length) return tcuTestCase.IterateResult.CONTINUE; testPassed('Verified'); return tcuTestCase.IterateResult.STOP; }; /** @typedef {{name: string, mode: number}} */ es3fTextureFilteringTests.WrapMode; /** @typedef {{name: string, mode: number}} */ es3fTextureFilteringTests.MinFilterMode; /** @typedef {{name: string, mode: number}} */ es3fTextureFilteringTests.MagFilterModes; /** @typedef {{width: number, height: number}} */ es3fTextureFilteringTests.Sizes2D; /** @typedef {{width: number, height: number}} */ es3fTextureFilteringTests.SizesCube; /** @typedef {{width: number, height: number, numLayers: number}} */ es3fTextureFilteringTests.Sizes2DArray; /** @typedef {{width: number, height: number, depth: number}} */ es3fTextureFilteringTests.Sizes3D; /** @typedef {{name: string, format: number}} */ es3fTextureFilteringTests.FilterableFormatsByType; /** * init */ es3fTextureFilteringTests.TextureFilteringTests.prototype.init = function() { /** @type {Array} */ var wrapModes = [{ name: 'clamp', mode: gl.CLAMP_TO_EDGE }, { name: 'repeat', mode: gl.REPEAT }, { name: 'mirror', mode: gl.MIRRORED_REPEAT } ]; /** @type {Array} */ var minFilterModes = [{ name: 'nearest', mode: gl.NEAREST }, { name: 'linear', mode: gl.LINEAR }, { name: 'nearest_mipmap_nearest', mode: gl.NEAREST_MIPMAP_NEAREST }, { name: 'linear_mipmap_nearest', mode: gl.LINEAR_MIPMAP_NEAREST }, { name: 'nearest_mipmap_linear', mode: gl.NEAREST_MIPMAP_LINEAR }, { name: 'linear_mipmap_linear', mode: gl.LINEAR_MIPMAP_LINEAR } ]; /** @type {Array} */ var magFilterModes = [{ name: 'nearest', mode: gl.NEAREST }, { name: 'linear', mode: gl.LINEAR } ]; /** @type {Array} */ var sizes2D = [{ width: 4, height: 8 }, { width: 32, height: 64 }, { width: 128, height: 128 }, { width: 3, height: 7 }, { width: 31, height: 55 }, { width: 127, height: 99 } ]; /** @type {Array} */ var sizesCube = [{ width: 8, height: 8 }, { width: 64, height: 64 }, { width: 128, height: 128 }, { width: 7, height: 7 }, { width: 63, height: 63 } ]; /** @type {Array} */ var sizes2DArray = [{ width: 4, height: 8, numLayers: 8 }, { width: 32, height: 64, numLayers: 16 }, { width: 128, height: 32, numLayers: 64 }, { width: 3, height: 7, numLayers: 5 }, { width: 63, height: 63, numLayers: 63 } ]; /** @type {Array} */ var sizes3D = [{ width: 4, height: 8, depth: 8 }, { width: 32, height: 64, depth: 16 }, { width: 128, height: 32, depth: 64 }, { width: 3, height: 7, depth: 5 }, { width: 63, height: 63, depth: 63 } ]; /** @type {Array} */ var filterableFormatsByType = [{ name: 'rgba16f', format: gl.RGBA16F }, { name: 'r11f_g11f_b10f', format: gl.R11F_G11F_B10F }, { name: 'rgb9_e5', format: gl.RGB9_E5 }, { name: 'rgba8', format: gl.RGBA8 }, { name: 'rgba8_snorm', format: gl.RGBA8_SNORM }, { name: 'rgb565', format: gl.RGB565 }, { name: 'rgba4', format: gl.RGBA4 }, { name: 'rgb5_a1', format: gl.RGB5_A1 }, { name: 'srgb8_alpha8', format: gl.SRGB8_ALPHA8 }, { name: 'rgb10_a2', format: gl.RGB10_A2 } ]; // 2D texture filtering. // Formats. /** @type {tcuTestCase.DeqpTest} */ var formatsGroup; for (var fmtNdx = 0; fmtNdx < filterableFormatsByType.length; fmtNdx++) { formatsGroup = new tcuTestCase.DeqpTest( '2d_formats', '2D Texture Formats'); this.addChild(formatsGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { /** @type {number} */ var minFilter = minFilterModes[filterNdx].mode; /** @type {string} */ var filterName = minFilterModes[filterNdx].name; /** @type {number} */ var format = filterableFormatsByType[fmtNdx].format; /** @type {string} */ var formatName = filterableFormatsByType[fmtNdx].name; var isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; /** @type {number} */ var magFilter = isMipmap ? gl.LINEAR : minFilter; /** @type {string} */ var name = formatName + '_' + filterName; /** @type {number} */ var wrapS = gl.REPEAT; /** @type {number} */ var wrapT = gl.REPEAT; /** @type {number} */ var width = 64; /** @type {number} */ var height = 64; formatsGroup.addChild( new es3fTextureFilteringTests.Texture2DFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, format, width, height ) ); } } // Sizes. /** @type {tcuTestCase.DeqpTest} */ var sizesGroup; for (var sizeNdx = 0; sizeNdx < sizes2D.length; sizeNdx++) { sizesGroup = new tcuTestCase.DeqpTest( '2d_sizes', '2D Texture Sizes'); this.addChild(sizesGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { minFilter = minFilterModes[filterNdx].mode; filterName = minFilterModes[filterNdx].name; format = gl.RGBA8; isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; magFilter = isMipmap ? gl.LINEAR : minFilter; wrapS = gl.REPEAT; wrapT = gl.REPEAT; width = sizes2D[sizeNdx].width; height = sizes2D[sizeNdx].height; name = '' + width + 'x' + height + '_' + filterName; sizesGroup.addChild( new es3fTextureFilteringTests.Texture2DFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, format, width, height ) ); } } // Wrap modes. /** @type {tcuTestCase.DeqpTest} */ var combinationsGroup; for (var minFilterNdx = 0; minFilterNdx < minFilterModes.length; minFilterNdx++) { combinationsGroup = new tcuTestCase.DeqpTest( '2d_combinations', '2D Filter and wrap mode combinations'); this.addChild(combinationsGroup); for (var magFilterNdx = 0; magFilterNdx < magFilterModes.length; magFilterNdx++) { for (var wrapSNdx = 0; wrapSNdx < wrapModes.length; wrapSNdx++) { for (var wrapTNdx = 0; wrapTNdx < wrapModes.length; wrapTNdx++) { minFilter = minFilterModes[minFilterNdx].mode; magFilter = magFilterModes[magFilterNdx].mode; format = gl.RGBA8; wrapS = wrapModes[wrapSNdx].mode; wrapT = wrapModes[wrapTNdx].mode; width = 63; height = 57; name = minFilterModes[minFilterNdx].name + '_' + magFilterModes[magFilterNdx].name + '_' + wrapModes[wrapSNdx].name + '_' + wrapModes[wrapTNdx].name; combinationsGroup.addChild( new es3fTextureFilteringTests.Texture2DFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, format, width, height ) ); } } } } // Cube map texture filtering. // Formats. for (var fmtNdx = 0; fmtNdx < filterableFormatsByType.length; fmtNdx++) { formatsGroup = new tcuTestCase.DeqpTest( 'cube_formats', 'Cube Texture Formats'); this.addChild(formatsGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { minFilter = minFilterModes[filterNdx].mode; filterName = minFilterModes[filterNdx].name; format = filterableFormatsByType[fmtNdx].format; formatName = filterableFormatsByType[fmtNdx].name; isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; magFilter = isMipmap ? gl.LINEAR : minFilter; name = formatName + '_' + filterName; wrapS = gl.REPEAT; wrapT = gl.REPEAT; width = 64; height = 64; formatsGroup.addChild( new es3fTextureFilteringTests.TextureCubeFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, false /* always sample exterior as well */, format, width, height ) ); } } // Sizes. for (var sizeNdx = 0; sizeNdx < sizesCube.length; sizeNdx++) { sizesGroup = new tcuTestCase.DeqpTest( 'cube_sizes', 'Cube Texture Sizes'); this.addChild(sizesGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { minFilter = minFilterModes[filterNdx].mode; filterName = minFilterModes[filterNdx].name; var format = gl.RGBA8; isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; magFilter = isMipmap ? gl.LINEAR : minFilter; wrapS = gl.REPEAT; wrapT = gl.REPEAT; width = sizesCube[sizeNdx].width; height = sizesCube[sizeNdx].height; name = '' + width + 'x' + height + '_' + filterName; sizesGroup.addChild( new es3fTextureFilteringTests.TextureCubeFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, false, format, width, height ) ); } } // Filter/wrap mode combinations. for (var minFilterNdx = 0; minFilterNdx < minFilterModes.length; minFilterNdx++) { combinationsGroup = new tcuTestCase.DeqpTest( 'cube_combinations', 'Cube Filter and wrap mode combinations' ); this.addChild(combinationsGroup); for (var magFilterNdx = 0; magFilterNdx < magFilterModes.length; magFilterNdx++) { for (var wrapSNdx = 0; wrapSNdx < wrapModes.length; wrapSNdx++) { for (var wrapTNdx = 0; wrapTNdx < wrapModes.length; wrapTNdx++) { minFilter = minFilterModes[minFilterNdx].mode; magFilter = magFilterModes[magFilterNdx].mode; format = gl.RGBA8; wrapS = wrapModes[wrapSNdx].mode; wrapT = wrapModes[wrapTNdx].mode; width = 63; height = 63; name = minFilterModes[minFilterNdx].name + '_' + magFilterModes[magFilterNdx].name + '_' + wrapModes[wrapSNdx].name + '_' + wrapModes[wrapTNdx].name; combinationsGroup.addChild( new es3fTextureFilteringTests. TextureCubeFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, false, format, width, height ) ); } } } } // Cases with no visible cube edges. /** @type {tcuTestCase.DeqpTest} */ var onlyFaceInteriorGroup = new tcuTestCase.DeqpTest( 'cube_no_edges_visible', "Don't sample anywhere near a face's edges" ); this.addChild(onlyFaceInteriorGroup); for (var isLinearI = 0; isLinearI <= 1; isLinearI++) { var isLinear = isLinearI != 0; var filter = isLinear ? gl.LINEAR : gl.NEAREST; onlyFaceInteriorGroup.addChild( new es3fTextureFilteringTests.TextureCubeFilteringCase( isLinear ? 'linear' : 'nearest', '', filter, filter, gl.REPEAT, gl.REPEAT, true, gl.RGBA8, 63, 63 ) ); } // Formats. for (var fmtNdx = 0; fmtNdx < filterableFormatsByType.length; fmtNdx++) { formatsGroup = new tcuTestCase.DeqpTest( '2d_array_formats', '2D Array Texture Formats'); this.addChild(formatsGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { minFilter = minFilterModes[filterNdx].mode; filterName = minFilterModes[filterNdx].name; format = filterableFormatsByType[fmtNdx].format; var formatName = filterableFormatsByType[fmtNdx].name; isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; magFilter = isMipmap ? gl.LINEAR : minFilter; name = formatName + '_' + filterName; wrapS = gl.REPEAT; wrapT = gl.REPEAT; width = 128; height = 128; /** @type {number} */ var numLayers = 8; formatsGroup.addChild( new es3fTextureFilteringTests.Texture2DArrayFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, format, width, height, numLayers ) ); } } // Sizes. for (var sizeNdx = 0; sizeNdx < sizes2DArray.length; sizeNdx++) { sizesGroup = new tcuTestCase.DeqpTest( '2d_array_sizes', '2D Array Texture Sizes'); this.addChild(sizesGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { minFilter = minFilterModes[filterNdx].mode; filterName = minFilterModes[filterNdx].name; format = gl.RGBA8; isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; magFilter = isMipmap ? gl.LINEAR : minFilter; wrapS = gl.REPEAT; wrapT = gl.REPEAT; width = sizes2DArray[sizeNdx].width; height = sizes2DArray[sizeNdx].height; numLayers = sizes2DArray[sizeNdx].numLayers; name = '' + width + 'x' + height + 'x' + numLayers + '_' + filterName; sizesGroup.addChild( new es3fTextureFilteringTests.Texture2DArrayFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, format, width, height, numLayers ) ); } } // Wrap modes. for (var minFilterNdx = 0; minFilterNdx < minFilterModes.length; minFilterNdx++) { combinationsGroup = new tcuTestCase.DeqpTest( '2d_array_combinations', '2D Array Filter and wrap mode combinations'); this.addChild(combinationsGroup); for (var magFilterNdx = 0; magFilterNdx < magFilterModes.length; magFilterNdx++) { for (var wrapSNdx = 0; wrapSNdx < wrapModes.length; wrapSNdx++) { for (var wrapTNdx = 0; wrapTNdx < wrapModes.length; wrapTNdx++) { minFilter = minFilterModes[minFilterNdx].mode; magFilter = magFilterModes[magFilterNdx].mode; format = gl.RGBA8; wrapS = wrapModes[wrapSNdx].mode; wrapT = wrapModes[wrapTNdx].mode; width = 123; height = 107; numLayers = 7; name = minFilterModes[minFilterNdx].name + '_' + magFilterModes[magFilterNdx].name + '_' + wrapModes[wrapSNdx].name + '_' + wrapModes[wrapTNdx].name; combinationsGroup.addChild( new es3fTextureFilteringTests. Texture2DArrayFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, format, width, height, numLayers ) ); } } } } // 3D texture filtering. // Formats. /** @type {number} */ var depth = 64; for (var fmtNdx = 0; fmtNdx < filterableFormatsByType.length; fmtNdx++) { formatsGroup = new tcuTestCase.DeqpTest( '3d_formats', '3D Texture Formats'); this.addChild(formatsGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { minFilter = minFilterModes[filterNdx].mode; filterName = minFilterModes[filterNdx].name; format = filterableFormatsByType[fmtNdx].format; formatName = filterableFormatsByType[fmtNdx].name; isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; magFilter = isMipmap ? gl.LINEAR : minFilter; name = formatName + '_' + filterName; wrapS = gl.REPEAT; wrapT = gl.REPEAT; /** @type {number} */ var wrapR = gl.REPEAT; width = 64; height = 64; depth = 64; formatsGroup.addChild( new es3fTextureFilteringTests.Texture3DFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, wrapR, format, width, height, depth ) ); } } // Sizes. for (var sizeNdx = 0; sizeNdx < sizes3D.length; sizeNdx++) { sizesGroup = new tcuTestCase.DeqpTest( '3d_sizes', '3D Texture Sizes'); this.addChild(sizesGroup); for (var filterNdx = 0; filterNdx < minFilterModes.length; filterNdx++) { minFilter = minFilterModes[filterNdx].mode; filterName = minFilterModes[filterNdx].name; format = gl.RGBA8; isMipmap = minFilter != gl.NEAREST && minFilter != gl.LINEAR; magFilter = isMipmap ? gl.LINEAR : minFilter; wrapS = gl.REPEAT; wrapT = gl.REPEAT; wrapR = gl.REPEAT; width = sizes3D[sizeNdx].width; height = sizes3D[sizeNdx].height; depth = sizes3D[sizeNdx].depth; name = '' + width + 'x' + height + 'x' + depth + '_' + filterName; sizesGroup.addChild( new es3fTextureFilteringTests.Texture3DFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, wrapR, format, width, height, depth ) ); } } // Wrap modes. for (var minFilterNdx = 0; minFilterNdx < minFilterModes.length; minFilterNdx++) { for (var magFilterNdx = 0; magFilterNdx < magFilterModes.length; magFilterNdx++) { for (var wrapSNdx = 0; wrapSNdx < wrapModes.length; wrapSNdx++) { combinationsGroup = new tcuTestCase.DeqpTest( '3d_combinations', '3D Filter and wrap mode combinations'); this.addChild(combinationsGroup); for (var wrapTNdx = 0; wrapTNdx < wrapModes.length; wrapTNdx++) { for (var wrapRNdx = 0; wrapRNdx < wrapModes.length; wrapRNdx++) { minFilter = minFilterModes[minFilterNdx].mode; magFilter = magFilterModes[magFilterNdx].mode; format = gl.RGBA8; wrapS = wrapModes[wrapSNdx].mode; wrapT = wrapModes[wrapTNdx].mode; wrapR = wrapModes[wrapRNdx].mode; width = 63; height = 57; depth = 67; name = minFilterModes[minFilterNdx].name + '_' + magFilterModes[magFilterNdx].name + '_' + wrapModes[wrapSNdx].name + '_' + wrapModes[wrapTNdx].name + '_' + wrapModes[wrapRNdx].name; combinationsGroup.addChild( new es3fTextureFilteringTests. Texture3DFilteringCase( name, '', minFilter, magFilter, wrapS, wrapT, wrapR, format, width, height, depth ) ); } } } } } }; /** * Create and execute the test cases * @param {WebGL2RenderingContext} context * @param {Array=} range Test range */ es3fTextureFilteringTests.run = function(context, range) { gl = context; const canvas = gl.canvas; canvas.width = canvasWH; canvas.height = canvasWH; //Set up Test Root parameters var state = tcuTestCase.runner; state.setRoot(new es3fTextureFilteringTests.TextureFilteringTests()); if (range) state.setRange(range); //Set up name and description of this test series. setCurrentTestName(state.testCases.fullName()); description(state.testCases.getDescription()); try { //Run test cases tcuTestCase.runTestCases(); } catch (err) { testFailedOptions('Failed to run tests', false); tcuTestCase.runner.terminate(); } }; });