diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/deqp/framework')
53 files changed, 31178 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuBilinearImageCompare.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuBilinearImageCompare.js new file mode 100644 index 000000000..bc23104c0 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuBilinearImageCompare.js @@ -0,0 +1,272 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuBilinearImageCompare'); +goog.require('framework.common.tcuRGBA'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + + var tcuBilinearImageCompare = framework.common.tcuBilinearImageCompare; + var deMath = framework.delibs.debase.deMath; + var tcuTexture = framework.common.tcuTexture; + var tcuRGBA = framework.common.tcuRGBA; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + // for bilinear interpolation + /** @const {number} */ tcuBilinearImageCompare.NUM_SUBPIXEL_BITS = 8; + + // Algorithm assumes that colors are packed to 32-bit values as dictated by + // tcu::RGBA::*_SHIFT values. + + function UintRGBA8_R(color) { + return (color >> 24) & 0xff; + } + function UintRGBA8_G(color) { + return (color >> 16) & 0xff; + } + function UintRGBA8_B(color) { + return (color >> 8) & 0xff; + } + function UintRGBA8_A(color) { + return color & 0xff; + } + + /** + * @param {number} fx1 deUint32 + * @param {number} fy1 deUint32 + * @param {number} p00 deUint8 + * @param {number} p01 deUint8 + * @param {number} p10 deUint8 + * @param {number} p11 deUint8 + * @return {number} deUint8 + */ + tcuBilinearImageCompare.interpolateChannel = function(fx1, fy1, p00, p01, p10, p11) { + /** @const {number} */ var fx0 = (1 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) - fx1; + /** @const {number} */ var fy0 = (1 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) - fy1; + /** @const {number} */ + var half = 1 << (tcuBilinearImageCompare.NUM_SUBPIXEL_BITS * 2 - 1); + /** @const {number} */ var sum = + (fx0 * fy0 * p00) + + (fx1 * fy0 * p10) + + (fx0 * fy1 * p01) + + (fx1 * fy1 * p11); + /** @const {number} */ + var rounded = (sum + half) >> (tcuBilinearImageCompare.NUM_SUBPIXEL_BITS * 2); + + DE_ASSERT(deMath.deInRange32(rounded, 0, 0xff)); + return rounded; + }; + + tcuBilinearImageCompare.compareUintRGBA8Threshold = function(a, b, thr) { + if (a == b) + return true; + + return (Math.abs(UintRGBA8_R(a) - UintRGBA8_R(b)) <= thr.getRed() && + Math.abs(UintRGBA8_G(a) - UintRGBA8_G(b)) <= thr.getGreen() && + Math.abs(UintRGBA8_B(a) - UintRGBA8_B(b)) <= thr.getBlue() && + Math.abs(UintRGBA8_A(a) - UintRGBA8_A(b)) <= thr.getAlpha()); + }; + + /** + * @param {tcuTexture.RGBA8View} view + * @param {number} u + * @param {number} v + * @return {number} + */ + tcuBilinearImageCompare.bilinearSampleUintRGBA8 = function(view, u, v) { + /** @type {number} */ var x0 = u >> tcuBilinearImageCompare.NUM_SUBPIXEL_BITS; + /** @type {number} */ var y0 = v >> tcuBilinearImageCompare.NUM_SUBPIXEL_BITS; + /** @type {number} */ var x1 = x0 + 1; + /** @type {number} */ var y1 = y0 + 1; + + DE_ASSERT(x1 < view.getWidth()); + DE_ASSERT(y1 < view.getHeight()); + + /** @type {number} */ var fx1 = u - (x0 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS); + /** @type {number} */ var fy1 = v - (y0 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS); + + /** @type {Array<number>} */ var channelsP00 = view.readUintRGBA8(x0, y0); + /** @type {Array<number>} */ var channelsP10 = view.readUintRGBA8(x1, y0); + /** @type {Array<number>} */ var channelsP01 = view.readUintRGBA8(x0, y1); + /** @type {Array<number>} */ var channelsP11 = view.readUintRGBA8(x1, y1); + + /** @type {number} */ var res = 0; + + res = (tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_R(channelsP00), + UintRGBA8_R(channelsP01), UintRGBA8_R(channelsP10), UintRGBA8_R(channelsP11)) & 0xff) << 24; + res += (tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_G(channelsP00), + UintRGBA8_G(channelsP01), UintRGBA8_G(channelsP10), UintRGBA8_G(channelsP11)) & 0xff) << 16; + res += (tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_B(channelsP00), + UintRGBA8_B(channelsP01), UintRGBA8_B(channelsP10), UintRGBA8_B(channelsP11)) & 0xff) << 8; + res += tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_A(channelsP00), + UintRGBA8_A(channelsP01), UintRGBA8_A(channelsP10), UintRGBA8_A(channelsP11)) & 0xff; + + return res; + }; + + /** + * @param {tcuTexture.RGBA8View} reference + * @param {tcuTexture.RGBA8View} result + * @param {tcuRGBA.RGBA} threshold + * @param {number} x + * @param {number} y + * @return {boolean} + */ + tcuBilinearImageCompare.comparePixelRGBA8 = function(reference, result, threshold, x, y) { + /** @const {tcuRGBA.RGBA} */ var resPix = result.readUintRGBA8(x, y); + + // Step 1: Compare result pixel to 3x3 neighborhood pixels in reference. + /** @const {number} */ var x0 = Math.max(x - 1, 0); + /** @const {number} */ var x1 = x; + /** @const {number} */ + var x2 = Math.min(x + 1, reference.getWidth() - 1); + /** @const {number} */ var y0 = Math.max(y - 1, 0); + /** @const {number} */ var y1 = y; + /** @const {number} */ + var y2 = Math.min(y + 1, reference.getHeight() - 1); + + //tcuBilinearImageCompare.readRGBA8List (reference, x0, y0, x2, y2); + + if (tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x1, y1), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x0, y1), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x2, y1), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x0, y0), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x1, y0), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x2, y0), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x0, y2), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x1, y2), threshold) || + tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x2, y2), threshold)) + return true; + + // Step 2: Compare using bilinear sampling. + // \todo [pyry] Optimize sample positions! + /** @const {Array<Array<number>>} */ var s_offsets = [ + [226, 186], + [335, 235], + [279, 334], + [178, 272], + [112, 202], + [306, 117], + [396, 299], + [206, 382], + [146, 96], + [423, 155], + [361, 412], + [84, 339], + [48, 130], + [367, 43], + [455, 367], + [105, 439], + [83, 46], + [217, 24], + [461, 71], + [450, 459], + [239, 469], + [67, 267], + [459, 255], + [13, 416], + [10, 192], + [141, 502], + [503, 304], + [380, 506] + ]; + + for (var sampleNdx = 0; sampleNdx < s_offsets.length; sampleNdx++) { + /** @const {number} */ + var u = ((x - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) + s_offsets[sampleNdx][0]; + /** @const {number} */ + var v = ((y - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) + s_offsets[sampleNdx][1]; + + if (!deMath.deInBounds32(u, 0, (reference.getWidth() - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) || + !deMath.deInBounds32(v, 0, (reference.getHeight() - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS)) + continue; + + if (tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, tcuBilinearImageCompare.bilinearSampleUintRGBA8(reference, u, v), threshold)) + return true; + } + + return false; + }; + + /** + * @param {tcuTexture.RGBA8View} reference + * @param {tcuTexture.RGBA8View} result + * @param {tcuTexture.PixelBufferAccess} errorMask + * @param {tcuRGBA.RGBA} threshold + * @return {boolean} + */ + tcuBilinearImageCompare.bilinearCompareRGBA8 = function(reference, result, errorMask, threshold) { + DE_ASSERT(reference.getFormat().isEqual(new tcuTexture.TextureFormat( + tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8))); + DE_ASSERT(result.getFormat().isEqual(new tcuTexture.TextureFormat( + tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8))); + + // Clear error mask first to green (faster this way). + errorMask.clear([0.0, 1.0, 0.0, 1.0]); + + /** @type {boolean} */ var allOk = true; + + for (var y = 0; y < reference.getHeight(); y++) { + for (var x = 0; x < reference.getWidth(); x++) { + if (!tcuBilinearImageCompare.comparePixelRGBA8(reference, result, threshold, x, y) && + !tcuBilinearImageCompare.comparePixelRGBA8(result, reference, threshold, x, y)) { + allOk = false; + errorMask.setPixel([1.0, 0.0, 0.0, 1.0], x, y); + } + } + } + + return allOk; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} reference + * @param {tcuTexture.ConstPixelBufferAccess} result + * @param {tcuTexture.PixelBufferAccess} errorMask + * @param {tcuRGBA.RGBA} threshold + * @return {boolean} + */ + tcuBilinearImageCompare.bilinearCompare = function(reference, result, errorMask, threshold) { + assertMsgOptions(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight() && result.getDepth() == reference.getDepth(), + 'Reference and result images have different dimensions', false, true); + + assertMsgOptions(errorMask.getWidth() == reference.getWidth() && errorMask.getHeight() == reference.getHeight() && errorMask.getDepth() == reference.getDepth(), + 'Reference and error mask images have different dimensions', false, true); + + /** @type {boolean} */ var isEqual = reference.getFormat().isEqual( + new tcuTexture.TextureFormat( + tcuTexture.ChannelOrder.RGBA, + tcuTexture.ChannelType.UNORM_INT8)); + if (isEqual) { + /** @type {tcuTexture.RGBA8View} */ var refView = new tcuTexture.RGBA8View(reference); + /** @type {tcuTexture.RGBA8View} */ var resView = new tcuTexture.RGBA8View(result); + return tcuBilinearImageCompare.bilinearCompareRGBA8(refView, resView, errorMask, threshold); + } else + throw new Error('Unsupported format for bilinear comparison'); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuCompressedTexture.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuCompressedTexture.js new file mode 100644 index 000000000..a309f81cf --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuCompressedTexture.js @@ -0,0 +1,967 @@ +/*------------------------------------------------------------------------- + * 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. + * + */ + +/*--------------------------------------------------------------------*//*! + * \brief Map tcu::TextureFormat to GL pixel transfer format. + * + * Maps generic texture format description to GL pixel transfer format. + * If no mapping is found, throws tcu::InternalError. + * + * \param texFormat Generic texture format. + * \return GL pixel transfer format. + *//*--------------------------------------------------------------------*/ +'use strict'; +goog.provide('framework.common.tcuCompressedTexture'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var tcuCompressedTexture = framework.common.tcuCompressedTexture; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + +/** + * @enum + */ +tcuCompressedTexture.Format = { + ETC1_RGB8: 0, + EAC_R11: 1, + EAC_SIGNED_R11: 2, + EAC_RG11: 3, + EAC_SIGNED_RG11: 4, + ETC2_RGB8: 5, + ETC2_SRGB8: 6, + ETC2_RGB8_PUNCHTHROUGH_ALPHA1: 7, + ETC2_SRGB8_PUNCHTHROUGH_ALPHA1: 8, + ETC2_EAC_RGBA8: 9, + ETC2_EAC_SRGB8_ALPHA8: 10, + + ASTC_4x4_RGBA: 11, + ASTC_5x4_RGBA: 12, + ASTC_5x5_RGBA: 13, + ASTC_6x5_RGBA: 14, + ASTC_6x6_RGBA: 15, + ASTC_8x5_RGBA: 16, + ASTC_8x6_RGBA: 17, + ASTC_8x8_RGBA: 18, + ASTC_10x5_RGBA: 19, + ASTC_10x6_RGBA: 20, + ASTC_10x8_RGBA: 21, + ASTC_10x10_RGBA: 22, + ASTC_12x10_RGBA: 23, + ASTC_12x12_RGBA: 24, + ASTC_4x4_SRGB8_ALPHA8: 25, + ASTC_5x4_SRGB8_ALPHA8: 26, + ASTC_5x5_SRGB8_ALPHA8: 27, + ASTC_6x5_SRGB8_ALPHA8: 28, + ASTC_6x6_SRGB8_ALPHA8: 29, + ASTC_8x5_SRGB8_ALPHA8: 30, + ASTC_8x6_SRGB8_ALPHA8: 31, + ASTC_8x8_SRGB8_ALPHA8: 32, + ASTC_10x5_SRGB8_ALPHA8: 33, + ASTC_10x6_SRGB8_ALPHA8: 34, + ASTC_10x8_SRGB8_ALPHA8: 35, + ASTC_10x10_SRGB8_ALPHA8: 36, + ASTC_12x10_SRGB8_ALPHA8: 37, + ASTC_12x12_SRGB8_ALPHA8: 38 +}; + +tcuCompressedTexture.divRoundUp = function(a, b) { + return Math.floor(a / b) + ((a % b) ? 1 : 0); +}; + +tcuCompressedTexture.isEtcFormat = function(fmt) { + // WebGL2 supports ETC2 and EAC formats + switch (fmt) { + // case tcuCompressedTexture.Format.ETC1_RGB8: + case tcuCompressedTexture.Format.EAC_R11: + case tcuCompressedTexture.Format.EAC_SIGNED_R11: + case tcuCompressedTexture.Format.EAC_RG11: + case tcuCompressedTexture.Format.EAC_SIGNED_RG11: + case tcuCompressedTexture.Format.ETC2_RGB8: + case tcuCompressedTexture.Format.ETC2_SRGB8: + case tcuCompressedTexture.Format.ETC2_RGB8_PUNCHTHROUGH_ALPHA1: + case tcuCompressedTexture.Format.ETC2_SRGB8_PUNCHTHROUGH_ALPHA1: + case tcuCompressedTexture.Format.ETC2_EAC_RGBA8: + case tcuCompressedTexture.Format.ETC2_EAC_SRGB8_ALPHA8: + return true; + + default: + return false; + } +}; + +tcuCompressedTexture.etcDecompressInternal = function() { + +var ETC2_BLOCK_WIDTH = 4; +var ETC2_BLOCK_HEIGHT = 4; +var ETC2_UNCOMPRESSED_PIXEL_SIZE_A8 = 1; +var ETC2_UNCOMPRESSED_PIXEL_SIZE_R11 = 2; +var ETC2_UNCOMPRESSED_PIXEL_SIZE_RG11 = 4; +var ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8 = 3; +var ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8 = 4; +var ETC2_UNCOMPRESSED_BLOCK_SIZE_A8 = ETC2_BLOCK_WIDTH * ETC2_BLOCK_HEIGHT * ETC2_UNCOMPRESSED_PIXEL_SIZE_A8; +var ETC2_UNCOMPRESSED_BLOCK_SIZE_R11 = ETC2_BLOCK_WIDTH * ETC2_BLOCK_HEIGHT * ETC2_UNCOMPRESSED_PIXEL_SIZE_R11; +var ETC2_UNCOMPRESSED_BLOCK_SIZE_RG11 = ETC2_BLOCK_WIDTH * ETC2_BLOCK_HEIGHT * ETC2_UNCOMPRESSED_PIXEL_SIZE_RG11; +var ETC2_UNCOMPRESSED_BLOCK_SIZE_RGB8 = ETC2_BLOCK_WIDTH * ETC2_BLOCK_HEIGHT * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; +var ETC2_UNCOMPRESSED_BLOCK_SIZE_RGBA8 = ETC2_BLOCK_WIDTH * ETC2_BLOCK_HEIGHT * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8; + +/** + * @param {ArrayBuffer} src Source ArrayBuffer + * @return {Uint8Array} + */ +var get64BitBlock = function(src, blockNdx) { + var block = new Uint8Array(src, blockNdx * 8, 8); + return block; +}; + +/** + * @param {ArrayBuffer} src Source ArrayBuffer + * Return the first 64 bits of a 128 bit block. + */ +var get128BitBlockStart = function(src, blockNdx) { + return get64BitBlock(src, 2 * blockNdx); +}; + +/** + * @param {ArrayBuffer} src Source ArrayBuffer + * Return the last 64 bits of a 128 bit block. + */ +var get128BitBlockEnd = function(src, blockNdx) { + return get64BitBlock(src, 2 * blockNdx + 1); +}; + +var mask8 = function(src, low, high) { + if (low > 7 || high < 0) + return { + value: 0, + bits: 0 + }; + + var numBits = high - low + 1; + var mask = (1 << numBits) - 1; + + return { + value: (src >> low) & mask, + bits: numBits + }; +}; + +var getBits64 = function(src, low, high) { + var result = 0; + var bits = 0; + var lowIndex = low; + var highIndex = high; + for (var i = 7; i >= 0; i--) { + var v = mask8(src[i], Math.max(0, lowIndex), Math.min(7, highIndex)); + lowIndex = lowIndex - 8; + highIndex = highIndex - 8; + result = result | (v.value << bits); + bits = v.bits; + } + return result; +}; + +var getBit64 = function(src, bit) { + return getBits64(src, bit, bit); +}; + +var extendSigned3To8 = function(src) { + var isNeg = (src & (1 << 2)) != 0; + var val = isNeg ? src - 8 : src; + return val; +}; + +var extend4To8 = function(src) { + return src * 255 / 15; +}; + +var extend5To8 = function(src) { + return src * 255 / 31; +}; + +var extend6To8 = function(src) { + return src * 255 / 63; +}; + +var extend7To8 = function(src) { + return src * 255 / 127; +}; + +var extend11To16 = function(src) { + return src * 32.015144; +}; + +var extend11To16WithSign = function(src) { + if (src < 0) + return -extend11To16(-src); + else + return extend11To16(src); +}; + +/** + * @param { (Uint16Array|Int16Array) } dst + * @param {Uint8Array} src + * @param {boolean} signedMode + */ +var decompressEAC11Block = function(dst, src, signedMode) { + var modifierTable = [ + [-3, -6, -9, -15, 2, 5, 8, 14], + [-3, -7, -10, -13, 2, 6, 9, 12], + [-2, -5, -8, -13, 1, 4, 7, 12], + [-2, -4, -6, -13, 1, 3, 5, 12], + [-3, -6, -8, -12, 2, 5, 7, 11], + [-3, -7, -9, -11, 2, 6, 8, 10], + [-4, -7, -8, -11, 3, 6, 7, 10], + [-3, -5, -8, -11, 2, 4, 7, 10], + [-2, -6, -8, -10, 1, 5, 7, 9], + [-2, -5, -8, -10, 1, 4, 7, 9], + [-2, -4, -8, -10, 1, 3, 7, 9], + [-2, -5, -7, -10, 1, 4, 6, 9], + [-3, -4, -7, -10, 2, 3, 6, 9], + [-1, -2, -3, -10, 0, 1, 2, 9], + [-4, -6, -8, -9, 3, 5, 7, 8], + [-3, -5, -7, -9, 2, 4, 6, 8] + ]; + + var multiplier = getBits64(src, 52, 55); + var tableNdx = getBits64(src, 48, 51); + var baseCodeword = getBits64(src, 56, 63); + + if (signedMode) { + if (baseCodeword > 127) + baseCodeword -= 256; + if (baseCodeword == -128) + baseCodeword = -127; + } + + var pixelNdx = 0; + for (var x = 0; x < ETC2_BLOCK_WIDTH; x++) { + for (var y = 0; y < ETC2_BLOCK_HEIGHT; y++) { + var dstOffset = (y * ETC2_BLOCK_WIDTH + x); + var pixelBitNdx = 45 - 3 * pixelNdx; + var modifierNdx = (getBit64(src, pixelBitNdx + 2) << 2) | (getBit64(src, pixelBitNdx + 1) << 1) | getBit64(src, pixelBitNdx); + var modifier = modifierTable[tableNdx][modifierNdx]; + + if (signedMode) { + if (multiplier != 0) + dst[dstOffset] = deMath.clamp(baseCodeword * 8 + multiplier * modifier * 8, -1023, 1023); + else + dst[dstOffset] = deMath.clamp(baseCodeword * 8 + modifier, -1023, 1023); + } else { + if (multiplier != 0) + dst[dstOffset] = deMath.clamp(baseCodeword * 8 + 4 + multiplier * modifier * 8, 0, 2047); + else + dst[dstOffset] = deMath.clamp(baseCodeword * 8 + 4 + modifier, 0, 2047); + } + pixelNdx++; + } + } +}; + +var decompressEAC_R11 = function(/*const tcu::PixelBufferAccess&*/ dst, width, height, src, signedMode) { + /** @const */ var numBlocksX = tcuCompressedTexture.divRoundUp(width, 4); + /** @const */ var numBlocksY = tcuCompressedTexture.divRoundUp(height, 4); + var dstPtr; + var dstRowPitch = dst.getRowPitch(); + var dstPixelSize = ETC2_UNCOMPRESSED_PIXEL_SIZE_R11; + var uncompressedBlockArray = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_R11); + var uncompressedBlock16; + if (signedMode) { + dstPtr = new Int16Array(dst.m_data); + uncompressedBlock16 = new Int16Array(uncompressedBlockArray); + } else { + dstPtr = new Uint16Array(dst.m_data); + uncompressedBlock16 = new Uint16Array(uncompressedBlockArray); + } + + for (var blockY = 0; blockY < numBlocksY; blockY++) { + for (var blockX = 0; blockX < numBlocksX; blockX++) { + /*const deUint64*/ var compressedBlock = get64BitBlock(src, blockY * numBlocksX + blockX); + + // Decompress. + decompressEAC11Block(uncompressedBlock16, compressedBlock, signedMode); + + // Write to dst. + var baseX = blockX * ETC2_BLOCK_WIDTH; + var baseY = blockY * ETC2_BLOCK_HEIGHT; + for (var y = 0; y < Math.min(ETC2_BLOCK_HEIGHT, height - baseY); y++) { + for (var x = 0; x < Math.min(ETC2_BLOCK_WIDTH, width - baseX); x++) { + DE_ASSERT(ETC2_UNCOMPRESSED_PIXEL_SIZE_R11 == 2); + + if (signedMode) { + var srcIndex = y * ETC2_BLOCK_WIDTH + x; + var dstIndex = (baseY + y) * dstRowPitch / dstPixelSize + baseX + x; + + dstPtr[dstIndex] = extend11To16WithSign(uncompressedBlock16[srcIndex]); + } else { + var srcIndex = y * ETC2_BLOCK_WIDTH + x; + var dstIndex = (baseY + y) * dstRowPitch / dstPixelSize + baseX + x; + + dstPtr[dstIndex] = extend11To16(uncompressedBlock16[srcIndex]); + } + } + } + } + } +}; + +var decompressEAC_RG11 = function(/*const tcu::PixelBufferAccess&*/ dst, width, height, src, signedMode) { + /** @const */ var numBlocksX = tcuCompressedTexture.divRoundUp(width, 4); + /** @const */ var numBlocksY = tcuCompressedTexture.divRoundUp(height, 4); + var dstPtr; + var dstRowPitch = dst.getRowPitch(); + var dstPixelSize = ETC2_UNCOMPRESSED_PIXEL_SIZE_RG11; + var uncompressedBlockArrayR = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_R11); + var uncompressedBlockArrayG = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_R11); + var uncompressedBlockR16; + var uncompressedBlockG16; + if (signedMode) { + dstPtr = new Int16Array(dst.m_data); + uncompressedBlockR16 = new Int16Array(uncompressedBlockArrayR); + uncompressedBlockG16 = new Int16Array(uncompressedBlockArrayG); + } else { + dstPtr = new Uint16Array(dst.m_data); + uncompressedBlockR16 = new Uint16Array(uncompressedBlockArrayR); + uncompressedBlockG16 = new Uint16Array(uncompressedBlockArrayG); + } + + for (var blockY = 0; blockY < numBlocksY; blockY++) { + for (var blockX = 0; blockX < numBlocksX; blockX++) { + /*const deUint64*/ var compressedBlockR = get128BitBlockStart(src, blockY * numBlocksX + blockX); + /*const deUint64*/ var compressedBlockG = get128BitBlockEnd(src, blockY * numBlocksX + blockX); + + // Decompress. + decompressEAC11Block(uncompressedBlockR16, compressedBlockR, signedMode); + decompressEAC11Block(uncompressedBlockG16, compressedBlockG, signedMode); + + // Write to dst. + var baseX = blockX * ETC2_BLOCK_WIDTH; + var baseY = blockY * ETC2_BLOCK_HEIGHT; + for (var y = 0; y < Math.min(ETC2_BLOCK_HEIGHT, height - baseY); y++) { + for (var x = 0; x < Math.min(ETC2_BLOCK_WIDTH, width - baseX); x++) { + DE_ASSERT(ETC2_UNCOMPRESSED_PIXEL_SIZE_RG11 == 4); + + if (signedMode) { + var srcIndex = y * ETC2_BLOCK_WIDTH + x; + var dstIndex = 2 * ((baseY + y) * dstRowPitch / dstPixelSize + baseX + x); + + dstPtr[dstIndex] = extend11To16WithSign(uncompressedBlockR16[srcIndex]); + dstPtr[dstIndex + 1] = extend11To16WithSign(uncompressedBlockG16[srcIndex]); + } else { + var srcIndex = y * ETC2_BLOCK_WIDTH + x; + var dstIndex = 2 * ((baseY + y) * dstRowPitch / dstPixelSize + baseX + x); + + dstPtr[dstIndex] = extend11To16(uncompressedBlockR16[srcIndex]); + dstPtr[dstIndex + 1] = extend11To16(uncompressedBlockG16[srcIndex]); + } + } + } + } + } +}; + +// if alphaMode is true, do PUNCHTHROUGH and store alpha to alphaDst; otherwise do ordinary ETC2 RGB8. +/** + * @param {Uint8Array} dst Destination array + * @param {Uint8Array} src Source array + * @param {Uint8Array} alphaDst Optional Alpha output channel + */ +var decompressETC2Block = function(dst, src, alphaDst, alphaMode) { + /** + * enum + */ + var Etc2Mode = { + MODE_INDIVIDUAL: 0, + MODE_DIFFERENTIAL: 1, + MODE_T: 2, + MODE_H: 3, + MODE_PLANAR: 4 + }; + + var diffOpaqueBit = getBit64(src, 33); + var selBR = getBits64(src, 59, 63); // 5 bits. + var selBG = getBits64(src, 51, 55); + var selBB = getBits64(src, 43, 47); + var selDR = extendSigned3To8(getBits64(src, 56, 58)); // 3 bits. + var selDG = extendSigned3To8(getBits64(src, 48, 50)); + var selDB = extendSigned3To8(getBits64(src, 40, 42)); + + var mode; + + if (!alphaMode && diffOpaqueBit == 0) + mode = Etc2Mode.MODE_INDIVIDUAL; + else if (!deMath.deInRange32(selBR + selDR, 0, 31)) + mode = Etc2Mode.MODE_T; + else if (!deMath.deInRange32(selBG + selDG, 0, 31)) + mode = Etc2Mode.MODE_H; + else if (!deMath.deInRange32(selBB + selDB, 0, 31)) + mode = Etc2Mode.MODE_PLANAR; + else + mode = Etc2Mode.MODE_DIFFERENTIAL; + + if (mode == Etc2Mode.MODE_INDIVIDUAL || mode == Etc2Mode.MODE_DIFFERENTIAL) { + // Individual and differential modes have some steps in common, handle them here. + var modifierTable = [ + // 00 01 10 11 + [2, 8, -2, -8], + [5, 17, -5, -17], + [9, 29, -9, -29], + [13, 42, -13, -42], + [18, 60, -18, -60], + [24, 80, -24, -80], + [33, 106, -33, -106], + [47, 183, -47, -183] + ]; + + var flipBit = getBit64(src, 32); + var table = [getBits64(src, 37, 39), getBits64(src, 34, 36)]; + var baseR = []; + var baseG = []; + var baseB = []; + + if (mode == Etc2Mode.MODE_INDIVIDUAL) { + // Individual mode, initial values. + baseR[0] = extend4To8(getBits64(src, 60, 63)); + baseR[1] = extend4To8(getBits64(src, 56, 59)); + baseG[0] = extend4To8(getBits64(src, 52, 55)); + baseG[1] = extend4To8(getBits64(src, 48, 51)); + baseB[0] = extend4To8(getBits64(src, 44, 47)); + baseB[1] = extend4To8(getBits64(src, 40, 43)); + } else { + // Differential mode, initial values. + baseR[0] = extend5To8(selBR); + baseG[0] = extend5To8(selBG); + baseB[0] = extend5To8(selBB); + + baseR[1] = extend5To8((selBR + selDR)); + baseG[1] = extend5To8((selBG + selDG)); + baseB[1] = extend5To8((selBB + selDB)); + } + + // Write final pixels for individual or differential mode. + var pixelNdx = 0; + for (var x = 0; x < ETC2_BLOCK_WIDTH; x++) { + for (var y = 0; y < ETC2_BLOCK_HEIGHT; y++, pixelNdx++) { + var dstOffset = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; + var subBlock = ((flipBit ? y : x) >= 2) ? 1 : 0; + var tableNdx = table[subBlock]; + var modifierNdx = (getBit64(src, 16 + pixelNdx) << 1) | getBit64(src, pixelNdx); + var alphaDstOffset = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_A8; // Only needed for PUNCHTHROUGH version. + + // If doing PUNCHTHROUGH version (alphaMode), opaque bit may affect colors. + if (alphaMode && diffOpaqueBit == 0 && modifierNdx == 2) { + dst[dstOffset + 0] = 0; + dst[dstOffset + 1] = 0; + dst[dstOffset + 2] = 0; + alphaDst[alphaDstOffset] = 0; + } else { + var modifier; + + // PUNCHTHROUGH version and opaque bit may also affect modifiers. + if (alphaMode && diffOpaqueBit == 0 && (modifierNdx == 0 || modifierNdx == 2)) + modifier = 0; + else + modifier = modifierTable[tableNdx][modifierNdx]; + + dst[dstOffset + 0] = deMath.clamp(baseR[subBlock] + modifier, 0, 255); + dst[dstOffset + 1] = deMath.clamp(baseG[subBlock] + modifier, 0, 255); + dst[dstOffset + 2] = deMath.clamp(baseB[subBlock] + modifier, 0, 255); + + if (alphaMode) + alphaDst[alphaDstOffset] = 255; + } + } + } + } else if (mode == Etc2Mode.MODE_T || mode == Etc2Mode.MODE_H) { + // T and H modes have some steps in common, handle them here. + var distTable = [3, 6, 11, 16, 23, 32, 41, 64]; + + var paintR = []; + var paintG = []; + var paintB = []; + + if (mode == Etc2Mode.MODE_T) { + // T mode, calculate paint values. + var R1a = getBits64(src, 59, 60); + var R1b = getBits64(src, 56, 57); + var G1 = getBits64(src, 52, 55); + var B1 = getBits64(src, 48, 51); + var R2 = getBits64(src, 44, 47); + var G2 = getBits64(src, 40, 43); + var B2 = getBits64(src, 36, 39); + var distNdx = (getBits64(src, 34, 35) << 1) | getBit64(src, 32); + var dist = distTable[distNdx]; + + paintR[0] = extend4To8((R1a << 2) | R1b); + paintG[0] = extend4To8(G1); + paintB[0] = extend4To8(B1); + paintR[2] = extend4To8(R2); + paintG[2] = extend4To8(G2); + paintB[2] = extend4To8(B2); + paintR[1] = deMath.clamp(paintR[2] + dist, 0, 255); + paintG[1] = deMath.clamp(paintG[2] + dist, 0, 255); + paintB[1] = deMath.clamp(paintB[2] + dist, 0, 255); + paintR[3] = deMath.clamp(paintR[2] - dist, 0, 255); + paintG[3] = deMath.clamp(paintG[2] - dist, 0, 255); + paintB[3] = deMath.clamp(paintB[2] - dist, 0, 255); + } else { + // H mode, calculate paint values. + var R1 = getBits64(src, 59, 62); + var G1a = getBits64(src, 56, 58); + var G1b = getBit64(src, 52); + var B1a = getBit64(src, 51); + var B1b = getBits64(src, 47, 49); + var R2 = getBits64(src, 43, 46); + var G2 = getBits64(src, 39, 42); + var B2 = getBits64(src, 35, 38); + var baseR = []; + var baseG = []; + var baseB = []; + var baseValue = []; + var distNdx; + var dist; + + baseR[0] = extend4To8(R1); + baseG[0] = extend4To8((G1a << 1) | G1b); + baseB[0] = extend4To8((B1a << 3) | B1b); + baseR[1] = extend4To8(R2); + baseG[1] = extend4To8(G2); + baseB[1] = extend4To8(B2); + baseValue[0] = ((baseR[0]) << 16) | ((baseG[0]) << 8) | baseB[0]; + baseValue[1] = ((baseR[1]) << 16) | ((baseG[1]) << 8) | baseB[1]; + distNdx = (getBit64(src, 34) << 2) | (getBit64(src, 32) << 1); + if (baseValue[0] >= baseValue[1]) + distNdx += 1; + dist = distTable[distNdx]; + + paintR[0] = deMath.clamp(baseR[0] + dist, 0, 255); + paintG[0] = deMath.clamp(baseG[0] + dist, 0, 255); + paintB[0] = deMath.clamp(baseB[0] + dist, 0, 255); + paintR[1] = deMath.clamp(baseR[0] - dist, 0, 255); + paintG[1] = deMath.clamp(baseG[0] - dist, 0, 255); + paintB[1] = deMath.clamp(baseB[0] - dist, 0, 255); + paintR[2] = deMath.clamp(baseR[1] + dist, 0, 255); + paintG[2] = deMath.clamp(baseG[1] + dist, 0, 255); + paintB[2] = deMath.clamp(baseB[1] + dist, 0, 255); + paintR[3] = deMath.clamp(baseR[1] - dist, 0, 255); + paintG[3] = deMath.clamp(baseG[1] - dist, 0, 255); + paintB[3] = deMath.clamp(baseB[1] - dist, 0, 255); + } + + // Write final pixels for T or H mode. + var pixelNdx = 0; + for (var x = 0; x < ETC2_BLOCK_WIDTH; x++) { + for (var y = 0; y < ETC2_BLOCK_HEIGHT; y++, pixelNdx++) { + var dstOffset = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; + var paintNdx = (getBit64(src, 16 + pixelNdx) << 1) | getBit64(src, pixelNdx); + var alphaDstOffset = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_A8; // Only needed for PUNCHTHROUGH version. + + if (alphaMode && diffOpaqueBit == 0 && paintNdx == 2) { + dst[dstOffset + 0] = 0; + dst[dstOffset + 1] = 0; + dst[dstOffset + 2] = 0; + alphaDst[alphaDstOffset] = 0; + } else { + dst[dstOffset + 0] = deMath.clamp(paintR[paintNdx], 0, 255); + dst[dstOffset + 1] = deMath.clamp(paintG[paintNdx], 0, 255); + dst[dstOffset + 2] = deMath.clamp(paintB[paintNdx], 0, 255); + + if (alphaMode) + alphaDst[alphaDstOffset] = 255; + } + } + } + } else { + // Planar mode. + var GO1 = getBit64(src, 56); + var GO2 = getBits64(src, 49, 54); + var BO1 = getBit64(src, 48); + var BO2 = getBits64(src, 43, 44); + var BO3 = getBits64(src, 39, 41); + var RH1 = getBits64(src, 34, 38); + var RH2 = getBit64(src, 32); + var RO = extend6To8(getBits64(src, 57, 62)); + var GO = extend7To8((GO1 << 6) | GO2); + var BO = extend6To8((BO1 << 5) | (BO2 << 3) | BO3); + var RH = extend6To8((RH1 << 1) | RH2); + var GH = extend7To8(getBits64(src, 25, 31)); + var BH = extend6To8(getBits64(src, 19, 24)); + var RV = extend6To8(getBits64(src, 13, 18)); + var GV = extend7To8(getBits64(src, 6, 12)); + var BV = extend6To8(getBits64(src, 0, 5)); + + // Write final pixels for planar mode. + for (var y = 0; y < 4; y++) { + for (var x = 0; x < 4; x++) { + var dstOffset = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; + var unclampedR = (x * (RH - RO) + y * (RV - RO) + 4 * RO + 2) / 4; + var unclampedG = (x * (GH - GO) + y * (GV - GO) + 4 * GO + 2) / 4; + var unclampedB = (x * (BH - BO) + y * (BV - BO) + 4 * BO + 2) / 4; + var alphaDstOffset = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_A8; // Only needed for PUNCHTHROUGH version. + + dst[dstOffset + 0] = deMath.clamp(unclampedR, 0, 255); + dst[dstOffset + 1] = deMath.clamp(unclampedG, 0, 255); + dst[dstOffset + 2] = deMath.clamp(unclampedB, 0, 255); + + if (alphaMode) + alphaDst[alphaDstOffset] = 255; + } + } + } +}; + +var decompressEAC8Block = function(dst, src) { + var modifierTable = [ + [-3, -6, -9, -15, 2, 5, 8, 14], + [-3, -7, -10, -13, 2, 6, 9, 12], + [-2, -5, -8, -13, 1, 4, 7, 12], + [-2, -4, -6, -13, 1, 3, 5, 12], + [-3, -6, -8, -12, 2, 5, 7, 11], + [-3, -7, -9, -11, 2, 6, 8, 10], + [-4, -7, -8, -11, 3, 6, 7, 10], + [-3, -5, -8, -11, 2, 4, 7, 10], + [-2, -6, -8, -10, 1, 5, 7, 9], + [-2, -5, -8, -10, 1, 4, 7, 9], + [-2, -4, -8, -10, 1, 3, 7, 9], + [-2, -5, -7, -10, 1, 4, 6, 9], + [-3, -4, -7, -10, 2, 3, 6, 9], + [-1, -2, -3, -10, 0, 1, 2, 9], + [-4, -6, -8, -9, 3, 5, 7, 8], + [-3, -5, -7, -9, 2, 4, 6, 8] + ]; + + var baseCodeword = getBits64(src, 56, 63); + var multiplier = getBits64(src, 52, 55); + var tableNdx = getBits64(src, 48, 51); + + var pixelNdx = 0; + for (var x = 0; x < ETC2_BLOCK_WIDTH; x++) { + for (var y = 0; y < ETC2_BLOCK_HEIGHT; y++, pixelNdx++) { + var dstOffset = (y * ETC2_BLOCK_WIDTH + x); + var pixelBitNdx = 45 - 3 * pixelNdx; + var modifierNdx = (getBit64(src, pixelBitNdx + 2) << 2) | (getBit64(src, pixelBitNdx + 1) << 1) | getBit64(src, pixelBitNdx); + var modifier = modifierTable[tableNdx][modifierNdx]; + + dst[dstOffset] = deMath.clamp(baseCodeword + multiplier * modifier, 0, 255); + } + } +}; + +var decompressETC2 = function(/*const tcu::PixelBufferAccess&*/ dst, width, height, src) { + var numBlocksX = tcuCompressedTexture.divRoundUp(width, 4); + var numBlocksY = tcuCompressedTexture.divRoundUp(height, 4); + var dstPtr = new Uint8Array(dst.m_data); + var dstRowPitch = dst.getRowPitch(); + var dstPixelSize = ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; + var uncompressedBlockArray = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_RGB8); + var uncompressedBlock = new Uint8Array(uncompressedBlockArray); + + for (var blockY = 0; blockY < numBlocksY; blockY++) { + for (var blockX = 0; blockX < numBlocksX; blockX++) { + var compressedBlock = get64BitBlock(src, blockY * numBlocksX + blockX); + + // Decompress. + decompressETC2Block(uncompressedBlock, compressedBlock, null, false); + + // Write to dst. + var baseX = blockX * ETC2_BLOCK_WIDTH; + var baseY = blockY * ETC2_BLOCK_HEIGHT; + for (var y = 0; y < Math.min(ETC2_BLOCK_HEIGHT, height - baseY); y++) { + for (var x = 0; x < Math.min(ETC2_BLOCK_WIDTH, width - baseX); x++) { + var srcIndex = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; + var dstIndex = (baseY + y) * dstRowPitch + (baseX + x) * dstPixelSize; + + for (var i = 0; i < ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; i++) + dstPtr[dstIndex + i] = uncompressedBlock[srcIndex + i]; + } + } + } + } +}; + +var decompressETC2_EAC_RGBA8 = function(/*const tcu::PixelBufferAccess&*/ dst, width, height, src) { + var numBlocksX = tcuCompressedTexture.divRoundUp(width, 4); + var numBlocksY = tcuCompressedTexture.divRoundUp(height, 4); + var dstPtr = new Uint8Array(dst.m_data); + var dstRowPitch = dst.getRowPitch(); + var dstPixelSize = ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8; + var uncompressedBlockArray = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_RGB8); + var uncompressedBlock = new Uint8Array(uncompressedBlockArray); + var uncompressedBlockAlphaArray = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_A8); + var uncompressedBlockAlpha = new Uint8Array(uncompressedBlockAlphaArray); + + for (var blockY = 0; blockY < numBlocksY; blockY++) { + for (var blockX = 0; blockX < numBlocksX; blockX++) { + var compressedBlockAlpha = get128BitBlockStart(src, blockY * numBlocksX + blockX); + var compressedBlockRGB = get128BitBlockEnd(src, blockY * numBlocksX + blockX); + + // Decompress. + decompressETC2Block(uncompressedBlock, compressedBlockRGB, null, false); + decompressEAC8Block(uncompressedBlockAlpha, compressedBlockAlpha); + + // Write to dst. + var baseX = blockX * ETC2_BLOCK_WIDTH; + var baseY = blockY * ETC2_BLOCK_HEIGHT; + for (var y = 0; y < Math.min(ETC2_BLOCK_HEIGHT, height - baseY); y++) { + for (var x = 0; x < Math.min(ETC2_BLOCK_WIDTH, width - baseX); x++) { + var srcIndex = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; + var srcAlphaIndex = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_A8; + var dstIndex = (baseY + y) * dstRowPitch + (baseX + x) * dstPixelSize; + + for (var i = 0; i < ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8 - 1; i++) + dstPtr[dstIndex + i] = uncompressedBlock[srcIndex + i]; + dstPtr[dstIndex + ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8 - 1] = uncompressedBlockAlpha[srcAlphaIndex]; + + } + } + } + } +}; + +var decompressETC2_RGB8_PUNCHTHROUGH_ALPHA1 = function(/*const tcu::PixelBufferAccess&*/ dst, width, height, src) { + var numBlocksX = tcuCompressedTexture.divRoundUp(width, 4); + var numBlocksY = tcuCompressedTexture.divRoundUp(height, 4); + var dstPtr = new Uint8Array(dst.m_data); + var dstRowPitch = dst.getRowPitch(); + var dstPixelSize = ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8; + var uncompressedBlockArray = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_RGB8); + var uncompressedBlock = new Uint8Array(uncompressedBlockArray); + var uncompressedBlockAlphaArray = new ArrayBuffer(ETC2_UNCOMPRESSED_BLOCK_SIZE_A8); + var uncompressedBlockAlpha = new Uint8Array(uncompressedBlockAlphaArray); + + for (var blockY = 0; blockY < numBlocksY; blockY++) { + for (var blockX = 0; blockX < numBlocksX; blockX++) { + var compressedBlock = get64BitBlock(src, blockY * numBlocksX + blockX); + + // Decompress. + decompressETC2Block(uncompressedBlock, compressedBlock, uncompressedBlockAlpha, true); + + // Write to dst. + var baseX = blockX * ETC2_BLOCK_WIDTH; + var baseY = blockY * ETC2_BLOCK_HEIGHT; + for (var y = 0; y < Math.min(ETC2_BLOCK_HEIGHT, height - baseY); y++) { + for (var x = 0; x < Math.min(ETC2_BLOCK_WIDTH, width - baseX); x++) { + var srcIndex = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_RGB8; + var srcAlphaIndex = (y * ETC2_BLOCK_WIDTH + x) * ETC2_UNCOMPRESSED_PIXEL_SIZE_A8; + var dstIndex = (baseY + y) * dstRowPitch + (baseX + x) * dstPixelSize; + + for (var i = 0; i < ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8 - 1; i++) + dstPtr[dstIndex + i] = uncompressedBlock[srcIndex + i]; + dstPtr[dstIndex + ETC2_UNCOMPRESSED_PIXEL_SIZE_RGBA8 - 1] = uncompressedBlockAlpha[srcAlphaIndex]; + + } + } + } + } +}; + +return { + decompressEAC_R11: decompressEAC_R11, + decompressEAC_RG11: decompressEAC_RG11, + decompressETC2: decompressETC2, + decompressETC2_RGB8_PUNCHTHROUGH_ALPHA1: decompressETC2_RGB8_PUNCHTHROUGH_ALPHA1, + decompressETC2_EAC_RGBA8: decompressETC2_EAC_RGBA8 +}; + +}(); + +/** + * @constructor + * @param {tcuCompressedTexture.Format} format + * @param {number} width + * @param {number} height + * @param {number=} depth + */ +tcuCompressedTexture.CompressedTexture = function(format, width, height, depth) { + depth = depth === undefined ? 1 : depth; + this.setStorage(format, width, height, depth); + /** @type {Uint8Array} */ this.m_data; +}; + +/** + * @return {number} + */ +tcuCompressedTexture.CompressedTexture.prototype.getDataSize = function() { + return this.m_data.length; +}; + +/** + * @return {Uint8Array} + */ +tcuCompressedTexture.CompressedTexture.prototype.getData = function() { + return this.m_data; +}; + +/** + * @return {number} + */ +tcuCompressedTexture.CompressedTexture.prototype.getWidth = function() { + return this.m_width; +}; + +/** + * @return {number} + */ +tcuCompressedTexture.CompressedTexture.prototype.getHeight = function() { + return this.m_height; +}; + +/** + * @return {tcuCompressedTexture.Format} + */ +tcuCompressedTexture.CompressedTexture.prototype.getFormat = function() { + return this.m_format; +}; + +tcuCompressedTexture.CompressedTexture.prototype.setStorage = function(format, width, height, depth) { + depth = depth === undefined ? 1 : depth; + this.m_format = format; + this.m_width = width; + this.m_height = height; + this.m_depth = depth; + + if (tcuCompressedTexture.isEtcFormat(this.m_format)) { + DE_ASSERT(this.m_depth == 1); + + var blockSizeMultiplier = 0; // How many 64-bit parts each compressed block contains. + + switch (this.m_format) { + case tcuCompressedTexture.Format.ETC1_RGB8: blockSizeMultiplier = 1; break; + case tcuCompressedTexture.Format.EAC_R11: blockSizeMultiplier = 1; break; + case tcuCompressedTexture.Format.EAC_SIGNED_R11: blockSizeMultiplier = 1; break; + case tcuCompressedTexture.Format.EAC_RG11: blockSizeMultiplier = 2; break; + case tcuCompressedTexture.Format.EAC_SIGNED_RG11: blockSizeMultiplier = 2; break; + case tcuCompressedTexture.Format.ETC2_RGB8: blockSizeMultiplier = 1; break; + case tcuCompressedTexture.Format.ETC2_SRGB8: blockSizeMultiplier = 1; break; + case tcuCompressedTexture.Format.ETC2_RGB8_PUNCHTHROUGH_ALPHA1: blockSizeMultiplier = 1; break; + case tcuCompressedTexture.Format.ETC2_SRGB8_PUNCHTHROUGH_ALPHA1: blockSizeMultiplier = 1; break; + case tcuCompressedTexture.Format.ETC2_EAC_RGBA8: blockSizeMultiplier = 2; break; + case tcuCompressedTexture.Format.ETC2_EAC_SRGB8_ALPHA8: blockSizeMultiplier = 2; break; + + default: + throw new Error('Unsupported format ' + format); + break; + } + + this.m_array = new ArrayBuffer(blockSizeMultiplier * 8 * tcuCompressedTexture.divRoundUp(this.m_width, 4) * tcuCompressedTexture.divRoundUp(this.m_height, 4)); + this.m_data = new Uint8Array(this.m_array); + } + // else if (isASTCFormat(this.m_format)) + // { + // if (this.m_depth > 1) + // throw tcu::InternalError("3D ASTC textures not currently supported"); + + // const IVec3 blockSize = getASTCBlockSize(this.m_format); + // this.m_data.resize(ASTC_BLOCK_SIZE_BYTES * tcuCompressedTexture.divRoundUp(this.m_width, blockSize[0]) * tcuCompressedTexture.divRoundUp(this.m_height, blockSize[1]) * tcuCompressedTexture.divRoundUp(this.m_depth, blockSize[2])); + // } + // else + // { + // DE_ASSERT(this.m_format == FORMAT_LAST); + // DE_ASSERT(this.m_width == 0 && this.m_height == 0 && this.m_depth == 0); + // this.m_data.resize(0); + // } +}; + +/*--------------------------------------------------------------------*//*! + * \brief Get uncompressed texture format + *//*--------------------------------------------------------------------*/ +tcuCompressedTexture.CompressedTexture.prototype.getUncompressedFormat = function() { + if (tcuCompressedTexture.isEtcFormat(this.m_format)) { + switch (this.m_format) { + case tcuCompressedTexture.Format.ETC1_RGB8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT8); + case tcuCompressedTexture.Format.EAC_R11: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNORM_INT16); + case tcuCompressedTexture.Format.EAC_SIGNED_R11: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SNORM_INT16); + case tcuCompressedTexture.Format.EAC_RG11: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNORM_INT16); + case tcuCompressedTexture.Format.EAC_SIGNED_RG11: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SNORM_INT16); + case tcuCompressedTexture.Format.ETC2_RGB8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT8); + case tcuCompressedTexture.Format.ETC2_SRGB8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.sRGB, tcuTexture.ChannelType.UNORM_INT8); + case tcuCompressedTexture.Format.ETC2_RGB8_PUNCHTHROUGH_ALPHA1: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8); + case tcuCompressedTexture.Format.ETC2_SRGB8_PUNCHTHROUGH_ALPHA1: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.sRGBA, tcuTexture.ChannelType.UNORM_INT8); + case tcuCompressedTexture.Format.ETC2_EAC_RGBA8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8); + case tcuCompressedTexture.Format.ETC2_EAC_SRGB8_ALPHA8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.sRGBA, tcuTexture.ChannelType.UNORM_INT8); + default: + throw new Error('Unsupported format ' + this.m_format); + } + } + // else if (isASTCFormat(m_format)) + // { + // if (isASTCSRGBFormat(m_format)) + // return TextureFormat(tcuTexture.ChannelType.sRGBA, tcuTexture.ChannelType.UNORM_INT8); + // else + // return TextureFormat(tcuTexture.ChannelType.RGBA, tcuTexture.ChannelType.HALF_FLOAT); + // } + // else + // { + // DE_ASSERT(false); + // return TextureFormat(); + // } +}; + +/** + * Decode to uncompressed pixel data + * @param {tcuTexture.PixelBufferAccess} dst Destination buffer + */ +tcuCompressedTexture.CompressedTexture.prototype.decompress = function(dst) { + DE_ASSERT(dst.getWidth() == this.m_width && dst.getHeight() == this.m_height && dst.getDepth() == 1); + var format = this.getUncompressedFormat(); + if (dst.getFormat().order != format.order || dst.getFormat().type != format.type) + throw new Error('Formats do not match.'); + + if (tcuCompressedTexture.isEtcFormat(this.m_format)) { + switch (this.m_format) { + // case tcuCompressedTexture.Format.ETC1_RGB8: decompressETC1 (dst, this.m_width, this.m_height, this.m_data); break; + case tcuCompressedTexture.Format.EAC_R11: tcuCompressedTexture.etcDecompressInternal.decompressEAC_R11(dst, this.m_width, this.m_height, this.m_array, false); break; + case tcuCompressedTexture.Format.EAC_SIGNED_R11: tcuCompressedTexture.etcDecompressInternal.decompressEAC_R11(dst, this.m_width, this.m_height, this.m_array, true); break; + case tcuCompressedTexture.Format.EAC_RG11: tcuCompressedTexture.etcDecompressInternal.decompressEAC_RG11(dst, this.m_width, this.m_height, this.m_array, false); break; + case tcuCompressedTexture.Format.EAC_SIGNED_RG11: tcuCompressedTexture.etcDecompressInternal.decompressEAC_RG11(dst, this.m_width, this.m_height, this.m_array, true); break; + case tcuCompressedTexture.Format.ETC2_RGB8: tcuCompressedTexture.etcDecompressInternal.decompressETC2(dst, this.m_width, this.m_height, this.m_array); break; + case tcuCompressedTexture.Format.ETC2_SRGB8: tcuCompressedTexture.etcDecompressInternal.decompressETC2(dst, this.m_width, this.m_height, this.m_array); break; + case tcuCompressedTexture.Format.ETC2_RGB8_PUNCHTHROUGH_ALPHA1: tcuCompressedTexture.etcDecompressInternal.decompressETC2_RGB8_PUNCHTHROUGH_ALPHA1(dst, this.m_width, this.m_height, this.m_array); break; + case tcuCompressedTexture.Format.ETC2_SRGB8_PUNCHTHROUGH_ALPHA1: tcuCompressedTexture.etcDecompressInternal.decompressETC2_RGB8_PUNCHTHROUGH_ALPHA1(dst, this.m_width, this.m_height, this.m_array); break; + case tcuCompressedTexture.Format.ETC2_EAC_RGBA8: tcuCompressedTexture.etcDecompressInternal.decompressETC2_EAC_RGBA8(dst, this.m_width, this.m_height, this.m_array); break; + case tcuCompressedTexture.Format.ETC2_EAC_SRGB8_ALPHA8: tcuCompressedTexture.etcDecompressInternal.decompressETC2_EAC_RGBA8(dst, this.m_width, this.m_height, this.m_array); break; + + default: + throw new Error('Unsupported format ' + this.m_format); + break; + } + } + // else if (isASTCFormat(m_format)) + // { + // const tcu::IVec3 blockSize = getASTCBlockSize(m_format); + // const bool isSRGBFormat = isASTCSRGBFormat(m_format); + + // if (blockSize[2] > 1) + // throw tcu::InternalError("3D ASTC textures not currently supported"); + + // decompressASTC(dst, m_width, m_height, &m_data[0], blockSize[0], blockSize[1], isSRGBFormat, isSRGBFormat || params.isASTCModeLDR); + // } /**/ + else + throw new Error('Unsupported format ' + this.m_format); +}; + + }); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFloat.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFloat.js new file mode 100644 index 000000000..3d91ab5aa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFloat.js @@ -0,0 +1,809 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuFloat'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var tcuFloat = framework.common.tcuFloat; +var deMath = framework.delibs.debase.deMath; + +var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); +}; + +tcuFloat.FloatFlags = { + FLOAT_HAS_SIGN: (1 << 0), + FLOAT_SUPPORT_DENORM: (1 << 1) +}; + +/** + * Defines a tcuFloat.FloatDescription object, which is an essential part of the tcuFloat.deFloat type. + * Holds the information that shapes the tcuFloat.deFloat. + * @constructor + */ +tcuFloat.FloatDescription = function(exponentBits, mantissaBits, exponentBias, flags) { + this.ExponentBits = exponentBits; + this.MantissaBits = mantissaBits; + this.ExponentBias = exponentBias; + this.Flags = flags; + + this.totalBitSize = 1 + this.ExponentBits + this.MantissaBits; + this.totalByteSize = Math.floor(this.totalBitSize / 8) + ((this.totalBitSize % 8) > 0 ? 1 : 0); +}; + +/** + * Builds a zero float of the current binary description. + * @param {number} sign + * @return {tcuFloat.deFloat} + */ +tcuFloat.FloatDescription.prototype.zero = function(sign) { + return tcuFloat.newDeFloatFromParameters(this.zeroNumber(sign), this); +}; + +tcuFloat.FloatDescription.prototype.zeroNumber = function(sign) { + return deMath.shiftLeft((sign > 0 ? 0 : 1), (this.ExponentBits + this.MantissaBits)); +}; + +/** + * Builds an infinity float representation of the current binary description. + * @param {number} sign + * @return {tcuFloat.deFloat} + */ +tcuFloat.FloatDescription.prototype.inf = function(sign) { + return tcuFloat.newDeFloatFromParameters(this.infNumber(sign), this); +}; + +tcuFloat.FloatDescription.prototype.infNumber = function(sign) { + return ((sign > 0 ? 0 : 1) << (this.ExponentBits + this.MantissaBits)) | + deMath.shiftLeft(((1 << this.ExponentBits) - 1), this.MantissaBits); //Unless using very large exponent types, native shift is safe here, i guess. +}; + +/** + * Builds a NaN float representation of the current binary description. + * @return {tcuFloat.deFloat} + */ +tcuFloat.FloatDescription.prototype.nan = function() { + return tcuFloat.newDeFloatFromParameters(this.nanNumber(), this); +}; + +tcuFloat.FloatDescription.prototype.nanNumber = function() { + return deMath.shiftLeft(1, (this.ExponentBits + this.MantissaBits)) - 1; +}; + +/** + * Builds a tcuFloat.deFloat number based on the description and the given + * sign, exponent and mantissa values. + * @param {number} sign + * @param {number} exponent + * @param {number} mantissa + * @return {tcuFloat.deFloat} + */ +tcuFloat.FloatDescription.prototype.construct = function(sign, exponent, mantissa) { + // Repurpose this otherwise invalid input as a shorthand notation for zero (no need for caller to care about internal representation) + /** @type {boolean} */ var isShorthandZero = exponent == 0 && mantissa == 0; + + // Handles the typical notation for zero (min exponent, mantissa 0). Note that the exponent usually used exponent (-ExponentBias) for zero/subnormals is not used. + // Instead zero/subnormals have the (normally implicit) leading mantissa bit set to zero. + + /** @type {boolean} */ var isDenormOrZero = (exponent == 1 - this.ExponentBias) && (deMath.shiftRight(mantissa, this.MantissaBits) == 0); + /** @type {number} */ var s = deMath.shiftLeft((sign < 0 ? 1 : 0), (this.ExponentBits + this.MantissaBits)); + /** @type {number} */ var exp = (isShorthandZero || isDenormOrZero) ? 0 : exponent + this.ExponentBias; + + DE_ASSERT(sign == +1 || sign == -1); + DE_ASSERT(isShorthandZero || isDenormOrZero || deMath.shiftRight(mantissa, this.MantissaBits) == 1); + DE_ASSERT((exp >> this.ExponentBits) == 0); //Native shift is safe + + return tcuFloat.newDeFloatFromParameters( + deMath.binaryOp( + deMath.binaryOp( + s, + deMath.shiftLeft(exp, this.MantissaBits), + deMath.BinaryOp.OR + ), + deMath.binaryOp( + mantissa, + deMath.shiftLeft(1, this.MantissaBits) - 1, + deMath.BinaryOp.AND + ), + deMath.BinaryOp.OR + ), + this + ); +}; + +/** + * Builds a tcuFloat.deFloat number based on the description and the given + * sign, exponent and binary mantissa values. + * @param {number} sign + * @param {number} exponent + * @param {number} mantissaBits The raw binary representation. + * @return {tcuFloat.deFloat} + */ +tcuFloat.FloatDescription.prototype.constructBits = function(sign, exponent, mantissaBits) { + /** @type {number} */ var signBit = sign < 0 ? 1 : 0; + /** @type {number} */ var exponentBits = exponent + this.ExponentBias; + + DE_ASSERT(sign == +1 || sign == -1); + DE_ASSERT((exponentBits >> this.ExponentBits) == 0); + DE_ASSERT(deMath.shiftRight(mantissaBits, this.MantissaBits) == 0); + + return tcuFloat.newDeFloatFromParameters( + deMath.binaryOp( + deMath.binaryOp( + deMath.shiftLeft( + signBit, + this.ExponentBits + this.MantissaBits + ), + deMath.shiftLeft(exponentBits, this.MantissaBits), + deMath.BinaryOp.OR + ), + mantissaBits, + deMath.BinaryOp.OR + ), + this + ); +}; + +/** + * Converts a tcuFloat.deFloat from it's own format description into the format described + * by this description. + * @param {tcuFloat.deFloat} other Other float to convert to this format description. + * @return {tcuFloat.deFloat} converted tcuFloat.deFloat + */ +tcuFloat.FloatDescription.prototype.convert = function(other) { + /** @type {number} */ var otherExponentBits = other.description.ExponentBits; + /** @type {number} */ var otherMantissaBits = other.description.MantissaBits; + /** @type {number} */ var otherExponentBias = other.description.ExponentBias; + /** @type {number} */ var otherFlags = other.description.Flags; + + /** @type {number} */ var bitDiff; + /** @type {number} */ var half; + /** @type {number} */ var bias; + + if (!(this.Flags & tcuFloat.FloatFlags.FLOAT_HAS_SIGN) && other.sign() < 0) { + // Negative number, truncate to zero. + return this.zero(+1); + } else if (other.isInf()) { + return this.inf(other.sign()); + } else if (other.isNaN()) { + return this.nan(); + } else if (other.isZero()) { + return this.zero(other.sign()); + } else { + /** @type {number} */ var eMin = 1 - this.ExponentBias; + /** @type {number} */ var eMax = ((1 << this.ExponentBits) - 2) - this.ExponentBias; + + /** @type {number} */ var s = deMath.shiftLeft(other.signBit(), (this.ExponentBits + this.MantissaBits)); // \note Not sign, but sign bit. + /** @type {number} */ var e = other.exponent(); + /** @type {number} */ var m = other.mantissa(); + + // Normalize denormalized values prior to conversion. + while (!deMath.binaryOp(m, deMath.shiftLeft(1, otherMantissaBits), deMath.BinaryOp.AND)) { + m = deMath.shiftLeft(m, 1); + e -= 1; + } + + if (e < eMin) { + // Underflow. + if ((this.Flags & tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM) && (eMin - e - 1 <= this.MantissaBits)) { + // Shift and round (RTE). + bitDiff = (otherMantissaBits - this.MantissaBits) + (eMin - e); + half = deMath.shiftLeft(1, (bitDiff - 1)) - 1; + bias = deMath.binaryOp(deMath.shiftRight(m, bitDiff), 1, deMath.BinaryOp.AND); + + return tcuFloat.newDeFloatFromParameters( + deMath.binaryOp( + s, + deMath.shiftRight( + m + half + bias, + bitDiff + ), + deMath.BinaryOp.OR + ), + this + ); + } else + return this.zero(other.sign()); + } else { + // Remove leading 1. + m = deMath.binaryOp(m, deMath.binaryNot(deMath.shiftLeft(1, otherMantissaBits)), deMath.BinaryOp.AND); + + if (this.MantissaBits < otherMantissaBits) { + // Round mantissa (round to nearest even). + bitDiff = otherMantissaBits - this.MantissaBits; + half = deMath.shiftLeft(1, (bitDiff - 1)) - 1; + bias = deMath.binaryOp(deMath.shiftRight(m, bitDiff), 1, deMath.BinaryOp.AND); + + m = deMath.shiftRight(m + half + bias, bitDiff); + + if (deMath.binaryOp(m, deMath.shiftLeft(1, this.MantissaBits), deMath.BinaryOp.AND)) { + // Overflow in mantissa. + m = 0; + e += 1; + } + } else { + bitDiff = this.MantissaBits - otherMantissaBits; + m = deMath.shiftLeft(m, bitDiff); + } + + if (e > eMax) { + // Overflow. + return this.inf(other.sign()); + } else { + DE_ASSERT(deMath.deInRange32(e, eMin, eMax)); + DE_ASSERT(deMath.binaryOp((e + this.ExponentBias), deMath.binaryNot(deMath.shiftLeft(1, this.ExponentBits) - 1), deMath.BinaryOp.AND) == 0); + DE_ASSERT(deMath.binaryOp(m, deMath.binaryNot(deMath.shiftLeft(1, this.MantissaBits) - 1), deMath.BinaryOp.AND) == 0); + + return tcuFloat.newDeFloatFromParameters( + deMath.binaryOp( + deMath.binaryOp( + s, + deMath.shiftLeft( + e + this.ExponentBias, + this.MantissaBits + ), + deMath.BinaryOp.OR + ), + m, + deMath.BinaryOp.OR + ), + this + ); + } + } + } +}; + +/** + * tcuFloat.deFloat class - Empty constructor, builds a 32 bit float by default + * @constructor + */ +tcuFloat.deFloat = function() { + this.description = tcuFloat.description32; + + this.m_buffer = null; + this.m_array = null; + this.bitValue = undefined; + this.signValue = undefined; + this.expValue = undefined; + this.mantissaValue = undefined; + + this.m_value = 0; +}; + +/** + * buffer - Get the deFloat's existing ArrayBuffer or create one if none exists. + * @return {ArrayBuffer} + */ +tcuFloat.deFloat.prototype.buffer = function() { + if (!this.m_buffer) + this.m_buffer = new ArrayBuffer(this.description.totalByteSize); + return this.m_buffer; +}; + +/** + * array - Get the deFloat's existing Uint8Array or create one if none exists. + * @return {Uint8Array} + */ +tcuFloat.deFloat.prototype.array = function() { + if (!this.m_array) + this.m_array = new Uint8Array(this.buffer()); + return this.m_array; +}; + +/** + * deFloatNumber - To be used immediately after constructor + * Builds a 32-bit tcuFloat.deFloat based on a 64-bit JS number. + * @param {number} jsnumber + * @return {tcuFloat.deFloat} + */ +tcuFloat.deFloat.prototype.deFloatNumber = function(jsnumber) { + var view32 = new DataView(this.buffer()); + view32.setFloat32(0, jsnumber, true); //little-endian + this.m_value = view32.getFloat32(0, true); //little-endian + + // Clear cached values + this.bitValue = undefined; + this.signValue = undefined; + this.expValue = undefined; + this.mantissaValue = undefined; + + return this; +}; + +/** + * Convenience function to build a 32-bit tcuFloat.deFloat based on a 64-bit JS number + * Builds a 32-bit tcuFloat.deFloat based on a 64-bit JS number. + * @param {number} jsnumber + * @return {tcuFloat.deFloat} + */ +tcuFloat.newDeFloatFromNumber = function(jsnumber) { + return new tcuFloat.deFloat().deFloatNumber(jsnumber); +}; + +/** + * deFloatBuffer - To be used immediately after constructor + * Builds a tcuFloat.deFloat based on a buffer and a format description. + * The buffer is assumed to contain data of the given description. + * @param {ArrayBuffer} buffer + * @param {tcuFloat.FloatDescription} description + * @return {tcuFloat.deFloat} + */ +tcuFloat.deFloat.prototype.deFloatBuffer = function(buffer, description) { + this.m_buffer = buffer; + this.m_array = new Uint8Array(this.m_buffer); + + this.m_value = deMath.arrayToNumber(this.m_array); + + // Clear cached values + this.bitValue = undefined; + this.signValue = undefined; + this.expValue = undefined; + this.mantissaValue = undefined; + + return this; +}; + +/** + * Convenience function to build a tcuFloat.deFloat based on a buffer and a format description + * The buffer is assumed to contain data of the given description. + * @param {ArrayBuffer} buffer + * @param {tcuFloat.FloatDescription} description + * @return {tcuFloat.deFloat} + */ +tcuFloat.newDeFloatFromBuffer = function(buffer, description) { + return new tcuFloat.deFloat().deFloatBuffer(buffer, description); +}; + +/** + * Set the tcuFloat.deFloat to the given number. + * It does not perform any conversion; it assumes the number is compatible with + * the previously set description. + * @param {number} jsnumber + * @return {tcuFloat.deFloat} + **/ +tcuFloat.deFloat.prototype.deFloatParametersNumber = function(jsnumber) { + this.m_value = jsnumber; + deMath.numberToArray(this.m_array, jsnumber); + + // Clear cached values + this.bitValue = undefined; + this.signValue = undefined; + this.expValue = undefined; + this.mantissaValue = undefined; + + return this; +}; + +/** + * Initializes a tcuFloat.deFloat from the given number, + * with the specified format description. + * It does not perform any conversion; it assumes the number is compatible with + * the given description. + * @param {number} jsnumber + * @param {tcuFloat.FloatDescription} description + * @return {tcuFloat.deFloat} + **/ +tcuFloat.deFloat.prototype.deFloatParameters = function(jsnumber, description) { + this.description = description; + + this.m_buffer = new ArrayBuffer(this.description.totalByteSize); + this.m_array = new Uint8Array(this.m_buffer); + + return this.deFloatParametersNumber(jsnumber); +}; + +/** + * Convenience function to create a tcuFloat.deFloat from the given number, + * with the specified format description. + * It does not perform any conversion; it assumes the number is compatible with + * the given description. + * @param {number} jsnumber + * @param {tcuFloat.FloatDescription} description + * @return {tcuFloat.deFloat} + **/ +tcuFloat.newDeFloatFromParameters = function(jsnumber, description) { + return new tcuFloat.deFloat().deFloatParameters(jsnumber, description); +}; + +/** + * Returns bit range [begin, end) + * @param {number} begin + * @param {number} end + * @return {number} + */ +tcuFloat.deFloat.prototype.getBitRange = function(begin, end) { + return deMath.getBitRange(this.bits(), begin, end); +}; + +/** + * Returns the raw binary representation value of the tcuFloat.deFloat + * @return {number} + */ +tcuFloat.deFloat.prototype.bits = function() { + if (typeof this.bitValue === 'undefined') + this.bitValue = deMath.arrayToNumber(this.array()); + return this.bitValue; +}; + +/** + * Returns the raw binary sign bit + * @return {number} + */ +tcuFloat.deFloat.prototype.signBit = function() { + if (typeof this.signValue === 'undefined') + this.signValue = this.getBitRange(this.description.totalBitSize - 1, this.description.totalBitSize); + return this.signValue; +}; + +/** + * Returns the raw binary exponent bits + * @return {number} + */ +tcuFloat.deFloat.prototype.exponentBits = function() { + if (typeof this.expValue === 'undefined') + this.expValue = this.getBitRange(this.description.MantissaBits, this.description.MantissaBits + this.description.ExponentBits); + return this.expValue; +}; + +/** + * Returns the raw binary mantissa bits + * @return {number} + */ +tcuFloat.deFloat.prototype.mantissaBits = function() { + if (typeof this.mantissaValue === 'undefined') + this.mantissaValue = this.getBitRange(0, this.description.MantissaBits); + return this.mantissaValue; +}; + +/** + * Returns the sign as a factor (-1 or 1) + * @return {number} + */ +tcuFloat.deFloat.prototype.sign = function() { + var sign = this.signBit(); + var signvalue = sign ? -1 : 1; + return signvalue; +}; + +/** + * Returns the real exponent, checking if it's a denorm or zero number or not + * @return {number} + */ +tcuFloat.deFloat.prototype.exponent = function() {return this.isDenorm() ? 1 - this.description.ExponentBias : this.exponentBits() - this.description.ExponentBias;}; + +/** + * Returns the (still raw) mantissa, checking if it's a denorm or zero number or not + * Makes the normally implicit bit explicit. + * @return {number} + */ +tcuFloat.deFloat.prototype.mantissa = function() {return this.isZero() || this.isDenorm() ? this.mantissaBits() : deMath.binaryOp(this.mantissaBits(), deMath.shiftLeft(1, this.description.MantissaBits), deMath.BinaryOp.OR);}; + +/** + * Returns if the number is infinity or not. + * @return {boolean} + */ +tcuFloat.deFloat.prototype.isInf = function() {return this.exponentBits() == ((1 << this.description.ExponentBits) - 1) && this.mantissaBits() == 0;}; + +/** + * Returns if the number is NaN or not. + * @return {boolean} + */ +tcuFloat.deFloat.prototype.isNaN = function() {return this.exponentBits() == ((1 << this.description.ExponentBits) - 1) && this.mantissaBits() != 0;}; + +/** + * Returns if the number is zero or not. + * @return {boolean} + */ +tcuFloat.deFloat.prototype.isZero = function() {return this.exponentBits() == 0 && this.mantissaBits() == 0;}; + +/** + * Returns if the number is denormalized or not. + * @return {boolean} + */ +tcuFloat.deFloat.prototype.isDenorm = function() {return this.exponentBits() == 0 && this.mantissaBits() != 0;}; + +/** + * Builds a zero float of the current binary description. + * @param {number} sign + * @return {tcuFloat.deFloat} + */ +tcuFloat.deFloat.prototype.zero = function(sign) { + return this.description.zero(sign); +}; + +/** + * Builds an infinity float representation of the current binary description. + * @param {number} sign + * @return {tcuFloat.deFloat} + */ +tcuFloat.deFloat.prototype.inf = function(sign) { + return this.description.inf(sign); +}; + +/** + * Builds a NaN float representation of the current binary description. + * @return {tcuFloat.deFloat} + */ +tcuFloat.deFloat.prototype.nan = function() { + return this.description.nan(); +}; + +/** + * Builds a float of the current binary description. + * Given a sign, exponent and mantissa. + * @param {number} sign + * @param {number} exponent + * @param {number} mantissa + * @return {tcuFloat.deFloat} + */ +tcuFloat.deFloat.prototype.construct = function(sign, exponent, mantissa) { + return this.description.construct(sign, exponent, mantissa); +}; + +/** + * Builds a float of the current binary description. + * Given a sign, exponent and a raw binary mantissa. + * @param {number} sign + * @param {number} exponent + * @param {number} mantissaBits Raw binary mantissa. + * @return {tcuFloat.deFloat} + */ +tcuFloat.deFloat.prototype.constructBits = function(sign, exponent, mantissaBits) { + return this.description.constructBits(sign, exponent, mantissaBits); +}; + +/** + * Calculates the JS float number from the internal representation. + * @return {number} The JS float value represented by this tcuFloat.deFloat. + */ +tcuFloat.deFloat.prototype.getValue = function() { + if ((this.description.Flags | tcuFloat.FloatFlags.FLOAT_HAS_SIGN) === 0 && this.sign() < 0) + return 0; + if (this.isInf()) + return Number.Infinity; + if (this.isNaN()) + return Number.NaN; + if (this.isZero()) + return this.sign() * 0; + /**@type {number} */ var mymantissa = this.mantissa(); + /**@type {number} */ var myexponent = this.exponent(); + /**@type {number} */ var sign = this.sign(); + + /**@type {number} */ var value = mymantissa / Math.pow(2, this.description.MantissaBits) * Math.pow(2, myexponent); + + if (this.description.Flags | tcuFloat.FloatFlags.FLOAT_HAS_SIGN != 0) + value = value * sign; + + return value; +}; + +tcuFloat.description10 = new tcuFloat.FloatDescription(5, 5, 15, 0); +tcuFloat.description11 = new tcuFloat.FloatDescription(5, 6, 15, 0); +tcuFloat.description16 = new tcuFloat.FloatDescription(5, 10, 15, tcuFloat.FloatFlags.FLOAT_HAS_SIGN); +tcuFloat.description32 = new tcuFloat.FloatDescription(8, 23, 127, tcuFloat.FloatFlags.FLOAT_HAS_SIGN | tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM); +tcuFloat.description64 = new tcuFloat.FloatDescription(11, 52, 1023, tcuFloat.FloatFlags.FLOAT_HAS_SIGN | tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM); + +tcuFloat.convertFloat32Inline = (function() { + var float32View = new Float32Array(1); + var int32View = new Int32Array(float32View.buffer); + + return function(fval, description) { + float32View[0] = fval; + var fbits = int32View[0]; + + var exponentBits = (fbits >> 23) & 0xff; + var mantissaBits = fbits & 0x7fffff; + var signBit = (fbits & 0x80000000) ? 1 : 0; + var sign = signBit ? -1 : 1; + + var isZero = exponentBits == 0 && mantissaBits == 0; + + var bitDiff; + var half; + var bias; + + if (!(description.Flags & tcuFloat.FloatFlags.FLOAT_HAS_SIGN) && sign < 0) { + // Negative number, truncate to zero. + return description.zeroNumber(+1); + } else if (exponentBits == ((1 << tcuFloat.description32.ExponentBits) - 1) && mantissaBits == 0) { // isInf + return description.infNumber(sign); + } else if (exponentBits == ((1 << tcuFloat.description32.ExponentBits) - 1) && mantissaBits != 0) { // isNaN + return description.nanNumber(); + } else if (isZero) { + return description.zeroNumber(sign); + } else { + var eMin = 1 - description.ExponentBias; + var eMax = ((1 << description.ExponentBits) - 2) - description.ExponentBias; + + var isDenorm = exponentBits == 0 && mantissaBits != 0; + + var s = signBit << (description.ExponentBits + description.MantissaBits); // \note Not sign, but sign bit. + var e = isDenorm ? 1 - tcuFloat.description32.ExponentBias : exponentBits - tcuFloat.description32.ExponentBias;// other.exponent(); + var m = isZero || isDenorm ? mantissaBits : mantissaBits | (1 << tcuFloat.description32.MantissaBits); // other.mantissa(); + + // Normalize denormalized values prior to conversion. + while (!(m & (1 << tcuFloat.description32.MantissaBits))) { + m = deMath.shiftLeft(m, 1); + e -= 1; + } + + if (e < eMin) { + // Underflow. + if ((description.Flags & tcuFloat.FloatFlags.FLOAT_SUPPORT_DENORM) && (eMin - e - 1 <= description.MantissaBits)) { + // Shift and round (RTE). + bitDiff = (tcuFloat.description32.MantissaBits - description.MantissaBits) + (eMin - e); + half = (1 << (bitDiff - 1)) - 1; + bias = ((m >> bitDiff) & 1); + return (s | ((m + half + bias) >> bitDiff)); + } else + return description.zeroNumber(sign); + } else { + // Remove leading 1. + m = (m & ~(1 << tcuFloat.description32.MantissaBits)); + + if (description.MantissaBits < tcuFloat.description32.MantissaBits) { + // Round mantissa (round to nearest even). + bitDiff = tcuFloat.description32.MantissaBits - description.MantissaBits; + half = (1 << (bitDiff - 1)) - 1; + bias = ((m >> bitDiff) & 1); + + m = (m + half + bias) >> bitDiff; + + if ((m & (1 << description.MantissaBits))) { + // Overflow in mantissa. + m = 0; + e += 1; + } + } else { + bitDiff = description.MantissaBits - tcuFloat.description32.MantissaBits; + m = (m << bitDiff); + } + + if (e > eMax) { + // Overflow. + return description.infNumber(sign); + } else { + DE_ASSERT(deMath.deInRange32(e, eMin, eMax)); + DE_ASSERT(((e + description.ExponentBias) & ~((1 << description.ExponentBits) - 1)) == 0); + DE_ASSERT((m & ~((1 << description.MantissaBits) - 1)) == 0); + + return (s | ((e + description.ExponentBias) << description.MantissaBits)) | m; + } + } + } + }; +})(); + +/** + * Builds a 10 bit tcuFloat.deFloat + * @param {number} value (64-bit JS float) + * @return {tcuFloat.deFloat} + */ +tcuFloat.newFloat10 = function(value) { + /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value); + return tcuFloat.description10.convert(other32); +}; + +/** + * Builds a 11 bit tcuFloat.deFloat + * @param {number} value (64-bit JS float) + * @return {tcuFloat.deFloat} + */ +tcuFloat.newFloat11 = function(value) { + /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value); + return tcuFloat.description11.convert(other32); +}; + +/** + * Builds a 16 bit tcuFloat.deFloat + * @param {number} value (64-bit JS float) + * @return {tcuFloat.deFloat} + */ +tcuFloat.newFloat16 = function(value) { + /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value); + return tcuFloat.description16.convert(other32); +}; + +/** + * Builds a 16 bit tcuFloat.deFloat from raw bits + * @param {number} value (16-bit value) + * @return {tcuFloat.deFloat} + */ +tcuFloat.newFloat32From16 = function(value) { + var other16 = tcuFloat.newDeFloatFromParameters(value, tcuFloat.description16); + return tcuFloat.description32.convert(other16); +}; + +/** + * Builds a 16 bit tcuFloat.deFloat with no denorm support + * @param {number} value (64-bit JS float) + * @return {tcuFloat.deFloat} + */ +tcuFloat.newFloat16NoDenorm = function(value) { + /**@type {tcuFloat.deFloat} */ var other32 = new tcuFloat.deFloat().deFloatNumber(value); + return tcuFloat.description16.convert(other32); +}; + +/** + * Builds a 32 bit tcuFloat.deFloat + * @param {number} value (64-bit JS float) + * @return {tcuFloat.deFloat} + */ +tcuFloat.newFloat32 = function(value) { + return new tcuFloat.deFloat().deFloatNumber(value); +}; + +tcuFloat.numberToFloat11 = function(value) { + return tcuFloat.convertFloat32Inline(value, tcuFloat.description11); +}; + +tcuFloat.float11ToNumber = (function() { + var x = tcuFloat.newDeFloatFromParameters(0, tcuFloat.description11); + return function(float11) { + x.deFloatParametersNumber(float11); + return x.getValue(); + }; +})(); + +tcuFloat.numberToFloat10 = function(value) { + return tcuFloat.convertFloat32Inline(value, tcuFloat.description10); +}; + +tcuFloat.float10ToNumber = (function() { + var x = tcuFloat.newDeFloatFromParameters(0, tcuFloat.description10); + return function(float10) { + x.deFloatParametersNumber(float10); + return x.getValue(); + }; +})(); + +tcuFloat.numberToHalfFloat = function(value) { + return tcuFloat.convertFloat32Inline(value, tcuFloat.description16); +}; + +tcuFloat.numberToHalfFloatNoDenorm = function(value) { + return tcuFloat.newFloat16NoDenorm(value).bits(); +}; + +tcuFloat.halfFloatToNumber = (function() { + var x = tcuFloat.newDeFloatFromParameters(0, tcuFloat.description16); + return function(half) { + x.deFloatParametersNumber(half); + return x.getValue(); + }; +})(); + +tcuFloat.halfFloatToNumberNoDenorm = tcuFloat.halfFloatToNumber; + +/** + * Builds a 64 bit tcuFloat.deFloat + * @param {number} value (64-bit JS float) + * @return {tcuFloat.deFloat} + */ +tcuFloat.newFloat64 = function(value) { + return new tcuFloat.deFloat().deFloatParameters(value, tcuFloat.description64); +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFloatFormat.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFloatFormat.js new file mode 100644 index 000000000..a0b4dc82c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFloatFormat.js @@ -0,0 +1,349 @@ +/*------------------------------------------------------------------------- + * drawElements Quality Program Tester Core + * ---------------------------------------- + * + * 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. + * + *//*! + * \file + * \brief Adjustable-precision floating point operations. + *//*--------------------------------------------------------------------*/ + 'use strict'; + goog.provide('framework.common.tcuFloatFormat'); + + goog.require('framework.common.tcuInterval'); +goog.require('framework.delibs.debase.deMath'); + + goog.scope(function() { + + var tcuFloatFormat = framework.common.tcuFloatFormat; + var deMath = framework.delibs.debase.deMath; + var tcuInterval = framework.common.tcuInterval; + + /** + * @param {tcuFloatFormat.YesNoMaybe} choice + * @param {tcuInterval.Interval} no + * @param {tcuInterval.Interval} yes + * @return {tcuInterval.Interval} + */ + tcuFloatFormat.chooseInterval = function(choice, no, yes) { + switch (choice) { + case tcuFloatFormat.YesNoMaybe.NO: return no; + case tcuFloatFormat.YesNoMaybe.YES: return yes; + case tcuFloatFormat.YesNoMaybe.MAYBE: return no.operatorOrBinary(yes); + default: throw new Error('Impossible case'); + } + }; + + /** + * @param {number} maxExp + * @param {number} fractionBits + * @return {number} + */ + tcuFloatFormat.computeMaxValue = function(maxExp, fractionBits) { + return deMath.deLdExp(1, maxExp) + deMath.deLdExp(Math.pow(2, fractionBits) - 1, maxExp - fractionBits); + }; + + /** + * @enum {number} + */ + tcuFloatFormat.YesNoMaybe = { + NO: 0, + MAYBE: 1, + YES: 2 + }; + + /** + * @constructor + * @param {number} minExp + * @param {number} maxExp + * @param {number} fractionBits + * @param {boolean} exactPrecision + * @param {tcuFloatFormat.YesNoMaybe=} hasSubnormal + * @param {tcuFloatFormat.YesNoMaybe=} hasInf + * @param {tcuFloatFormat.YesNoMaybe=} hasNaN + */ + tcuFloatFormat.FloatFormat = function(minExp, maxExp, fractionBits, exactPrecision, hasSubnormal, hasInf, hasNaN) { + // /** @type{number} */ var exponentShift (int exp) const; + // Interval clampValue (double d) const; + + /** @type {number} */ this.m_minExp = minExp; // Minimum exponent, inclusive + /** @type {number} */ this.m_maxExp = maxExp; // Maximum exponent, inclusive + /** @type {number} */ this.m_fractionBits = fractionBits; // Number of fractional bits in significand + /** @type {tcuFloatFormat.YesNoMaybe} */ this.m_hasSubnormal = hasSubnormal === undefined ? tcuFloatFormat.YesNoMaybe.MAYBE : hasSubnormal; // Does the format support denormalized numbers? + /** @type {tcuFloatFormat.YesNoMaybe} */ this.m_hasInf = hasInf === undefined ? tcuFloatFormat.YesNoMaybe.MAYBE : hasInf; // Does the format support infinities? + /** @type {tcuFloatFormat.YesNoMaybe} */ this.m_hasNaN = hasNaN === undefined ? tcuFloatFormat.YesNoMaybe.MAYBE : hasNaN; // Does the format support NaNs? + /** @type {boolean} */ this.m_exactPrecision = exactPrecision; // Are larger precisions disallowed? + /** @type {number} */ this.m_maxValue = tcuFloatFormat.computeMaxValue(maxExp, fractionBits); + }; + + /** + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.getMinExp = function() { + return this.m_minExp; + }; + + /** + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.getMaxExp = function() { + return this.m_maxExp; + }; + + /** + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.getMaxValue = function() { + return this.m_maxValue; + }; + + /** + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.getFractionBits = function() { + return this.m_fractionBits; + }; + + /** + * @return {tcuFloatFormat.YesNoMaybe} + */ + tcuFloatFormat.FloatFormat.prototype.hasSubnormal = function() { + return this.m_hasSubnormal; + }; + + /** + * @return {tcuFloatFormat.YesNoMaybe} + */ + tcuFloatFormat.FloatFormat.prototype.hasInf = function() { + return this.m_hasInf; + }; + + /** + * @param {number} x + * @param {number} count + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.ulp = function(x, count) { + var breakdown = deMath.deFractExp(Math.abs(x)); + /** @type {number} */ var exp = breakdown.exponent; + /** @type {number} */ var frac = breakdown.significand; + + if (isNaN(frac)) + return NaN; + else if (!isFinite(frac)) + return deMath.deLdExp(1.0, this.m_maxExp - this.m_fractionBits); + else if (frac == 1.0) { + // Harrison's ULP: choose distance to closest (i.e. next lower) at binade + // boundary. + --exp; + } else if (frac == 0.0) + exp = this.m_minExp; + + // ULP cannot be lower than the smallest quantum. + exp = Math.max(exp, this.m_minExp); + + /** @type {number} */ var oneULP = deMath.deLdExp(1.0, exp - this.m_fractionBits); + // ScopedRoundingMode ctx (DE_ROUNDINGMODE_TO_POSITIVE_INF); + + return oneULP * count; + }; + + /** + * Return the difference between the given nominal exponent and + * the exponent of the lowest significand bit of the + * representation of a number with this format. + * For normal numbers this is the number of significand bits, but + * for subnormals it is less and for values of exp where 2^exp is too + * small to represent it is <0 + * @param {number} exp + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.exponentShift = function(exp) { + return this.m_fractionBits - Math.max(this.m_minExp - exp, 0); + }; + + /** + * @param {number} d + * @param {boolean} upward + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.round = function(d, upward) { + var breakdown = deMath.deFractExp(d); + /** @type {number} */ var exp = breakdown.exponent; + /** @type {number} */ var frac = breakdown.significand; + + var shift = this.exponentShift(exp); + var shiftFrac = deMath.deLdExp(frac, shift); + var roundFrac = upward ? Math.ceil(shiftFrac) : Math.floor(shiftFrac); + + return deMath.deLdExp(roundFrac, exp - shift); + }; + + /** + * Return the range of numbers that `d` might be converted to in the + * floatformat, given its limitations with infinities, subnormals and maximum + * exponent. + * @param {number} d + * @return {tcuInterval.Interval} + */ + tcuFloatFormat.FloatFormat.prototype.clampValue = function(d) { + /** @type {number} */ var rSign = deMath.deSign(d); + /** @type {number} */ var rExp = 0; + + // DE_ASSERT(!isNaN(d)); + + var breakdown = deMath.deFractExp(d); + rExp = breakdown.exponent; + if (rExp < this.m_minExp) + return tcuFloatFormat.chooseInterval(this.m_hasSubnormal, new tcuInterval.Interval(rSign * 0.0), new tcuInterval.Interval(d)); + else if (!isFinite(d) || rExp > this.m_maxExp) + return tcuFloatFormat.chooseInterval(this.m_hasInf, new tcuInterval.Interval(rSign * this.getMaxValue()), new tcuInterval.Interval(rSign * Number.POSITIVE_INFINITY)); + + return new tcuInterval.Interval(d); + }; + + /** + * @param {number} d + * @param {boolean} upward + * @param {boolean} roundUnderOverflow + * @return {number} + */ + tcuFloatFormat.FloatFormat.prototype.roundOutDir = function(d, upward, roundUnderOverflow) { + var breakdown = deMath.deFractExp(d); + var exp = breakdown.exponent; + + if (roundUnderOverflow && exp > this.m_maxExp && (upward == (d < 0.0))) + return deMath.deSign(d) * this.getMaxValue(); + else + return this.round(d, upward); + }; + + /** + * @param {tcuInterval.Interval} x + * @param {boolean} roundUnderOverflow + * @return {tcuInterval.Interval} + */ + tcuFloatFormat.FloatFormat.prototype.roundOut = function(x, roundUnderOverflow) { + /** @type {tcuInterval.Interval} */ var ret = x.nan(); + + if (!x.empty()) { + var a = new tcuInterval.Interval(this.roundOutDir(x.lo(), false, roundUnderOverflow)); + var b = new tcuInterval.Interval(this.roundOutDir(x.hi(), true, roundUnderOverflow)); + ret.operatorOrAssignBinary(tcuInterval.withIntervals(a, b)); + } + return ret; + }; + + //! Return the range of numbers that might be used with this format to + //! represent a number within `x`. + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuFloatFormat.FloatFormat.prototype.convert = function(x) { + /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); + /** @type {tcuInterval.Interval} */ var tmp = x; + + if (x.hasNaN()) { + // If NaN might be supported, NaN is a legal return value + if (this.m_hasNaN != tcuFloatFormat.YesNoMaybe.NO) + ret.operatorOrAssignBinary(new tcuInterval.Interval(NaN)); + + // If NaN might not be supported, any (non-NaN) value is legal, + // _subject_ to clamping. Hence we modify tmp, not ret. + if (this.m_hasNaN != tcuFloatFormat.YesNoMaybe.YES) + tmp = tcuInterval.unbounded(); + } + + // Round both bounds _inwards_ to closest representable values. + if (!tmp.empty()) + ret.operatorOrAssignBinary( + this.clampValue(this.round(tmp.lo(), true)).operatorOrBinary( + this.clampValue(this.round(tmp.hi(), false)))); + + // If this format's precision is not exact, the (possibly out-of-bounds) + // original value is also a possible result. + if (!this.m_exactPrecision) + ret.operatorOrAssignBinary(x); + + return ret; + }; + + /** + * @param {number} x + * @return {string} + */ + tcuFloatFormat.FloatFormat.prototype.floatToHex = function(x) { + if (isNaN(x)) + return 'NaN'; + else if (!isFinite(x)) + return (x < 0.0 ? '-' : '+') + ('inf'); + else if (x == 0.0) // \todo [2014-03-27 lauri] Negative zero + return '0.0'; + + return x.toString(10); + // TODO + // var breakdown = deMath.deFractExp(deAbs(x)); + // /** @type{number} */ var exp = breakdown.exponent; + // /** @type{number} */ var frac = breakdown.significand; + // /** @type{number} */ var shift = this.exponentShift(exp); + // /** @type{number} */ var bits = deUint64(deLdExp(frac, shift)); + // /** @type{number} */ var whole = bits >> m_fractionBits; + // /** @type{number} */ var fraction = bits & ((deUint64(1) << m_fractionBits) - 1); + // /** @type{number} */ var exponent = exp + m_fractionBits - shift; + // /** @type{number} */ var numDigits = (this.m_fractionBits + 3) / 4; + // /** @type{number} */ var aligned = fraction << (numDigits * 4 - m_fractionBits); + // /** @type{string} */ var oss = ''; + + // oss + (x < 0 ? '-' : '') + // + '0x' + whole + '.' + // + std::hex + std::setw(numDigits) + std::setfill('0') + aligned + // + 'p' + std::dec + std::setw(0) + exponent; + //return oss; + }; + + /** + * @param {tcuInterval.Interval} interval + * @return {string} + */ + tcuFloatFormat.FloatFormat.prototype.intervalToHex = function(interval) { + if (interval.empty()) + return interval.hasNaN() ? '{ NaN }' : '{}'; + + else if (interval.lo() == interval.hi()) + return ((interval.hasNaN() ? '{ NaN, ' : '{ ') + + this.floatToHex(interval.lo()) + ' }'); + else if (interval == tcuInterval.unbounded(true)) + return '<any>'; + + return ((interval.hasNaN() ? '{ NaN } | ' : '') + + '[' + this.floatToHex(interval.lo()) + ', ' + this.floatToHex(interval.hi()) + ']'); + }; + + /** + * @return {tcuFloatFormat.FloatFormat} + */ + tcuFloatFormat.nativeDouble = function() { + return new tcuFloatFormat.FloatFormat(-1021 - 1, // min_exponent + 1024 - 1, // max_exponent + 53 - 1, // digits + true, // has_denorm + tcuFloatFormat.YesNoMaybe.YES, // has_infinity + tcuFloatFormat.YesNoMaybe.YES, // has_quiet_nan + tcuFloatFormat.YesNoMaybe.YES); // has_denorm + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFuzzyImageCompare.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFuzzyImageCompare.js new file mode 100644 index 000000000..aea5bf7e0 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuFuzzyImageCompare.js @@ -0,0 +1,338 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuFuzzyImageCompare'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.delibs.debase.deRandom'); + +goog.scope(function() { + +var tcuFuzzyImageCompare = framework.common.tcuFuzzyImageCompare; +var deMath = framework.delibs.debase.deMath; +var deRandom = framework.delibs.debase.deRandom; +var tcuTexture = framework.common.tcuTexture; +var tcuTextureUtil = framework.common.tcuTextureUtil; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * tcuFuzzyImageCompare.FuzzyCompareParams struct + * @constructor + * @param {number=} maxSampleSkip_ + * @param {number=} minErrThreshold_ + * @param {number=} errExp_ + */ + tcuFuzzyImageCompare.FuzzyCompareParams = function(maxSampleSkip_, minErrThreshold_, errExp_) { + /** @type {number} */ this.maxSampleSkip = maxSampleSkip_ === undefined ? 8 : maxSampleSkip_; + /** @type {number} */ this.minErrThreshold = minErrThreshold_ === undefined ? 4 : minErrThreshold_; + /** @type {number} */ this.errExp = errExp_ === undefined ? 4.0 : errExp_; + }; + + /** + * @param {Array<number>} v + * @return {Array<number>} + */ + tcuFuzzyImageCompare.roundArray4ToUint8Sat = function(v) { + return [ + deMath.clamp(Math.trunc(v[0] + 0.5), 0, 255), + deMath.clamp(Math.trunc(v[1] + 0.5), 0, 255), + deMath.clamp(Math.trunc(v[2] + 0.5), 0, 255), + deMath.clamp(Math.trunc(v[3] + 0.5), 0, 255) + ]; + }; + + /** + * @param {Array<number>} pa + * @param {Array<number>} pb + * @param {number} minErrThreshold + * @return {number} + */ + tcuFuzzyImageCompare.compareColors = function(pa, pb, minErrThreshold) { + /** @type {number}*/ var r = Math.max(Math.abs(pa[0] - pb[0]) - minErrThreshold, 0); + /** @type {number}*/ var g = Math.max(Math.abs(pa[1] - pb[1]) - minErrThreshold, 0); + /** @type {number}*/ var b = Math.max(Math.abs(pa[2] - pb[2]) - minErrThreshold, 0); + /** @type {number}*/ var a = Math.max(Math.abs(pa[3] - pb[3]) - minErrThreshold, 0); + + /** @type {number}*/ var scale = 1.0 / (255 - minErrThreshold); + /** @type {number}*/ var sqSum = (r * r + g * g + b * b + a * a) * (scale * scale); + + return Math.sqrt(sqSum); + }; + + /** + * @param {tcuTexture.RGBA8View} src + * @param {number} u + * @param {number} v + * @param {number} NumChannels + * @return {Array<number>} + */ + tcuFuzzyImageCompare.bilinearSample = function(src, u, v, NumChannels) { + /** @type {number}*/ var w = src.width; + /** @type {number}*/ var h = src.height; + + /** @type {number}*/ var x0 = Math.floor(u - 0.5); + /** @type {number}*/ var x1 = x0 + 1; + /** @type {number}*/ var y0 = Math.floor(v - 0.5); + /** @type {number}*/ var y1 = y0 + 1; + + /** @type {number}*/ var i0 = deMath.clamp(x0, 0, w - 1); + /** @type {number}*/ var i1 = deMath.clamp(x1, 0, w - 1); + /** @type {number}*/ var j0 = deMath.clamp(y0, 0, h - 1); + /** @type {number}*/ var j1 = deMath.clamp(y1, 0, h - 1); + + /** @type {number}*/ var a = (u - 0.5) - Math.floor(u - 0.5); + /** @type {number}*/ var b = (u - 0.5) - Math.floor(u - 0.5); + + /** @type {Array<number>} */ var p00 = src.read(i0, j0, NumChannels); + /** @type {Array<number>} */ var p10 = src.read(i1, j0, NumChannels); + /** @type {Array<number>} */ var p01 = src.read(i0, j1, NumChannels); + /** @type {Array<number>} */ var p11 = src.read(i1, j1, NumChannels); + /** @type {number} */ var dst = 0; + + // Interpolate. + /** @type {Array<number>}*/ var f = []; + for (var c = 0; c < NumChannels; c++) { + f[c] = p00[c] * (1.0 - a) * (1.0 - b) + + (p10[c] * a * (1.0 - b)) + + (p01[c] * (1.0 - a) * b) + + (p11[c] * a * b); + } + + return tcuFuzzyImageCompare.roundArray4ToUint8Sat(f); + }; + + /** + * @param {tcuTexture.RGBA8View} dst + * @param {tcuTexture.RGBA8View} src + * @param {number} shiftX + * @param {number} shiftY + * @param {Array<number>} kernelX + * @param {Array<number>} kernelY + * @param {number} DstChannels + * @param {number} SrcChannels + */ + tcuFuzzyImageCompare.separableConvolve = function(dst, src, shiftX, shiftY, kernelX, kernelY, DstChannels, SrcChannels) { + DE_ASSERT(dst.width == src.width && dst.height == src.height); + + /** @type {tcuTexture.TextureLevel} */ var tmp = new tcuTexture.TextureLevel(dst.getFormat(), dst.height, dst.width); + var tmpView = new tcuTexture.RGBA8View(tmp.getAccess()); + + /** @type {number} */ var kw = kernelX.length; + /** @type {number} */ var kh = kernelY.length; + + /** @type {Array<number>} */ var sum = []; + /** @type {number} */ var f; + /** @type {Array<number>} */ var p; + + // Horizontal pass + // \note Temporary surface is written in column-wise order + for (var j = 0; j < src.height; j++) { + for (var i = 0; i < src.width; i++) { + sum[0] = sum[1] = sum[2] = sum[3] = 0; + for (var kx = 0; kx < kw; kx++) { + f = kernelX[kw - kx - 1]; + p = src.read(deMath.clamp(i + kx - shiftX, 0, src.width - 1), j, SrcChannels); + sum = deMath.add(sum, deMath.scale(p, f)); + } + + sum = tcuFuzzyImageCompare.roundArray4ToUint8Sat(sum); + tmpView.write(j, i, sum, DstChannels); + } + } + + // Vertical pass + for (var j = 0; j < src.height; j++) { + for (var i = 0; i < src.width; i++) { + sum[0] = sum[1] = sum[2] = sum[3] = 0; + for (var ky = 0; ky < kh; ky++) { + f = kernelY[kh - ky - 1]; + p = tmpView.read(deMath.clamp(j + ky - shiftY, 0, tmpView.width - 1), i, DstChannels); + sum = deMath.add(sum, deMath.scale(p, f)); + } + + sum = tcuFuzzyImageCompare.roundArray4ToUint8Sat(sum); + dst.write(i, j, sum, DstChannels); + } + } + }; + + /** + * @param {tcuFuzzyImageCompare.FuzzyCompareParams} params + * @param {deRandom.Random} rnd + * @param {Array<number>} pixel + * @param {tcuTexture.RGBA8View} surface + * @param {number} x + * @param {number} y + * @param {number} NumChannels + * @return {number} + */ + tcuFuzzyImageCompare.compareToNeighbor = function(params, rnd, pixel, surface, x, y, NumChannels) { + /** @type {number} */ var minErr = 100; + + // (x, y) + (0, 0) + minErr = Math.min(minErr, tcuFuzzyImageCompare.compareColors(pixel, surface.read(x, y, NumChannels), params.minErrThreshold)); + if (minErr == 0.0) + return minErr; + + // Area around (x, y) + /** @type {Array<Array.<number>>} */ var s_coords = + [ + [-1, -1], + [0, -1], + [1, -1], + [-1, 0], + [1, 0], + [-1, 1], + [0, 1], + [1, 1] + ]; + + /** @type {number} */ var dx; + /** @type {number} */ var dy; + + for (var d = 0; d < s_coords.length; d++) { + dx = x + s_coords[d][0]; + dy = y + s_coords[d][1]; + + if (!deMath.deInBounds32(dx, 0, surface.width) || !deMath.deInBounds32(dy, 0, surface.height)) + continue; + + minErr = Math.min(minErr, tcuFuzzyImageCompare.compareColors(pixel, surface.read(dx, dy, NumChannels), params.minErrThreshold)); + if (minErr == 0.0) + return minErr; + } + + // Random bilinear-interpolated samples around (x, y) + for (var s = 0; s < 32; s++) { + dx = x + rnd.getFloat() * 2.0 - 0.5; + dy = y + rnd.getFloat() * 2.0 - 0.5; + + /** @type {Array<number>} */ var sample = tcuFuzzyImageCompare.bilinearSample(surface, dx, dy, NumChannels); + + minErr = Math.min(minErr, tcuFuzzyImageCompare.compareColors(pixel, sample, params.minErrThreshold)); + if (minErr == 0.0) + return minErr; + } + + return minErr; + }; + + /** + * @param {Array<number>} c + * @return {number} + */ + tcuFuzzyImageCompare.toGrayscale = function(c) { + return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; + }; + + /** + * @param {tcuTexture.TextureFormat} format + * @return {boolean} + */ + tcuFuzzyImageCompare.isFormatSupported = function(format) { + return format.type == tcuTexture.ChannelType.UNORM_INT8 && (format.order == tcuTexture.ChannelOrder.RGB || format.order == tcuTexture.ChannelOrder.RGBA); + }; + + /** + * @param {tcuFuzzyImageCompare.FuzzyCompareParams} params + * @param {tcuTexture.ConstPixelBufferAccess} ref + * @param {tcuTexture.ConstPixelBufferAccess} cmp + * @param {tcuTexture.PixelBufferAccess} errorMask + * @return {number} + */ + tcuFuzzyImageCompare.fuzzyCompare = function(params, ref, cmp, errorMask) { + assertMsgOptions(ref.getWidth() == cmp.getWidth() && ref.getHeight() == cmp.getHeight(), + 'Reference and result images have different dimensions', false, true); + + assertMsgOptions(ref.getWidth() == errorMask.getWidth() && ref.getHeight() == errorMask.getHeight(), + 'Reference and error mask images have different dimensions', false, true); + + if (!tcuFuzzyImageCompare.isFormatSupported(ref.getFormat()) || !tcuFuzzyImageCompare.isFormatSupported(cmp.getFormat())) + throw new Error('Unsupported format in fuzzy comparison'); + + /** @type {number} */ var width = ref.getWidth(); + /** @type {number} */ var height = ref.getHeight(); + /** @type {deRandom.Random} */ var rnd = new deRandom.Random(667); + + // Filtered + /** @type {tcuTexture.TextureLevel} */ var refFiltered = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height); + /** @type {tcuTexture.TextureLevel} */ var cmpFiltered = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height); + + var refView = new tcuTexture.RGBA8View(ref); + var cmpView = new tcuTexture.RGBA8View(cmp); + var refFilteredView = new tcuTexture.RGBA8View(tcuTexture.PixelBufferAccess.newFromTextureLevel(refFiltered)); + var cmpFilteredView = new tcuTexture.RGBA8View(tcuTexture.PixelBufferAccess.newFromTextureLevel(cmpFiltered)); + + // Kernel = {0.15, 0.7, 0.15} + /** @type {Array<number>} */ var kernel = [0.1, 0.8, 0.1]; + /** @type {number} */ var shift = Math.floor((kernel.length - 1) / 2); + + switch (ref.getFormat().order) { + case tcuTexture.ChannelOrder.RGBA: tcuFuzzyImageCompare.separableConvolve(refFilteredView, refView, shift, shift, kernel, kernel, 4, 4); break; + case tcuTexture.ChannelOrder.RGB: tcuFuzzyImageCompare.separableConvolve(refFilteredView, refView, shift, shift, kernel, kernel, 4, 3); break; + default: + throw new Error('tcuFuzzyImageCompare.fuzzyCompare - Invalid ChannelOrder'); + } + + switch (cmp.getFormat().order) { + case tcuTexture.ChannelOrder.RGBA: tcuFuzzyImageCompare.separableConvolve(cmpFilteredView, cmpView, shift, shift, kernel, kernel, 4, 4); break; + case tcuTexture.ChannelOrder.RGB: tcuFuzzyImageCompare.separableConvolve(cmpFilteredView, cmpView, shift, shift, kernel, kernel, 4, 3); break; + default: + throw new Error('tcuFuzzyImageCompare.fuzzyCompare - Invalid ChannelOrder'); + } + + /** @type {number} */ var numSamples = 0; + /** @type {number} */ var errSum = 0.0; + + // Clear error mask to green. + errorMask.clear([0.0, 1.0, 0.0, 1.0]); + + for (var y = 1; y < height - 1; y++) { + for (var x = 1; x < width - 1; x += params.maxSampleSkip > 0 ? rnd.getInt(0, params.maxSampleSkip) : 1) { + /** @type {number} */ var err = Math.min(tcuFuzzyImageCompare.compareToNeighbor(params, rnd, refFilteredView.read(x, y, 4), cmpFilteredView, x, y, 4), + tcuFuzzyImageCompare.compareToNeighbor(params, rnd, cmpFilteredView.read(x, y, 4), refFilteredView, x, y, 4)); + + err = Math.pow(err, params.errExp); + + errSum += err; + numSamples += 1; + + // Build error image. + /** @type {number} */ var red = err * 500.0; + /** @type {number} */ var luma = tcuFuzzyImageCompare.toGrayscale(cmp.getPixel(x, y)); + /** @type {number} */ var rF = 0.7 + 0.3 * luma; + errorMask.setPixel([red * rF, (1.0 - red) * rF, 0.0, 1.0], x, y); + + } + } + + // Scale error sum based on number of samples taken + errSum *= ((width - 2) * (height - 2)) / numSamples; + + return errSum; + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuImageCompare.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuImageCompare.js new file mode 100644 index 000000000..3a8138ef2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuImageCompare.js @@ -0,0 +1,757 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuImageCompare'); +goog.require('framework.common.tcuBilinearImageCompare'); +goog.require('framework.common.tcuFloat'); +goog.require('framework.common.tcuFuzzyImageCompare'); +goog.require('framework.common.tcuLogImage'); +goog.require('framework.common.tcuRGBA'); +goog.require('framework.common.tcuSurface'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var tcuImageCompare = framework.common.tcuImageCompare; +var tcuSurface = framework.common.tcuSurface; +var deMath = framework.delibs.debase.deMath; +var tcuTexture = framework.common.tcuTexture; +var tcuTextureUtil = framework.common.tcuTextureUtil; +var tcuFloat = framework.common.tcuFloat; +var tcuFuzzyImageCompare = framework.common.tcuFuzzyImageCompare; +var tcuBilinearImageCompare = framework.common.tcuBilinearImageCompare; +var tcuRGBA = framework.common.tcuRGBA; +var tcuLogImage = framework.common.tcuLogImage; + +/** + * @enum + */ +tcuImageCompare.CompareLogMode = { + EVERYTHING: 0, + RESULT: 1, + ON_ERROR: 2 +}; + +/** + * @param {framework.common.tcuTexture.ConstPixelBufferAccess} result + * @param {framework.common.tcuTexture.ConstPixelBufferAccess} reference + * @param {framework.common.tcuTexture.ConstPixelBufferAccess=} diff + */ +tcuImageCompare.displayImages = function(result, reference, diff) { + var limits = tcuImageCompare.computeScaleAndBias(reference, result); + tcuLogImage.logImage('Result', '', result, limits.scale, limits.bias); + tcuLogImage.logImage('Reference', '', reference, limits.scale, limits.bias); + if (diff) + tcuLogImage.logImage('Error', 'error mask', diff); +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} reference + * @param {tcuTexture.ConstPixelBufferAccess} result + * @return {{scale: Array<number>, bias: Array<number>}} + */ +tcuImageCompare.computeScaleAndBias = function(reference, result) { + var minVal = []; + var maxVal = []; + var scale = []; + var bias = []; + + var eps = 0.0001; + var referenceRange = tcuTextureUtil.estimatePixelValueRange(reference); + var resultRange = tcuTextureUtil.estimatePixelValueRange(result); + + minVal[0] = Math.min(referenceRange[0][0], resultRange[0][0]); + minVal[1] = Math.min(referenceRange[0][1], resultRange[0][1]); + minVal[2] = Math.min(referenceRange[0][2], resultRange[0][2]); + minVal[3] = Math.min(referenceRange[0][3], resultRange[0][3]); + + maxVal[0] = Math.max(referenceRange[1][0], resultRange[1][0]); + maxVal[1] = Math.max(referenceRange[1][1], resultRange[1][1]); + maxVal[2] = Math.max(referenceRange[1][2], resultRange[1][2]); + maxVal[3] = Math.max(referenceRange[1][3], resultRange[1][3]); + + for (var c = 0; c < 4; c++) { + if (maxVal[c] - minVal[c] < eps) { + scale[c] = (maxVal[c] < eps) ? 1 : (1 / maxVal[c]); + bias[c] = (c == 3) ? (1 - maxVal[c] * scale[c]) : (0 - minVal[c] * scale[c]); + } else { + scale[c] = 1 / (maxVal[c] - minVal[c]); + bias[c] = 0 - minVal[c] * scale[c]; + } + } + return { + scale: scale, + bias: bias + }; +}; + +/** + * \brief Per-pixel threshold-based comparison + * + * This compare computes per-pixel differences between result and reference + * image. Comparison fails if any pixels exceed the given threshold value. + * + * This comparison can be used for integer- and fixed-point texture formats. + * Difference is computed in integer space. + * + * On failure error image is generated that shows where the failing pixels + * are. + * + * @param {string} imageSetName Name for image set when logging results + * @param {string} imageSetDesc Description for image set + * @param {tcuTexture.ConstPixelBufferAccess} reference Reference image + * @param {tcuTexture.ConstPixelBufferAccess} result Result image + * @param {Array<number>} threshold Maximum allowed difference + * @param {tcuImageCompare.CompareLogMode=} logMode + * @param {Array< Array<number> >} skipPixels pixels that are skipped comparison + * @return {boolean} true if comparison passes, false otherwise + */ + tcuImageCompare.intThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode, skipPixels) { + var width = reference.getWidth(); + var height = reference.getHeight(); + var depth = reference.getDepth(); + var errorMask = new tcuSurface.Surface(width, height); + + var maxDiff = [0, 0, 0, 0]; + // var pixelBias = [0, 0, 0, 0]; // Vec4 // TODO: check, only used in computeScaleAndBias, which is not included + // var pixelScale = [1, 1, 1, 1]; // Vec4 // TODO: check, only used in computeScaleAndBias + + assertMsgOptions(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth, + 'Reference and result images have different dimensions', false, true); + + for (var z = 0; z < depth; z++) { + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + if (skipPixels && skipPixels.length > 0) { + var skip = false; + for (var ii = 0; ii < skipPixels.length; ++ii) { + var refZ = (skipPixels[ii].length > 2 ? skipPixels[ii][2] : 0); + if (x == skipPixels[ii][0] && y == skipPixels[ii][1] && z == refZ) { + skip = true; + break; + } + } + if (skip) + continue; + } + var refPix = reference.getPixelInt(x, y, z); + var cmpPix = result.getPixelInt(x, y, z); + + var diff = deMath.absDiff(refPix, cmpPix); + var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold)); + + maxDiff = deMath.max(maxDiff, diff); + var color = [0, 255, 0, 255]; + if (!isOk) + color = [255, 0, 0, 255]; + errorMask.setPixel(x, y, color); + } + } + } + + var compareOk = deMath.boolAll(deMath.lessThanEqual(maxDiff, threshold)); + + if (!compareOk) { + debug('Image comparison failed: max difference = ' + maxDiff + ', threshold = ' + threshold); + tcuImageCompare.displayImages(result, reference, errorMask.getAccess()); + } + + return compareOk; +}; + +/** + * \brief Per-pixel threshold-based deviation-ignoring comparison + * + * This compare computes per-pixel differences between result and reference + * image. Pixel fails the test if there is no pixel matching the given + * threshold value in the search volume. Comparison fails if the number of + * failing pixels exceeds the given limit. + * + * If the search volume contains out-of-bounds pixels, comparison can be set + * to either ignore these pixels in search or to accept any pixel that has + * out-of-bounds pixels in its search volume. + * + * This comparison can be used for integer- and fixed-point texture formats. + * Difference is computed in integer space. + * + * On failure error image is generated that shows where the failing pixels + * are. + * + * @param {string} imageSetName Name for image set when logging results + * @param {string} imageSetDesc Description for image set + * @param {tcuTexture.ConstPixelBufferAccess} reference Reference image + * @param {tcuTexture.ConstPixelBufferAccess} result Result image + * @param {Array<number>} threshold Maximum allowed difference + * @param {Array<number>} maxPositionDeviation Maximum allowed distance in the search volume. + * @param {boolean} acceptOutOfBoundsAsAnyValue Accept any pixel in the boundary region + * @param {number} maxAllowedFailingPixels Maximum number of failing pixels + * @return {boolean} true if comparison passes, false otherwise + */ +tcuImageCompare.intThresholdPositionDeviationErrorThresholdCompare = function( + imageSetName, imageSetDesc, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue, maxAllowedFailingPixels) { + /** @type {number} */ var width = reference.getWidth(); + /** @type {number} */ var height = reference.getHeight(); + /** @type {number} */ var depth = reference.getDepth(); + /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height); + /** @type {number} */ var numFailingPixels = tcuImageCompare.findNumPositionDeviationFailingPixels(errorMask.getAccess(), reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue); + var compareOk = numFailingPixels <= maxAllowedFailingPixels; + /** @type {Array<number>} */ var pixelBias = [0.0, 0.0, 0.0, 0.0]; + /** @type {Array<number>} */ var pixelScale = [1.0, 1.0, 1.0, 1.0]; + + if (!compareOk) { + debug('Position deviation error threshold image comparison failed: failed pixels = ' + numFailingPixels + ', threshold = ' + threshold); + tcuImageCompare.displayImages(result, reference, errorMask.getAccess()); + } else + tcuLogImage.logImage('Result', '', result); + + /*if (!compareOk) { + // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. + if (tcuTexture.getTextureChannelClass(reference.getFormat().type) != tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT || + tcuTexture.getTextureChannelClass(result.getFormat().type) != tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) { + computeScaleAndBias(reference, result, pixelScale, pixelBias); + log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; + } + + if (!compareOk) + log << TestLog::Message + << "Image comparison failed:\n" + << "\tallowed position deviation = " << maxPositionDeviation << "\n" + << "\tcolor threshold = " << threshold + << TestLog::EndMessage; + log << TestLog::Message << "Number of failing pixels = " << numFailingPixels << ", max allowed = " << maxAllowedFailingPixels << TestLog::EndMessage; + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) + << TestLog::Image("ErrorMask", "Error mask", errorMask) + << TestLog::EndImageSet; + } else if (logMode == COMPARE_LOG_RESULT) { + if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) + computePixelScaleBias(result, pixelScale, pixelBias); + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::EndImageSet; + }*/ + + return compareOk; +}; + +/** + * tcuImageCompare.floatUlpThresholdCompare + * @param {string} imageSetName + * @param {string} imageSetDesc + * @param {tcuTexture.ConstPixelBufferAccess} reference + * @param {tcuTexture.ConstPixelBufferAccess} result + * @param {Array<number>} threshold - previously used as an Uint32Array + * @return {boolean} + */ +tcuImageCompare.floatUlpThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold) { + /** @type {number} */ var width = reference.getWidth(); + /** @type {number} */ var height = reference.getHeight(); + /** @type {number} */ var depth = reference.getDepth(); + /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height); + + /** @type {Array<number>} */ var maxDiff = [0, 0, 0, 0]; // UVec4 + // var pixelBias = [0, 0, 0, 0]; // Vec4 + // var pixelScale = [1, 1, 1, 1]; // Vec4 + + assertMsgOptions(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth, + 'Reference and result images have different dimensions', false, true); + + for (var z = 0; z < depth; z++) { + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + /** @type {ArrayBuffer} */ var arrayBufferRef = new ArrayBuffer(4 * 4); + /** @type {ArrayBuffer} */ var arrayBufferCmp = new ArrayBuffer(4 * 4); + + /** @type {Array<number>} */ var refPix = reference.getPixel(x, y, z); // getPixel returns a Vec4 pixel color + + /** @type {Array<number>} */ var cmpPix = result.getPixel(x, y, z); // getPixel returns a Vec4 pixel color + + /** @type {Uint32Array} */ var refBits = new Uint32Array(arrayBufferRef); // UVec4 + /** @type {Uint32Array} */ var cmpBits = new Uint32Array(arrayBufferCmp); // UVec4 + + // Instead of memcpy(), which is the way to do float->uint32 reinterpretation in C++ + for (var i = 0; i < refPix.length; i++) { + refBits[i] = tcuFloat.convertFloat32Inline(refPix[i], tcuFloat.description32); + cmpBits[i] = tcuFloat.convertFloat32Inline(cmpPix[i], tcuFloat.description32); + } + + /** @type {Array<number>} */ var diff = deMath.absDiff(refBits, cmpBits); // UVec4 + /** @type {boolean} */ var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold)); + + maxDiff = deMath.max(maxDiff, diff); + + errorMask.setPixel(x, y, isOk ? [0, 255, 0, 255] : [255, 0, 0, 255]); + } + } + } + + /** @type {boolean} */ var compareOk = deMath.boolAll(deMath.lessThanEqual(maxDiff, threshold)); + + if (!compareOk) { + debug('Image comparison failed: max difference = ' + maxDiff + ', threshold = ' + threshold); + tcuImageCompare.displayImages(result, reference, errorMask.getAccess()); + } + + /*if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) { + // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. + if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || + tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) { + computeScaleAndBias(reference, result, pixelScale, pixelBias); + log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; + } + + if (!compareOk) + log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << TestLog::EndMessage; + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) + << TestLog::Image("ErrorMask", "Error mask", errorMask) + << TestLog::EndImageSet; + } else if (logMode == COMPARE_LOG_RESULT) { + if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) + computePixelScaleBias(result, pixelScale, pixelBias); + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::EndImageSet; + }*/ + + return compareOk; +}; + +/** + * tcuImageCompare.floatThresholdCompare + * @param {string} imageSetName + * @param {string} imageSetDesc + * @param {tcuTexture.ConstPixelBufferAccess} reference + * @param {tcuTexture.ConstPixelBufferAccess} result + * @param {Array<number>} threshold + * @return {boolean} + */ +tcuImageCompare.floatThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold) { + /** @type {number} */ var width = reference.getWidth(); + /** @type {number} */ var height = reference.getHeight(); + /** @type {number} */ var depth = reference.getDepth(); + /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height); + + /** @type {Array<number>} */ var maxDiff = [0, 0, 0, 0]; // Vec4 + // var pixelBias = [0, 0, 0, 0]; // Vec4 + // var pixelScale = [1, 1, 1, 1]; // Vec4 + + assertMsgOptions(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth, + 'Reference and result images have different dimensions', false, true); + + for (var z = 0; z < depth; z++) { + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + var refPix = reference.getPixel(x, y, z); // Vec4 + var cmpPix = result.getPixel(x, y, z); // Vec4 + + /** @type {Array<number>} */ var diff = deMath.absDiff(refPix, cmpPix); // Vec4 + /** @type {boolean} */ var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold)); + + maxDiff = deMath.max(maxDiff, diff); + + errorMask.setPixel(x, y, isOk ? [0, 255, 0, 255] : [255, 0, 0, 255]); + } + } + } + + /** @type {boolean} */ var compareOk = deMath.boolAll(deMath.lessThanEqual(maxDiff, threshold)); + + if (!compareOk) { + debug('Image comparison failed: max difference = ' + maxDiff + ', threshold = ' + threshold); + tcuImageCompare.displayImages(result, reference, errorMask.getAccess()); + } + + /*if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) { + // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. + if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || + tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) { + computeScaleAndBias(reference, result, pixelScale, pixelBias); + log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; + } + + if (!compareOk) + log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << TestLog::EndMessage; + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) + << TestLog::Image("ErrorMask", "Error mask", errorMask) + << TestLog::EndImageSet; + } else if (logMode == COMPARE_LOG_RESULT) { + if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) + computePixelScaleBias(result, pixelScale, pixelBias); + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::EndImageSet; + }*/ + + return compareOk; +}; + +/** + * \brief Per-pixel threshold-based comparison + * + * This compare computes per-pixel differences between result and reference + * image. Comparison fails if any pixels exceed the given threshold value. + * + * On failure error image is generated that shows where the failing pixels + * are. + * + * @param {string} imageSetName Name for image set when logging results + * @param {string} imageSetDesc Description for image set + * @param {tcuSurface.Surface} reference Reference image + * @param {tcuSurface.Surface} result Result image + * @param {Array<number>} threshold Maximum allowed difference + * @param {tcuImageCompare.CompareLogMode=} logMode + * @param {Array< Array<number> >} skipPixels pixels that are skipped comparison + * @return {boolean} true if comparison passes, false otherwise + */ +tcuImageCompare.pixelThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode, skipPixels) { + return tcuImageCompare.intThresholdCompare(imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(), threshold, logMode, skipPixels); +}; + +/** + * @param {tcuTexture.PixelBufferAccess} errorMask + * @param {tcuTexture.ConstPixelBufferAccess} reference + * @param {tcuTexture.ConstPixelBufferAccess} result + * @param {Array<number>} threshold + * @param {Array<number>} maxPositionDeviation + * @param {boolean} acceptOutOfBoundsAsAnyValue + * @return {number} + */ +tcuImageCompare.findNumPositionDeviationFailingPixels = function(errorMask, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue) { + /** @type {number} */ var width = reference.getWidth(); + /** @type {number} */ var height = reference.getHeight(); + /** @type {number} */ var depth = reference.getDepth(); + /** @type {number} */ var numFailingPixels = 0; + + checkMessage(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth, 'Surfaces have different dimensions'); + + for (var z = 0; z < depth; z++) { + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + /** @type {Array<number>} */ var refPix = reference.getPixelInt(x, y, z); + /** @type {Array<number>} */ var cmpPix = result.getPixelInt(x, y, z); + + // Exact match + /** @type {Array<number>} */ var diff = deMath.absDiff(refPix, cmpPix); + /** @type {boolean} */ var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold)); + + if (isOk) { + errorMask.setPixel([0, 0xff, 0, 0xff], x, y, z); + continue; + } + + // Accept over the image bounds pixels since they could be anything + + if (acceptOutOfBoundsAsAnyValue && + (x < maxPositionDeviation[0] || x + maxPositionDeviation[0] >= width || + y < maxPositionDeviation[1] || y + maxPositionDeviation[1] >= height || + z < maxPositionDeviation[2] || z + maxPositionDeviation[2] >= depth)) { + errorMask.setPixel([0, 0xff, 0, 0xff], x, y, z); + continue; + } + + // Find matching pixels for both result and reference pixel + + var pixelFoundForReference = false; + var pixelFoundForResult = false; + + // Find deviated result pixel for reference + + for (var sz = Math.max(0, z - maxPositionDeviation[2]); sz <= Math.min(depth - 1, z + maxPositionDeviation[2]) && !pixelFoundForReference; ++sz) + for (var sy = Math.max(0, y - maxPositionDeviation[1]); sy <= Math.min(height - 1, y + maxPositionDeviation[1]) && !pixelFoundForReference; ++sy) + for (var sx = Math.max(0, x - maxPositionDeviation[0]); sx <= Math.min(width - 1, x + maxPositionDeviation[0]) && !pixelFoundForReference; ++sx) { + /** @type {Array<number>} */ var deviatedCmpPix = result.getPixelInt(sx, sy, sz); + diff = deMath.absDiff(refPix, deviatedCmpPix); + isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold)); + + pixelFoundForReference |= isOk; + } + + // Find deviated reference pixel for result + + for (var sz = Math.max(0, z - maxPositionDeviation[2]); sz <= Math.min(depth - 1, z + maxPositionDeviation[2]) && !pixelFoundForResult; ++sz) + for (var sy = Math.max(0, y - maxPositionDeviation[1]); sy <= Math.min(height - 1, y + maxPositionDeviation[1]) && !pixelFoundForResult; ++sy) + for (var sx = Math.max(0, x - maxPositionDeviation[0]); sx <= Math.min(width - 1, x + maxPositionDeviation[0]) && !pixelFoundForResult; ++sx) { + /** @type {Array<number>} */ var deviatedRefPix = reference.getPixelInt(sx, sy, sz); + diff = deMath.absDiff(cmpPix, deviatedRefPix); + isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold)); + + pixelFoundForResult |= isOk; + } + + if (pixelFoundForReference && pixelFoundForResult) + errorMask.setPixel([0, 0xff, 0, 0xff], x, y, z); + else { + errorMask.setPixel([0xff, 0, 0, 0xff], x, y, z); + ++numFailingPixels; + } + } + } + } + + return numFailingPixels; +}; + + /** + * tcuImageCompare.fuzzyCompare + * @param {string} imageSetName + * @param {string} imageSetDesc + * @param {tcuTexture.ConstPixelBufferAccess} reference + * @param {tcuTexture.ConstPixelBufferAccess} result + * @param {number} threshold + * @param {tcuImageCompare.CompareLogMode=} logMode + * @return {boolean} + */ +tcuImageCompare.fuzzyCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode) { + /** @type {tcuFuzzyImageCompare.FuzzyCompareParams} */ var params = new tcuFuzzyImageCompare.FuzzyCompareParams(); // Use defaults. + /** @type {tcuTexture.TextureLevel} */ var errorMask = new tcuTexture.TextureLevel( + new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, + tcuTexture.ChannelType.UNORM_INT8), + reference.getWidth(), + reference.getHeight() + ); + /** @type {number} */ var difference = tcuFuzzyImageCompare.fuzzyCompare( + params, + reference, + result, + tcuTexture.PixelBufferAccess.newFromTextureLevel(errorMask) + ); + /** @type {boolean} */ var isOk = difference <= threshold; + /** @type {Array<number>} */ var pixelBias = [0.0, 0.0, 0.0, 0.0]; + /** @type {Array<number>} */ var pixelScale = [1.0, 1.0, 1.0, 1.0]; + + if (!isOk) { + debug('Fuzzy image comparison failed: difference = ' + difference + ', threshold = ' + threshold); + tcuImageCompare.displayImages(result, reference, errorMask.getAccess()); + } + + /* + if (!isOk || logMode == COMPARE_LOG_EVERYTHING) { + // Generate more accurate error mask. + params.maxSampleSkip = 0; + tcuImageCompare.fuzzyCompare(params, reference, result, errorMask.getAccess()); + + if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) && reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) + computeScaleAndBias(reference, result, pixelScale, pixelBias); + + if (!isOk) + log << TestLog::Message << "Image comparison failed: difference = " << difference << ", threshold = " << threshold << TestLog::EndMessage; + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) + << TestLog::Image("ErrorMask", "Error mask", errorMask) + << TestLog::EndImageSet; + } else if (logMode == COMPARE_LOG_RESULT) { + if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) + computePixelScaleBias(result, pixelScale, pixelBias); + + log << TestLog::ImageSet(imageSetName, imageSetDesc) + << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + << TestLog::EndImageSet; + } + */ + return isOk; +}; + +tcuImageCompare.unitTest = function() { + var width = 128; + var height = 128; + + var weirdLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SNORM_INT32), width, height); + var access = weirdLevel.getAccess(); + access.clear([0.1, 0.5, 0, 0]); + access.clear([0.11, 0.52, 0, 0], [0, width], [0, height / 2]); + access.clear([0.12, 0.52, 0, 0], [0, width], [height / 2, height / 2 + height / 8]); + var limits = tcuTextureUtil.computePixelScaleBias(access); + debug('Scale: ' + limits.scale); + debug('Bias: ' + limits.bias); + tcuLogImage.logImage('Weird', 'weird format without scaling', access); + tcuLogImage.logImage('Weird', 'weird format', access, limits.scale, limits.bias); + + var srcLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height); + var dstLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height); + var src = srcLevel.getAccess(); + var dst = dstLevel.getAccess(); + + src.clear(); + dst.clear(); + + for (var i = 0; i < width - 1; i++) { + for (var j = 0; j < height - 1; j++) { + src.setPixelInt([i, j, 90, 255], i, j); + dst.setPixelInt([i, j, 90, 255], i + 1, j + 1); + } + } + + debug('Src format: ' + src.getFormat()); + debug('Destination: ' + dst); + debug(src); + tcuLogImage.logImage('Source', 'Source image', src); + + if (!tcuImageCompare.fuzzyCompare('compare', 'compare similar images', src, dst, 0.05)) + throw new Error('Compare should return true'); + + src.clear(); + dst.clear(); + + for (var i = 0; i < width - 2; i++) { + for (var j = 0; j < height - 2; j++) { + src.setPixelInt([i, j, 90, 255], i, j); + dst.setPixelInt([i, j, 90, 255], i + 2, j + 2); + } + } + + if (tcuImageCompare.fuzzyCompare('compare', 'compare different images', src, dst, 0.05)) + throw new Error('Compare should return false'); + + debug('Passed'); +}; + +tcuImageCompare.unitTest2 = function() { + var width = 128; + var height = 128; + var srcLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height); + var dstLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height); + var src = srcLevel.getAccess(); + var dst = dstLevel.getAccess(); + var threshold = tcuRGBA.newRGBAComponents(1, 1, 1, 1); + debug('Threshold: ' + threshold); + + src.clear(); + dst.clear(); + + for (var i = 0; i < width - 1; i++) { + for (var j = 0; j < height - 1; j++) { + src.setPixelInt([i, j, 90, 255], i, j); + dst.setPixelInt([i, j, 90, 255], i, j); + } + } + if (!tcuImageCompare.bilinearCompare('compare', 'compare similar images', src, dst, threshold)) + throw new Error('Compare should return true'); + debug('bilinear compare the same images passed'); + + src.clear(); + dst.clear(); + + for (var i = 0; i < width - 1; i++) { + for (var j = 0; j < height - 1; j++) { + src.setPixelInt([i, j, 90, 255], i, j); + dst.setPixelInt([i, j + 1, 90, 255], i, j + 1); + } + } + if (!tcuImageCompare.bilinearCompare('compare', 'compare similar images', src, dst, threshold)) + throw new Error('Compare should return true'); + debug('bilinear compare very similar images passed'); + + src.clear(); + dst.clear(); + + for (var i = 0; i < width - 2; i++) { + for (var j = 0; j < height - 2; j++) { + src.setPixelInt([i, j, 90, 255], i, j); + // dst.setPixelInt([i, j, 90, 255], i + 2, j + 2); + } + } + + if (tcuImageCompare.bilinearCompare('compare', 'compare different images', src, dst, threshold)) + throw new Error('Compare should return false'); + + debug('bilinear compare very different images passed'); +}; + +/** + * Bilinear image comparison + * On failure error image is generated that shows where the failing pixels + * are. + * Currently supports only RGBA, UNORM_INT8 formats + * + * @param {string} imageSetName Name for image set when logging results + * @param {string} imageSetDesc Description for image set + * @param {tcuTexture.ConstPixelBufferAccess} reference Reference image + * @param {tcuTexture.ConstPixelBufferAccess} result Result image + * @param {tcuRGBA.RGBA} threshold Maximum local difference + * @param {tcuImageCompare.CompareLogMode=} logMode Logging mode + * @return {boolean} if comparison passes, false otherwise + */ +tcuImageCompare.bilinearCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode) { + /** @type {tcuTexture.TextureLevel} */ + var errorMask = new tcuTexture.TextureLevel( + new tcuTexture.TextureFormat( + tcuTexture.ChannelOrder.RGB, + tcuTexture.ChannelType.UNORM_INT8), + reference.getWidth(), + reference.getHeight()); + + /** @type {boolean} */ + var isOk = tcuBilinearImageCompare.bilinearCompare( + reference, + result, + tcuTexture.PixelBufferAccess.newFromTextureLevel(errorMask), + threshold); + + if (!isOk) { + debug('Image comparison failed: threshold = ' + threshold); + tcuImageCompare.displayImages(result, reference, errorMask.getAccess()); + } + + // /* @type {Array<number>} */ var pixelBias = [0.0, 0.0, 0.0, 0.0]; + // /* @type {Array<number>} */ var pixelScale = [1.0, 1.0, 1.0, 1.0]; + // if (!isOk || logMode == COMPARE_LOG_EVERYTHING) + // { + // if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) && reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) + // computeScaleAndBias(reference, result, pixelScale, pixelBias); + // + // if (!isOk) + // log << TestLog::Message << "Image comparison failed, threshold = " << threshold << TestLog::EndMessage; + // + // log << TestLog::ImageSet(imageSetName, imageSetDesc) + // << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + // << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) + // << TestLog::Image("ErrorMask", "Error mask", errorMask) + // << TestLog::EndImageSet; + // } + // else if (logMode == COMPARE_LOG_RESULT) + // { + // if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) + // computePixelScaleBias(result, pixelScale, pixelBias); + // + // log << TestLog::ImageSet(imageSetName, imageSetDesc) + // << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) + // << TestLog::EndImageSet; + // } + + return isOk; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuInterval.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuInterval.js new file mode 100644 index 000000000..23296c1f3 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuInterval.js @@ -0,0 +1,609 @@ +/*------------------------------------------------------------------------- + * drawElements Quality Program Tester Core + * ---------------------------------------- + * + * 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. + * + *//*! + * \file + * \brief Interval arithmetic and floating point precisions. + *//*--------------------------------------------------------------------*/ + 'use strict'; + goog.provide('framework.common.tcuInterval'); + goog.require('framework.delibs.debase.deMath'); + + goog.scope(function() { + + var tcuInterval = framework.common.tcuInterval; + var deMath = framework.delibs.debase.deMath; + + /** + * @typedef {function(number):number} + */ + tcuInterval.DoubleFunc1; + + /** + * @typedef {function(number, number):number} + */ + tcuInterval.DoubleFunc2; + + /** + * @typedef {function(number,number,number):number} + */ + tcuInterval.DoubleFunc3; + + /** + * @typedef {function(number):tcuInterval.Interval} + */ + tcuInterval.DoubleIntervalFunc1; + + /** + * @typedef {function(number,number):tcuInterval.Interval} + */ + tcuInterval.DoubleIntervalFunc2; + + /** + * @typedef {function(number,number,number):tcuInterval.Interval} + */ + tcuInterval.DoubleIntervalFunc3; + + /** + * @param {function(number): number} func + * @param {tcuInterval.Interval} arg0 + * @return {tcuInterval.Interval} + */ + tcuInterval.applyMonotone1p = function(func, arg0) { + /** + * @param {number=} x + * @param {number=} y + * @return {number} + */ + var body = function(x, y) { + x = x || 0; + return func(x); + }; + return tcuInterval.applyMonotone1(arg0, + function(x) { return tcuInterval.setInterval(body, x); }); + }; + + /** + * @param {function(number): tcuInterval.Interval} func + * @param {tcuInterval.Interval} arg0 + * @return {tcuInterval.Interval} + */ + tcuInterval.applyMonotone1i = function(func, arg0) { + return tcuInterval.withIntervals(func(arg0.lo()), func(arg0.hi())); + }; + + /** + * @param {function(number, number): number} func + * @param {tcuInterval.Interval} arg0 + * @param {tcuInterval.Interval} arg1 + * @return {tcuInterval.Interval} + */ + tcuInterval.applyMonotone2p = function(func, arg0, arg1) { + /** + * @param {number=} x + * @param {number=} y + * @return {number} + */ + var body = function(x, y) { + x = x || 0; + y = y || 0; + return func(x, y); + }; + return tcuInterval.applyMonotone2(arg0, arg1, + function(x, y) { return tcuInterval.setInterval(body, x, y); }); + }; + + /** + * @param {function(number, number): tcuInterval.Interval} func + * @param {tcuInterval.Interval} arg0 + * @param {tcuInterval.Interval} arg1 + * @return {tcuInterval.Interval} + */ + tcuInterval.applyMonotone2i = function(func, arg0, arg1) { + /** @type {number} */ var lo0 = arg0.lo(); + /** @type {number} */ var hi0 = arg0.hi(); + /** @type {number} */ var lo1 = arg1.lo(); + /** @type {number} */ var hi1 = arg1.hi(); + var a = tcuInterval.withIntervals(func(lo0, lo1), func(lo0, hi1)); + var b = tcuInterval.withIntervals(func(hi0, lo1), func(hi0, hi1)); + return tcuInterval.withIntervals(a, b); + }; + + /** + * @constructor + * @param {number=} val + */ + tcuInterval.Interval = function(val) { + if (val === undefined) { + this.m_hasNaN = false; + this.m_lo = Number.POSITIVE_INFINITY; + this.m_hi = Number.NEGATIVE_INFINITY; + } else { + this.m_hasNaN = isNaN(val); + this.m_lo = this.m_hasNaN ? Number.POSITIVE_INFINITY : val; + this.m_hi = this.m_hasNaN ? Number.NEGATIVE_INFINITY : val; + } + }; + + tcuInterval.Interval.prototype.toString = function() { + var str = 'Interval(' + this.m_lo + ', ' + this.m_hi; + if (this.m_hasNaN) + str += ', hasNaN'; + str += ')'; + return str; + }; + + /** + * @param {tcuInterval.Interval} a + * @param {tcuInterval.Interval} b + * @return {tcuInterval.Interval} + */ + tcuInterval.withIntervals = function(a, b) { + /** @type {tcuInterval.Interval} */ var interval = new tcuInterval.Interval(); + interval.m_hasNaN = (a.m_hasNaN || b.m_hasNaN); + interval.m_lo = Math.min(a.m_lo, b.m_lo); + interval.m_hi = Math.max(a.m_hi, b.m_hi); + return interval; + }; + + /** + * @param {number} a + * @param {number} b + * @return {tcuInterval.Interval} + */ + tcuInterval.withNumbers = function(a, b) { + var x = new tcuInterval.Interval(a); + var y = new tcuInterval.Interval(b); + return tcuInterval.withIntervals(x, y); + }; + + /** + * @param {boolean} hasNaN_ + * @param {number} lo_ + * @param {number} hi_ + * @return {tcuInterval.Interval} + */ + tcuInterval.withParams = function(hasNaN_, lo_, hi_) { + /** @type {tcuInterval.Interval} */ var interval = new tcuInterval.Interval(); + interval.m_hasNaN = hasNaN_; + interval.m_lo = lo_; + interval.m_hi = hi_; + return interval; + }; + + /** + * @return {number} + */ + tcuInterval.Interval.prototype.length = function() { + return this.m_hi - this.m_lo; + }; + + /** + * @return {number} + */ + tcuInterval.Interval.prototype.lo = function() { + return this.m_lo; + }; + + /** + * @return {number} + */ + tcuInterval.Interval.prototype.hi = function() { + return this.m_hi; + }; + + /** + * @return {boolean} + */ + tcuInterval.Interval.prototype.hasNaN = function() { + return this.m_hasNaN; + }; + + /** + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.prototype.nan = function() { + return this.m_hasNaN ? new tcuInterval.Interval(NaN) : new tcuInterval.Interval(); + }; + + /** + * @return {boolean} + */ + tcuInterval.Interval.prototype.empty = function() { + return this.m_lo > this.m_hi; + }; + + /** + * @return {boolean} + */ + tcuInterval.Interval.prototype.isFinite = function() { + return isFinite(this.m_lo) && isFinite(this.m_hi); + }; + + /** + * @return {boolean} + */ + tcuInterval.Interval.prototype.isOrdinary = function() { + return !this.hasNaN() && !this.empty() && this.isFinite(); + }; + + /** + * @param {tcuInterval.Interval} other + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.prototype.operatorOrBinary = function(other) { + /** @type {tcuInterval.Interval} */ var temp = new tcuInterval.Interval(); + temp.m_hasNaN = this.m_hasNaN || other.m_hasNaN; + temp.m_lo = Math.min(this.m_lo, other.m_lo); + temp.m_hi = Math.max(this.m_hi, other.m_hi); + return temp; + }; + + /** + * @param {tcuInterval.Interval} other + */ + tcuInterval.Interval.prototype.operatorOrAssignBinary = function(other) { + /** @type {tcuInterval.Interval} */ var temp = this.operatorOrBinary(other); + this.m_hasNaN = temp.m_hasNaN; + this.m_lo = temp.m_lo; + this.m_hi = temp.m_hi; + }; + + /** + * @param {tcuInterval.Interval} other + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.prototype.operatorAndBinary = function(other) { + /** @type {tcuInterval.Interval} */ var temp = new tcuInterval.Interval(); + temp.m_hasNaN = this.m_hasNaN && other.m_hasNaN; + temp.m_lo = Math.max(this.m_lo, other.m_lo); + temp.m_hi = Math.min(this.m_hi, other.m_hi); + return temp; + }; + + /** + * @param {tcuInterval.Interval} other + */ + tcuInterval.Interval.prototype.operatorAndAssignBinary = function(other) { + /** @type {tcuInterval.Interval} */ var temp = this.operatorAndBinary(other); + this.m_hasNaN = temp.m_hasNaN; + this.m_lo = temp.m_lo; + this.m_hi = temp.m_hi; + }; + + /** + * @param {tcuInterval.Interval} other + * @return {boolean} + */ + tcuInterval.Interval.prototype.contains = function(other) { + return (other.lo() >= this.lo() && other.hi() <= this.hi() && + (!other.hasNaN() || this.hasNaN())); + }; + + /** + * @param {tcuInterval.Interval} other + * @return {boolean} + */ + tcuInterval.Interval.prototype.intersects = function(other) { + return ((other.hi() >= this.lo() && other.lo() >= this.hi()) || + (other.hasNaN() && this.hasNaN())); + }; + + /** + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.prototype.operatorNegative = function() { + /** @type {tcuInterval.Interval} */ var temp = new tcuInterval.Interval(); + temp.m_hasNaN = this.m_hasNaN; + temp.m_lo = -this.m_hi; + temp.m_hi = -this.m_lo; + return temp; + }; + + /** + * @param {boolean=} nan + * @return {tcuInterval.Interval} + */ + tcuInterval.unbounded = function(nan) { + if (nan === undefined) + nan = false; + return tcuInterval.withParams(nan, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY); + }; + + /** + * @return {number} + */ + tcuInterval.Interval.prototype.midpoint = function() { + return 0.5 * (this.hi() + this.lo()); // returns NaN when not bounded + }; + + /** + * @param {tcuInterval.Interval} other + * @return {boolean} + */ + tcuInterval.Interval.prototype.operatorCompare = function(other) { + return ((this.m_hasNaN == other.m_hasNaN) && + ((this.empty() && other.empty()) || + (this.m_lo == other.m_lo && this.m_hi == other.m_hi))); + }; + + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.operatorPositive = function(x) { + return x; + }; + + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.exp2 = function(x) { + // std::pow + return tcuInterval.applyMonotone2p(Math.pow, new tcuInterval.Interval(2), x); + }; + + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.exp = function(x) { + // std::exp + return tcuInterval.applyMonotone1p(Math.exp, x); + }; + + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.sign = function(x) { + // TODO + throw new Error('Unimplemented'); + }; + + /** + * @param {tcuInterval.Interval} x + * @param {tcuInterval.Interval} y + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.operatorSum = function(x, y) { + /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); + + if (!x.empty() && !y.empty()) + ret = tcuInterval.setIntervalBounds(function(dummy) {return x.lo() + y.lo();}, function(dummy) {return x.hi() + y.hi();}); + if (x.hasNaN() || y.hasNaN()) + ret.operatorOrAssignBinary(new tcuInterval.Interval(NaN)); + + return ret; + }; + + /** + * @param {tcuInterval.Interval} x + * @param {tcuInterval.Interval} y + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.operatorSub = function(x, y) { + /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); + + /** + * @param {number=} x + * @param {number=} y + * @return {tcuInterval.Interval} + */ + var body = function(x, y) { + return new tcuInterval.Interval(x - y); + }; + + ret = tcuInterval.applyMonotone2(x, y, body); + return ret; + }; + + /** + * @param {tcuInterval.Interval} x + * @param {tcuInterval.Interval} y + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.operatorMul = function(x, y) { + /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); + + /** + * @param {number=} x + * @param {number=} y + * @return {tcuInterval.Interval} + */ + var body = function(x, y) { + return new tcuInterval.Interval(x * y); + }; + + ret = tcuInterval.applyMonotone2(x, y, body); + + return ret; + }; + + /** + * @param {tcuInterval.Interval} nom + * @param {tcuInterval.Interval} den + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.operatorDiv = function(nom, den) { + if (den.contains(new tcuInterval.Interval(0))) { + // \todo [2014-03-21 lauri] Non-inf endpoint when one den endpoint is + // zero and nom doesn't cross zero? + return tcuInterval.unbounded(); + } else { + /** @type {tcuInterval.Interval} */ var ret = new tcuInterval.Interval(); + /** + * @param {number=} x + * @param {number=} y + * @return {tcuInterval.Interval} + */ + var body = function(x, y) { + return new tcuInterval.Interval(x / y); + }; + + ret = tcuInterval.applyMonotone2(nom, den, body); + + return ret; + } + }; + + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.prototype.abs = function(x) { + //std::abs + /** @type {tcuInterval.Interval} */ var mono = tcuInterval.applyMonotone1p(Math.abs, x); + var zero = new tcuInterval.Interval(0); + if (x.contains(zero)) + return tcuInterval.withIntervals(zero, mono); + + return mono; + }; + + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.sqrt = function(x) { + return tcuInterval.applyMonotone1p(Math.sqrt, x); + }; + + /** + * @param {tcuInterval.Interval} x + * @return {tcuInterval.Interval} + */ + tcuInterval.Interval.inverseSqrt = function(x) { + var ret = new tcuInterval.Interval(1); + ret = tcuInterval.Interval.operatorDiv(ret, tcuInterval.Interval.sqrt(x)); + return ret; + }; + +/** + * @param {function(number=, number=): number} setLow + * @param {function(number=, number=): number} setHigh + * @param {number=} arg0 + * @param {number=} arg1 + * @return {tcuInterval.Interval} + */ +tcuInterval.setIntervalBounds = function(setLow, setHigh, arg0, arg1) { + // TODO: No support for rounding modes. Originally, setLow() was rounded down and setHigh() rounded up + var lo = new tcuInterval.Interval(setLow(arg0, arg1)); + var hi = new tcuInterval.Interval(setHigh(arg0, arg1)); + return lo.operatorOrBinary(hi); +}; + +/** + * @param {function(number=, number=): number} set + * @param {number=} arg0 + * @param {number=} arg1 + * @return {tcuInterval.Interval} + */ +tcuInterval.setInterval = function(set, arg0, arg1) { + return tcuInterval.setIntervalBounds(set, set, arg0, arg1); +}; + +/** + * @param {tcuInterval.Interval} arg + * @param {function(number): tcuInterval.Interval} body + * @return {tcuInterval.Interval} + */ +tcuInterval.applyMonotone1 = function(arg, body) { + var ret = new tcuInterval.Interval(); + + if (!arg.empty()) { + var lo = body(arg.lo()); + var hi = body(arg.hi()); + ret = lo.operatorOrBinary(hi); + } + + if (arg.hasNaN()) { + ret = ret.operatorOrBinary(new tcuInterval.Interval(NaN)); + } + + return ret; +}; + +/** + * TODO: Check if this function works properly + * @param {tcuInterval.Interval} arg0 + * @param {tcuInterval.Interval} arg1 + * @param {function(number, number): tcuInterval.Interval} body + * @return {tcuInterval.Interval} + */ +tcuInterval.applyMonotone2 = function(arg0, arg1, body) { + var ret = new tcuInterval.Interval(); + + if (!arg0.empty() && !arg1.empty()) { + var lo0 = body(arg0.lo(), arg1.lo()); + var lo1 = body(arg0.lo(), arg1.hi()); + var hi0 = body(arg0.hi(), arg1.lo()); + var hi1 = body(arg0.hi(), arg1.hi()); + var a = lo0.operatorOrBinary(hi0); + var b = lo1.operatorOrBinary(hi1); + ret = a.operatorOrBinary(b); + } + + if (arg0.hasNaN() || arg1.hasNaN()) { + ret = ret.operatorOrBinary(new tcuInterval.Interval(NaN)); + } + + return ret; +}; + +/** + * TODO: Check if this function works properly + * @param {tcuInterval.Interval} arg0 + * @param {tcuInterval.Interval} arg1 + * @param {tcuInterval.Interval} arg2 + * @param {function(number, number, number): tcuInterval.Interval} body + * @return {tcuInterval.Interval} + */ +tcuInterval.applyMonotone3 = function(arg0, arg1, arg2, body) { + var ret = new tcuInterval.Interval(); + + if (!arg0.empty() && !arg1.empty() && !arg2.empty()) { + var i0 = body(arg0.lo(), arg1.lo(), arg2.lo()); + var i1 = body(arg0.lo(), arg1.lo(), arg2.hi()); + var i2 = body(arg0.lo(), arg1.hi(), arg2.lo()); + var i3 = body(arg0.lo(), arg1.hi(), arg2.hi()); + var i4 = body(arg0.hi(), arg1.lo(), arg2.lo()); + var i5 = body(arg0.hi(), arg1.lo(), arg2.hi()); + var i6 = body(arg0.hi(), arg1.hi(), arg2.lo()); + var i7 = body(arg0.hi(), arg1.hi(), arg2.hi()); + + var low = Math.min(i0.lo(), i1.lo(), i2.lo(), i3.lo(), i4.lo(), i5.lo(), i6.lo(), i7.lo()); + var high = Math.max(i0.hi(), i1.hi(), i2.hi(), i3.hi(), i4.hi(), i5.hi(), i6.hi(), i7.hi()); + var hasNaN = i0.hasNaN() || i1.hasNaN() || i2.hasNaN() || i3.hasNaN() || i4.hasNaN() || i5.hasNaN() || i6.hasNaN() || i7.hasNaN(); + + ret = tcuInterval.withParams(hasNaN, low, high); + } + + if (arg0.hasNaN() || arg1.hasNaN() || arg2.hasNaN()) { + ret = ret.operatorOrBinary(new tcuInterval.Interval(NaN)); + } + + return ret; +}; + +/** @const */ tcuInterval.POSITIVE_INFINITY = new tcuInterval.Interval(Infinity); +/** @const */ tcuInterval.NEGATIVE_INFINITY = new tcuInterval.Interval(-Infinity); +/** @const */ tcuInterval.ZERO = new tcuInterval.Interval(0); +/** @const */ tcuInterval.NAN = new tcuInterval.Interval(NaN); +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuLogImage.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuLogImage.js new file mode 100644 index 000000000..2dabc9060 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuLogImage.js @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuLogImage'); +goog.require('framework.common.tcuSurface'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var tcuLogImage = framework.common.tcuLogImage; +var tcuTexture = framework.common.tcuTexture; +var tcuSurface = framework.common.tcuSurface; +var deMath = framework.delibs.debase.deMath; + +/** @const */ var MAX_IMAGE_SIZE_2D = 4096; +/** + * @param {tcuTexture.ConstPixelBufferAccess} src + */ +tcuLogImage.createImage = function(ctx, src) { + var w = src.getWidth(); + var h = src.getHeight(); + var pixelSize = src.getFormat().getPixelSize(); + var imgData = ctx.createImageData(w, h); + var index = 0; + for (var y = 0; y < h; y++) { + for (var x = 0; x < w; x++) { + var pixel = src.getPixelInt(x, h - y - 1, 0); + for (var i = 0; i < pixelSize; i++) { + imgData.data[index] = pixel[i]; + index = index + 1; + } + if (pixelSize < 4) + imgData.data[index++] = 255; + } + } + return imgData; +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} image + * @param {string} info + */ +tcuLogImage.logImageWithInfo = function(image, info) { + var elem = document.getElementById('console'); + var span = document.createElement('span'); + tcuLogImage.logImage.counter = tcuLogImage.logImage.counter || 0; + var i = tcuLogImage.logImage.counter++; + var width = image.getWidth(); + var height = image.getHeight(); + + elem.appendChild(span); + span.innerHTML = info + '<br> <canvas id="logImage' + i + '" width=' + width + ' height=' + height + '></canvas><br>'; + + var imageCanvas = document.getElementById('logImage' + i); + var ctx = imageCanvas.getContext('2d'); + var data = tcuLogImage.createImage(ctx, image); + ctx.putImageData(data, 0, 0); +}; + + +/** + * @param {Array<number>=} scale + * @param {Array<number>=} bias + * @return {string} HTML string to add to log. + */ +tcuLogImage.logScaleAndBias = function(scale, bias) { + if (scale && bias) + return '<br> Image normalized with formula p * (' + scale + ') + (' + bias + ')'; + else if (scale) + return '<br> Image normalized with formula p * (' + scale + ')'; + else if (bias) + return '<br> Image normalized with formula p + (' + bias + ')'; + return ''; +}; + +/** + * @param {string} name + * @param {string} description + * @param {tcuTexture.ConstPixelBufferAccess} image + * @param {Array<number>=} scale + * @param {Array<number>=} bias + */ +tcuLogImage.logImageRGB = function(name, description, image, scale, bias) { + var elem = document.getElementById('console'); + var span = document.createElement('span'); + var info = name + ' ' + description + '<br> ' + image; + if (scale || bias) + info += tcuLogImage.logScaleAndBias(scale, bias); + tcuLogImage.logImageWithInfo(image, info); +}; + +/** + * @param {string} name + * @param {string} description + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {Array<number>=} pixelScale + * @param {Array<number>=} pixelBias + */ +tcuLogImage.logImage = function(name, description, access, pixelScale, pixelBias) { + pixelScale = pixelScale || [1, 1, 1, 1]; + pixelBias = pixelBias || [0, 0, 0, 0]; + var format = access.getFormat(); + var width = access.getWidth(); + var height = access.getHeight(); + var depth = access.getDepth(); + var needScaling = pixelBias[0] != 0 || pixelBias[1] != 0 || pixelBias[2] != 0 || pixelBias[3] != 0 || + pixelScale[0] != 1 || pixelScale[1] != 1 || pixelScale[2] != 1 || pixelScale[3] != 1; + + if (depth == 1 && format.type == tcuTexture.ChannelType.UNORM_INT8 && + width <= MAX_IMAGE_SIZE_2D && height <= MAX_IMAGE_SIZE_2D && + (format.order == tcuTexture.ChannelOrder.RGB || tcuTexture.ChannelOrder.RGBA) && + !needScaling) + // Fast-path. + tcuLogImage.logImageRGB(name, description, access); + else if (depth == 1) { + var sampler = new tcuTexture.Sampler(tcuTexture.WrapMode.CLAMP_TO_EDGE, tcuTexture.WrapMode.CLAMP_TO_EDGE, tcuTexture.WrapMode.CLAMP_TO_EDGE, + tcuTexture.FilterMode.LINEAR, tcuTexture.FilterMode.NEAREST); + var logImageSize = [width, height]; /* TODO: Add scaling */ + var logImageAccess = new tcuSurface.Surface(width, height).getAccess(); + + for (var y = 0; y < logImageAccess.getHeight(); y++) { + for (var x = 0; x < logImageAccess.getWidth(); x++) { + var yf = (y + 0.5) / logImageAccess.getHeight(); + var xf = (x + 0.5) / logImageAccess.getWidth(); + var s = access.sample2D(sampler, sampler.minFilter, xf, yf, 0); + + if (needScaling) + s = deMath.add(deMath.multiply(s, pixelScale), pixelBias); + + logImageAccess.setPixel(s, x, y); + } + } + var info = name + ' ' + description + '<br> ' + access; + if (needScaling) { + info += tcuLogImage.logScaleAndBias(pixelScale, pixelBias); + } + + tcuLogImage.logImageWithInfo(logImageAccess, info); + } else { + /* TODO: Implement */ + } +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuMatrix.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuMatrix.js new file mode 100644 index 000000000..e2959ecdc --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuMatrix.js @@ -0,0 +1,354 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuMatrix'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + + var tcuMatrix = framework.common.tcuMatrix; + var deMath = framework.delibs.debase.deMath; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * @constructor + * @param {number} rows + * @param {number} cols + * @param {*=} value + * Initialize to identity. + */ + tcuMatrix.Matrix = function(rows, cols, value) { + value = value == undefined ? 1 : value; + this.rows = rows; + this.cols = cols; + this.matrix = []; + for (var i = 0; i < cols; i++) + this.matrix[i] = []; + for (var row = 0; row < rows; row++) + for (var col = 0; col < cols; col++) + this.set(row, col, (row == col) ? value : 0); + }; + + /** + * @param {number} rows + * @param {number} cols + * @param {Array<number>} vector + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.matrixFromVector = function(rows, cols, vector) { + var matrix = new tcuMatrix.Matrix(rows, cols); + for (var row = 0; row < vector.length; row++) + for (var col = 0; col < vector.length; col++) + matrix.matrix[col][row] = row == col ? vector[row] : 0; + return matrix; + }; + + /** + * @param {number} rows + * @param {number} cols + * @param {Array<number>} src + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.matrixFromDataArray = function(rows, cols, src) { + var matrix = new tcuMatrix.Matrix(rows, cols); + for (var row = 0; row < rows; row++) { + for (var col = 0; col < cols; col++) { + matrix.matrix[col][row] = src[row * cols + col]; + } + } + return matrix; + }; + + /** + * Fill the Matrix with data from array + * @param {number} rows + * @param {number} cols + * @param {Array<number>} array + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.matrixFromArray = function(rows, cols, array) { + DE_ASSERT(array.length === rows * cols); + var matrix = new tcuMatrix.Matrix(rows, cols); + for (var row = 0; row < rows; row++) + for (var col = 0; col < cols; col++) + matrix.matrix[col][row] = array[row * cols + col]; + return matrix; + }; + + tcuMatrix.Matrix.prototype.set = function(x, y, value) { + this.isRangeValid(x, y); + this.matrix[y][x] = value; + }; + + tcuMatrix.Matrix.prototype.setRow = function(row, values) { + if (!deMath.deInBounds32(row, 0, this.rows)) + throw new Error('Rows out of range'); + if (values.length > this.cols) + throw new Error('Too many columns'); + for (var col = 0; col < values.length; col++) + this.matrix[col][row] = values[col]; + }; + + tcuMatrix.Matrix.prototype.setCol = function(col, values) { + if (!deMath.deInBounds32(col, 0, this.cols)) + throw new Error('Columns out of range'); + if (values.length > this.rows) + throw new Error('Too many rows'); + for (var row = 0; row < values.length; row++) + this.matrix[col][row] = values[row]; + }; + + tcuMatrix.Matrix.prototype.get = function(x, y) { + this.isRangeValid(x, y); + return this.matrix[y][x]; + }; + + tcuMatrix.Matrix.prototype.getColumn = function(y) { + return this.matrix[y]; + }; + + tcuMatrix.Matrix.prototype.isRangeValid = function(x, y) { + if (!deMath.deInBounds32(x, 0, this.rows)) + throw new Error('Rows out of range'); + if (!deMath.deInBounds32(y, 0, this.cols)) + throw new Error('Columns out of range'); + }; + + /** + * @return {Array<number>} + */ + tcuMatrix.Matrix.prototype.getColumnMajorData = function() { + /** @type {Array<number>} */ var a = []; + for (var col = 0; col < this.cols; col++) + for (var row = 0; row < this.rows; row++) + a.push(this.get(row, col)); + return a; + }; + + /** + * @param {tcuMatrix.Matrix} matrixA + * @param {tcuMatrix.Matrix} matrixB + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.add = function(matrixA, matrixB) { + var res = new tcuMatrix.Matrix(matrixA.rows, matrixB.cols); + for (var col = 0; col < matrixA.cols; col++) + for (var row = 0; row < matrixA.rows; row++) + res.set(row, col, matrixA.get(row, col) + matrixB.get(row, col)); + return res; + }; + + /** + * @param {tcuMatrix.Matrix} matrixA + * @param {tcuMatrix.Matrix} matrixB + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.subtract = function(matrixA, matrixB) { + var res = new tcuMatrix.Matrix(matrixA.rows, matrixB.cols); + for (var col = 0; col < matrixA.cols; col++) + for (var row = 0; row < matrixA.rows; row++) + res.set(row, col, matrixA.get(row, col) - matrixB.get(row, col)); + return res; + }; + + /** + * @param {tcuMatrix.Matrix} matrixA + * @param {tcuMatrix.Matrix} matrixB + * @return {tcuMatrix.Matrix} + * Multiplication of two matrices. + */ + tcuMatrix.multiply = function(matrixA, matrixB) { + if (matrixA.cols != matrixB.rows) + throw new Error('Wrong matrices sizes'); + var res = new tcuMatrix.Matrix(matrixA.rows, matrixB.cols); + for (var row = 0; row < matrixA.rows; row++) + for (var col = 0; col < matrixB.cols; col++) { + var v = 0; + for (var ndx = 0; ndx < matrixA.cols; ndx++) + v += matrixA.get(row, ndx) * matrixB.get(ndx, col); + res.set(row, col, v); + } + return res; + }; + + /** + * @param {tcuMatrix.Matrix} matrixA + * @param {tcuMatrix.Matrix} matrixB + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.divide = function(matrixA, matrixB) { + var res = new tcuMatrix.Matrix(matrixA.rows, matrixA.cols); + for (var col = 0; col < matrixA.cols; col++) + for (var row = 0; row < matrixA.rows; row++) + res.set(row, col, matrixA.get(row, col) / matrixB.get(row, col)); + return res; + }; + + /** + * @param {tcuMatrix.Matrix} mtx + * @param {Array<number>} vec + * @return {Array<number>} + */ + tcuMatrix.multiplyMatVec = function(mtx, vec) { + /** @type {Array<number>} */ var res = []; + /** @type {number} */ var value; + for (var row = 0; row < mtx.rows; row++) { + value = 0; + for (var col = 0; col < mtx.cols; col++) + value += mtx.get(row, col) * vec[col]; + res[row] = value; + } + + return res; + }; + + /** + * @param {Array<number>} vec + * @param {tcuMatrix.Matrix} mtx + * @return {Array<number>} + */ + tcuMatrix.multiplyVecMat = function(vec, mtx) { + /** @type {Array<number>} */ var res = []; + /** @type {number} */ var value; + for (var col = 0; col < mtx.cols; col++) { + value = 0; + for (var row = 0; row < mtx.rows; row++) + value += mtx.get(row, col) * vec[row]; + res[col] = value; + } + + return res; + }; + + tcuMatrix.Matrix.prototype.toString = function() { + var str = 'mat' + this.cols; + if (this.rows !== this.cols) + str += 'x' + this.rows; + str += '('; + for (var col = 0; col < this.cols; col++) { + str += '['; + for (var row = 0; row < this.rows; row++) { + str += this.matrix[col][row]; + if (row != this.rows - 1) + str += ', '; + } + str += ']'; + + if (col != this.cols - 1) + str += ', '; + } + str += ')'; + return str; + }; + + /** + * @param {tcuMatrix.Matrix} mtx + * @param {number} scalar + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.subtractMatScal = function(mtx, scalar) { + /** @type {tcuMatrix.Matrix} */ var res = new tcuMatrix.Matrix(mtx.rows, mtx.cols); + for (var col = 0; col < mtx.cols; col++) + for (var row = 0; row < mtx.rows; row++) + res.set(row, col, mtx.get(row, col) - scalar); + + return res; + }; + + /** + * @param {tcuMatrix.Matrix} mtx + * @param {number} scalar + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.addMatScal = function(mtx, scalar) { + /** @type {tcuMatrix.Matrix} */ var res = new tcuMatrix.Matrix(mtx.rows, mtx.cols); + for (var col = 0; col < mtx.cols; col++) + for (var row = 0; row < mtx.rows; row++) + res.set(row, col, mtx.get(row, col) + scalar); + + return res; + }; + + /** + * @param {tcuMatrix.Matrix} mtx + * @param {number} scalar + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.multiplyMatScal = function(mtx, scalar) { + /** @type {tcuMatrix.Matrix} */ var res = new tcuMatrix.Matrix(mtx.rows, mtx.cols); + for (var col = 0; col < mtx.cols; col++) + for (var row = 0; row < mtx.rows; row++) + res.set(row, col, mtx.get(row, col) * scalar); + + return res; + }; + + /** + * @param {tcuMatrix.Matrix} mtx + * @param {number} scalar + * @return {tcuMatrix.Matrix} + */ + tcuMatrix.divideMatScal = function(mtx, scalar) { + /** @type {tcuMatrix.Matrix} */ var res = new tcuMatrix.Matrix(mtx.rows, mtx.cols); + for (var col = 0; col < mtx.cols; col++) + for (var row = 0; row < mtx.rows; row++) + res.set(row, col, mtx.get(row, col) / scalar); + + return res; + }; + + /** + * @constructor + * @extends {tcuMatrix.Matrix} + */ + tcuMatrix.Mat2 = function() { + tcuMatrix.Matrix.call(this, 2, 2); + }; + + tcuMatrix.Mat2.prototype = Object.create(tcuMatrix.Matrix.prototype); + tcuMatrix.Mat2.prototype.constructor = tcuMatrix.Mat2; + + /** + * @constructor + * @extends {tcuMatrix.Matrix} + */ + tcuMatrix.Mat3 = function() { + tcuMatrix.Matrix.call(this, 3, 3); + }; + + tcuMatrix.Mat3.prototype = Object.create(tcuMatrix.Matrix.prototype); + tcuMatrix.Mat3.prototype.constructor = tcuMatrix.Mat3; + + /** + * @constructor + * @extends {tcuMatrix.Matrix} + */ + tcuMatrix.Mat4 = function() { + tcuMatrix.Matrix.call(this, 4, 4); + }; + + tcuMatrix.Mat4.prototype = Object.create(tcuMatrix.Matrix.prototype); + tcuMatrix.Mat4.prototype.constructor = tcuMatrix.Mat4; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuMatrixUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuMatrixUtil.js new file mode 100644 index 000000000..63dcaba87 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuMatrixUtil.js @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuMatrixUtil'); +goog.require('framework.common.tcuMatrix'); + +goog.scope(function() { + + var tcuMatrixUtil = framework.common.tcuMatrixUtil; + var tcuMatrix = framework.common.tcuMatrix; + + /** + * @param {Array<number>} translation + * @return {tcuMatrix.Matrix} + */ + tcuMatrixUtil.translationMatrix = function(translation) { + var len = translation.length; + var res = new tcuMatrix.Matrix(len + 1, len + 1); + for (var row = 0; row < len; row++) + res.set(row, len, translation[row]); + return res; + }; + + /** + * Flatten an array of arrays or matrices + * @param {(Array<Array<number>> | Array<tcuMatrix.Matrix>)} a + * @return {Array<number>} + */ + tcuMatrixUtil.flatten = function(a) { + if (a[0] instanceof Array) { + var merged = []; + return merged.concat.apply(merged, a); + } + + if (a[0] instanceof tcuMatrix.Matrix) { + /** @type {tcuMatrix.Matrix} */ var m = a[0]; + var rows = m.rows; + var cols = m.cols; + var size = a.length; + var result = []; + for (var col = 0; col < cols; col++) + for (var i = 0; i < size; i++) + result.push(a[i].getColumn(col)); + return [].concat.apply([], result); + } + + if (typeof(a[0]) === 'number') + return a; + + throw new Error('Invalid input'); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuPixelFormat.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuPixelFormat.js new file mode 100644 index 000000000..daf3297a9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuPixelFormat.js @@ -0,0 +1,79 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuPixelFormat'); + +goog.scope(function() { + +var tcuPixelFormat = framework.common.tcuPixelFormat; + +/** + * @constructor + * @param {number=} r + * @param {number=} g + * @param {number=} b + * @param {number=} a + */ +tcuPixelFormat.PixelFormat = function(r, g, b, a) { + this.redBits = r || 0; + this.greenBits = g || 0; + this.blueBits = b || 0; + this.alphaBits = a || 0; +}; + +/** + * @param {WebGL2RenderingContext} context + * @return {tcuPixelFormat.PixelFormat} + */ +tcuPixelFormat.PixelFormatFromContext = function(context) { + var r = /** @type {number} */ (context.getParameter(gl.RED_BITS)); + var g = /** @type {number} */ (context.getParameter(gl.GREEN_BITS)); + var b = /** @type {number} */ (context.getParameter(gl.BLUE_BITS)); + var a = /** @type {number} */ (context.getParameter(gl.ALPHA_BITS)); + + return new tcuPixelFormat.PixelFormat(r, g, b, a); +}; + +/** + * @param {number} r + * @param {number} g + * @param {number} b + * @param {number} a + * @return {boolean} + */ +tcuPixelFormat.PixelFormat.prototype.equals = function(r, g, b, a) { + return this.redBits === r && + this.greenBits === g && + this.blueBits === b && + this.alphaBits === a; +}; + +/** + * @return {Array<number>} + */ +tcuPixelFormat.PixelFormat.prototype.getColorThreshold = function() { + return [1 << (8 - this.redBits), + 1 << (8 - this.greenBits), + 1 << (8 - this.blueBits), + (this.alphaBits > 0) ? (1 << (8 - this.alphaBits)) : 0]; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuRGBA.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuRGBA.js new file mode 100644 index 000000000..0bab841d1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuRGBA.js @@ -0,0 +1,279 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuRGBA'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var tcuRGBA = framework.common.tcuRGBA; +var deMath = framework.delibs.debase.deMath; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * class tcuRGBA.RGBA + * @constructor + * @struct + * @param {goog.NumberArray=} value + */ + tcuRGBA.RGBA = function(value) { + /** @type {goog.NumberArray} */ this.m_value = value || null; + + }; + + /** + * @enum + * In JS, these are not shift values, but positions in a typed array + */ + tcuRGBA.RGBA.Shift = { + RED: 0, + GREEN: 1, + BLUE: 2, + ALPHA: 3 + }; + + /** + * @enum + * Mask used as flags + * Hopefully will not use typed arrays + */ + tcuRGBA.RGBA.Mask = function() { + return { + RED: false, + GREEN: false, + BLUE: false, + ALPHA: false + }; + }; + + /** + * Builds an tcuRGBA.RGBA object from color components + * @param {number} r + * @param {number} g + * @param {number} b + * @param {number} a + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.newRGBAComponents = function(r, g, b, a) { + DE_ASSERT(deMath.deInRange32(r, 0, 255)); + DE_ASSERT(deMath.deInRange32(g, 0, 255)); + DE_ASSERT(deMath.deInRange32(b, 0, 255)); + DE_ASSERT(deMath.deInRange32(a, 0, 255)); + + return new tcuRGBA.RGBA([r, g, b, a]); + }; + + /** + * Builds an tcuRGBA.RGBA object from a number array + * @param {goog.NumberArray} v + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.newRGBAFromArray = function(v) { + return new tcuRGBA.RGBA(v.slice(0, 4)); + }; + + /** + * @param {number} value + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.newRGBAFromValue = function(value) { + var rgba = new tcuRGBA.RGBA(); + var array32 = new Uint32Array([value]); + rgba.m_value = (new Uint8Array(array32.buffer)); + return rgba; + }; + + /** + * @param {Array<number>} v + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.newRGBAFromVec = function (v) { + var r = deMath.clamp(v[0] * 255.0, 0, 255); + var g = deMath.clamp(v[1] * 255.0, 0, 255); + var b = deMath.clamp(v[2] * 255.0, 0, 255); + var a = deMath.clamp(v[3] * 255.0, 0, 255); + + return new tcuRGBA.RGBA([r, g, b, a]); + }; + + /** + * @param {number} v + */ + tcuRGBA.RGBA.prototype.setRed = function(v) { DE_ASSERT(deMath.deInRange32(v, 0, 255)); this.m_value[tcuRGBA.RGBA.Shift.RED] = v; }; + + /** + * @param {number} v + */ + tcuRGBA.RGBA.prototype.setGreen = function(v) { DE_ASSERT(deMath.deInRange32(v, 0, 255)); this.m_value[tcuRGBA.RGBA.Shift.GREEN] = v; }; + + /** + * @param {number} v + */ + tcuRGBA.RGBA.prototype.setBlue = function(v) { DE_ASSERT(deMath.deInRange32(v, 0, 255)); this.m_value[tcuRGBA.RGBA.Shift.BLUE] = v; }; + + /** + * @param {number} v + */ + tcuRGBA.RGBA.prototype.setAlpha = function(v) { DE_ASSERT(deMath.deInRange32(v, 0, 255)); this.m_value[tcuRGBA.RGBA.Shift.ALPHA] = v; }; + + /** + * @return {number} + */ + tcuRGBA.RGBA.prototype.getRed = function() { return this.m_value[tcuRGBA.RGBA.Shift.RED]; }; + + /** + * @return {number} + */ + tcuRGBA.RGBA.prototype.getGreen = function() { return this.m_value[tcuRGBA.RGBA.Shift.GREEN]; }; + + /** + * @return {number} + */ + tcuRGBA.RGBA.prototype.getBlue = function() { return this.m_value[tcuRGBA.RGBA.Shift.BLUE]; }; + + /** + * @return {number} + */ + tcuRGBA.RGBA.prototype.getAlpha = function() { return this.m_value[tcuRGBA.RGBA.Shift.ALPHA]; }; + + /** + * @param {tcuRGBA.RGBA} thr + * @return {boolean} + */ + tcuRGBA.RGBA.prototype.isBelowThreshold = function(thr) { return (this.getRed() <= thr.getRed()) && (this.getGreen() <= thr.getGreen()) && (this.getBlue() <= thr.getBlue()) && (this.getAlpha() <= thr.getAlpha()); }; + + /** + * @param {Uint8Array} bytes + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.RGBA.fromBytes = function(bytes) { return tcuRGBA.newRGBAFromArray(bytes); }; + + /** + * @param {Uint8Array} bytes + */ + tcuRGBA.RGBA.prototype.toBytes = function(bytes) { var result = new Uint8Array(this.m_value); bytes[0] = result[0]; bytes[1] = result[1]; bytes[2] = result[2]; bytes[3] = result[3]; }; + + /** + * @return {Array<number>} + */ + tcuRGBA.RGBA.prototype.toVec = function() { + return [ + this.getRed() / 255.0, + this.getGreen() / 255.0, + this.getBlue() / 255.0, + this.getAlpha() / 255.0 + ]; + }; + + /** + * @return {Array<number>} + */ + tcuRGBA.RGBA.prototype.toIVec = function() { + return [ + this.getRed(), + this.getGreen(), + this.getBlue(), + this.getAlpha() + ]; + }; + + /** + * @param {tcuRGBA.RGBA} v + * @return {boolean} + */ + tcuRGBA.RGBA.prototype.equals = function(v) { + return ( + this.m_value[0] == v.m_value[0] && + this.m_value[1] == v.m_value[1] && + this.m_value[2] == v.m_value[2] && + this.m_value[3] == v.m_value[3] + ); + }; + + /** + * @param {tcuRGBA.RGBA} a + * @param {tcuRGBA.RGBA} b + * @param {tcuRGBA.RGBA} threshold + * @return {boolean} + */ + tcuRGBA.compareThreshold = function(a, b, threshold) { + if (a.equals(b)) return true; // Quick-accept + return tcuRGBA.computeAbsDiff(a, b).isBelowThreshold(threshold); + }; + + /** + * @param {tcuRGBA.RGBA} a + * @param {tcuRGBA.RGBA} b + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.computeAbsDiff = function(a, b) { + return tcuRGBA.newRGBAComponents( + Math.abs(a.getRed() - b.getRed()), + Math.abs(a.getGreen() - b.getGreen()), + Math.abs(a.getBlue() - b.getBlue()), + Math.abs(a.getAlpha() - b.getAlpha()) + ); + }; + + /** + * @param {tcuRGBA.RGBA} a + * @param {number} b + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.multiply = function(a, b) { + return tcuRGBA.newRGBAComponents( + deMath.clamp(a.getRed() * b, 0, 255), + deMath.clamp(a.getGreen() * b, 0, 255), + deMath.clamp(a.getBlue() * b, 0, 255), + deMath.clamp(a.getAlpha() * b, 0, 255)); + }; + + /** + * @param {tcuRGBA.RGBA} a + * @param {tcuRGBA.RGBA} b + * @return {tcuRGBA.RGBA} + */ + tcuRGBA.max = function(a, b) { + return tcuRGBA.newRGBAComponents( + Math.max(a.getRed(), b.getRed()), + Math.max(a.getGreen(), b.getGreen()), + Math.max(a.getBlue(), b.getBlue()), + Math.max(a.getAlpha(), b.getAlpha()) + ); + }; + + tcuRGBA.RGBA.prototype.toString = function() { + return '[' + this.m_value[0] + ',' + this.m_value[1] + ',' + this.m_value[2] + ',' + this.m_value[3] + ']'; + }; + + // Color constants + tcuRGBA.RGBA.red = tcuRGBA.newRGBAComponents(0xFF, 0, 0, 0xFF); + tcuRGBA.RGBA.green = tcuRGBA.newRGBAComponents(0, 0xFF, 0, 0xFF); + tcuRGBA.RGBA.blue = tcuRGBA.newRGBAComponents(0, 0, 0xFF, 0xFF); + tcuRGBA.RGBA.gray = tcuRGBA.newRGBAComponents(0x80, 0x80, 0x80, 0xFF); + tcuRGBA.RGBA.white = tcuRGBA.newRGBAComponents(0xFF, 0xFF, 0xFF, 0xFF); + tcuRGBA.RGBA.black = tcuRGBA.newRGBAComponents(0, 0, 0, 0xFF); + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuSkipList.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuSkipList.js new file mode 100644 index 000000000..541f074de --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuSkipList.js @@ -0,0 +1,324 @@ +/* +** Copyright (c) 2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/** + * This class defines the individual tests which are skipped because + * of graphics driver bugs which simply can not be worked around in + * WebGL 2.0 implementations. + * + * The intent is that this list be kept as small as possible; and that + * bugs are filed with the respective GPU vendors for entries in this + * list. + * + * Pass the query argument "runSkippedTests" in the URL in order to + * force the skipped tests to be run. So, for example: + * + * http://localhost:8080/sdk/tests/deqp/functional/gles3/transformfeedback.html?filter=transform_feedback.basic_types.separate.points&runSkippedTests + */ +'use strict'; +goog.provide('framework.common.tcuSkipList'); + +goog.scope(function() { + + var tcuSkipList = framework.common.tcuSkipList; + + var _skipEntries = {}; + var _wildcardSkipEntries = {}; + var _reason = ""; + + function _setReason(reason) { + _reason = reason; + } + + function _skip(testName) { + if(testName.indexOf("*") >= 0){ + testName = testName.split("*")[0]; + _wildcardSkipEntries[testName] = _reason; + }else{ + _skipEntries[testName] = _reason; + } + } + + var runSkippedTests = false; + var queryVars = window.location.search.substring(1).split('&'); + for (var i = 0; i < queryVars.length; i++) { + var value = queryVars[i].split('='); + if (decodeURIComponent(value[0]) === 'runSkippedTests') { + // Assume that presence of this query arg implies to run + // the skipped tests; the value is ignored. + runSkippedTests = true; + break; + } + } + + if (!runSkippedTests) { + // Example usage: + // + // _setReason("Bugs in FooVendor 30.03 driver"); + // _skip("transform_feedback.basic_types.separate.points.lowp_mat2"); + + // Please see https://android.googlesource.com/platform/external/deqp/+/7c5323116bb164d64bfecb68e8da1af634317b24 + _setReason("Native dEQP also fails on these tests and suppresses them"); + _skip("texture_functions.textureoffset.sampler3d_fixed_fragment"); + _skip("texture_functions.textureoffset.isampler3d_fragment"); + _skip("texture_functions.textureoffset.usampler3d_fragment"); + _skip("texture_functions.textureprojoffset.sampler3d_fixed_fragment"); + _skip("texture_functions.textureprojoffset.isampler3d_fragment"); + _skip("texture_functions.textureprojoffset.usampler3d_fragment"); + // Please see https://android.googlesource.com/platform/external/deqp/+/master/android/cts/master/src/gles3-hw-issues.txt + _skip("texture_functions.textureprojlod.isampler3d_vertex"); + _skip("texture_functions.textureprojlod.usampler3d_vertex"); + // Please see https://android.googlesource.com/platform/external/deqp/+/master/android/cts/master/src/gles3-test-issues.txt + _skip("texture_functions.textureprojlodoffset.usampler3d_vertex"); + _skip("texture_functions.textureoffset.sampler3d_float_fragment"); + _skip("texture_functions.textureprojoffset.sampler3d_float_fragment"); + // Please see https://android.googlesource.com/platform/external/deqp/+/master/android/cts/master/src/gles3-driver-issues.txt + _skip("texture_functions.textureprojlodoffset.isampler3d_vertex"); + _skip("texture_functions.texturegrad.samplercubeshadow*"); + + // https://android.googlesource.com/platform/external/deqp/+/0c1f83aee4709eef7ef2a3edd384f9c192f476fd/android/cts/master/src/gles3-hw-issues.txt#801 + _setReason("Tricky blit rects can result in imperfect copies on some HW."); + _skip("blit.rect.nearest_consistency_mag"); + _skip("blit.rect.nearest_consistency_mag_reverse_dst_x"); + _skip("blit.rect.nearest_consistency_mag_reverse_src_dst_x"); + _skip("blit.rect.nearest_consistency_mag_reverse_src_x"); + _skip("blit.rect.nearest_consistency_mag_reverse_src_y"); + _skip("blit.rect.nearest_consistency_min"); + _skip("blit.rect.nearest_consistency_min_reverse_dst_x"); + _skip("blit.rect.nearest_consistency_min_reverse_src_dst_x"); + _skip("blit.rect.nearest_consistency_min_reverse_src_x"); + _skip("blit.rect.nearest_consistency_out_of_bounds_mag"); + _skip("blit.rect.nearest_consistency_out_of_bounds_mag_reverse_dst_x"); + _skip("blit.rect.nearest_consistency_out_of_bounds_mag_reverse_src_dst_x"); + _skip("blit.rect.nearest_consistency_out_of_bounds_mag_reverse_src_x"); + _skip("blit.rect.nearest_consistency_out_of_bounds_mag_reverse_src_y"); + _skip("blit.rect.nearest_consistency_out_of_bounds_min"); + _skip("blit.rect.nearest_consistency_out_of_bounds_min_reverse_dst_x"); + _skip("blit.rect.nearest_consistency_out_of_bounds_min_reverse_src_dst_x"); + _skip("blit.rect.nearest_consistency_out_of_bounds_min_reverse_src_x"); + _skip("blit.rect.nearest_consistency_out_of_bounds_min_reverse_src_y"); + + _setReason("Tricky blit rects can result in imperfect copies on Mac Intel driver."); + // crbug.com/658724 + // deqp/functional/gles3/framebufferblit/rect_03.html + _skip("blit.rect.nearest_consistency_mag_reverse_src_dst_y"); + // deqp/functional/gles3/framebufferblit/rect_04.html + _skip("blit.rect.nearest_consistency_min_reverse_src_dst_y"); + + // https://android.googlesource.com/platform/external/deqp/+/0c1f83aee4709eef7ef2a3edd384f9c192f476fd/android/cts/master/src/gles3-driver-issues.txt#381 + _setReason("Tricky blit rects can result in imperfect copies on some drivers."); + _skip("blit.rect.out_of_bounds_linear"); + _skip("blit.rect.out_of_bounds_reverse_src_x_linear"); + _skip("blit.rect.out_of_bounds_reverse_src_y_linear"); + _skip("blit.rect.out_of_bounds_reverse_dst_x_linear"); + _skip("blit.rect.out_of_bounds_reverse_dst_y_linear"); + _skip("blit.rect.out_of_bounds_reverse_src_dst_x_linear"); + _skip("blit.rect.out_of_bounds_reverse_src_dst_y_linear"); + + // https://android.googlesource.com/platform/external/deqp/+/0c1f83aee4709eef7ef2a3edd384f9c192f476fd/android/cts/master/src/gles3-driver-issues.txt#368 + _skip("blit.rect.nearest_consistency_out_of_bounds_mag_reverse_dst_y"); + _skip("blit.rect.nearest_consistency_out_of_bounds_mag_reverse_src_dst_y"); + _skip("blit.rect.nearest_consistency_out_of_bounds_min_reverse_dst_y"); + _skip("blit.rect.nearest_consistency_out_of_bounds_min_reverse_src_dst_y"); + + _setReason("Mac OSX drivers handle R11F_G11F_B10F format incorrectly"); + // https://github.com/KhronosGroup/WebGL/issues/1832 + // deqp/functional/gles3/fragmentoutput/basic.float.html + _skip("fragment_outputs.basic.float.r11f_g11f_b10f_mediump*"); + _skip("fragment_outputs.basic.float.r11f_g11f_b10f_highp*"); + // deqp/functional/gles3/fragmentoutput/array.float.html + _skip("fragment_outputs.array.float.r11f_g11f_b10f_mediump*"); + _skip("fragment_outputs.array.float.r11f_g11f_b10f_highp*"); + // deqp/functional/gles3/fragmentoutput/random_00.html + _skip("fragment_outputs.random.57"); + // deqp/functional/gles3/fragmentoutput/random_02.html + _skip("fragment_outputs.random.11"); + // deqp/functional/gles3/fborender/resize_01.html + _skip("render.resize.rbo_r11f_g11f_b10f"); + // deqp/functional/gles3/fborender/recreate_color_02.html + _skip("render.recreate_color.rbo_r11f_g11f_b10f_depth_stencil_rbo_depth24_stencil8"); + // deqp/functional/gles3/fbocolorbuffer/clear.html + _skip("color.clear.r11f_g11f_b10f"); + // deqp/functional/gles3/fbomultisample.2_samples.html + _skip("msaa.2_samples.r11f_g11f_b10f"); + // deqp/functional/gles3/fbomultisample.4_samples.html + _skip("msaa.4_samples.r11f_g11f_b10f"); + // deqp/functional/gles3/fbomultisample.8_samples.html + _skip("msaa.8_samples.r11f_g11f_b10f"); + + _setReason("Missing shadow sampler functions in D3D11"); + // https://github.com/KhronosGroup/WebGL/issues/1870 + // deqp/functional/gles3/shadertexturefunction/texture.html + _skip("texture_functions.texture.sampler2darrayshadow_vertex"); + // deqp/functional/gles3/shadertexturefunction/texturelod.html + _skip("texture_functions.texturelod.sampler2dshadow_vertex"); + _skip("texture_functions.texturelod.sampler2dshadow_fragment"); + // deqp/functional/gles3/shadertexturefunction/texturelodoffset.html + _skip("texture_functions.texturelodoffset.sampler2dshadow_vertex"); + _skip("texture_functions.texturelodoffset.sampler2dshadow_fragment"); + // deqp/functional/gles3/shadertexturefunction/textureprojlod.html + _skip("texture_functions.textureprojlod.sampler2dshadow_vertex"); + _skip("texture_functions.textureprojlod.sampler2dshadow_fragment"); + // deqp/functional/gles3/shadertexturefunction/textureprojlodoffset.html + _skip("texture_functions.textureprojlodoffset.sampler2dshadow_vertex"); + _skip("texture_functions.textureprojlodoffset.sampler2dshadow_fragment"); + // deqp/functional/gles3/shadertexturefunction/texturegrad.html + _skip("texture_functions.texturegrad.sampler2dshadow_vertex"); + _skip("texture_functions.texturegrad.sampler2dshadow_fragment"); + _skip("texture_functions.texturegrad.sampler2darrayshadow_vertex"); + _skip("texture_functions.texturegrad.sampler2darrayshadow_fragment"); + // deqp/functional/gles3/shadertexturefunction/texturegradoffset.html + _skip("texture_functions.texturegradoffset.sampler2dshadow_vertex"); + _skip("texture_functions.texturegradoffset.sampler2dshadow_fragment"); + _skip("texture_functions.texturegradoffset.sampler2darrayshadow_vertex"); + _skip("texture_functions.texturegradoffset.sampler2darrayshadow_fragment"); + // deqp/functional/gles3/shadertexturefunction/textureprojgrad.html + _skip("texture_functions.textureprojgrad.sampler2dshadow_vertex"); + _skip("texture_functions.textureprojgrad.sampler2dshadow_fragment"); + // deqp/functional/gles3/shadertexturefunction/textureprojgradoffset.html + _skip("texture_functions.textureprojgradoffset.sampler2dshadow_vertex"); + _skip("texture_functions.textureprojgradoffset.sampler2dshadow_fragment"); + + _setReason("MacOSX drivers share namespaces where they should not"); + // https://github.com/KhronosGroup/WebGL/issues/1890 + // deqp/data/gles3/shaders/scoping.html + _skip("scoping.valid.local_int_variable_hides_struct_type_vertex"); + _skip("scoping.valid.local_int_variable_hides_struct_type_fragment"); + _skip("scoping.valid.local_struct_variable_hides_struct_type_vertex"); + _skip("scoping.valid.local_struct_variable_hides_struct_type_fragment"); + _skip("scoping.valid.function_parameter_hides_struct_type_vertex"); + _skip("scoping.valid.function_parameter_hides_struct_type_fragment"); + + _setReason("NVidia Linux drivers does not clamp gl_FragDepth to [0.0, 1.0]"); + // Standalone Test case: + // https://github.com/Kangz/GLDriverBugs/blob/master/frag_depth_clamp_32f_depth/Main.cpp + // deqp/functional/gles3/fbodepthbuffer.html + _skip("depth.depth_write_clamp.depth_component32f"); + _skip("depth.depth_write_clamp.depth32f_stencil8"); + _skip("depth.depth_test_clamp.depth_component32f"); + _skip("depth.depth_test_clamp.depth32f_stencil8"); + + _setReason("NVidia Linux driver bug in nested uniform block location assignment"); + // crbug.com/621178 + // deqp/functional/gles3/uniformapi/random.html + _skip("uniform_api.random.64"); + + _setReason("Mac AMD / Linux AMD / older mesa handles clipping of wide points incorrectly"); + // crbug.com/642822 + // deqp/functional/gles3/clipping.html + _skip("clipping.wide_points_full_viewport"); + _skip("clipping.wide_points_partial_viewport"); + + + _setReason("Mac AMD drivers handle instanced rendering incorrectly under certain situation"); + // crbug.com/645298 + // deqp/functional/gles3/instancedrendering.html + _skip("instanced_rendering.draw_arrays_instanced.attribute_divisor.4_instances"); + _skip("instanced_rendering.draw_arrays_instanced.attribute_divisor.20_instances"); + _skip("instanced_rendering.draw_arrays_instanced.mixed.4_instances"); + _skip("instanced_rendering.draw_arrays_instanced.mixed.20_instances"); + _skip("instanced_rendering.draw_elements_instanced.attribute_divisor.4_instances"); + _skip("instanced_rendering.draw_elements_instanced.attribute_divisor.20_instances"); + _skip("instanced_rendering.draw_elements_instanced.mixed.4_instances"); + _skip("instanced_rendering.draw_elements_instanced.mixed.20_instances"); + + _setReason("Mac AMD drivers readPixels from RGB buffer into PIXEL_PACK_BUFFER got alpha values wrong"); + // crbug.com/646182 + // deqp/functional/gles3/pixelbufferobject.html + _skip("pixel_buffer_object.renderbuffer.rgb8_triangles"); + _skip("pixel_buffer_object.renderbuffer.rgb8_clears"); + + _setReason("Some Windows AMD D3D11 drivers have issues with blit and depth/stencil formats."); + // crbug.com/638323 + // deqp/functional/gles3/framebufferblit/depth_stencil.html + // Also see conformance2/rendering/blitframebuffer-stencil-only.html for 2.0.1 test. + _skip("blit.depth_stencil.depth24_stencil8_scale"); + _skip("blit.depth_stencil.depth24_stencil8_stencil_only"); + + _setReason("Transform feedback does not pass any tests on Mac AMD."); + // crbug.com/526748 + // Apple Radar: 28126946 + _skip("transform_feedback.*"); + + _setReason("Texture minification filtering is buggy for LINEAR mode on Mac Intel."); + // crbug.com/656478 + // Apple Radar: 28902129 + _skip("filtering.2d_combinations.linear_nearest_*"); + _skip("filtering.cube_combinations.linear_nearest_*"); + _skip("filtering.2d_array_combinations.linear_nearest_clamp_repeat"); + _skip("filtering.2d_array_combinations.linear_nearest_clamp_mirror"); + _skip("filtering.2d_array_combinations.linear_nearest_repeat_*"); + _skip("filtering.2d_array_combinations.linear_nearest_mirror_*"); + _skip("filtering.3d_combinations.linear_nearest_clamp_clamp_repeat"); + _skip("filtering.3d_combinations.linear_nearest_clamp_clamp_mirror"); + _skip("filtering.3d_combinations.linear_nearest_clamp_repeat_*"); + _skip("filtering.3d_combinations.linear_nearest_clamp_mirror_*"); + _skip("filtering.3d_combinations.linear_nearest_repeat_*"); + _skip("filtering.3d_combinations.linear_nearest_mirror_*"); + + _setReason("Setting of GL_READ_BUFFER and GL_DRAW_BUFFERs affects framebuffer completeness on Mac Intel."); + // crbug.com/630800 + // Apple Radar: 28236629 + _skip("completeness.attachment_combinations.none_rbo_none_none"); + _skip("completeness.attachment_combinations.none_tex_none_none"); + + _setReason("multisample constancy_alpha_to_coverage tests fail on Mac Intel."); + // crbug.com/663184 + _skip("multisample.fbo_4_samples.constancy_alpha_to_coverage"); + _skip("multisample.fbo_8_samples.constancy_alpha_to_coverage"); + _skip("multisample.fbo_max_samples.constancy_alpha_to_coverage"); + } // if (!runSkippedTests) + + /* + * Gets the skip status of the given test. Returns an + * object with the properties "skip", a boolean, and "reason", a + * string. + */ + tcuSkipList.getSkipStatus = function(testName) { + var skipEntry = _skipEntries[testName]; + if (skipEntry === undefined) { + return this._getWildcardSkipStatus(testName); + } else { + return { 'skip': true, 'reason': skipEntry }; + } + } + + /* + * Gets the skip status of the given tests like testpath* + * object with the properties "skip", a boolean, and "reason", a + * string. + */ + tcuSkipList._getWildcardSkipStatus = function(testName) { + var skipEntry; + for (var key in _wildcardSkipEntries) { + if (testName.indexOf(key) >=0 ) { + skipEntry = _wildcardSkipEntries[key]; + if (skipEntry != undefined) { + return { 'skip': true, 'reason': skipEntry }; + } + } + } + return { 'skip': false, 'reason': '' }; + } + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuStringTemplate.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuStringTemplate.js new file mode 100644 index 000000000..d70056733 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuStringTemplate.js @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuStringTemplate'); + +goog.scope(function() { + +var tcuStringTemplate = framework.common.tcuStringTemplate; + +tcuStringTemplate.escapeRegExp = function(string) { + return string.replace(/([.*+?^=!:$ {}()|\[\]\/\\])/g, '\\$1'); +}; + +tcuStringTemplate.specialize = function(str, params) { + var dst = str; + for (var key in params) { + var value = params[key]; + var re = new RegExp(tcuStringTemplate.escapeRegExp('\$\{' + key + '\}'), 'g'); + dst = dst.replace(re, value); + } + return dst; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuSurface.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuSurface.js new file mode 100644 index 000000000..47d3634aa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuSurface.js @@ -0,0 +1,184 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuSurface'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.opengl.gluTextureUtil'); + +goog.scope(function() { + +var tcuSurface = framework.common.tcuSurface; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var gluTextureUtil = framework.opengl.gluTextureUtil; + +var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); +}; + +/** + * \brief RGBA8888 surface + * + * tcuSurface.Surface provides basic pixel storage functionality. Only single format + * (RGBA8888) is supported. + * + * PixelBufferAccess (see tcuTexture.h) provides much more flexible API + * for handling various pixel formats. This is mainly a convinience class. + * @constructor + * @param {number=} width + * @param {number=} height + */ +tcuSurface.Surface = function(width, height) { + width = width || 0; + height = height || 0; + this.setSize(width, height); +}; + +/** + * @param {number} width + * @param {number} height + */ +tcuSurface.Surface.prototype.setSize = function(width, height) { + this.m_width = width; + this.m_height = height; + if (width * height > 0) { + this.m_data = new ArrayBuffer(4 * width * height); + this.m_pixels = new Uint8Array(this.m_data); + } else { + this.m_data = null; + this.m_pixels = null; + } +}; + +/** + * @return {number} + */ +tcuSurface.Surface.prototype.getWidth = function() { return this.m_width; }; + +/** + * @return {number} + */ +tcuSurface.Surface.prototype.getHeight = function() { return this.m_height; }; + +/** + * @param {number} x + * @param {number} y + * @param {Array<number>} color Vec4 color + */ +tcuSurface.Surface.prototype.setPixel = function(x, y, color) { + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + + var offset = 4 * (x + y * this.m_width); + for (var i = 0; i < 4; i++) + this.m_pixels[offset + i] = color[i]; +}; + +/** + * @param {number} x + * @param {number} y + * @return {Array<number>} + */ +tcuSurface.Surface.prototype.getPixel = function(x, y) { + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + + var color = []; + color.length = 4; + + var offset = 4 * (x + y * this.m_width); + for (var i = 0; i < 4; i++) + color[i] = this.m_pixels[offset + i]; + + return color; +}; + +/** + * @param {number} x + * @param {number} y + * @return {number} + */ +tcuSurface.Surface.prototype.getPixelUintRGB8 = function(x, y) { + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + + var offset = 4 * (x + y * this.m_width); + return (this.m_pixels[offset] << 16) + + (this.m_pixels[offset + 1] << 8) + + this.m_pixels[offset + 2]; +}; + +/** + * Read the viewport contents into this surface + * @param {*=} ctx WebGL or ref context + * @param {(Array<number>|{x: number, y: number, width: number, height: number})=} view + */ +tcuSurface.Surface.prototype.readViewport = function(ctx, view) { + ctx = ctx || gl; + var v = view || /** @type {Array<number>} */ (ctx.getParameter(gl.VIEWPORT)); + /** @type {number} */ var x; + /** @type {number} */ var y; + /** @type {number} */ var width; + /** @type {number} */ var height; + if (v instanceof Array || ArrayBuffer.isView(v)) { + x = v[0]; + y = v[1]; + width = v[2]; + height = v[3]; + } else { + x = v.x; + y = v.y; + width = v.width; + height = v.height; + } + if (width != this.m_width || height != this.m_height) + this.setSize(width, height); + ctx.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, this.m_pixels); +}; + +/** + * @return {Uint8Array} + */ +tcuSurface.Surface.prototype.getPixels = function() { return this.m_pixels || null; }; + +/** + * @return {tcuTexture.PixelBufferAccess} Pixel Buffer Access object + */ +tcuSurface.Surface.prototype.getAccess = function() { + return new tcuTexture.PixelBufferAccess({ + format: new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), + width: this.m_width, + height: this.m_height, + data: this.m_data + }); + +}; + +tcuSurface.Surface.prototype.getSubAccess = function(x, y, width, height) { + /* TODO: Implement. the deqp getSubAccess() looks broken. It will probably fail if + * x != 0 or width != m_width + */ + throw new Error('Unimplemented'); +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js new file mode 100644 index 000000000..d4322de4d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js @@ -0,0 +1,484 @@ +/*------------------------------------------------------------------------- + * 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. + * + */ + +/** + * This class allows one to create a hierarchy of tests and iterate over them. + * It replaces TestCase and TestCaseGroup classes. + */ +'use strict'; +goog.provide('framework.common.tcuTestCase'); +goog.require('framework.common.tcuSkipList'); + +goog.scope(function() { + + var tcuTestCase = framework.common.tcuTestCase; + var tcuSkipList = framework.common.tcuSkipList; + + /** + * Reads the filter parameter from the URL to filter tests. + * @return {?string } + */ + tcuTestCase.getFilter = function() { + var queryVars = window.location.search.substring(1).split('&'); + + for (var i = 0; i < queryVars.length; i++) { + var value = queryVars[i].split('='); + if (decodeURIComponent(value[0]) === 'filter') + return decodeURIComponent(value[1]); + } + return null; + }; + + /** + * Indicates the state of an iteration operation. + * @enum {number} + */ + tcuTestCase.IterateResult = { + STOP: 0, + CONTINUE: 1 + }; + + /**************************************** + * Runner + ***************************************/ + + /** + * A simple state machine. + * The purpose of this this object is to break + * long tests into small chunks that won't cause a timeout + * @constructor + */ + tcuTestCase.Runner = function() { + /** @type {tcuTestCase.DeqpTest} */ this.currentTest = null; + /** @type {tcuTestCase.DeqpTest} */ this.nextTest = null; + /** @type {tcuTestCase.DeqpTest} */ this.testCases = null; + /** @type {?string } */ this.filter = tcuTestCase.getFilter(); + }; + + /** + * @param {tcuTestCase.DeqpTest} root The root test of the test tree. + */ + tcuTestCase.Runner.prototype.setRoot = function(root) { + this.currentTest = null; + this.testCases = root; + }; + + tcuTestCase.Runner.prototype.setRange = function(range) { + this.range = range; + }; + + /** + * Searches the test tree for the next executable test + * @return {?tcuTestCase.DeqpTest } + */ + tcuTestCase.Runner.prototype.next = function() { + + // First time? Use root test + if (!this.currentTest) { + this.currentTest = this.testCases; + + // Root is executable? Use it + if (this.currentTest.isExecutable()) + return this.currentTest; + } + + // Should we proceed with the next test? + if (tcuTestCase.lastResult == tcuTestCase.IterateResult.STOP) { + // Look for next executable test + do { + if (this.range) + this.currentTest = this.currentTest.nextInRange(this.filter, this.range); + else + this.currentTest = this.currentTest.next(this.filter); + } while (this.currentTest && !this.currentTest.isExecutable()); + } + + return this.currentTest; + }; + + /** + * Schedule the callback to be run ASAP + * @param {function()} callback Callback to schedule + */ + tcuTestCase.Runner.prototype.runCallback = function(callback) { + setTimeout(function() { + callback(); + }.bind(this), 0); + }; + + /** + * Call this function at the end of the test + */ + tcuTestCase.Runner.prototype.terminate = function() { + finishTest(); + }; + + tcuTestCase.runner = new tcuTestCase.Runner(); + + /** @type {tcuTestCase.IterateResult} */ tcuTestCase.lastResult = tcuTestCase.IterateResult.STOP; + + /*************************************** + * DeqpTest + ***************************************/ + + /** + * Assigns name, description and specification to test + * @constructor + * @param {?string} name + * @param {?string} description + * @param {Object=} spec + */ + tcuTestCase.DeqpTest = function(name, description, spec) { + this.name = name || ''; + this.description = description || ''; + this.spec = spec; + this.currentTestNdx = 0; + this.parentTest = null; + this.childrenTests = []; + this.executeAlways = false; + }; + + /** + * Abstract init function(each particular test will implement it, or not) + */ + tcuTestCase.DeqpTest.prototype.init = function() {}; + + /** + * Abstract deinit function(each particular test will implement it, or not) + */ + tcuTestCase.DeqpTest.prototype.deinit = function() {}; + + /** + * Abstract iterate function(each particular test will implement it, or not) + * @return {tcuTestCase.IterateResult} + */ + tcuTestCase.DeqpTest.prototype.iterate = function() { return tcuTestCase.IterateResult.STOP; }; + + /** + * Checks if the test is executable + * @return {boolean} + */ + tcuTestCase.DeqpTest.prototype.isExecutable = function() { + return this.childrenTests.length == 0 || this.executeAlways; + }; + + /** + * Checks if the test is a leaf + */ + tcuTestCase.DeqpTest.prototype.isLeaf = function() { + return this.childrenTests.length == 0; + }; + + /** + * Marks the test as always executable + */ + tcuTestCase.DeqpTest.prototype.makeExecutable = function() { + this.executeAlways = true; + }; + + /** + * Adds a child test to the test's children + * @param {tcuTestCase.DeqpTest} test + */ + tcuTestCase.DeqpTest.prototype.addChild = function(test) { + test.parentTest = this; + this.childrenTests.push(test); + }; + + /** + * Sets the whole children tests array + * @param {Array<tcuTestCase.DeqpTest>} tests + */ + tcuTestCase.DeqpTest.prototype.setChildren = function(tests) { + for (var test in tests) + tests[test].parentTest = this; + this.childrenTests = tests; + }; + + /** + * Returns the next test in the hierarchy of tests + * + * @param {?string } pattern Optional pattern to search for + * @return {tcuTestCase.DeqpTest} + */ + tcuTestCase.DeqpTest.prototype.next = function(pattern) { + return this._nextHonoringSkipList(pattern); + }; + + /** + * Returns the next test in the hierarchy of tests, honoring the + * skip list, and reporting skipped tests. + * + * @param {?string } pattern Optional pattern to search for + * @return {tcuTestCase.DeqpTest} + */ + tcuTestCase.DeqpTest.prototype._nextHonoringSkipList = function(pattern) { + var tryAgain = false; + var test = null; + do { + tryAgain = false; + test = this._nextIgnoringSkipList(pattern); + if (test != null) { + // See whether the skip list vetoes the execution of + // this test. + var fullTestName = test.fullName(); + var skipDisposition = tcuSkipList.getSkipStatus(fullTestName); + if (skipDisposition.skip) { + tryAgain = true; + setCurrentTestName(fullTestName); + checkMessage(false, 'Skipping test due to tcuSkipList: ' + fullTestName); + } + } + } while (tryAgain); + return test; + }; + + + /** + * Returns the next test in the hierarchy of tests, ignoring the + * skip list. + * + * @param {?string } pattern Optional pattern to search for + * @return {tcuTestCase.DeqpTest} + */ + tcuTestCase.DeqpTest.prototype._nextIgnoringSkipList = function(pattern) { + if (pattern) + return this._findIgnoringSkipList(pattern); + + var test = null; + + //Look for the next child + if (this.currentTestNdx < this.childrenTests.length) { + test = this.childrenTests[this.currentTestNdx]; + this.currentTestNdx++; + } + + // If no more children, get the next brother + if (test == null && this.parentTest != null) { + test = this.parentTest._nextIgnoringSkipList(null); + } + + return test; + }; + + /** + * Returns the next test in the hierarchy of tests + * whose 1st level is in the given range + * + * @param {?string } pattern Optional pattern to search for + * @param {Array<number>} range + * @return {tcuTestCase.DeqpTest} + */ + tcuTestCase.DeqpTest.prototype.nextInRange = function(pattern, range) { + while (true) { + var test = this._nextHonoringSkipList(pattern); + if (!test) + return null; + var topLevelId = tcuTestCase.runner.testCases.currentTestNdx - 1; + if (topLevelId >= range[0] && topLevelId < range[1]) + return test; + } + }; + + /** + * Returns the full name of the test + * + * @return {string} Full test name. + */ + tcuTestCase.DeqpTest.prototype.fullName = function() { + if (this.parentTest) { + var parentName = this.parentTest.fullName(); + if (parentName) + return parentName + '.' + this.name; + } + return this.name; + }; + + /** + * Returns the description of the test + * + * @return {string} Test description. + */ + tcuTestCase.DeqpTest.prototype.getDescription = function() { + return this.description; + }; + + /** + * Find a test with a matching name. Fast-forwards to a test whose + * full name matches the given pattern. + * + * @param {string} pattern Regular expression to search for + * @return {?tcuTestCase.DeqpTest } Found test or null. + */ + tcuTestCase.DeqpTest.prototype.find = function(pattern) { + return this._findHonoringSkipList(pattern); + }; + + /** + * Find a test with a matching name. Fast-forwards to a test whose + * full name matches the given pattern, honoring the skip list, and + * reporting skipped tests. + * + * @param {string} pattern Regular expression to search for + * @return {?tcuTestCase.DeqpTest } Found test or null. + */ + tcuTestCase.DeqpTest.prototype._findHonoringSkipList = function(pattern) { + var tryAgain = false; + var test = null; + do { + tryAgain = false; + test = this._findIgnoringSkipList(pattern); + if (test != null) { + // See whether the skip list vetoes the execution of + // this test. + var fullTestName = test.fullName(); + var skipDisposition = tcuSkipList.getSkipStatus(fullTestName); + if (skipDisposition.skip) { + tryAgain = true; + checkMessage(false, 'Skipping test due to tcuSkipList: ' + fullTestName); + } + } + } while (tryAgain); + return test; + }; + + /** + * Find a test with a matching name. Fast-forwards to a test whose + * full name matches the given pattern. + * + * @param {string} pattern Regular expression to search for + * @return {?tcuTestCase.DeqpTest } Found test or null. + */ + tcuTestCase.DeqpTest.prototype._findIgnoringSkipList = function(pattern) { + var test = this; + while (true) { + test = test._nextIgnoringSkipList(null); + if (!test) + break; + if (test.fullName().match(pattern) || test.executeAlways) + break; + } + return test; + }; + + /** + * Reset the iterator. + */ + tcuTestCase.DeqpTest.prototype.reset = function() { + this.currentTestNdx = 0; + + for (var i = 0; i < this.childrenTests.length; i++) + this.childrenTests[i].reset(); + }; + + /** + * Defines a new test + * + * @param {?string} name Short test name + * @param {?string} description Description of the test + * @param {Object=} spec Test specification + * + * @return {tcuTestCase.DeqpTest} The new test + */ + tcuTestCase.newTest = function(name, description, spec) { + var test = new tcuTestCase.DeqpTest(name, description, spec); + + return test; + }; + + /** + * Defines a new executable test so it gets run even if it's not a leaf + * + * @param {string} name Short test name + * @param {string} description Description of the test + * @param {Object=} spec Test specification + * + * @return {tcuTestCase.DeqpTest} The new test + */ + tcuTestCase.newExecutableTest = function(name, description, spec) { + var test = tcuTestCase.newTest(name, description, spec); + test.makeExecutable(); + + return test; + }; + + /** + * Run through the test cases giving time to system operation. + */ + tcuTestCase.runTestCases = function() { + var state = tcuTestCase.runner; + + if (state.next()) { + try { + // If proceeding with the next test, prepare it. + var fullTestName = state.currentTest.fullName(); + var inited = true; + if (tcuTestCase.lastResult == tcuTestCase.IterateResult.STOP) { + // Update current test name + setCurrentTestName(fullTestName); + bufferedLogToConsole('Init testcase: ' + fullTestName); //Show also in console so we can see which test crashed the browser's tab + + // Initialize particular test + inited = state.currentTest.init(); + inited = inited === undefined ? true : inited; + + //If it's a leaf test, notify of it's execution. + if (state.currentTest.isLeaf() && inited) + debug('<hr/><br/>Start testcase: ' + fullTestName); + } + + if (inited) { + // Run the test, save the result. + tcuTestCase.lastResult = state.currentTest.iterate(); + } else { + // Skip uninitialized test. + tcuTestCase.lastResult = tcuTestCase.IterateResult.STOP; + } + + // Cleanup + if (tcuTestCase.lastResult == tcuTestCase.IterateResult.STOP) + state.currentTest.deinit(); + } + catch (err) { + // If the exception was not thrown by a test check, log it, but don't throw it again + if (!(err instanceof TestFailedException)) { + //Stop execution of current test. + tcuTestCase.lastResult = tcuTestCase.IterateResult.STOP; + try { + // Cleanup + if (tcuTestCase.lastResult == tcuTestCase.IterateResult.STOP) + state.currentTest.deinit(); + } catch (cerr) { + bufferedLogToConsole('Error while cleaning up test: ' + cerr); + } + var msg = err; + if (err.message) + msg = err.message; + testFailedOptions(msg, false); + } + bufferedLogToConsole(err); + } + + tcuTestCase.runner.runCallback(tcuTestCase.runTestCases); + } else + tcuTestCase.runner.terminate(); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexCompareVerifier.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexCompareVerifier.js new file mode 100644 index 000000000..254963ae6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexCompareVerifier.js @@ -0,0 +1,1356 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuTexCompareVerifier'); +goog.require('framework.common.tcuTexVerifierUtil'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var tcuTexCompareVerifier = framework.common.tcuTexCompareVerifier; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var tcuTextureUtil = framework.common.tcuTextureUtil; +var tcuTexVerifierUtil = framework.common.tcuTexVerifierUtil; + +/** + * \brief Texture compare (shadow) lookup precision parameters. + * @constructor + * @struct + * @param {Array<number>=} coordBits + * @param {Array<number>=} uvwBits + * @param {number=} pcfBits + * @param {number=} referenceBits + * @param {number=} resultBits + */ +tcuTexCompareVerifier.TexComparePrecision = function(coordBits, uvwBits, pcfBits, referenceBits, resultBits) { + this.coordBits = coordBits === undefined ? [22, 22, 22] : coordBits; + this.uvwBits = uvwBits === undefined ? [22, 22, 22] : uvwBits; + this.pcfBits = pcfBits === undefined ? 16 : pcfBits; + this.referenceBits = referenceBits === undefined ? 16 : referenceBits; + this.resultBits = resultBits === undefined ? 16 : resultBits; +}; + +/** + * @constructor + * @struct + */ +tcuTexCompareVerifier.CmpResultSet = function() { + this.isTrue = false; + this.isFalse = false; +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {number} cmpValue_ + * @param {number} cmpReference_ + * @param {number} referenceBits + * @param {boolean} isFixedPoint + * @return {tcuTexCompareVerifier.CmpResultSet} + */ +tcuTexCompareVerifier.execCompare = function(compareMode, + cmpValue_, + cmpReference_, + referenceBits, + isFixedPoint) { + var clampValues = isFixedPoint; // if comparing against a floating point texture, ref (and value) is not clamped + var cmpValue = (clampValues) ? (deMath.clamp(cmpValue_, 0, 1)) : (cmpValue_); + var cmpReference = (clampValues) ? (deMath.clamp(cmpReference_, 0, 1)) : (cmpReference_); + var err = tcuTexVerifierUtil.computeFixedPointError(referenceBits); + var res = new tcuTexCompareVerifier.CmpResultSet(); + + switch (compareMode) { + case tcuTexture.CompareMode.COMPAREMODE_LESS: + res.isTrue = cmpReference - err < cmpValue; + res.isFalse = cmpReference + err >= cmpValue; + break; + + case tcuTexture.CompareMode.COMPAREMODE_LESS_OR_EQUAL: + res.isTrue = cmpReference - err <= cmpValue; + res.isFalse = cmpReference + err > cmpValue; + break; + + case tcuTexture.CompareMode.COMPAREMODE_GREATER: + res.isTrue = cmpReference + err > cmpValue; + res.isFalse = cmpReference - err <= cmpValue; + break; + + case tcuTexture.CompareMode.COMPAREMODE_GREATER_OR_EQUAL: + res.isTrue = cmpReference + err >= cmpValue; + res.isFalse = cmpReference - err < cmpValue; + break; + + case tcuTexture.CompareMode.COMPAREMODE_EQUAL: + res.isTrue = deMath.deInRange32(cmpValue, cmpReference - err, cmpReference + err); + res.isFalse = err != 0 || cmpValue != cmpReference; + break; + + case tcuTexture.CompareMode.COMPAREMODE_NOT_EQUAL: + res.isTrue = err != 0 || cmpValue != cmpReference; + res.isFalse = deMath.deInRange32(cmpValue, cmpReference - err, cmpReference + err); + break; + + case tcuTexture.CompareMode.COMPAREMODE_ALWAYS: + res.isTrue = true; + break; + + case tcuTexture.CompareMode.COMPAREMODE_NEVER: + res.isFalse = true; + break; + + default: + throw new Error('Invalid compare mode:' + compareMode); + } + + assertMsgOptions(res.isTrue || res.isFalse, 'Both tests failed!', false, true); + return res; +}; + +/** + * @param {tcuTexture.TextureFormat} format + * @return {boolean} + */ +tcuTexCompareVerifier.isFixedPointDepthTextureFormat = function(format) { + var channelClass = tcuTexture.getTextureChannelClass(format.type); + + if (format.order == tcuTexture.ChannelOrder.D) { + // depth internal formats cannot be non-normalized integers + return channelClass != tcuTexture.TextureChannelClass.FLOATING_POINT; + } else if (format.order == tcuTexture.ChannelOrder.DS) { + // combined formats have no single channel class, detect format manually + switch (format.type) { + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: return false; + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: return true; + + default: + throw new Error('Invalid texture format: ' + format); + } + } + + return false; +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} depths + * @param {Array<number>} fBounds + * @param {number} cmpReference + * @param {number} result + * @param {boolean} isFixedPointDepth + * @return {boolean} + */ +tcuTexCompareVerifier.isLinearCompareValid = function(compareMode, prec, depths, fBounds, cmpReference, result, isFixedPointDepth) { + assertMsgOptions(fBounds[0] >= 0 && fBounds[0] <= fBounds[1] && fBounds[1] <= 1, 'Invalid fBounds', false, true); + + var d0 = depths[0]; + var d1 = depths[1]; + + var cmp0 = tcuTexCompareVerifier.execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp1 = tcuTexCompareVerifier.execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp = [cmp0, cmp1]; + + var isTrue = getMask(cmp, function(x) {return x.isTrue;}); + var isFalse = getMask(cmp, function(x) {return x.isFalse;}); + + var f0 = fBounds[0]; + var f1 = fBounds[1]; + + var pcfErr = tcuTexVerifierUtil.computeFixedPointError(prec.pcfBits); + var resErr = tcuTexVerifierUtil.computeFixedPointError(prec.resultBits); + var totalErr = pcfErr + resErr; + + for (var comb = 0; comb < 4; comb++) { + if (((comb & isTrue) | (~comb & isFalse )) != 3) + continue; + + var cmp0True = ((comb >> 0) & 1) != 0; + var cmp1True = ((comb >> 1) & 1) != 0; + + var ref0 = cmp0True ? 1 : 0; + var ref1 = cmp1True ? 1 : 0; + + var v0 = ref0 * (1 - f0) + ref1 * f0; + var v1 = ref0 * (1 - f1) + ref1 * f1; + var minV = Math.min(v0, v1); + var maxV = Math.max(v0, v1); + var minR = minV - totalErr; + var maxR = maxV + totalErr; + + if (deMath.deInRange32(result, minR, maxR)) + return true; + } + return false; +}; + +/** + * @param {number} val + * @param {number} offset + * @return {Array<boolean>} + */ +tcuTexCompareVerifier.extractBVec4 = function(val, offset) { + return [ + ((val >> (offset + 0)) & 1) != 0, + ((val >> (offset + 1)) & 1) != 0, + ((val >> (offset + 2)) & 1) != 0, + ((val >> (offset + 3)) & 1) != 0]; +}; + +/** + * Values are in order (0,0), (1,0), (0,1), (1,1) + * @param {Array<number>} values + * @param {number} x + * @param {number} y + * @return {number} + */ +tcuTexCompareVerifier.bilinearInterpolate = function(values, x, y) { + var v00 = values[0]; + var v10 = values[1]; + var v01 = values[2]; + var v11 = values[3]; + var res = v00 * (1 - x) * (1 - y) + v10 * x * (1 - y) + v01 * (1 - x) * y + v11 * x * y; + return res; +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} depths vec4 + * @param {number} cmpReference + * @param {number} result + * @param {boolean} isFixedPointDepth + * @return {boolean} + */ +tcuTexCompareVerifier.isBilinearAnyCompareValid = function(compareMode, + prec, + depths, + cmpReference, + result, + isFixedPointDepth) { + assertMsgOptions(prec.pcfBits === 0, 'PCF bits must be 0', false, true); + + var d0 = depths[0]; + var d1 = depths[1]; + var d2 = depths[2]; + var d3 = depths[3]; + + var cmp0 = tcuTexCompareVerifier.execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp1 = tcuTexCompareVerifier.execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp2 = tcuTexCompareVerifier.execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp3 = tcuTexCompareVerifier.execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth); + + var canBeTrue = cmp0.isTrue || cmp1.isTrue || cmp2.isTrue || cmp3.isTrue; + var canBeFalse = cmp0.isFalse || cmp1.isFalse || cmp2.isFalse || cmp3.isFalse; + + var resErr = tcuTexVerifierUtil.computeFixedPointError(prec.resultBits); + + var minBound = canBeFalse ? 0 : 1; + var maxBound = canBeTrue ? 1 : 0; + + return deMath.deInRange32(result, minBound - resErr, maxBound + resErr); +}; + +/** + * @param {Array<tcuTexCompareVerifier.CmpResultSet>} arr + * @param {function(tcuTexCompareVerifier.CmpResultSet): boolean} getValue + * @return {number} + */ +var getMask = function(arr, getValue) { + var mask = 0; + for (var i = 0; i < arr.length; i++) { + var val = getValue(arr[i]); + if (val) + mask |= 1 << i; + } + return mask; +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} depths vec4 + * @param {Array<number>} xBounds vec2 + * @param {Array<number>} yBounds vec2 + * @param {number} cmpReference + * @param {number} result + * @param {boolean} isFixedPointDepth + * @return {boolean} + */ +tcuTexCompareVerifier.isBilinearPCFCompareValid = function(compareMode, + prec, + depths, + xBounds, + yBounds, + cmpReference, + result, + isFixedPointDepth) { + assertMsgOptions(0.0 <= xBounds[0] && xBounds[0] <= xBounds[1] && xBounds[1] <= 1.0, 'x coordinate out of bounds', false, true); + assertMsgOptions(0.0 <= yBounds[0] && yBounds[0] <= yBounds[1] && yBounds[1] <= 1.0, 'y coordinate out of bounds', false, true); + assertMsgOptions(prec.pcfBits > 0, 'PCF bits must be > 0', false, true); + + var d0 = depths[0]; + var d1 = depths[1]; + var d2 = depths[2]; + var d3 = depths[3]; + + /** @type {Array<tcuTexCompareVerifier.CmpResultSet>} */ var cmp = []; + cmp[0] = tcuTexCompareVerifier.execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); + cmp[1] = tcuTexCompareVerifier.execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); + cmp[2] = tcuTexCompareVerifier.execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth); + cmp[3] = tcuTexCompareVerifier.execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth); + + var isTrue = getMask(cmp, function(x) {return x.isTrue}); + var isFalse = getMask(cmp, function(x) {return x.isFalse}); + + // Interpolation parameters + var x0 = xBounds[0]; + var x1 = xBounds[1]; + var y0 = yBounds[0]; + var y1 = yBounds[1]; + + // Error parameters + var pcfErr = tcuTexVerifierUtil.computeFixedPointError(prec.pcfBits); + var resErr = tcuTexVerifierUtil.computeFixedPointError(prec.resultBits); + var totalErr = pcfErr + resErr; + + // Iterate over all valid combinations. + // \note It is not enough to compute minmax over all possible result sets, as ranges may + // not necessarily overlap, i.e. there are gaps between valid ranges. + for (var comb = 0; comb < (1 << 4); comb++) { + // Filter out invalid combinations: + // 1) True bit is set in comb but not in isTrue => sample can not be true + // 2) True bit is NOT set in comb and not in isFalse => sample can not be false + if (((comb & isTrue) | (~comb & isFalse)) != (1 << 4) - 1) + continue; + + var cmpTrue = tcuTexCompareVerifier.extractBVec4(comb, 0); + var refVal = tcuTextureUtil.select([1, 1, 1, 1], [0, 0, 0, 0], cmpTrue); + + var v0 = tcuTexCompareVerifier.bilinearInterpolate(refVal, x0, y0); + var v1 = tcuTexCompareVerifier.bilinearInterpolate(refVal, x1, y0); + var v2 = tcuTexCompareVerifier.bilinearInterpolate(refVal, x0, y1); + var v3 = tcuTexCompareVerifier.bilinearInterpolate(refVal, x1, y1); + var minV = Math.min(v0, v1, v2, v3); + var maxV = Math.max(v0, v1, v2, v3); + var minR = minV - totalErr; + var maxR = maxV + totalErr; + + if (deMath.deInRange32(result, minR, maxR)) + return true; + } + + return false; +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} depths vec4 + * @param {Array<number>} xBounds vec2 + * @param {Array<number>} yBounds vec2 + * @param {number} cmpReference + * @param {number} result + * @param {boolean} isFixedPointDepth + * @return {boolean} + */ +tcuTexCompareVerifier.isBilinearCompareValid = function(compareMode, + prec, + depths, + xBounds, + yBounds, + cmpReference, + result, + isFixedPointDepth) { + if (prec.pcfBits > 0) + return tcuTexCompareVerifier.isBilinearPCFCompareValid(compareMode, prec, depths, xBounds, yBounds, cmpReference, result, isFixedPointDepth); + else + return tcuTexCompareVerifier.isBilinearAnyCompareValid(compareMode, prec, depths, cmpReference, result, isFixedPointDepth); +}; +/** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {number} coordZ + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isLinearCompareResultValid = function(level, + sampler, + prec, + coord, + coordZ, + cmpReference, + result) { + var isFixedPointDepth = tcuTexCompareVerifier.isFixedPointDepthTextureFormat(level.getFormat()); + var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord[0], prec.coordBits[0], prec.uvwBits[0]); + var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord[1], prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinate bounds for (x0,y0) - without wrap mode + var minI = Math.floor(uBounds[0] - 0.5); + var maxI = Math.floor(uBounds[1] - 0.5); + var minJ = Math.floor(vBounds[0] - 0.5); + var maxJ = Math.floor(vBounds[1] - 0.5); + + var w = level.getWidth(); + var h = level.getHeight(); + + // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode. + + for (var j = minJ; j <= maxJ; j++) { + for (var i = minI; i <= maxI; i++) { + // Wrapped coordinates + var x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i, w); + var x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i + 1, w); + var y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j, h); + var y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j + 1, h); + + // Bounds for filtering factors + var minA = deMath.clamp((uBounds[0] - 0.5) - i, 0, 1); + var maxA = deMath.clamp((uBounds[1] - 0.5) - i, 0, 1); + var minB = deMath.clamp((vBounds[0] - 0.5) - j, 0, 1); + var maxB = deMath.clamp((vBounds[1] - 0.5) - j, 0, 1); + + var depths = [ + level.getPixDepth(x0, y0, coordZ), + level.getPixDepth(x1, y0, coordZ), + level.getPixDepth(x0, y1, coordZ), + level.getPixDepth(x1, y1, coordZ) + ]; + + if (tcuTexCompareVerifier.isBilinearCompareValid(sampler.compare, prec, depths, [minA, maxA], [minB, maxB], cmpReference, result, isFixedPointDepth)) + return true; + } + } + + return false; +}; + +/** + * @param {tcuTexCompareVerifier.CmpResultSet} resultSet + * @param {number} result + * @param {number} resultBits + */ +tcuTexCompareVerifier.isResultInSet = function(resultSet, result, resultBits) { + var err = tcuTexVerifierUtil.computeFixedPointError(resultBits); + var minR = result - err; + var maxR = result + err; + + return (resultSet.isTrue && deMath.deInRange32(1, minR, maxR)) || + (resultSet.isFalse && deMath.deInRange32(0, minR, maxR)); +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {number} coordZ + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isNearestCompareResultValid = function(level, + sampler, + prec, + coord, + coordZ, + cmpReference, + result) { + var isFixedPointDepth = tcuTexCompareVerifier.isFixedPointDepthTextureFormat(level.getFormat()); + var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord[0], prec.coordBits[0], prec.uvwBits[0]); + var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord[1], prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinates - without wrap mode + var minI = Math.floor(uBounds[0]); + var maxI = Math.floor(uBounds[1]); + var minJ = Math.floor(vBounds[0]); + var maxJ = Math.floor(vBounds[1]); + + for (var j = minJ; j <= maxJ; j++) { + for (var i = minI; i <= maxI; i++) { + var x = tcuTexVerifierUtil.wrap(sampler.wrapS, i, level.getWidth()); + var y = tcuTexVerifierUtil.wrap(sampler.wrapT, j, level.getHeight()); + var depth = level.getPixDepth(x, y, coordZ); + var resSet = tcuTexCompareVerifier.execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth); + + if (tcuTexCompareVerifier.isResultInSet(resSet, result, prec.resultBits)) + return true; + } + } + + return false; +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filterMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {number} coordZ + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isLevelCompareResultValid = function(level, + sampler, + filterMode, + prec, + coord, + coordZ, + cmpReference, + result) { + if (filterMode == tcuTexture.FilterMode.LINEAR) + return tcuTexCompareVerifier.isLinearCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result); + else + return tcuTexCompareVerifier.isNearestCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result); +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} depths0 vec4 + * @param {Array<number>} depths1 vec4 + * @param {number} cmpReference + * @param {number} result + * @param {boolean} isFixedPointDepth + * @return {boolean} + */ +tcuTexCompareVerifier.isTrilinearAnyCompareValid = function(compareMode, + prec, + depths0, + depths1, + cmpReference, + result, + isFixedPointDepth) { + assertMsgOptions(prec.pcfBits === 0, 'PCF bits must be 0', false, true); + + var cmp00 = tcuTexCompareVerifier.execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp01 = tcuTexCompareVerifier.execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp02 = tcuTexCompareVerifier.execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp03 = tcuTexCompareVerifier.execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp10 = tcuTexCompareVerifier.execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp11 = tcuTexCompareVerifier.execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp12 = tcuTexCompareVerifier.execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth); + var cmp13 = tcuTexCompareVerifier.execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth); + + var canBeTrue = cmp00.isTrue || + cmp01.isTrue || + cmp02.isTrue || + cmp03.isTrue || + cmp10.isTrue || + cmp11.isTrue || + cmp12.isTrue || + cmp13.isTrue; + var canBeFalse = cmp00.isFalse || + cmp01.isFalse || + cmp02.isFalse || + cmp03.isFalse || + cmp10.isFalse || + cmp11.isFalse || + cmp12.isFalse || + cmp13.isFalse; + + var resErr = tcuTexVerifierUtil.computeFixedPointError(prec.resultBits); + + var minBound = canBeFalse ? 0 : 1; + var maxBound = canBeTrue ? 1 : 0; + + return deMath.deInRange32(result, minBound - resErr, maxBound + resErr); +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} depths0 vec4 + * @param {Array<number>} depths1 vec4 + * @param {Array<number>} xBounds0 + * @param {Array<number>} yBounds0 + * @param {Array<number>} xBounds1 + * @param {Array<number>} yBounds1 + * @param {Array<number>} fBounds + * @param {number} cmpReference + * @param {number} result + * @param {boolean} isFixedPointDepth + * @return {boolean} + */ +tcuTexCompareVerifier.isTrilinearPCFCompareValid = function(compareMode, + prec, + depths0, + depths1, + xBounds0, + yBounds0, + xBounds1, + yBounds1, + fBounds, + cmpReference, + result, + isFixedPointDepth) { + assertMsgOptions(0.0 <= xBounds0[0] && xBounds0[0] <= xBounds0[1] && xBounds0[1] <= 1.0, 'x0 coordinate out of bounds', false, true); + assertMsgOptions(0.0 <= yBounds0[0] && yBounds0[0] <= yBounds0[1] && yBounds0[1] <= 1.0, 'y0 coordinate out of bounds', false, true); + assertMsgOptions(0.0 <= xBounds1[0] && xBounds1[0] <= xBounds1[1] && xBounds1[1] <= 1.0, 'x1 coordinate out of bounds', false, true); + assertMsgOptions(0.0 <= yBounds1[0] && yBounds1[0] <= yBounds1[1] && yBounds1[1] <= 1.0, 'y1 coordinate out of bounds', false, true); + assertMsgOptions(0.0 <= fBounds[0] && fBounds[0] <= fBounds[1] && fBounds[1] <= 1.0, 'linear factor out of bounds', false, true); + assertMsgOptions(prec.pcfBits > 0, 'PCF bits must be > 0', false, true); + + /** @type {Array<tcuTexCompareVerifier.CmpResultSet>} */ var cmp = []; + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth)); + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth)); + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth)); + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth)); + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth)); + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth)); + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth)); + cmp.push(tcuTexCompareVerifier.execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth)); + + var isTrue = getMask(cmp, function(x) {return x.isTrue}); + var isFalse = getMask(cmp, function(x) {return x.isFalse}); + + // Error parameters + var pcfErr = tcuTexVerifierUtil.computeFixedPointError(prec.pcfBits); + var resErr = tcuTexVerifierUtil.computeFixedPointError(prec.resultBits); + var totalErr = pcfErr + resErr; + + // Iterate over all valid combinations. + for (var comb = 0; comb < (1 << 8); comb++) { + // Filter out invalid combinations. + if (((comb & isTrue) | (~comb & isFalse)) != (1 << 8) - 1) + continue; + + var cmpTrue0 = tcuTexCompareVerifier.extractBVec4(comb, 0); + var cmpTrue1 = tcuTexCompareVerifier.extractBVec4(comb, 4); + var refVal0 = tcuTextureUtil.select([1, 1, 1, 1], [0, 0, 0, 0], cmpTrue0); + var refVal1 = tcuTextureUtil.select([1, 1, 1, 1], [0, 0, 0, 0], cmpTrue1); + + // Bilinear interpolation within levels. + var v00 = tcuTexCompareVerifier.bilinearInterpolate(refVal0, xBounds0[0], yBounds0[0]); + var v01 = tcuTexCompareVerifier.bilinearInterpolate(refVal0, xBounds0[1], yBounds0[0]); + var v02 = tcuTexCompareVerifier.bilinearInterpolate(refVal0, xBounds0[0], yBounds0[1]); + var v03 = tcuTexCompareVerifier.bilinearInterpolate(refVal0, xBounds0[1], yBounds0[1]); + var minV0 = Math.min(v00, v01, v02, v03); + var maxV0 = Math.max(v00, v01, v02, v03); + + var v10 = tcuTexCompareVerifier.bilinearInterpolate(refVal1, xBounds1[0], yBounds1[0]); + var v11 = tcuTexCompareVerifier.bilinearInterpolate(refVal1, xBounds1[1], yBounds1[0]); + var v12 = tcuTexCompareVerifier.bilinearInterpolate(refVal1, xBounds1[0], yBounds1[1]); + var v13 = tcuTexCompareVerifier.bilinearInterpolate(refVal1, xBounds1[1], yBounds1[1]); + var minV1 = Math.min(v10, v11, v12, v13); + var maxV1 = Math.max(v10, v11, v12, v13); + + // Compute min-max bounds by filtering between minimum bounds and maximum bounds between levels. + // HW can end up choosing pretty much any of samples between levels, and thus interpolating + // between minimums should yield lower bound for range, and same for upper bound. + // \todo [2013-07-17 pyry] This seems separable? Can this be optimized? At least ranges could be pre-computed and later combined. + var minF0 = minV0 * (1 - fBounds[0]) + minV1 * fBounds[0]; + var minF1 = minV0 * (1 - fBounds[1]) + minV1 * fBounds[1]; + var maxF0 = maxV0 * (1 - fBounds[0]) + maxV1 * fBounds[0]; + var maxF1 = maxV0 * (1 - fBounds[1]) + maxV1 * fBounds[1]; + + var minF = Math.min(minF0, minF1); + var maxF = Math.max(maxF0, maxF1); + + var minR = minF - totalErr; + var maxR = maxF + totalErr; + + if (deMath.deInRange32(result, minR, maxR)) + return true; + } + + return false; + +}; + +/** + * @param {tcuTexture.CompareMode} compareMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} depths0 vec4 + * @param {Array<number>} depths1 vec4 + * @param {Array<number>} xBounds0 + * @param {Array<number>} yBounds0 + * @param {Array<number>} xBounds1 + * @param {Array<number>} yBounds1 + * @param {Array<number>} fBounds + * @param {number} cmpReference + * @param {number} result + * @param {boolean} isFixedPointDepth + * @return {boolean} + */ +tcuTexCompareVerifier.isTrilinearCompareValid = function(compareMode, + prec, + depths0, + depths1, + xBounds0, + yBounds0, + xBounds1, + yBounds1, + fBounds, + cmpReference, + result, + isFixedPointDepth) { + if (prec.pcfBits > 0) + return tcuTexCompareVerifier.isTrilinearPCFCompareValid(compareMode, prec, depths0, depths1, xBounds0, yBounds0, xBounds1, yBounds1, fBounds, cmpReference, result, isFixedPointDepth); + else + return tcuTexCompareVerifier.isTrilinearAnyCompareValid(compareMode, prec, depths0, depths1, cmpReference, result, isFixedPointDepth); +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {number} coordZ + * @param {Array<number>} fBounds vec2 + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isLinearMipmapLinearCompareResultValid = function(level0, + level1, + sampler, + prec, + coord, + coordZ, + fBounds, + cmpReference, + result) { + var isFixedPointDepth = tcuTexCompareVerifier.isFixedPointDepthTextureFormat(level0.getFormat()); + + // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent. + // Right now this allows pairing any two valid bilinear quads. + + var w0 = level0.getWidth(); + var w1 = level1.getWidth(); + var h0 = level0.getHeight(); + var h1 = level1.getHeight(); + + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord[0], prec.coordBits[0], prec.uvwBits[0]); + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord[0], prec.coordBits[0], prec.uvwBits[0]); + var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord[1], prec.coordBits[1], prec.uvwBits[1]); + var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord[1], prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinates - without wrap mode + var minI0 = Math.floor(uBounds0[0] - 0.5); + var maxI0 = Math.floor(uBounds0[1] - 0.5); + var minI1 = Math.floor(uBounds1[0] - 0.5); + var maxI1 = Math.floor(uBounds1[1] - 0.5); + var minJ0 = Math.floor(vBounds0[0] - 0.5); + var maxJ0 = Math.floor(vBounds0[1] - 0.5); + var minJ1 = Math.floor(vBounds1[0] - 0.5); + var maxJ1 = Math.floor(vBounds1[1] - 0.5); + + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + var minA0 = deMath.clamp((uBounds0[0] - 0.5) - i0, 0, 1); + var maxA0 = deMath.clamp((uBounds0[1] - 0.5) - i0, 0, 1); + var minB0 = deMath.clamp((vBounds0[0] - 0.5) - j0, 0, 1); + var maxB0 = deMath.clamp((vBounds0[1] - 0.5) - j0, 0, 1); + var depths0 = []; + + var x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i0, w0); + var x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i0 + 1, w0); + var y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j0, h0); + var y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j0 + 1, h0); + + depths0[0] = level0.getPixDepth(x0, y0, coordZ); + depths0[1] = level0.getPixDepth(x1, y0, coordZ); + depths0[2] = level0.getPixDepth(x0, y1, coordZ); + depths0[3] = level0.getPixDepth(x1, y1, coordZ); + + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + var minA1 = deMath.clamp((uBounds1[0] - 0.5) - i1, 0, 1); + var maxA1 = deMath.clamp((uBounds1[1] - 0.5) - i1, 0, 1); + var minB1 = deMath.clamp((vBounds1[0] - 0.5) - j1, 0, 1); + var maxB1 = deMath.clamp((vBounds1[1] - 0.5) - j1, 0, 1); + var depths1 = []; + + x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i1, w1); + x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i1 + 1, w1); + y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j1, h1); + y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j1 + 1, h1); + + depths1[0] = level1.getPixDepth(x0, y0, coordZ); + depths1[1] = level1.getPixDepth(x1, y0, coordZ); + depths1[2] = level1.getPixDepth(x0, y1, coordZ); + depths1[3] = level1.getPixDepth(x1, y1, coordZ); + + if (tcuTexCompareVerifier.isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, + [minA0, maxA0], [minB0, maxB0], + [minA1, maxA1], [minB1, maxB1], + fBounds, cmpReference, result, isFixedPointDepth)) + return true; + } + } + } + } + + return false; +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {number} coordZ + * @param {Array<number>} fBounds vec2 + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isNearestMipmapLinearCompareResultValid = function(level0, + level1, + sampler, + prec, + coord, + coordZ, + fBounds, + cmpReference, + result) { + var isFixedPointDepth = tcuTexCompareVerifier.isFixedPointDepthTextureFormat(level0.getFormat()); + + var w0 = level0.getWidth(); + var w1 = level1.getWidth(); + var h0 = level0.getHeight(); + var h1 = level1.getHeight(); + + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord[0], prec.coordBits[0], prec.uvwBits[0]); + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord[0], prec.coordBits[0], prec.uvwBits[0]); + var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord[1], prec.coordBits[1], prec.uvwBits[1]); + var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord[1], prec.coordBits[1], prec.uvwBits[1]); + + var minI0 = Math.floor(uBounds0[0]); + var maxI0 = Math.floor(uBounds0[1]); + var minI1 = Math.floor(uBounds1[0]); + var maxI1 = Math.floor(uBounds1[1]); + var minJ0 = Math.floor(vBounds0[0]); + var maxJ0 = Math.floor(vBounds0[1]); + var minJ1 = Math.floor(vBounds1[0]); + var maxJ1 = Math.floor(vBounds1[1]); + + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + var x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i0, w0); + var y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j0, h0); + + // Derivated from C++ dEQP function lookupDepth() + // Since x0 and y0 are wrapped, here lookupDepth() returns the same result as getPixDepth() + assertMsgOptions(deMath.deInBounds32(x0, 0, level0.getWidth()) && deMath.deInBounds32(y0, 0, level0.getHeight()) && deMath.deInBounds32(coordZ, 0, level0.getDepth()), 'x0, y0 or coordZ out of bound.', false, true); + var depth0 = level0.getPixDepth(x0, y0, coordZ); + + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + var x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i1, w1); + var y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j1, h1); + + // Derivated from C++ dEQP function lookupDepth() + // Since x1 and y1 are wrapped, here lookupDepth() returns the same result as getPixDepth() + assertMsgOptions(deMath.deInBounds32(x1, 0, level1.getWidth()) && deMath.deInBounds32(y1, 0, level1.getHeight()), 'x1 or y1 out of bound.', false, true); + var depth1 = level1.getPixDepth(x1, y1, coordZ); + + if (tcuTexCompareVerifier.isLinearCompareValid(sampler.compare, prec, [depth0, depth1], fBounds, cmpReference, result, isFixedPointDepth)) + return true; + } + } + } + } + + return false; +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} levelFilter + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {number} coordZ + * @param {Array<number>} fBounds vec2 + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isMipmapLinearCompareResultValid = function(level0, + level1, + sampler, + levelFilter, + prec, + coord, + coordZ, + fBounds, + cmpReference, + result) { + if (levelFilter == tcuTexture.FilterMode.LINEAR) + return tcuTexCompareVerifier.isLinearMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result); + else + return tcuTexCompareVerifier.isNearestMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result); +}; + +/** + * @param {tcuTexture.Texture2DView} texture + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {Array<number>} lodBounds vec2 level-of-detail bounds + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isTexCompareResultValid2D = function(texture, sampler, prec, coord, lodBounds, cmpReference, result) { + var minLod = lodBounds[0]; + var maxLod = lodBounds[1]; + var canBeMagnified = minLod <= sampler.lodThreshold; + var canBeMinified = maxLod > sampler.lodThreshold; + + if (canBeMagnified) { + if (tcuTexCompareVerifier.isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, cmpReference, result)) + return true; + } + + if (canBeMinified) { + var isNearestMipmap = tcuTexVerifierUtil.isNearestMipmapFilter(sampler.minFilter); + var isLinearMipmap = tcuTexVerifierUtil.isLinearMipmapFilter(sampler.minFilter); + var minTexLevel = 0; + var maxTexLevel = texture.getNumLevels() - 1; + + assertMsgOptions(minTexLevel < maxTexLevel, 'Invalid texture levels.', false, true); + + if (isLinearMipmap) { + var minLevel = deMath.clamp(Math.floor(minLod), minTexLevel, maxTexLevel - 1); + var maxLevel = deMath.clamp(Math.floor(maxLod), minTexLevel, maxTexLevel - 1); + + assertMsgOptions(minLevel <= maxLevel, 'Invalid texture levels.', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + var minF = deMath.clamp(minLod - level, 0, 1); + var maxF = deMath.clamp(maxLod - level, 0, 1); + + if (tcuTexCompareVerifier.isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coord, 0, [minF, maxF], cmpReference, result)) + return true; + } + } else if (isNearestMipmap) { + // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made + // decision to allow floor(lod + 0.5) as well. + var minLevel = deMath.clamp(Math.ceil(minLod + 0.5) - 1, minTexLevel, maxTexLevel); + var maxLevel = deMath.clamp(Math.floor(maxLod + 0.5), minTexLevel, maxTexLevel); + + assertMsgOptions(minLevel <= maxLevel, 'Invalid texture levels.', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + if (tcuTexCompareVerifier.isLevelCompareResultValid(texture.getLevel(level), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coord, 0, cmpReference, result)) + return true; + } + } else { + if (tcuTexCompareVerifier.isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, cmpReference, result)) + return true; + } + } + + return false; +}; + +/** + * @param {tcuTexture.TextureCubeView} texture + * @param {number} baseLevelNdx + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {Array<number>} fBounds vec2 + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isSeamplessLinearMipmapLinearCompareResultValid = function(texture, + baseLevelNdx, + sampler, + prec, + coords, + fBounds, + cmpReference, + result) { + var isFixedPointDepth = tcuTexCompareVerifier.isFixedPointDepthTextureFormat(texture.getLevelFace(baseLevelNdx, tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X).getFormat()); + var size0 = texture.getLevelFace(baseLevelNdx, coords.face).getWidth(); + var size1 = texture.getLevelFace(baseLevelNdx + 1, coords.face).getWidth(); + + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.s, prec.coordBits[0], prec.uvwBits[0]); + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.s, prec.coordBits[0], prec.uvwBits[0]); + var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.t, prec.coordBits[1], prec.uvwBits[1]); + var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.t, prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinates - without wrap mode + var minI0 = Math.floor(uBounds0[0] - 0.5); + var maxI0 = Math.floor(uBounds0[1] - 0.5); + var minI1 = Math.floor(uBounds1[0] - 0.5); + var maxI1 = Math.floor(uBounds1[1] - 0.5); + var minJ0 = Math.floor(vBounds0[0] - 0.5); + var maxJ0 = Math.floor(vBounds0[1] - 0.5); + var minJ1 = Math.floor(vBounds1[0] - 0.5); + var maxJ1 = Math.floor(vBounds1[1] - 0.5); + + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faces0 = []; + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faces1 = []; + + for (var key in tcuTexture.CubeFace) { + var face = tcuTexture.CubeFace[key]; + faces0[face] = texture.getLevelFace(baseLevelNdx, face); + faces1[face] = texture.getLevelFace(baseLevelNdx + 1, face); + } + + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + var minA0 = deMath.clamp((uBounds0[0] - 0.5) - i0, 0, 1); + var maxA0 = deMath.clamp((uBounds0[1] - 0.5) - i0, 0, 1); + var minB0 = deMath.clamp((vBounds0[0] - 0.5) - j0, 0, 1); + var maxB0 = deMath.clamp((vBounds0[1] - 0.5) - j0, 0, 1); + var depths0 = []; + + var c00 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 0, j0 + 0]), size0); + var c10 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 1, j0 + 0]), size0); + var c01 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 0, j0 + 1]), size0); + var c11 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 1, j0 + 1]), size0); + + // If any of samples is out of both edges, implementations can do pretty much anything according to spec. + // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. + if (c00 == null || c01 == null || c10 == null || c11 == null) + return true; + + depths0[0] = faces0[c00.face].getPixDepth(c00.s, c00.t); + depths0[1] = faces0[c10.face].getPixDepth(c10.s, c10.t); + depths0[2] = faces0[c01.face].getPixDepth(c01.s, c01.t); + depths0[3] = faces0[c11.face].getPixDepth(c11.s, c11.t); + + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + var minA1 = deMath.clamp((uBounds1[0] - 0.5) - i1, 0, 1); + var maxA1 = deMath.clamp((uBounds1[1] - 0.5) - i1, 0, 1); + var minB1 = deMath.clamp((vBounds1[0] - 0.5) - j1, 0, 1); + var maxB1 = deMath.clamp((vBounds1[1] - 0.5) - j1, 0, 1); + var depths1 = []; + + c00 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 0, j1 + 0]), size1); + c10 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 1, j1 + 0]), size1); + c01 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 0, j1 + 1]), size1); + c11 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 1, j1 + 1]), size1); + + if (c00 == null || c01 == null || c10 == null || c11 == null) + return true; + + depths1[0] = faces1[c00.face].getPixDepth(c00.s, c00.t); + depths1[1] = faces1[c10.face].getPixDepth(c10.s, c10.t); + depths1[2] = faces1[c01.face].getPixDepth(c01.s, c01.t); + depths1[3] = faces1[c11.face].getPixDepth(c11.s, c11.t); + + if (tcuTexCompareVerifier.isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, + [minA0, maxA0], [minB0, maxB0], + [minA1, maxA1], [minB1, maxB1], + fBounds, cmpReference, result, isFixedPointDepth)) + return true; + } + } + } + } + + return false; +}; + +/** + * @param {tcuTexture.TextureCubeView} texture + * @param {number} levelNdx + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ + +tcuTexCompareVerifier.isSeamlessLinearCompareResultValid = function(texture, + levelNdx, + sampler, + prec, + coords, + cmpReference, + result) { + var isFixedPointDepth = tcuTexCompareVerifier.isFixedPointDepthTextureFormat(texture.getLevelFace(levelNdx, tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X).getFormat()); + var size = texture.getLevelFace(levelNdx, coords.face).getWidth(); + + var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits[0], prec.uvwBits[0]); + var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinate bounds for (x0,y0) - without wrap mode + var minI = Math.floor(uBounds[0] - 0.5); + var maxI = Math.floor(uBounds[1] - 0.5); + var minJ = Math.floor(vBounds[0] - 0.5); + var maxJ = Math.floor(vBounds[1] - 0.5); + + // Face accesses + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faces = []; + + for (var key in tcuTexture.CubeFace) { + var face = tcuTexture.CubeFace[key]; + faces[face] = texture.getLevelFace(levelNdx, face); + } + + for (var j = minJ; j <= maxJ; j++) { + for (var i = minI; i <= maxI; i++) { + var c00 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 0, j + 0]), size); + var c10 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 1, j + 0]), size); + var c01 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 0, j + 1]), size); + var c11 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 1, j + 1]), size); + + // If any of samples is out of both edges, implementations can do pretty much anything according to spec. + // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. + if (!c00 || !c01 || !c10 || !c11) + return true; + + // Bounds for filtering factors + var minA = deMath.clamp((uBounds[0] - 0.5) - i, 0, 1); + var maxA = deMath.clamp((uBounds[1] - 0.5) - i, 0, 1); + var minB = deMath.clamp((vBounds[0] - 0.5) - j, 0, 1); + var maxB = deMath.clamp((vBounds[1] - 0.5) - j, 0, 1); + + var depths = []; + depths[0] = faces[c00.face].getPixDepth(c00.s, c00.t); + depths[1] = faces[c10.face].getPixDepth(c10.s, c10.t); + depths[2] = faces[c01.face].getPixDepth(c01.s, c01.t); + depths[3] = faces[c11.face].getPixDepth(c11.s, c11.t); + + if (tcuTexCompareVerifier.isBilinearCompareValid(sampler.compare, prec, depths, [minA, maxA], [minB, maxB], cmpReference, result, isFixedPointDepth)) + return true; + } + } + + return false; +}; + +/** + * @param {tcuTexture.TextureCubeView} texture + * @param {number} levelNdx + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filterMode + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isCubeLevelCompareResultValid = function(texture, + levelNdx, + sampler, + filterMode, + prec, + coords, + cmpReference, + result) { + if (filterMode == tcuTexture.FilterMode.LINEAR) { + if (sampler.seamlessCubeMap) + return tcuTexCompareVerifier.isSeamlessLinearCompareResultValid(texture, levelNdx, sampler, prec, coords, cmpReference, result); + else + return tcuTexCompareVerifier.isLinearCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, [coords.s, coords.t], 0, cmpReference, result); + } else + return tcuTexCompareVerifier.isNearestCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, [coords.s, coords.t], 0, cmpReference, result); +}; + +/** + * @param {tcuTexture.TextureCubeView} texture + * @param {number} baseLevelNdx + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} levelFilter + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {Array<number>} fBounds vec2 + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isCubeMipmapLinearCompareResultValid = function(texture, + baseLevelNdx, + sampler, + levelFilter, + prec, + coords, + fBounds, + cmpReference, + result) { + if (levelFilter == tcuTexture.FilterMode.LINEAR) { + if (sampler.seamlessCubeMap) + return tcuTexCompareVerifier.isSeamplessLinearMipmapLinearCompareResultValid(texture, baseLevelNdx, sampler, prec, coords, fBounds, cmpReference, result); + else + return tcuTexCompareVerifier.isLinearMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx, coords.face), + texture.getLevelFace(baseLevelNdx + 1, coords.face), + sampler, prec, [coords.s, coords.t], 0, fBounds, cmpReference, result); + } else + return tcuTexCompareVerifier.isNearestMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx, coords.face), + texture.getLevelFace(baseLevelNdx + 1, coords.face), + sampler, prec, [coords.s, coords.t], 0, fBounds, cmpReference, result); +}; + +/** + * @param {tcuTexture.TextureCubeView} texture + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec2 texture coordinates + * @param {Array<number>} lodBounds vec2 level-of-detail bounds + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isTexCompareResultValidCube = function(texture, sampler, prec, coord, lodBounds, cmpReference, result) { + /** @type {Array<tcuTexture.CubeFace>} */var possibleFaces = tcuTexVerifierUtil.getPossibleCubeFaces(coord, prec.coordBits); + + if (!possibleFaces) + return true; // Result is undefined. + + for (var tryFaceNdx = 0; tryFaceNdx < possibleFaces.length; tryFaceNdx++) { + var face = possibleFaces[tryFaceNdx]; + var faceCoords = new tcuTexture.CubeFaceCoords(face, tcuTexture.projectToFace(face, coord)); + var minLod = lodBounds[0]; + var maxLod = lodBounds[1]; + var canBeMagnified = minLod <= sampler.lodThreshold; + var canBeMinified = maxLod > sampler.lodThreshold; + + if (canBeMagnified) { + if (tcuTexCompareVerifier.isCubeLevelCompareResultValid(texture, 0, sampler, sampler.magFilter, prec, faceCoords, cmpReference, result)) + return true; + } + + if (canBeMinified) { + var isNearestMipmap = tcuTexVerifierUtil.isNearestMipmapFilter(sampler.minFilter); + var isLinearMipmap = tcuTexVerifierUtil.isLinearMipmapFilter(sampler.minFilter); + var minTexLevel = 0; + var maxTexLevel = texture.getNumLevels() - 1; + + assertMsgOptions(minTexLevel < maxTexLevel, 'Invalid texture levels.', false, true); + + if (isLinearMipmap) { + var minLevel = deMath.clamp(Math.floor(minLod), minTexLevel, maxTexLevel - 1); + var maxLevel = deMath.clamp(Math.floor(maxLod), minTexLevel, maxTexLevel - 1); + + assertMsgOptions(minLevel <= maxLevel, 'Invalid texture levels.', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + var minF = deMath.clamp(minLod - level, 0, 1); + var maxF = deMath.clamp(maxLod - level, 0, 1); + + if (tcuTexCompareVerifier.isCubeMipmapLinearCompareResultValid(texture, level, sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, faceCoords, [minF, maxF], cmpReference, result)) + return true; + } + } else if (isNearestMipmap) { + // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made + // decision to allow floor(lod + 0.5) as well. + var minLevel = deMath.clamp(Math.ceil(minLod + 0.5) - 1, minTexLevel, maxTexLevel); + var maxLevel = deMath.clamp(Math.floor(maxLod + 0.5), minTexLevel, maxTexLevel); + + assertMsgOptions(minLevel <= maxLevel, 'Invalid texture levels.', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + if (tcuTexCompareVerifier.isCubeLevelCompareResultValid(texture, level, sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, faceCoords, cmpReference, result)) + return true; + } + } else { + if (tcuTexCompareVerifier.isCubeLevelCompareResultValid(texture, 0, sampler, sampler.minFilter, prec, faceCoords, cmpReference, result)) + return true; + } + } + } + + return false; +}; + +/** + * @param {tcuTexture.Texture2DArrayView} texture + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexCompareVerifier.TexComparePrecision} prec + * @param {Array<number>} coord vec3 texture coordinates + * @param {Array<number>} lodBounds vec2 level-of-detail bounds + * @param {number} cmpReference + * @param {number} result + * @return {boolean} + */ +tcuTexCompareVerifier.isTexCompareResultValid2DArray = function(texture, sampler, prec, coord, lodBounds, cmpReference, result) { + var depthErr = tcuTexVerifierUtil.computeFloatingPointError(coord[2], prec.coordBits[2]) + tcuTexVerifierUtil.computeFixedPointError(prec.uvwBits[2]); + var minZ = coord[2] - depthErr; + var maxZ = coord[2] + depthErr; + var minLayer = deMath.clamp(Math.floor(minZ + 0.5), 0, texture.getNumLayers() - 1); + var maxLayer = deMath.clamp(Math.floor(maxZ + 0.5), 0, texture.getNumLayers() - 1); + + for (var layer = minLayer; layer <= maxLayer; layer++) { + var minLod = lodBounds[0]; + var maxLod = lodBounds[1]; + var canBeMagnified = minLod <= sampler.lodThreshold; + var canBeMinified = maxLod > sampler.lodThreshold; + + if (canBeMagnified) { + if (tcuTexCompareVerifier.isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, deMath.swizzle(coord, [0, 1]), layer, cmpReference, result)) + return true; + } + + if (canBeMinified) { + var isNearestMipmap = tcuTexVerifierUtil.isNearestMipmapFilter(sampler.minFilter); + var isLinearMipmap = tcuTexVerifierUtil.isLinearMipmapFilter(sampler.minFilter); + var minTexLevel = 0; + var maxTexLevel = texture.getNumLevels() - 1; + + assertMsgOptions(minTexLevel < maxTexLevel, 'Invalid texture levels.', false, true); + + if (isLinearMipmap) { + var minLevel = deMath.clamp(Math.floor(minLod), minTexLevel, maxTexLevel - 1); + var maxLevel = deMath.clamp(Math.floor(maxLod), minTexLevel, maxTexLevel - 1); + + assertMsgOptions(minLevel <= maxLevel, 'Invalid texture levels.', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + var minF = deMath.clamp(minLod - level, 0, 1); + var maxF = deMath.clamp(maxLod - level, 0, 1); + + if (tcuTexCompareVerifier.isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, deMath.swizzle(coord, [0, 1]), layer, [minF, maxF], cmpReference, result)) + return true; + } + } else if (isNearestMipmap) { + // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made + // decision to allow floor(lod + 0.5) as well. + var minLevel = deMath.clamp(Math.ceil(minLod + 0.5) - 1, minTexLevel, maxTexLevel); + var maxLevel = deMath.clamp(Math.floor(maxLod + 0.5), minTexLevel, maxTexLevel); + + assertMsgOptions(minLevel <= maxLevel, 'Invalid texture levels.', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + if (tcuTexCompareVerifier.isLevelCompareResultValid(texture.getLevel(level), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, deMath.swizzle(coord, [0, 1]), layer, cmpReference, result)) + return true; + } + } else { + if (tcuTexCompareVerifier.isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, deMath.swizzle(coord, [0, 1]), layer, cmpReference, result)) + return true; + } + } + } + + return false; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexLookupVerifier.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexLookupVerifier.js new file mode 100644 index 000000000..6b471998a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexLookupVerifier.js @@ -0,0 +1,2225 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuTexLookupVerifier'); +goog.require('framework.common.tcuTexVerifierUtil'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + + var tcuTexLookupVerifier = framework.common.tcuTexLookupVerifier; + var tcuTexture = framework.common.tcuTexture; + var tcuTextureUtil = framework.common.tcuTextureUtil; + var tcuTexVerifierUtil = framework.common.tcuTexVerifierUtil; + var deMath = framework.delibs.debase.deMath; + + /** @typedef {(tcuTexLookupVerifier.LookupPrecision|{tcuTexLookupVerifier.LookupPrecision})} */ + tcuTexLookupVerifier.PrecType; + + /** + * Generic lookup precision parameters + * @constructor + * @struct + * @param {Array<number>=} coordBits + * @param {Array<number>=} uvwBits + * @param {Array<number>=} colorThreshold + * @param {Array<boolean>=} colorMask + */ + tcuTexLookupVerifier.LookupPrecision = function(coordBits, uvwBits, colorThreshold, colorMask) { + /** @type {Array<number>} */ this.coordBits = coordBits || [22, 22, 22]; + /** @type {Array<number>} */ this.uvwBits = uvwBits || [16, 16, 16]; + /** @type {Array<number>} */ this.colorThreshold = colorThreshold || [0, 0, 0, 0]; + /** @type {Array<boolean>} */ this.colorMask = colorMask || [true, true, true, true]; + }; + + /** + * Lod computation precision parameters + * @constructor + * @struct + * @param {number=} derivateBits + * @param {number=} lodBits + */ + tcuTexLookupVerifier.LodPrecision = function(derivateBits, lodBits) { + /** @type {number} */ this.derivateBits = derivateBits === undefined ? 22 : derivateBits; + /** @type {number} */ this.lodBits = lodBits === undefined ? 16 : lodBits; + }; + + /** + * @enum {number} + */ + tcuTexLookupVerifier.TexLookupScaleMode = { + MINIFY: 0, + MAGNIFY: 1 + }; + + // Generic utilities + + /** + * @param {tcuTexture.Sampler} sampler + * @return {boolean} + */ + tcuTexLookupVerifier.isSamplerSupported = function(sampler) { + return sampler.compare == tcuTexture.CompareMode.COMPAREMODE_NONE && + tcuTexVerifierUtil.isWrapModeSupported(sampler.wrapS) && + tcuTexVerifierUtil.isWrapModeSupported(sampler.wrapT) && + tcuTexVerifierUtil.isWrapModeSupported(sampler.wrapR); + }; + + // Color read & compare utilities + + /** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {number} x + * @param {number} y + * @param {number} z + * @return {boolean} + */ + tcuTexLookupVerifier.coordsInBounds = function(access, x, y, z) { + return deMath.deInBounds32(x, 0, access.getWidth()) && deMath.deInBounds32(y, 0, access.getHeight()) && deMath.deInBounds32(z, 0, access.getDepth()); + }; + + /** + * @param {tcuTexture.TextureFormat} format + * @return {boolean} + */ + tcuTexLookupVerifier.isSRGB = function(format) { + return format.order == tcuTexture.ChannelOrder.sRGB || format.order == tcuTexture.ChannelOrder.sRGBA; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {number} i + * @param {number} j + * @param {number} k + * @return {Array<number>} + */ + tcuTexLookupVerifier.lookupScalar = function(access, sampler, i, j, k) { + if (tcuTexLookupVerifier.coordsInBounds(access, i, j, k)) + return access.getPixel(i, j, k); + else + return deMath.toIVec(sampler.borderColor); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {number} i + * @param {number} j + * @param {number} k + * @return {Array<number>} + */ + tcuTexLookupVerifier.lookupFloat = function(access, sampler, i, j, k) { + // Specialization for float lookups: sRGB conversion is performed as specified in format. + if (tcuTexLookupVerifier.coordsInBounds(access, i, j, k)) { + /** @type {Array<number>} */ var p = access.getPixel(i, j, k); + return tcuTexLookupVerifier.isSRGB(access.getFormat()) ? tcuTextureUtil.sRGBToLinear(p) : p; + } else + return sampler.borderColor; + }; + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} ref + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isColorValid = function(prec, ref, result) { + return deMath.boolAll( + deMath.logicalOrBool( + deMath.lessThanEqual(deMath.absDiff(ref, result), prec.colorThreshold), + deMath.logicalNotBool(prec.colorMask))); + }; + + /** + * @constructor + * @struct + * @param {Array<number>=} p00 + * @param {Array<number>=} p01 + * @param {Array<number>=} p10 + * @param {Array<number>=} p11 + */ + tcuTexLookupVerifier.ColorQuad = function(p00, p01, p10, p11) { + /** @type {Array<number>} */ this.p00 = p00 || null; //!< (0, 0) + /** @type {Array<number>} */ this.p01 = p01 || null; //!< (1, 0) + /** @type {Array<number>} */ this.p10 = p10 || null; //!< (0, 1) + /** @type {Array<number>} */ this.p11 = p11 || null; //!< (1, 1) + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {number} x0 + * @param {number} x1 + * @param {number} y0 + * @param {number} y1 + * @param {number} z + * @return {tcuTexLookupVerifier.ColorQuad} + */ + tcuTexLookupVerifier.lookupQuad = function(level, sampler, x0, x1, y0, y1, z) { + var p00 = tcuTexLookupVerifier.lookupFloat(level, sampler, x0, y0, z); + var p10 = tcuTexLookupVerifier.lookupFloat(level, sampler, x1, y0, z); + var p01 = tcuTexLookupVerifier.lookupFloat(level, sampler, x0, y1, z); + var p11 = tcuTexLookupVerifier.lookupFloat(level, sampler, x1, y1, z); + return new tcuTexLookupVerifier.ColorQuad(p00, p01, p10, p11); + }; + + /** + * @constructor + * @struct + * @param {Array<number>=} p0 + * @param {Array<number>=} p1 + */ + tcuTexLookupVerifier.ColorLine = function(p0, p1) { + /** @type {Array<number>} */ this.p0 = p0 || null; //!< 0 + /** @type {Array<number>} */ this.p1 = p1 || null; //!< 1 + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {number} x0 + * @param {number} x1 + * @param {number} y + * @return {tcuTexLookupVerifier.ColorLine} + */ + tcuTexLookupVerifier.lookupLine = function(level, sampler, x0, x1, y) { + return new tcuTexLookupVerifier.ColorLine( + tcuTexLookupVerifier.lookupFloat(level, sampler, x0, y, 0), + tcuTexLookupVerifier.lookupFloat(level, sampler, x1, y, 0) + ); + }; + + /** + * @param {Array<number>} vec + * @return {number} + */ + tcuTexLookupVerifier.minComp = function(vec) { + /** @type {number} */ var minVal = vec[0]; + for (var ndx = 1; ndx < vec.length; ndx++) + minVal = Math.min(minVal, vec[ndx]); + return minVal; + }; + + /** + * @param {Array<number>} vec + * @return {number} + */ + tcuTexLookupVerifier.maxComp = function(vec) { + /** @type {number} */ var maxVal = vec[0]; + for (var ndx = 1; ndx < vec.length; ndx++) + maxVal = Math.max(maxVal, vec[ndx]); + return maxVal; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorLine} line + * @return {number} + */ + tcuTexLookupVerifier.computeBilinearSearchStepFromFloatLine = function(prec, line) { + assertMsgOptions(deMath.boolAll(deMath.greaterThan(prec.colorThreshold, [0, 0, 0, 0])), 'Threshold not greater than 0.', false, true); + + /** @type {number} */ var maxSteps = 1 << 16; + /** @type {Array<number>} */ var d = deMath.absDiff(line.p1, line.p0); + /** @type {Array<number>} */ var stepCount = deMath.divide([d, d, d, d], prec.colorThreshold); + /** @type {Array<number>} */ + var minStep = deMath.divide([1, 1, 1, 1], deMath.add(stepCount, [1, 1, 1, 1])); + /** @type {number} */ var step = Math.max(tcuTexLookupVerifier.minComp(minStep), 1 / maxSteps); + + return step; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad + * @return {number} + */ + tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad = function(prec, quad) { + assertMsgOptions(deMath.boolAll(deMath.greaterThan(prec.colorThreshold, [0, 0, 0, 0])), 'Threshold not greater than 0.', false, true); + + /** @type {number} */ var maxSteps = 1 << 16; + /** @type {Array<number>} */ var d0 = deMath.absDiff(quad.p10, quad.p00); + /** @type {Array<number>} */ var d1 = deMath.absDiff(quad.p01, quad.p00); + /** @type {Array<number>} */ var d2 = deMath.absDiff(quad.p11, quad.p10); + /** @type {Array<number>} */ var d3 = deMath.absDiff(quad.p11, quad.p01); + /** @type {Array<number>} */ var maxD = deMath.max(d0, deMath.max(d1, deMath.max(d2, d3))); + /** @type {Array<number>} */ var stepCount = deMath.divide(maxD, prec.colorThreshold); + /** @type {Array<number>} */ var minStep = deMath.divide([1, 1, 1, 1], deMath.add(stepCount, [1, 1, 1, 1])); + /** @type {number} */ var step = Math.max(tcuTexLookupVerifier.minComp(minStep), 1 / maxSteps); + + return step; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @return {number} + */ + tcuTexLookupVerifier.computeBilinearSearchStepForUnorm = function(prec) { + assertMsgOptions(deMath.boolAll(deMath.greaterThan(prec.colorThreshold, [0, 0, 0, 0])), 'Threshold not greater than 0.', false, true); + + /** @type {Array<number>} */ var stepCount = deMath.divide([1, 1, 1, 1], prec.colorThreshold); + /** @type {Array<number>} */ var minStep = deMath.divide([1, 1, 1, 1], (deMath.add(stepCount, [1, 1, 1, 1]))); + /** @type {number} */ var step = tcuTexLookupVerifier.minComp(minStep); + + return step; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @return {number} + */ + tcuTexLookupVerifier.computeBilinearSearchStepForSnorm = function(prec) { + assertMsgOptions(deMath.boolAll(deMath.greaterThan(prec.colorThreshold, [0, 0, 0, 0])), 'Threshold not greater than 0.', false, true); + + /** @type {Array<number>} */ var stepCount = deMath.divide([2.0, 2.0, 2.0, 2.0], prec.colorThreshold); + /** @type {Array<number>} */ var minStep = deMath.divide([1, 1, 1, 1], deMath.add(stepCount, [1, 1, 1, 1])); + /** @type {number} */ var step = tcuTexLookupVerifier.minComp(minStep); + + return step; + }; + + /** + * @param {tcuTexLookupVerifier.ColorLine} line + * @return {Array<number>} + */ + tcuTexLookupVerifier.minLine = function(line) { + return deMath.min(line.p0, line.p1); + }; + + /** + * @param {tcuTexLookupVerifier.ColorLine} line + * @return {Array<number>} + */ + tcuTexLookupVerifier.maxLine = function(line) { + var max = deMath.max; + return max(line.p0, line.p1); + }; + + /** + * @param {tcuTexLookupVerifier.ColorQuad} quad + * @return {Array<number>} + */ + tcuTexLookupVerifier.minQuad = function(quad) { + var min = deMath.min; + return min(quad.p00, min(quad.p10, min(quad.p01, quad.p11))); + }; + + /** + * @param {tcuTexLookupVerifier.ColorQuad} quad + * @return {Array<number>} + */ + tcuTexLookupVerifier.maxQuad = function(quad) { + var max = deMath.max; + return max(quad.p00, max(quad.p10, max(quad.p01, quad.p11))); + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isInColorBounds_1Quad = function(prec, quad, result) { + var quadMin = tcuTexLookupVerifier.minQuad; + var quadMax = tcuTexLookupVerifier.maxQuad; + /** @type {Array<number>} */ var minVal = deMath.subtract(quadMin(quad), prec.colorThreshold); + /** @type {Array<number>} */ var maxVal = deMath.add(quadMax(quad), prec.colorThreshold); + return deMath.boolAll( + deMath.logicalOrBool( + deMath.logicalAndBool( + deMath.greaterThanEqual(result, minVal), + deMath.lessThanEqual(result, maxVal)), + deMath.logicalNotBool(prec.colorMask))); + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad0 + * @param {tcuTexLookupVerifier.ColorQuad} quad1 + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isInColorBounds_2Quad = function(prec, quad0, quad1, result) { + var min = deMath.min; + var max = deMath.max; + var quadMin = tcuTexLookupVerifier.minQuad; + var quadMax = tcuTexLookupVerifier.maxQuad; + /** @type {Array<number>} */ var minVal = deMath.subtract(min(quadMin(quad0), quadMin(quad1)), prec.colorThreshold); + /** @type {Array<number>} */ var maxVal = deMath.add(max(quadMax(quad0), quadMax(quad1)), prec.colorThreshold); + return deMath.boolAll( + deMath.logicalOrBool( + deMath.logicalAndBool( + deMath.greaterThanEqual(result, minVal), + deMath.lessThanEqual(result, maxVal)), + deMath.logicalNotBool(prec.colorMask))); + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorLine} line0 + * @param {tcuTexLookupVerifier.ColorLine} line1 + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isInColorBounds_2Line = function(prec, line0, line1, result) { + var min = deMath.min; + var max = deMath.max; + var lineMin = tcuTexLookupVerifier.minLine; + var lineMax = tcuTexLookupVerifier.maxLine; + /** @type {Array<number>} */ var minVal = deMath.subtract(min(lineMin(line0), lineMin(line1)), prec.colorThreshold); + /** @type {Array<number>} */ var maxVal = deMath.add(max(lineMax(line0), lineMax(line1)), prec.colorThreshold); + return deMath.boolAll( + deMath.logicalOrBool( + deMath.logicalAndBool( + deMath.greaterThanEqual(result, minVal), + deMath.lessThanEqual(result, maxVal)), + deMath.logicalNotBool(prec.colorMask))); + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad00 + * @param {tcuTexLookupVerifier.ColorQuad} quad01 + * @param {tcuTexLookupVerifier.ColorQuad} quad10 + * @param {tcuTexLookupVerifier.ColorQuad} quad11 + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isInColorBounds_4Quad = function(prec, quad00, quad01, quad10, quad11, result) { + var min = deMath.min; + var max = deMath.max; + var quadMin = tcuTexLookupVerifier.minQuad; + var quadMax = tcuTexLookupVerifier.maxQuad; + /** @type {Array<number>} */ var minVal = deMath.subtract(min(quadMin(quad00), min(quadMin(quad01), min(quadMin(quad10), quadMin(quad11)))), prec.colorThreshold); + /** @type {Array<number>} */ var maxVal = deMath.add(max(quadMax(quad00), max(quadMax(quad01), max(quadMax(quad10), quadMax(quad11)))), prec.colorThreshold); + return deMath.boolAll( + deMath.logicalOrBool( + deMath.logicalAndBool( + deMath.greaterThanEqual(result, minVal), + deMath.lessThanEqual(result, maxVal)), + deMath.logicalNotBool(prec.colorMask))); + }; + + // Range search utilities + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} c0 + * @param {Array<number>} c1 + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLinearRangeValid = function(prec, c0, c1, fBounds, result) { + // This is basically line segment - AABB test. Valid interpolation line is checked + // against result AABB constructed by applying threshold. + + /** @type {Array<number>} */ var rMin = deMath.subtract(result, prec.colorThreshold); + /** @type {Array<number>} */ var rMax = deMath.add(result, prec.colorThreshold); + + // Algorithm: For each component check whether segment endpoints are inside, or intersect with slab. + // If all intersect or are inside, line segment intersects the whole 4D AABB. + for (var compNdx = 0; compNdx < 4; compNdx++) { + if (!prec.colorMask[compNdx]) + continue; + + /** @type {number} */ var i0 = c0[compNdx] * (1 - fBounds[0]) + c1[compNdx] * fBounds[0]; + /** @type {number} */ var i1 = c0[compNdx] * (1 - fBounds[1]) + c1[compNdx] * fBounds[1]; + if ((i0 > rMax[compNdx] && i1 > rMax[compNdx]) || + (i0 < rMin[compNdx] && i1 < rMin[compNdx])) { + return false; + } + } + + return true; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad + * @param {Array<number>} xBounds + * @param {Array<number>} yBounds + * @param {number} searchStep + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isBilinearRangeValid = function(prec, quad, xBounds, yBounds, searchStep, result) { + assertMsgOptions(xBounds[0] <= xBounds[1], 'Out of bounds: X direction.', false, true); + assertMsgOptions(yBounds[0] <= yBounds[1], 'Out of bounds: Y direction.', false, true); + + if (!tcuTexLookupVerifier.isInColorBounds_1Quad(prec, quad, result)) + return false; + + for (var x = xBounds[0]; x < xBounds[1] + searchStep; x += searchStep) { + /** @type {number} */ var a = Math.min(x, xBounds[1]); + /** @type {Array<number>} */ var c0 = deMath.add(deMath.scale(quad.p00, (1 - a)), deMath.scale(quad.p10, a)); + /** @type {Array<number>} */ var c1 = deMath.add(deMath.scale(quad.p01, (1 - a)), deMath.scale(quad.p11, a)); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, c0, c1, yBounds, result)) + return true; + } + + return false; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad0 + * @param {tcuTexLookupVerifier.ColorQuad} quad1 + * @param {Array<number>} xBounds + * @param {Array<number>} yBounds + * @param {Array<number>} zBounds + * @param {number} searchStep + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isTrilinearRangeValid = function(prec, quad0, quad1, xBounds, yBounds, zBounds, searchStep, result) { + assertMsgOptions(xBounds[0] <= xBounds[1], 'Out of bounds: X direction.', false, true); + assertMsgOptions(yBounds[0] <= yBounds[1], 'Out of bounds: Y direction.', false, true); + assertMsgOptions(zBounds[0] <= zBounds[1], 'Out of bounds: Z direction.', false, true); + + if (!tcuTexLookupVerifier.isInColorBounds_2Quad(prec, quad0, quad1, result)) + return false; + + for (var x = xBounds[0]; x < xBounds[1] + searchStep; x += searchStep) { + for (var y = yBounds[0]; y < yBounds[1] + searchStep; y += searchStep) { + /** @type {number} */ var a = Math.min(x, xBounds[1]); + /** @type {number} */ var b = Math.min(y, yBounds[1]); + /** @type {Array<number>} */ + var c0 = deMath.add( + deMath.add( + deMath.add( + deMath.scale(quad0.p00, (1 - a) * (1 - b)), + deMath.scale(quad0.p10, a * (1 - b))), + deMath.scale(quad0.p01, (1 - a) * b)), + deMath.scale(quad0.p11, a * b)); + /** @type {Array<number>} */ + var c1 = deMath.add( + deMath.add( + deMath.add( + deMath.scale(quad1.p00, (1 - a) * (1 - b)), + deMath.scale(quad1.p10, a * (1 - b))), + deMath.scale(quad1.p01, (1 - a) * b)), + deMath.scale(quad1.p11, a * b)); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, c0, c1, zBounds, result)) + return true; + } + } + + return false; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad0 + * @param {tcuTexLookupVerifier.ColorQuad} quad1 + * @param {Array<number>} xBounds0 + * @param {Array<number>} yBounds0 + * @param {Array<number>} xBounds1 + * @param {Array<number>} yBounds1 + * @param {Array<number>} zBounds + * @param {number} searchStep + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.is2DTrilinearFilterResultValid = function(prec, quad0, quad1, xBounds0, yBounds0, xBounds1, yBounds1, zBounds, searchStep, result) { + assertMsgOptions(xBounds0[0] <= xBounds0[1], 'Out of bounds: X direction.', false, true); + assertMsgOptions(yBounds0[0] <= yBounds0[1], 'Out of bounds: Y direction.', false, true); + assertMsgOptions(xBounds1[0] <= xBounds1[1], 'Out of bounds: X direction.', false, true); + assertMsgOptions(yBounds1[0] <= yBounds1[1], 'Out of bounds: Y direction.', false, true); + + if (!tcuTexLookupVerifier.isInColorBounds_2Quad(prec, quad0, quad1, result)) + return false; + + for (var x0 = xBounds0[0]; x0 < xBounds0[1] + searchStep; x0 += searchStep) { + for (var y0 = yBounds0[0]; y0 < yBounds0[1] + searchStep; y0 += searchStep) { + /** @type {number} */ var a0 = Math.min(x0, xBounds0[1]); + /** @type {number} */ var b0 = Math.min(y0, yBounds0[1]); + /** @type {Array<number>} */ + var c0 = deMath.add( + deMath.add( + deMath.add( + deMath.scale(quad0.p00, (1 - a0) * (1 - b0)), + deMath.scale(quad0.p10, a0 * (1 - b0))), + deMath.scale(quad0.p01, (1 - a0) * b0)), + deMath.scale(quad0.p11, a0 * b0)); + + for (var x1 = xBounds1[0]; x1 <= xBounds1[1]; x1 += searchStep) { + for (var y1 = yBounds1[0]; y1 <= yBounds1[1]; y1 += searchStep) { + /** @type {number} */ var a1 = Math.min(x1, xBounds1[1]); + /** @type {number} */ var b1 = Math.min(y1, yBounds1[1]); + /** @type {Array<number>} */ + var c1 = deMath.add( + deMath.add( + deMath.add( + deMath.scale(quad1.p00, (1 - a1) * (1 - b1)), + deMath.scale(quad1.p10, a1 * (1 - b1))), + deMath.scale(quad1.p01, (1 - a1) * b1)), + deMath.scale(quad1.p11, a1 * b1)); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, c0, c1, zBounds, result)) + return true; + } + } + } + } + + return false; + }; + + /** + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexLookupVerifier.ColorQuad} quad00 + * @param {tcuTexLookupVerifier.ColorQuad} quad01 + * @param {tcuTexLookupVerifier.ColorQuad} quad10 + * @param {tcuTexLookupVerifier.ColorQuad} quad11 + * @param {Array<number>} xBounds0 + * @param {Array<number>} yBounds0 + * @param {Array<number>} zBounds0 + * @param {Array<number>} xBounds1 + * @param {Array<number>} yBounds1 + * @param {Array<number>} zBounds1 + * @param {Array<number>} wBounds + * @param {number} searchStep + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.is3DTrilinearFilterResultValid = function(prec, quad00, quad01, quad10, quad11, xBounds0, yBounds0, zBounds0, xBounds1, yBounds1, zBounds1, wBounds, searchStep, result) { + assertMsgOptions(xBounds0[0] <= xBounds0[1], 'Out of bounds: X direction.', false, true); + assertMsgOptions(yBounds0[0] <= yBounds0[1], 'Out of bounds: Y direction.', false, true); + assertMsgOptions(zBounds0[0] <= zBounds0[1], 'Out of bounds: Z direction.', false, true); + assertMsgOptions(xBounds1[0] <= xBounds1[1], 'Out of bounds: X direction.', false, true); + assertMsgOptions(yBounds1[0] <= yBounds1[1], 'Out of bounds: Y direction.', false, true); + assertMsgOptions(zBounds1[0] <= zBounds1[1], 'Out of bounds: Z direction.', false, true); + + if (!tcuTexLookupVerifier.isInColorBounds_4Quad(prec, quad00, quad01, quad10, quad11, result)) + return false; + + function biInterp(result, p00, p01, p10, p11, s00, s01, s10, s11) { + for (var ii = 0; ii < 4; ++ii) { + result[ii] = p00[ii] * s00 + p10[ii] * s10 + p01[ii] * s01 + p11[ii] * s11; + } + } + + function interp(result, p0, p1, s) { + for (var ii = 0; ii < 4; ++ii) { + result[ii] = p0[ii] * (1 - s) + p1[ii] * s; + } + } + + /** @type {Array<number>} */ var c00 = [0, 0, 0, 0]; + /** @type {Array<number>} */ var c01 = [0, 0, 0, 0]; + /** @type {Array<number>} */ var c10 = [0, 0, 0, 0]; + /** @type {Array<number>} */ var c11 = [0, 0, 0, 0]; + /** @type {Array<number>} */ var cz0 = [0, 0, 0, 0]; + /** @type {Array<number>} */ var cz1 = [0, 0, 0, 0]; + + for (var x0 = xBounds0[0]; x0 < xBounds0[1] + searchStep; x0 += searchStep) { + for (var y0 = yBounds0[0]; y0 < yBounds0[1] + searchStep; y0 += searchStep) { + /** @type {number} */ var a0 = Math.min(x0, xBounds0[1]); + /** @type {number} */ var b0 = Math.min(y0, yBounds0[1]); + + /** @type {number} */ var s00 = (1 - a0) * (1 - b0); + /** @type {number} */ var s01 = (1 - a0) * b0; + /** @type {number} */ var s10 = a0 * (1 - b0); + /** @type {number} */ var s11 = a0 * b0; + + biInterp(c00, quad00.p00, quad00.p01, quad00.p10, quad00.p11, s00, s01, s10, s11); + biInterp(c01, quad01.p00, quad01.p01, quad01.p10, quad01.p11, s00, s01, s10, s11); + + for (var z0 = zBounds0[0]; z0 < zBounds0[1] + searchStep; z0 += searchStep) { + /** @type {number} */ var c0 = Math.min(z0, zBounds0[1]); + interp(cz0, c00, c01, c0); + + for (var x1 = xBounds1[0]; x1 < xBounds1[1] + searchStep; x1 += searchStep) { + for (var y1 = yBounds1[0]; y1 < yBounds1[1] + searchStep; y1 += searchStep) { + /** @type {number} */ var a1 = Math.min(x1, xBounds1[1]); + /** @type {number} */ var b1 = Math.min(y1, yBounds1[1]); + + /** @type {number} */ var t00 = (1 - a1) * (1 - b1); + /** @type {number} */ var t01 = (1 - a1) * b1; + /** @type {number} */ var t10 = a1 * (1 - b1); + /** @type {number} */ var t11 = a1 * b1; + + biInterp(c10, quad10.p00, quad10.p01, quad10.p10, quad10.p11, t00, t01, t10, t11); + biInterp(c11, quad11.p00, quad11.p01, quad11.p10, quad11.p11, t00, t01, t10, t11); + + for (var z1 = zBounds1[0]; z1 < zBounds1[1] + searchStep; z1 += searchStep) { + /** @type {number} */ var c1 = Math.min(z1, zBounds1[1]); + interp(cz1, c10, c11, c1); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, cz0, cz1, wBounds, result)) + return true; + } + } + } + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {number} coordX + * @param {number} coordY + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isNearestSampleResultValid_CoordXYAsNumber = function(level, sampler, prec, coordX, coordY, result) { + assertMsgOptions(level.getDepth() == 1, 'Depth must be 1.', false, true); + + /** @type {Array<number>} */ + var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getWidth(), coordX, prec.coordBits[0], prec.uvwBits[0]); + + /** @type {number} */ var minI = Math.floor(uBounds[0]); + /** @type {number} */ var maxI = Math.floor(uBounds[1]); + + for (var i = minI; i <= maxI; i++) { + /** @type {number} */ var x = tcuTexVerifierUtil.wrap(sampler.wrapS, i, level.getWidth()); + /** @type {Array<number>} */ var color; + if (tcuTexLookupVerifier.isSRGB(level.getFormat())) { + color = tcuTexLookupVerifier.lookupFloat(level, sampler, x, coordY, 0); + } else { + color = tcuTexLookupVerifier.lookupScalar(level, sampler, x, coordY, 0); + } + + if (tcuTexLookupVerifier.isColorValid(prec, color, result)) + return true; + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord vec2 + * @param {number} coordZ int + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isNearestSampleResultValid_CoordAsVec2AndInt = function(level, sampler, prec, coord, coordZ, result) { + /** @type {Array<number>} */ + var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getWidth(), coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getHeight(), coord[1], prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI = Math.floor(uBounds[0]); + /** @type {number} */ var maxI = Math.floor(uBounds[1]); + /** @type {number} */ var minJ = Math.floor(vBounds[0]); + /** @type {number} */ var maxJ = Math.floor(vBounds[1]); + + // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode. + + for (var j = minJ; j <= maxJ; j++) + for (var i = minI; i <= maxI; i++) { + /** @type {number} */ var x = tcuTexVerifierUtil.wrap(sampler.wrapS, i, level.getWidth()); + /** @type {number} */ var y = tcuTexVerifierUtil.wrap(sampler.wrapT, j, level.getHeight()); + /** @type {Array<number>} */ var color; + if (tcuTexLookupVerifier.isSRGB(level.getFormat())) { + color = tcuTexLookupVerifier.lookupFloat(level, sampler, x, y, coordZ); + } else { + color = tcuTexLookupVerifier.lookupScalar(level, sampler, x, y, coordZ); + } + + if (tcuTexLookupVerifier.isColorValid(prec, color, result)) + return true; + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord vec3 + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isNearestSampleResultValid_CoordAsVec3 = function(level, sampler, prec, coord, result) { + /** @type {Array<number>} */ + var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getWidth(), coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getHeight(), coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var wBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getDepth(), coord[2], prec.coordBits[2], prec.uvwBits[2]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI = Math.floor(uBounds[0]); + /** @type {number} */ var maxI = Math.floor(uBounds[1]); + /** @type {number} */ var minJ = Math.floor(vBounds[0]); + /** @type {number} */ var maxJ = Math.floor(vBounds[1]); + /** @type {number} */ var minK = Math.floor(wBounds[0]); + /** @type {number} */ var maxK = Math.floor(wBounds[1]); + + // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode. + + for (var k = minK; k <= maxK; k++) { + for (var j = minJ; j <= maxJ; j++) { + for (var i = minI; i <= maxI; i++) { + /** @type {number} */ var x = tcuTexVerifierUtil.wrap(sampler.wrapS, i, level.getWidth()); + /** @type {number} */ var y = tcuTexVerifierUtil.wrap(sampler.wrapT, j, level.getHeight()); + /** @type {number} */ var z = tcuTexVerifierUtil.wrap(sampler.wrapR, k, level.getDepth()); + /** @type {Array<number>} */ var color; + if (tcuTexLookupVerifier.isSRGB(level.getFormat())) { + color = tcuTexLookupVerifier.lookupFloat(level, sampler, x, y, z); + } else { + color = tcuTexLookupVerifier.lookupScalar(level, sampler, x, y, z); + } + + if (tcuTexLookupVerifier.isColorValid(prec, color, result)) + return true; + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {number} coordX + * @param {number} coordY + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLinearSampleResultValid_CoordXYAsNumber = function(level, sampler, prec, coordX, coordY, result) { + /** @type {Array<number>} */ var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coordX, prec.coordBits[0], prec.uvwBits[0]); + + /** @type {number} */ var minI = Math.floor(uBounds[0] - 0.5); + /** @type {number} */ var maxI = Math.floor(uBounds[1] - 0.5); + + /** @type {number} */ var w = level.getWidth(); + + for (var i = minI; i <= maxI; i++) { + // Wrapped coordinates + /** @type {number} */ var x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i, w); + /** @type {number} */ var x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i + 1, w); + + // Bounds for filtering factors + /** @type {number} */ var minA = deMath.clamp((uBounds[0] - 0.5) - i, 0, 1); + /** @type {number} */ var maxA = deMath.clamp((uBounds[1] - 0.5) - i, 0, 1); + + /** @type {Array<number>} */ var colorA = tcuTexLookupVerifier.lookupFloat(level, sampler, x0, coordY, 0); + /** @type {Array<number>} */ var colorB = tcuTexLookupVerifier.lookupFloat(level, sampler, x1, coordY, 0); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, colorA, colorB, [minA, maxA], result)) + return true; + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord vec2 + * @param {number} coordZ int + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLinearSampleResultValid_CoordAsVec2AndInt = function(level, sampler, prec, coord, coordZ, result) { + /** @type {Array<number>} */ var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord[1], prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinate bounds for (x0,y0) - without wrap mode + /** @type {number} */ var minI = Math.floor(uBounds[0] - 0.5); + /** @type {number} */ var maxI = Math.floor(uBounds[1] - 0.5); + /** @type {number} */ var minJ = Math.floor(vBounds[0] - 0.5); + /** @type {number} */ var maxJ = Math.floor(vBounds[1] - 0.5); + + /** @type {number} */ var w = level.getWidth(); + /** @type {number} */ var h = level.getHeight(); + + /** @type {tcuTexture.TextureChannelClass} */ + var texClass = tcuTexture.getTextureChannelClass(level.getFormat().type); + + /** @type {number} */ + var searchStep = (texClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForUnorm(prec) : + (texClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForSnorm(prec) : + 0; // Step is computed for floating-point quads based on texel values. + + // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode. + + for (var j = minJ; j <= maxJ; j++) + for (var i = minI; i <= maxI; i++) { + // Wrapped coordinates + /** @type {number} */ var x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i, w); + /** @type {number} */ var x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i + 1, w); + /** @type {number} */ var y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j, h); + /** @type {number} */ var y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j + 1, h); + + // Bounds for filtering factors + /** @type {number} */ var minA = deMath.clamp((uBounds[0] - 0.5) - i, 0, 1); + /** @type {number} */ var maxA = deMath.clamp((uBounds[1] - 0.5) - i, 0, 1); + /** @type {number} */ var minB = deMath.clamp((vBounds[0] - 0.5) - j, 0, 1); + /** @type {number} */ var maxB = deMath.clamp((vBounds[1] - 0.5) - j, 0, 1); + + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad = tcuTexLookupVerifier.lookupQuad(level, sampler, x0, x1, y0, y1, coordZ); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep = tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad); + + if (tcuTexLookupVerifier.isBilinearRangeValid(prec, quad, [minA, maxA], [minB, maxB], searchStep, result)) + return true; + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord vec3 + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLinearSampleResultValid_CoordAsVec3 = function(level, sampler, prec, coord, result) { + /** @type {Array<number>} */ + var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getWidth(), coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getHeight(), coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var wBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, level.getDepth(), coord[2], prec.coordBits[2], prec.uvwBits[2]); + + // Integer coordinate bounds for (x0,y0) - without wrap mode + /** @type {number} */ var minI = Math.floor(uBounds[0] - 0.5); + /** @type {number} */ var maxI = Math.floor(uBounds[1] - 0.5); + /** @type {number} */ var minJ = Math.floor(vBounds[0] - 0.5); + /** @type {number} */ var maxJ = Math.floor(vBounds[1] - 0.5); + /** @type {number} */ var minK = Math.floor(wBounds[0] - 0.5); + /** @type {number} */ var maxK = Math.floor(wBounds[1] - 0.5); + + /** @type {number} */ var w = level.getWidth(); + /** @type {number} */ var h = level.getHeight(); + /** @type {number} */ var d = level.getDepth(); + + /** @type {tcuTexture.TextureChannelClass} */ + var texClass = tcuTexture.getTextureChannelClass(level.getFormat().type); + /** @type {number} */ + var searchStep = (texClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForUnorm(prec) : + (texClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForSnorm(prec) : + 0; // Step is computed for floating-point quads based on texel values. + + // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode. + + for (var k = minK; k <= maxK; k++) { + for (var j = minJ; j <= maxJ; j++) { + for (var i = minI; i <= maxI; i++) { + // Wrapped coordinates + /** @type {number} */ var x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i, w); + /** @type {number} */ var x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i + 1, w); + /** @type {number} */ var y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j, h); + /** @type {number} */ var y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j + 1, h); + /** @type {number} */ var z0 = tcuTexVerifierUtil.wrap(sampler.wrapR, k, d); + /** @type {number} */ var z1 = tcuTexVerifierUtil.wrap(sampler.wrapR, k + 1, d); + + // Bounds for filtering factors + /** @type {number} */ var minA = deMath.clamp((uBounds[0] - 0.5) - i, 0, 1); + /** @type {number} */ var maxA = deMath.clamp((uBounds[1] - 0.5) - i, 0, 1); + /** @type {number} */ var minB = deMath.clamp((vBounds[0] - 0.5) - j, 0, 1); + /** @type {number} */ var maxB = deMath.clamp((vBounds[1] - 0.5) - j, 0, 1); + /** @type {number} */ var minC = deMath.clamp((wBounds[0] - 0.5) - k, 0, 1); + /** @type {number} */ var maxC = deMath.clamp((wBounds[1] - 0.5) - k, 0, 1); + + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad0 = tcuTexLookupVerifier.lookupQuad(level, sampler, x0, x1, y0, y1, z0); + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad1 = tcuTexLookupVerifier.lookupQuad(level, sampler, x0, x1, y0, y1, z1); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep = Math.min(tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad0), tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad1)); + + if (tcuTexLookupVerifier.isTrilinearRangeValid(prec, quad0, quad1, [minA, maxA], [minB, maxB], [minC, maxC], searchStep, result)) + return true; + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {number} coord + * @param {number} coordY + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isNearestMipmapLinearSampleResultValid_CoordXYAsNumber = function(level0, level1, sampler, prec, coord, coordY, fBounds, result) { + /** @type {number} */ var w0 = level0.getWidth(); + /** @type {number} */ var w1 = level1.getWidth(); + + /** @type {Array<number>} */ + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w0, coord, prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w1, coord, prec.coordBits[0], prec.uvwBits[0]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI0 = Math.floor(uBounds0[0]); + /** @type {number} */ var maxI0 = Math.floor(uBounds0[1]); + /** @type {number} */ var minI1 = Math.floor(uBounds1[0]); + /** @type {number} */ var maxI1 = Math.floor(uBounds1[1]); + + for (var i0 = minI0; i0 <= maxI0; i0++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + /** @type {Array<number>} */ + var c0 = tcuTexLookupVerifier.lookupFloat(level0, sampler, tcuTexVerifierUtil.wrap(sampler.wrapS, i0, w0), coordY, 0); + /** @type {Array<number>} */ + var c1 = tcuTexLookupVerifier.lookupFloat(level1, sampler, tcuTexVerifierUtil.wrap(sampler.wrapS, i1, w1), coordY, 0); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, c0, c1, fBounds, result)) + return true; + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {number} coordZ + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isNearestMipmapLinearSampleResultValid_CoordAsVec2AndInt = function(level0, level1, sampler, prec, coord, coordZ, fBounds, result) { + /** @type {number} */ var w0 = level0.getWidth(); + /** @type {number} */ var w1 = level1.getWidth(); + /** @type {number} */ var h0 = level0.getHeight(); + /** @type {number} */ var h1 = level1.getHeight(); + + /** @type {Array<number>} */ + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w0, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w1, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h0, coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h1, coord[1], prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI0 = Math.floor(uBounds0[0]); + /** @type {number} */ var maxI0 = Math.floor(uBounds0[1]); + /** @type {number} */ var minI1 = Math.floor(uBounds1[0]); + /** @type {number} */ var maxI1 = Math.floor(uBounds1[1]); + /** @type {number} */ var minJ0 = Math.floor(vBounds0[0]); + /** @type {number} */ var maxJ0 = Math.floor(vBounds0[1]); + /** @type {number} */ var minJ1 = Math.floor(vBounds1[0]); + /** @type {number} */ var maxJ1 = Math.floor(vBounds1[1]); + + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + /** @type {Array<number>} */ var c0 = tcuTexLookupVerifier.lookupFloat(level0, sampler, tcuTexVerifierUtil.wrap(sampler.wrapS, i0, w0), tcuTexVerifierUtil.wrap(sampler.wrapT, j0, h0), coordZ); + /** @type {Array<number>} */ var c1 = tcuTexLookupVerifier.lookupFloat(level1, sampler, tcuTexVerifierUtil.wrap(sampler.wrapS, i1, w1), tcuTexVerifierUtil.wrap(sampler.wrapT, j1, h1), coordZ); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, c0, c1, fBounds, result)) + return true; + } + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isNearestMipmapLinearSampleResultValid_CoordAsVec3 = function(level0, level1, sampler, prec, coord, fBounds, result) { + /** @type {number} */ var w0 = level0.getWidth(); + /** @type {number} */ var w1 = level1.getWidth(); + /** @type {number} */ var h0 = level0.getHeight(); + /** @type {number} */ var h1 = level1.getHeight(); + /** @type {number} */ var d0 = level0.getDepth(); + /** @type {number} */ var d1 = level1.getDepth(); + + /** @type {Array<number>} */ + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w0, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w1, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h0, coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h1, coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var wBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, d0, coord[2], prec.coordBits[2], prec.uvwBits[2]); + /** @type {Array<number>} */ + var wBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, d1, coord[2], prec.coordBits[2], prec.uvwBits[2]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI0 = Math.floor(uBounds0[0]); + /** @type {number} */ var maxI0 = Math.floor(uBounds0[1]); + /** @type {number} */ var minI1 = Math.floor(uBounds1[0]); + /** @type {number} */ var maxI1 = Math.floor(uBounds1[1]); + /** @type {number} */ var minJ0 = Math.floor(vBounds0[0]); + /** @type {number} */ var maxJ0 = Math.floor(vBounds0[1]); + /** @type {number} */ var minJ1 = Math.floor(vBounds1[0]); + /** @type {number} */ var maxJ1 = Math.floor(vBounds1[1]); + /** @type {number} */ var minK0 = Math.floor(wBounds0[0]); + /** @type {number} */ var maxK0 = Math.floor(wBounds0[1]); + /** @type {number} */ var minK1 = Math.floor(wBounds1[0]); + /** @type {number} */ var maxK1 = Math.floor(wBounds1[1]); + + for (var k0 = minK0; k0 <= maxK0; k0++) { + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + for (var k1 = minK1; k1 <= maxK1; k1++) { + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + /** @type {Array<number>} */ var c0 = tcuTexLookupVerifier.lookupFloat(level0, sampler, tcuTexVerifierUtil.wrap(sampler.wrapS, i0, w0), tcuTexVerifierUtil.wrap(sampler.wrapT, j0, h0), tcuTexVerifierUtil.wrap(sampler.wrapR, k0, d0)); + /** @type {Array<number>} */ var c1 = tcuTexLookupVerifier.lookupFloat(level1, sampler, tcuTexVerifierUtil.wrap(sampler.wrapS, i1, w1), tcuTexVerifierUtil.wrap(sampler.wrapT, j1, h1), tcuTexVerifierUtil.wrap(sampler.wrapR, k1, d1)); + + if (tcuTexLookupVerifier.isLinearRangeValid(prec, c0, c1, fBounds, result)) + return true; + } + } + } + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {number} coordZ + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLinearMipmapLinearSampleResultValid_CoordAsVec2AndInt = function(level0, level1, sampler, prec, coord, coordZ, fBounds, result) { + // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent. + // Right now this allows pairing any two valid bilinear quads. + + /** @type {number} */ var w0 = level0.getWidth(); + /** @type {number} */ var w1 = level1.getWidth(); + /** @type {number} */ var h0 = level0.getHeight(); + /** @type {number} */ var h1 = level1.getHeight(); + + /** @type {Array<number>} */ + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w0, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w1, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h0, coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h1, coord[1], prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI0 = Math.floor(uBounds0[0] - 0.5); + /** @type {number} */ var maxI0 = Math.floor(uBounds0[1] - 0.5); + /** @type {number} */ var minI1 = Math.floor(uBounds1[0] - 0.5); + /** @type {number} */ var maxI1 = Math.floor(uBounds1[1] - 0.5); + /** @type {number} */ var minJ0 = Math.floor(vBounds0[0] - 0.5); + /** @type {number} */ var maxJ0 = Math.floor(vBounds0[1] - 0.5); + /** @type {number} */ var minJ1 = Math.floor(vBounds1[0] - 0.5); + /** @type {number} */ var maxJ1 = Math.floor(vBounds1[1] - 0.5); + + /** @type {tcuTexture.TextureChannelClass} */ + var texClass = tcuTexture.getTextureChannelClass(level0.getFormat().type); + /** @type {number} */ var cSearchStep = (texClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForUnorm(prec) : + (texClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForSnorm(prec) : + 0; // Step is computed for floating-point quads based on texel values. + + /** @type {number} */ var x0; + /** @type {number} */ var x1; + /** @type {number} */ var y0; + /** @type {number} */ var y1; + + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + /** @type {number} */ var searchStep0; + + x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i0, w0); + x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i0 + 1, w0); + y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j0, h0); + y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j0 + 1, h0); + + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad0 = tcuTexLookupVerifier.lookupQuad(level0, sampler, x0, x1, y0, y1, coordZ); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep0 = tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad0); + else + searchStep0 = cSearchStep; + + /** @type {number} */ var minA0 = deMath.clamp((uBounds0[0] - 0.5) - i0, 0, 1); + /** @type {number} */ var maxA0 = deMath.clamp((uBounds0[1] - 0.5) - i0, 0, 1); + /** @type {number} */ var minB0 = deMath.clamp((vBounds0[0] - 0.5) - j0, 0, 1); + /** @type {number} */ var maxB0 = deMath.clamp((vBounds0[1] - 0.5) - j0, 0, 1); + + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + /** @type {number} */ var searchStep1; + + x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i1, w1); + x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i1 + 1, w1); + y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j1, h1); + y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j1 + 1, h1); + + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad1 = tcuTexLookupVerifier.lookupQuad(level1, sampler, x0, x1, y0, y1, coordZ); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep1 = tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad1); + else + searchStep1 = cSearchStep; + + /** @type {number} */ var minA1 = deMath.clamp((uBounds1[0] - 0.5) - i1, 0, 1); + /** @type {number} */ var maxA1 = deMath.clamp((uBounds1[1] - 0.5) - i1, 0, 1); + /** @type {number} */ var minB1 = deMath.clamp((vBounds1[0] - 0.5) - j1, 0, 1); + /** @type {number} */ var maxB1 = deMath.clamp((vBounds1[1] - 0.5) - j1, 0, 1); + + if (tcuTexLookupVerifier.is2DTrilinearFilterResultValid(prec, quad0, quad1, [minA0, maxA0], [minB0, maxB0], [minA1, maxA1], [minB1, maxB1], + fBounds, Math.min(searchStep0, searchStep1), result)) + return true; + } + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLinearMipmapLinearSampleResultValid_CoordAsVec3 = function(level0, level1, sampler, prec, coord, fBounds, result) { + // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent. + // Right now this allows pairing any two valid bilinear quads. + + /** @type {number} */ var w0 = level0.getWidth(); + /** @type {number} */ var w1 = level1.getWidth(); + /** @type {number} */ var h0 = level0.getHeight(); + /** @type {number} */ var h1 = level1.getHeight(); + /** @type {number} */ var d0 = level0.getDepth(); + /** @type {number} */ var d1 = level1.getDepth(); + + /** @type {Array<number>} */ + var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w0, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, w1, coord[0], prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ + var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h0, coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, h1, coord[1], prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ + var wBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, d0, coord[2], prec.coordBits[2], prec.uvwBits[2]); + /** @type {Array<number>} */ + var wBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds( + sampler.normalizedCoords, d1, coord[2], prec.coordBits[2], prec.uvwBits[2]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI0 = Math.floor(uBounds0[0] - 0.5); + /** @type {number} */ var maxI0 = Math.floor(uBounds0[1] - 0.5); + /** @type {number} */ var minI1 = Math.floor(uBounds1[0] - 0.5); + /** @type {number} */ var maxI1 = Math.floor(uBounds1[1] - 0.5); + /** @type {number} */ var minJ0 = Math.floor(vBounds0[0] - 0.5); + /** @type {number} */ var maxJ0 = Math.floor(vBounds0[1] - 0.5); + /** @type {number} */ var minJ1 = Math.floor(vBounds1[0] - 0.5); + /** @type {number} */ var maxJ1 = Math.floor(vBounds1[1] - 0.5); + /** @type {number} */ var minK0 = Math.floor(wBounds0[0] - 0.5); + /** @type {number} */ var maxK0 = Math.floor(wBounds0[1] - 0.5); + /** @type {number} */ var minK1 = Math.floor(wBounds1[0] - 0.5); + /** @type {number} */ var maxK1 = Math.floor(wBounds1[1] - 0.5); + + /** @type {tcuTexture.TextureChannelClass} */ + var texClass = tcuTexture.getTextureChannelClass(level0.getFormat().type); + /** @type {number} */ var cSearchStep = texClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT ? tcuTexLookupVerifier.computeBilinearSearchStepForUnorm(prec) : + texClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT ? tcuTexLookupVerifier.computeBilinearSearchStepForSnorm(prec) : + 0; // Step is computed for floating-point quads based on texel values. + + /** @type {number} */ var x0; + /** @type {number} */ var x1; + /** @type {number} */ var y0; + /** @type {number} */ var y1; + /** @type {number} */ var z0; + /** @type {number} */ var z1; + + for (var k0 = minK0; k0 <= maxK0; k0++) { + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + /** @type {number} */ var searchStep0; + + x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i0, w0); + x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i0 + 1, w0); + y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j0, h0); + y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j0 + 1, h0); + z0 = tcuTexVerifierUtil.wrap(sampler.wrapR, k0, d0); + z1 = tcuTexVerifierUtil.wrap(sampler.wrapR, k0 + 1, d0); + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad00 = tcuTexLookupVerifier.lookupQuad(level0, sampler, x0, x1, y0, y1, z0); + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad01 = tcuTexLookupVerifier.lookupQuad(level0, sampler, x0, x1, y0, y1, z1); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep0 = Math.min(tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad00), tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad01)); + else + searchStep0 = cSearchStep; + + /** @type {number} */ var minA0 = deMath.clamp((uBounds0[0] - 0.5) - i0, 0, 1); + /** @type {number} */ var maxA0 = deMath.clamp((uBounds0[1] - 0.5) - i0, 0, 1); + /** @type {number} */ var minB0 = deMath.clamp((vBounds0[0] - 0.5) - j0, 0, 1); + /** @type {number} */ var maxB0 = deMath.clamp((vBounds0[1] - 0.5) - j0, 0, 1); + /** @type {number} */ var minC0 = deMath.clamp((wBounds0[0] - 0.5) - k0, 0, 1); + /** @type {number} */ var maxC0 = deMath.clamp((wBounds0[1] - 0.5) - k0, 0, 1); + + for (var k1 = minK1; k1 <= maxK1; k1++) { + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + + /** @type {number} */ var searchStep1; + + x0 = tcuTexVerifierUtil.wrap(sampler.wrapS, i1, w1); + x1 = tcuTexVerifierUtil.wrap(sampler.wrapS, i1 + 1, w1); + y0 = tcuTexVerifierUtil.wrap(sampler.wrapT, j1, h1); + y1 = tcuTexVerifierUtil.wrap(sampler.wrapT, j1 + 1, h1); + z0 = tcuTexVerifierUtil.wrap(sampler.wrapR, k1, d1); + z1 = tcuTexVerifierUtil.wrap(sampler.wrapR, k1 + 1, d1); + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad10 = tcuTexLookupVerifier.lookupQuad(level1, sampler, x0, x1, y0, y1, z0); + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad11 = tcuTexLookupVerifier.lookupQuad(level1, sampler, x0, x1, y0, y1, z1); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep1 = Math.min(tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad10), tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad11)); + else + searchStep1 = cSearchStep; + + /** @type {number} */ var minA1 = deMath.clamp((uBounds1[0] - 0.5) - i1, 0, 1); + /** @type {number} */ var maxA1 = deMath.clamp((uBounds1[1] - 0.5) - i1, 0, 1); + /** @type {number} */ var minB1 = deMath.clamp((vBounds1[0] - 0.5) - j1, 0, 1); + /** @type {number} */ var maxB1 = deMath.clamp((vBounds1[1] - 0.5) - j1, 0, 1); + /** @type {number} */ var minC1 = deMath.clamp((wBounds1[0] - 0.5) - k1, 0, 1); + /** @type {number} */ var maxC1 = deMath.clamp((wBounds1[1] - 0.5) - k1, 0, 1); + + if (tcuTexLookupVerifier.is3DTrilinearFilterResultValid( + prec, quad00, quad01, quad10, quad11, + [minA0, maxA0], [minB0, maxB0], [minC0, maxC0], + [minA1, maxA1], [minB1, maxB1], [minC1, maxC1], + fBounds, Math.min(searchStep0, searchStep1), result)) + return true; + } + } + } + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filterMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {number} coordX + * @param {number} coordY + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLevelSampleResultValid_CoordXYAsNumber = function(level, sampler, filterMode, prec, coordX, coordY, result) { + if (filterMode == tcuTexture.FilterMode.LINEAR) + return tcuTexLookupVerifier.isLinearSampleResultValid_CoordXYAsNumber(level, sampler, prec, coordX, coordY, result); + else + return tcuTexLookupVerifier.isNearestSampleResultValid_CoordXYAsNumber(level, sampler, prec, coordX, coordY, result); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filterMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {number} coordZ + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt = function(level, sampler, filterMode, prec, coord, coordZ, result) { + if (filterMode == tcuTexture.FilterMode.LINEAR) + return tcuTexLookupVerifier.isLinearSampleResultValid_CoordAsVec2AndInt(level, sampler, prec, coord, coordZ, result); + else + return tcuTexLookupVerifier.isNearestSampleResultValid_CoordAsVec2AndInt(level, sampler, prec, coord, coordZ, result); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filterMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec3 = function(level, sampler, filterMode, prec, coord, result) { + if (filterMode == tcuTexture.FilterMode.LINEAR) + return tcuTexLookupVerifier.isLinearSampleResultValid_CoordAsVec3(level, sampler, prec, coord, result); + else + return tcuTexLookupVerifier.isNearestSampleResultValid_CoordAsVec3(level, sampler, prec, coord, result); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} levelFilter + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {number} coordZ + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isMipmapLinearSampleResultValid_CoordAsVec2AndInt = function(level0, level1, sampler, levelFilter, prec, coord, coordZ, fBounds, result) { + if (levelFilter == tcuTexture.FilterMode.LINEAR) + return tcuTexLookupVerifier.isLinearMipmapLinearSampleResultValid_CoordAsVec2AndInt(level0, level1, sampler, prec, coord, coordZ, fBounds, result); + else + return tcuTexLookupVerifier.isNearestMipmapLinearSampleResultValid_CoordAsVec2AndInt(level0, level1, sampler, prec, coord, coordZ, fBounds, result); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} level0 + * @param {tcuTexture.ConstPixelBufferAccess} level1 + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} levelFilter + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isMipmapLinearSampleResultValid_CoordAsVec3 = function(level0, level1, sampler, levelFilter, prec, coord, fBounds, result) { + if (levelFilter == tcuTexture.FilterMode.LINEAR) + return tcuTexLookupVerifier.isLinearMipmapLinearSampleResultValid_CoordAsVec3(level0, level1, sampler, prec, coord, fBounds, result); + else + return tcuTexLookupVerifier.isNearestMipmapLinearSampleResultValid_CoordAsVec3(level0, level1, sampler, prec, coord, fBounds, result); + }; + + /** + * @param {tcuTexture.Texture2DView} texture + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} lodBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLookupResultValid_Texture2DView = function(texture, sampler, prec, coord, lodBounds, result) { + /** @type {number} */ var minLod = lodBounds[0]; + /** @type {number} */ var maxLod = lodBounds[1]; + /** @type {boolean} */ var canBeMagnified = minLod <= sampler.lodThreshold; + /** @type {boolean} */ var canBeMinified = maxLod > sampler.lodThreshold; + + assertMsgOptions(tcuTexLookupVerifier.isSamplerSupported(sampler), 'Sampler not supported.', false, true); + + /** @type {number} */ var minLevel; + /** @type {number} */ var maxLevel; + + if (canBeMagnified) + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, result)) + return true; + + if (canBeMinified) { + /** @type {boolean} */ var isNearestMipmap = tcuTexVerifierUtil.isNearestMipmapFilter(sampler.minFilter); + /** @type {boolean} */ var isLinearMipmap = tcuTexVerifierUtil.isLinearMipmapFilter(sampler.minFilter); + /** @type {number} */ var minTexLevel = 0; + /** @type {number} */ var maxTexLevel = texture.getNumLevels() - 1; + + assertMsgOptions(minTexLevel <= maxTexLevel, 'minTexLevel > maxTexLevel', false, true); + + if (isLinearMipmap && minTexLevel < maxTexLevel) { + minLevel = deMath.clamp(Math.floor(minLod), minTexLevel, maxTexLevel - 1); + maxLevel = deMath.clamp(Math.floor(maxLod), minTexLevel, maxTexLevel - 1); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + /** @type {number} */ var minF = deMath.clamp(minLod - level, 0, 1); + /** @type {number} */ var maxF = deMath.clamp(maxLod - level, 0, 1); + + if (tcuTexLookupVerifier.isMipmapLinearSampleResultValid_CoordAsVec2AndInt(texture.getLevel(level), texture.getLevel(level + 1), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coord, 0, [minF, maxF], result)) + return true; + } + } else if (isNearestMipmap) { + // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made + // decision to allow floor(lod + 0.5) as well. + minLevel = deMath.clamp(Math.ceil(minLod + 0.5) - 1, minTexLevel, maxTexLevel); + maxLevel = deMath.clamp(Math.floor(maxLod + 0.5), minTexLevel, maxTexLevel); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt(texture.getLevel(level), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coord, 0, result)) + return true; + } + } else { + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, result)) + return true; + } + } + + return false; + }; + + /** + * @param {tcuTexture.TextureCubeView} texture + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} lodBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLookupResultValid_TextureCubeView = function(texture, sampler, prec, coord, lodBounds, result) { + /** @type {number} */ var numPossibleFaces = 0; + + assertMsgOptions(tcuTexLookupVerifier.isSamplerSupported(sampler), 'Sampler not supported.', false, true); + + /** @type {Array<tcuTexture.CubeFace>} */ var possibleFaces = tcuTexVerifierUtil.getPossibleCubeFaces(coord, prec.coordBits); + + /** @type {number} */ var minLevel; + /** @type {number} */ var maxLevel; + + if (!possibleFaces) + return true; // Result is undefined. + + for (var tryFaceNdx = 0; tryFaceNdx < possibleFaces.length; tryFaceNdx++) { + /** @type {tcuTexture.CubeFaceCoords} */ + var faceCoords = new tcuTexture.CubeFaceCoords(possibleFaces[tryFaceNdx], tcuTexture.projectToFace(possibleFaces[tryFaceNdx], coord)); + /** @type {number} */ var minLod = lodBounds[0]; + /** @type {number} */ var maxLod = lodBounds[1]; + /** @type {boolean} */ var canBeMagnified = minLod <= sampler.lodThreshold; + /** @type {boolean} */ var canBeMinified = maxLod > sampler.lodThreshold; + + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faces = []; + if (canBeMagnified) { + tcuTexLookupVerifier.getCubeLevelFaces(texture, 0, faces); + + if (tcuTexLookupVerifier.isCubeLevelSampleResultValid(faces, sampler, sampler.magFilter, prec, faceCoords, result)) + return true; + } + + if (canBeMinified) { + /** @type {boolean} */ var isNearestMipmap = tcuTexVerifierUtil.isNearestMipmapFilter(sampler.minFilter); + /** @type {boolean} */ var isLinearMipmap = tcuTexVerifierUtil.isLinearMipmapFilter(sampler.minFilter); + /** @type {number} */ var minTexLevel = 0; + /** @type {number} */ var maxTexLevel = texture.getNumLevels() - 1; + + assertMsgOptions(minTexLevel <= maxTexLevel, 'minTexLevel > maxTexLevel', false, true); + + if (isLinearMipmap && minTexLevel < maxTexLevel) { + minLevel = deMath.clamp(Math.floor(minLod), minTexLevel, maxTexLevel - 1); + maxLevel = deMath.clamp(Math.floor(maxLod), minTexLevel, maxTexLevel - 1); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++) { + /** @type {number} */ var minF = deMath.clamp(minLod - levelNdx, 0, 1); + /** @type {number} */ var maxF = deMath.clamp(maxLod - levelNdx, 0, 1); + + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faces0 = []; + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faces1 = []; + + tcuTexLookupVerifier.getCubeLevelFaces(texture, levelNdx, faces0); + tcuTexLookupVerifier.getCubeLevelFaces(texture, levelNdx + 1, faces1); + + if (tcuTexLookupVerifier.isCubeMipmapLinearSampleResultValid(faces0, faces1, sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, faceCoords, [minF, maxF], result)) + return true; + } + } else if (isNearestMipmap) { + // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made + // decision to allow floor(lod + 0.5) as well. + minLevel = deMath.clamp(Math.ceil(minLod + 0.5) - 1, minTexLevel, maxTexLevel); + maxLevel = deMath.clamp(Math.floor(maxLod + 0.5), minTexLevel, maxTexLevel); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++) { + tcuTexLookupVerifier.getCubeLevelFaces(texture, levelNdx, faces); + + if (tcuTexLookupVerifier.isCubeLevelSampleResultValid(faces, sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, faceCoords, result)) + return true; + } + } else { + tcuTexLookupVerifier.getCubeLevelFaces(texture, 0, faces); + + if (tcuTexLookupVerifier.isCubeLevelSampleResultValid(faces, sampler, sampler.minFilter, prec, faceCoords, result)) + return true; + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.Texture2DArrayView} texture + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} lodBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLookupResultValid_Texture2DArrayView = function(texture, sampler, prec, coord, lodBounds, result) { + /** @type {Array<number>} */ var layerRange = tcuTexLookupVerifier.computeLayerRange(texture.getNumLayers(), prec.coordBits[2], coord[2]); + /** @type {Array<number>} */ var coordXY = deMath.swizzle(coord, [0, 1]); + /** @type {number} */ var minLod = lodBounds[0]; + /** @type {number} */ var maxLod = lodBounds[1]; + /** @type {boolean} */ var canBeMagnified = minLod <= sampler.lodThreshold; + /** @type {boolean} */ var canBeMinified = maxLod > sampler.lodThreshold; + + assertMsgOptions(tcuTexLookupVerifier.isSamplerSupported(sampler), 'Sampler not supported.', false, true); + /** @type {number} */ var minLevel; + /** @type {number} */ var maxLevel; + + for (var layer = layerRange[0]; layer <= layerRange[1]; layer++) { + if (canBeMagnified) { + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt(texture.getLevel(0), sampler, sampler.magFilter, prec, coordXY, layer, result)) + return true; + } + + if (canBeMinified) { + /** @type {boolean} */ var isNearestMipmap = tcuTexVerifierUtil.isNearestMipmapFilter(sampler.minFilter); + /** @type {boolean} */ var isLinearMipmap = tcuTexVerifierUtil.isLinearMipmapFilter(sampler.minFilter); + /** @type {number} */ var minTexLevel = 0; + /** @type {number} */ var maxTexLevel = texture.getNumLevels() - 1; + + assertMsgOptions(minTexLevel <= maxTexLevel, 'minTexLevel > maxTexLevel', false, true); + + if (isLinearMipmap && minTexLevel < maxTexLevel) { + minLevel = deMath.clamp(Math.floor(minLod), minTexLevel, maxTexLevel - 1); + maxLevel = deMath.clamp(Math.floor(maxLod), minTexLevel, maxTexLevel - 1); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + /** @type {number} */ var minF = deMath.clamp(minLod - level, 0, 1); + /** @type {number} */ var maxF = deMath.clamp(maxLod - level, 0, 1); + + if (tcuTexLookupVerifier.isMipmapLinearSampleResultValid_CoordAsVec2AndInt(texture.getLevel(level), texture.getLevel(level + 1), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coordXY, layer, [minF, maxF], result)) + return true; + } + } else if (isNearestMipmap) { + // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made + // decision to allow floor(lod + 0.5) as well. + minLevel = deMath.clamp(Math.ceil(minLod + 0.5) - 1, minTexLevel, maxTexLevel); + maxLevel = deMath.clamp(Math.floor(maxLod + 0.5), minTexLevel, maxTexLevel); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt(texture.getLevel(level), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coordXY, layer, result)) + return true; + } + } else { + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt(texture.getLevel(0), sampler, sampler.minFilter, prec, coordXY, layer, result)) + return true; + } + } + } + + return false; + }; + + /** + * @param {tcuTexture.Texture3DView} texture + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} lodBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLookupResultValid = function(texture, sampler, prec, coord, lodBounds, result) { + /** @type {number} */ var minLod = lodBounds[0]; + /** @type {number} */ var maxLod = lodBounds[1]; + /** @type {boolean} */ var canBeMagnified = minLod <= sampler.lodThreshold; + /** @type {boolean} */ var canBeMinified = maxLod > sampler.lodThreshold; + + assertMsgOptions(tcuTexLookupVerifier.isSamplerSupported(sampler), 'Sampler not supported.', false, true); + + /** @type {number} */ var minLevel; + /** @type {number} */ var maxLevel; + + if (canBeMagnified) + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec3(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, result)) + return true; + + if (canBeMinified) { + /** @type {boolean} */ var isNearestMipmap = tcuTexVerifierUtil.isNearestMipmapFilter(sampler.minFilter); + /** @type {boolean} */ var isLinearMipmap = tcuTexVerifierUtil.isLinearMipmapFilter(sampler.minFilter); + /** @type {number} */ var minTexLevel = 0; + /** @type {number} */ var maxTexLevel = texture.getNumLevels() - 1; + + assertMsgOptions(minTexLevel <= maxTexLevel, 'minTexLevel > maxTexLevel', false, true); + + if (isLinearMipmap && minTexLevel < maxTexLevel) { + minLevel = deMath.clamp(Math.floor(minLod), minTexLevel, maxTexLevel - 1); + maxLevel = deMath.clamp(Math.floor(maxLod), minTexLevel, maxTexLevel - 1); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + /** @type {number} */ var minF = deMath.clamp(minLod - level, 0, 1); + /** @type {number} */ var maxF = deMath.clamp(maxLod - level, 0, 1); + + if (tcuTexLookupVerifier.isMipmapLinearSampleResultValid_CoordAsVec3(texture.getLevel(level), texture.getLevel(level + 1), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coord, [minF, maxF], result)) + return true; + } + } else if (isNearestMipmap) { + // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made + // decision to allow floor(lod + 0.5) as well. + minLevel = deMath.clamp(Math.ceil(minLod + 0.5) - 1, minTexLevel, maxTexLevel); + maxLevel = deMath.clamp(Math.floor(maxLod + 0.5), minTexLevel, maxTexLevel); + + assertMsgOptions(minLevel <= maxLevel, 'minLevel > maxLevel', false, true); + + for (var level = minLevel; level <= maxLevel; level++) { + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec3(texture.getLevel(level), sampler, tcuTexVerifierUtil.getLevelFilter(sampler.minFilter), prec, coord, result)) + return true; + } + } else { + if (tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec3(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, result)) + return true; + } + } + + return false; + }; + + /** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faces (&faces)[CUBEFACE_LAST] + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isSeamlessLinearSampleResultValid = function(faces, sampler, prec, coords, result) { + /** @type {number} */ var size = faces[coords.face].getWidth(); + + /** @type {Array<number>} */ var uBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ var vBounds = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinate bounds for (x0,y0) - without wrap mode + /** @type {number} */ var minI = Math.floor(uBounds[0] - 0.5); + /** @type {number} */ var maxI = Math.floor(uBounds[1] - 0.5); + /** @type {number} */ var minJ = Math.floor(vBounds[0] - 0.5); + /** @type {number} */ var maxJ = Math.floor(vBounds[1] - 0.5); + + /** @type {tcuTexture.TextureChannelClass} */ var texClass = tcuTexture.getTextureChannelClass(faces[coords.face].getFormat().type); + /** @type {number} */ var searchStep = (texClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForUnorm(prec) : + (texClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForSnorm(prec) : + 0; // Step is computed for floating-point quads based on texel values. + + for (var j = minJ; j <= maxJ; j++) { + for (var i = minI; i <= maxI; i++) { + /** @type {tcuTexture.CubeFaceCoords} */ var c00 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 0, j + 0]), size); + /** @type {tcuTexture.CubeFaceCoords} */ var c10 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 1, j + 0]), size); + /** @type {tcuTexture.CubeFaceCoords} */ var c01 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 0, j + 1]), size); + /** @type {tcuTexture.CubeFaceCoords} */ var c11 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i + 1, j + 1]), size); + + // If any of samples is out of both edges, implementations can do pretty much anything according to spec. + // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. + if (c00 == null || c01 == null || c10 == null || c11 == null || + c00.face == null || c01.face == null || c10.face == null || c11.face == null) + return true; + + // Bounds for filtering factors + /** @type {number} */ var minA = deMath.clamp((uBounds[0] - 0.5) - i, 0, 1); + /** @type {number} */ var maxA = deMath.clamp((uBounds[1] - 0.5) - i, 0, 1); + /** @type {number} */ var minB = deMath.clamp((vBounds[0] - 0.5) - j, 0, 1); + /** @type {number} */ var maxB = deMath.clamp((vBounds[1] - 0.5) - j, 0, 1); + + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad = new tcuTexLookupVerifier.ColorQuad([], [], [], []); + quad.p00 = tcuTexLookupVerifier.lookupFloat(faces[c00.face], sampler, c00.s, c00.t, 0); + quad.p10 = tcuTexLookupVerifier.lookupFloat(faces[c10.face], sampler, c10.s, c10.t, 0); + quad.p01 = tcuTexLookupVerifier.lookupFloat(faces[c01.face], sampler, c01.s, c01.t, 0); + quad.p11 = tcuTexLookupVerifier.lookupFloat(faces[c11.face], sampler, c11.s, c11.t, 0); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep = tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad); + + if (tcuTexLookupVerifier.isBilinearRangeValid(prec, quad, [minA, maxA], [minB, maxB], searchStep, result)) + return true; + } + } + + return false; + }; + + /** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faces0 (&faces0)[CUBEFACE_LAST] + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faces1 (&faces1)[CUBEFACE_LAST] + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isSeamplessLinearMipmapLinearSampleResultValid = function(faces0, faces1, sampler, prec, coords, fBounds, result) { + // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent. + // Right now this allows pairing any two valid bilinear quads. + /** @type {number} */ var size0 = faces0[coords.face].getWidth(); + /** @type {number} */ var size1 = faces1[coords.face].getWidth(); + + /** @type {Array<number>} */ var uBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.s, prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ var uBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.s, prec.coordBits[0], prec.uvwBits[0]); + /** @type {Array<number>} */ var vBounds0 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.t, prec.coordBits[1], prec.uvwBits[1]); + /** @type {Array<number>} */ var vBounds1 = tcuTexVerifierUtil.computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.t, prec.coordBits[1], prec.uvwBits[1]); + + // Integer coordinates - without wrap mode + /** @type {number} */ var minI0 = Math.floor(uBounds0[0] - 0.5); + /** @type {number} */ var maxI0 = Math.floor(uBounds0[1] - 0.5); + /** @type {number} */ var minI1 = Math.floor(uBounds1[0] - 0.5); + /** @type {number} */ var maxI1 = Math.floor(uBounds1[1] - 0.5); + /** @type {number} */ var minJ0 = Math.floor(vBounds0[0] - 0.5); + /** @type {number} */ var maxJ0 = Math.floor(vBounds0[1] - 0.5); + /** @type {number} */ var minJ1 = Math.floor(vBounds1[0] - 0.5); + /** @type {number} */ var maxJ1 = Math.floor(vBounds1[1] - 0.5); + + /** @type {tcuTexture.TextureChannelClass} */ var texClass = tcuTexture.getTextureChannelClass(faces0[coords.face].getFormat().type); + /** @type {number} */ var cSearchStep = (texClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForUnorm(prec) : + (texClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT) ? tcuTexLookupVerifier.computeBilinearSearchStepForSnorm(prec) : + 0; // Step is computed for floating-point quads based on texel values. + + /** @type {tcuTexture.CubeFaceCoords} */ var c00; + /** @type {tcuTexture.CubeFaceCoords} */ var c10; + /** @type {tcuTexture.CubeFaceCoords} */ var c01; + /** @type {tcuTexture.CubeFaceCoords} */ var c11; + + for (var j0 = minJ0; j0 <= maxJ0; j0++) { + for (var i0 = minI0; i0 <= maxI0; i0++) { + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad0 = new tcuTexLookupVerifier.ColorQuad([], [], [], []); + /** @type {number} */ var searchStep0; + + c00 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 0, j0 + 0]), size0); + c10 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 1, j0 + 0]), size0); + c01 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 0, j0 + 1]), size0); + c11 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i0 + 1, j0 + 1]), size0); + + // If any of samples is out of both edges, implementations can do pretty much anything according to spec. + // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. + if (c00 == null || c01 == null || c10 == null || c11 == null || + c00.face == null || c01.face == null || c10.face == null || c11.face == null) + return true; + + quad0.p00 = tcuTexLookupVerifier.lookupFloat(faces0[c00.face], sampler, c00.s, c00.t, 0); + quad0.p10 = tcuTexLookupVerifier.lookupFloat(faces0[c10.face], sampler, c10.s, c10.t, 0); + quad0.p01 = tcuTexLookupVerifier.lookupFloat(faces0[c01.face], sampler, c01.s, c01.t, 0); + quad0.p11 = tcuTexLookupVerifier.lookupFloat(faces0[c11.face], sampler, c11.s, c11.t, 0); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep0 = tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad0); + else + searchStep0 = cSearchStep; + + /** @type {number} */ var minA0 = deMath.clamp((uBounds0[0] - 0.5) - i0, 0, 1); + /** @type {number} */ var maxA0 = deMath.clamp((uBounds0[1] - 0.5) - i0, 0, 1); + /** @type {number} */ var minB0 = deMath.clamp((vBounds0[0] - 0.5) - j0, 0, 1); + /** @type {number} */ var maxB0 = deMath.clamp((vBounds0[1] - 0.5) - j0, 0, 1); + + for (var j1 = minJ1; j1 <= maxJ1; j1++) { + for (var i1 = minI1; i1 <= maxI1; i1++) { + /** @type {tcuTexLookupVerifier.ColorQuad} */ + var quad1 = new tcuTexLookupVerifier.ColorQuad([], [], [], []); + /** @type {number} */ var searchStep1; + + c00 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 0, j1 + 0]), size1); + c10 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 1, j1 + 0]), size1); + c01 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 0, j1 + 1]), size1); + c11 = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(coords.face, [i1 + 1, j1 + 1]), size1); + + if (c00 == null || c01 == null || c10 == null || c11 == null || + c00.face == null || c01.face == null || c10.face == null || c11.face == null) + return true; + + quad1.p00 = tcuTexLookupVerifier.lookupFloat(faces1[c00.face], sampler, c00.s, c00.t, 0); + quad1.p10 = tcuTexLookupVerifier.lookupFloat(faces1[c10.face], sampler, c10.s, c10.t, 0); + quad1.p01 = tcuTexLookupVerifier.lookupFloat(faces1[c01.face], sampler, c01.s, c01.t, 0); + quad1.p11 = tcuTexLookupVerifier.lookupFloat(faces1[c11.face], sampler, c11.s, c11.t, 0); + + if (texClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + searchStep1 = tcuTexLookupVerifier.computeBilinearSearchStepFromFloatQuad(prec, quad1); + else + searchStep1 = cSearchStep; + + /** @type {number} */ var minA1 = deMath.clamp((uBounds1[0] - 0.5) - i1, 0, 1); + /** @type {number} */ var maxA1 = deMath.clamp((uBounds1[1] - 0.5) - i1, 0, 1); + /** @type {number} */ var minB1 = deMath.clamp((vBounds1[0] - 0.5) - j1, 0, 1); + /** @type {number} */ var maxB1 = deMath.clamp((vBounds1[1] - 0.5) - j1, 0, 1); + + if (tcuTexLookupVerifier.is2DTrilinearFilterResultValid(prec, quad0, quad1, [minA0, maxA0], [minB0, maxB0], [minA1, maxA1], [minB1, maxB1], + fBounds, Math.min(searchStep0, searchStep1), result)) + return true; + } + } + } + } + + return false; + }; + + /** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} level (&level)[CUBEFACE_LAST] + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filterMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isCubeLevelSampleResultValid = function(level, sampler, filterMode, prec, coords, result) { + if (filterMode == tcuTexture.FilterMode.LINEAR) { + if (sampler.seamlessCubeMap) + return tcuTexLookupVerifier.isSeamlessLinearSampleResultValid(level, sampler, prec, coords, result); + else + return tcuTexLookupVerifier.isLinearSampleResultValid_CoordAsVec2AndInt(level[coords.face], sampler, prec, [coords.s, coords.t], 0, result); + } else + return tcuTexLookupVerifier.isNearestSampleResultValid_CoordAsVec2AndInt(level[coords.face], sampler, prec, [coords.s, coords.t], 0, result); + }; + + /** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faces0 (&faces0)[CUBEFACE_LAST] + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faces1 (&faces1)[CUBEFACE_LAST] + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} levelFilter + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {tcuTexture.CubeFaceCoords} coords + * @param {Array<number>} fBounds + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isCubeMipmapLinearSampleResultValid = function(faces0, faces1, sampler, levelFilter, prec, coords, fBounds, result) { + if (levelFilter == tcuTexture.FilterMode.LINEAR) { + if (sampler.seamlessCubeMap) + return tcuTexLookupVerifier.isSeamplessLinearMipmapLinearSampleResultValid(faces0, faces1, sampler, prec, coords, fBounds, result); + else + return tcuTexLookupVerifier.isLinearMipmapLinearSampleResultValid_CoordAsVec2AndInt(faces0[coords.face], faces1[coords.face], sampler, prec, [coords.s, coords.t], 0, fBounds, result); + } else + return tcuTexLookupVerifier.isNearestMipmapLinearSampleResultValid_CoordAsVec2AndInt(faces0[coords.face], faces1[coords.face], sampler, prec, [coords.s, coords.t], 0, fBounds, result); + }; + + /** + * @param {tcuTexture.TextureCubeView} texture + * @param {number} levelNdx + * @param {Array<tcuTexture.ConstPixelBufferAccess>} out (&out)[CUBEFACE_LAST] + */ + tcuTexLookupVerifier.getCubeLevelFaces = function(texture, levelNdx, out) { + for (var faceNdx = 0; faceNdx < 6; faceNdx++) + out[faceNdx] = texture.getLevelFace(levelNdx, /** @type {tcuTexture.CubeFace} */ (faceNdx)); + }; + + /** + * @param {number} numLayers + * @param {number} numCoordBits + * @param {number} layerCoord + * @return {Array<number>} + */ + tcuTexLookupVerifier.computeLayerRange = function(numLayers, numCoordBits, layerCoord) { + /** @type {number} */ var err = tcuTexVerifierUtil.computeFloatingPointError(layerCoord, numCoordBits); + /** @type {number} */ var minL = Math.floor(layerCoord - err + 0.5); // Round down + /** @type {number} */ var maxL = Math.ceil(layerCoord + err + 0.5) - 1; // Round up + + assertMsgOptions(minL <= maxL, 'minL > maxL', false, true); + + return [deMath.clamp(minL, 0, numLayers - 1), deMath.clamp(maxL, 0, numLayers - 1)]; + }; + + /** + * @param {Array<number>} bits + * @return {Array<number>} + */ + tcuTexLookupVerifier.computeFixedPointThreshold = function(bits) { + return tcuTexVerifierUtil.computeFixedPointError_Vector(bits); + }; + + /** + * @param {Array<number>} bits + * @param {Array<number>} value + * @return {Array<number>} + */ + tcuTexLookupVerifier.computeFloatingPointThreshold = function(bits, value) { + return tcuTexVerifierUtil.computeFloatingPointError_Vector(value, bits); + }; + + /** + * @param {number} dudx + * @param {number} dvdx + * @param {number} dwdx + * @param {number} dudy + * @param {number} dvdy + * @param {number} dwdy + * @param {tcuTexLookupVerifier.LodPrecision} prec + * @return {Array<number>} + */ + tcuTexLookupVerifier.computeLodBoundsFromDerivates = function(dudx, dvdx, dwdx, dudy, dvdy, dwdy, prec) { + /** @type {number} */ var mu = Math.max(Math.abs(dudx), Math.abs(dudy)); + /** @type {number} */ var mv = Math.max(Math.abs(dvdx), Math.abs(dvdy)); + /** @type {number} */ var mw = Math.max(Math.abs(dwdx), Math.abs(dwdy)); + /** @type {number} */ var minDBound = Math.max(Math.max(mu, mv), mw); + /** @type {number} */ var maxDBound = mu + mv + mw; + /** @type {number} */ var minDErr = tcuTexVerifierUtil.computeFloatingPointError(minDBound, prec.derivateBits); + /** @type {number} */ var maxDErr = tcuTexVerifierUtil.computeFloatingPointError(maxDBound, prec.derivateBits); + /** @type {number} */ var minLod = Math.log2(minDBound - minDErr); + /** @type {number} */ var maxLod = Math.log2(maxDBound + maxDErr); + /** @type {number} */ var lodErr = tcuTexVerifierUtil.computeFixedPointError(prec.lodBits); + + assertMsgOptions(minLod <= maxLod, 'Error: minLod > maxLod', false, true); + return [minLod - lodErr, maxLod + lodErr]; + }; + + /** + * @param {number} dudx + * @param {number} dvdx + * @param {number} dudy + * @param {number} dvdy + * @param {tcuTexLookupVerifier.LodPrecision} prec + * @return {Array<number>} + */ + tcuTexLookupVerifier.computeLodBoundsFromDerivatesUV = function(dudx, dvdx, dudy, dvdy, prec) { + return tcuTexLookupVerifier.computeLodBoundsFromDerivates(dudx, dvdx, 0, dudy, dvdy, 0, prec); + }; + + /** + * @param {number} dudx + * @param {number} dudy + * @param {tcuTexLookupVerifier.LodPrecision} prec + * @return {Array<number>} + */ + tcuTexLookupVerifier.computeLodBoundsFromDerivatesU = function(dudx, dudy, prec) { + return tcuTexLookupVerifier.computeLodBoundsFromDerivates(dudx, 0, 0, dudy, 0, 0, prec); + }; + + /** + * @param {Array<number>} coord + * @param {Array<number>} coordDx + * @param {Array<number>} coordDy + * @param {number} faceSize + * @param {tcuTexLookupVerifier.LodPrecision} prec + * @return {Array<number>} + */ + tcuTexLookupVerifier.computeCubeLodBoundsFromDerivates = function(coord, coordDx, coordDy, faceSize, prec) { + /** @type {boolean} */ var allowBrokenEdgeDerivate = false; + /** @type {tcuTexture.CubeFace} */ var face = tcuTexture.selectCubeFace(coord); + /** @type {number} */ var maNdx = 0; + /** @type {number} */ var sNdx = 0; + /** @type {number} */ var tNdx = 0; + + // \note Derivate signs don't matter when computing lod + switch (face) { + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X: + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_X: maNdx = 0; sNdx = 2; tNdx = 1; break; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y: + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y: maNdx = 1; sNdx = 0; tNdx = 2; break; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z: + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z: maNdx = 2; sNdx = 0; tNdx = 1; break; + default: + throw new Error('Invalid CubeFace.'); + } + + /** @type {number} */ var sc = coord[sNdx]; + /** @type {number} */ var tc = coord[tNdx]; + /** @type {number} */ var ma = Math.abs(coord[maNdx]); + /** @type {number} */ var scdx = coordDx[sNdx]; + /** @type {number} */ var tcdx = coordDx[tNdx]; + /** @type {number} */ var madx = Math.abs(coordDx[maNdx]); + /** @type {number} */ var scdy = coordDy[sNdx]; + /** @type {number} */ var tcdy = coordDy[tNdx]; + /** @type {number} */ var mady = Math.abs(coordDy[maNdx]); + /** @type {number} */ var dudx = faceSize * 0.5 * (scdx * ma - sc * madx) / (ma * ma); + /** @type {number} */ var dvdx = faceSize * 0.5 * (tcdx * ma - tc * madx) / (ma * ma); + /** @type {number} */ var dudy = faceSize * 0.5 * (scdy * ma - sc * mady) / (ma * ma); + /** @type {number} */ var dvdy = faceSize * 0.5 * (tcdy * ma - tc * mady) / (ma * ma); + /** @type {Array<number>} */ var bounds = tcuTexLookupVerifier.computeLodBoundsFromDerivatesUV(dudx, dvdx, dudy, dvdy, prec); + + // Implementations may compute derivate from projected (s, t) resulting in incorrect values at edges. + if (allowBrokenEdgeDerivate) { + /** @type {Array<number>} */ var dxErr = tcuTexVerifierUtil.computeFloatingPointError_Vector(coordDx, [prec.derivateBits, prec.derivateBits, prec.derivateBits]); + /** @type {Array<number>} */ var dyErr = tcuTexVerifierUtil.computeFloatingPointError_Vector(coordDy, [prec.derivateBits, prec.derivateBits, prec.derivateBits]); + /** @type {Array<number>} */ var xoffs = deMath.add(deMath.abs(coordDx), dxErr); + /** @type {Array<number>} */ var yoffs = deMath.add(deMath.abs(coordDy), dyErr); + + if (tcuTexture.selectCubeFace(deMath.add(coord, xoffs)) != face || + tcuTexture.selectCubeFace(deMath.subtract(coord, xoffs)) != face || + tcuTexture.selectCubeFace(deMath.add(coord, yoffs)) != face || + tcuTexture.selectCubeFace(deMath.subtract(coord, yoffs)) != face) { + return [bounds[0], 1000]; + } + } + + return bounds; + }; + + /** + * @param {Array<number>} lodBounds + * @param {Array<number>} lodMinMax + * @param {tcuTexLookupVerifier.LodPrecision} prec + * @return {Array<number>} + */ + tcuTexLookupVerifier.clampLodBounds = function(lodBounds, lodMinMax, prec) { + /** @type {number} */ var lodErr = tcuTexVerifierUtil.computeFixedPointError(prec.lodBits); + /** @type {number} */ var a = lodMinMax[0]; + /** @type {number} */ var b = lodMinMax[1]; + return [deMath.clamp(lodBounds[0], a - lodErr, b - lodErr), deMath.clamp(lodBounds[1], a + lodErr, b + lodErr)]; + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.TexLookupScaleMode} scaleMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {number} coordZ + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLevel2DLookupResultValid = function(access, sampler, scaleMode, prec, coord, coordZ, result) { + /** @type {tcuTexture.FilterMode} */ + var filterMode = (scaleMode == tcuTexLookupVerifier.TexLookupScaleMode.MAGNIFY) ? sampler.magFilter : sampler.minFilter; + return tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec2AndInt(access, sampler, filterMode, prec, coord, coordZ, result); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.TexLookupScaleMode} scaleMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {number} coordZ + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLevel2DLookupResultValid_Int = function(access, sampler, scaleMode, prec, coord, coordZ, result) { + assertMsgOptions(sampler.minFilter == tcuTexture.FilterMode.NEAREST && sampler.magFilter == tcuTexture.FilterMode.NEAREST, 'minFilter and magFilter must be NEAREST', false, true); + return tcuTexLookupVerifier.isNearestSampleResultValid_CoordAsVec2AndInt(access, sampler, prec, coord, coordZ, result); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.TexLookupScaleMode} scaleMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLevel3DLookupResultValid = function(access, sampler, scaleMode, prec, coord, result) { + /** @type {tcuTexture.FilterMode} */ + var filterMode = (scaleMode == tcuTexLookupVerifier.TexLookupScaleMode.MAGNIFY) ? sampler.magFilter : sampler.minFilter; + return tcuTexLookupVerifier.isLevelSampleResultValid_CoordAsVec3(access, sampler, filterMode, prec, coord, result); + }; + + /** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexLookupVerifier.TexLookupScaleMode} scaleMode + * @param {tcuTexLookupVerifier.LookupPrecision} prec + * @param {Array<number>} coord + * @param {Array<number>} result + * @return {boolean} + */ + tcuTexLookupVerifier.isLevel3DLookupResultValid_Int = function(access, sampler, scaleMode, prec, coord, result) { + assertMsgOptions(sampler.minFilter == tcuTexture.FilterMode.NEAREST && sampler.magFilter == tcuTexture.FilterMode.NEAREST, 'minFilter and magFilter must be NEAREST', false, true); + return tcuTexLookupVerifier.isNearestSampleResultValid_CoordAsVec3(access, sampler, prec, coord, result); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexVerifierUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexVerifierUtil.js new file mode 100644 index 000000000..4c88f4460 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexVerifierUtil.js @@ -0,0 +1,265 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuTexVerifierUtil'); +goog.require('framework.common.tcuFloat'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.delibs.debase.deUtil'); + +goog.scope(function() { + + var tcuTexVerifierUtil = framework.common.tcuTexVerifierUtil; + var deMath = framework.delibs.debase.deMath; + var deUtil = framework.delibs.debase.deUtil; + var tcuFloat = framework.common.tcuFloat; + var tcuTexture = framework.common.tcuTexture; + + /** + * @param {number} value + * @param {number} numAccurateBits + * @return {number} + */ + tcuTexVerifierUtil.computeFloatingPointError = function(value, numAccurateBits) { + /** @type {number} */ var numGarbageBits = 23 - numAccurateBits; + /** @type {number} */ var mask = (1 << numGarbageBits) - 1; + /** @type {number} */ var exp = tcuFloat.newFloat32(value).exponent(); + + /** @type {tcuFloat.deFloat} */ var v1 = new tcuFloat.deFloat(); + /** @type {tcuFloat.deFloat} */ var v2 = new tcuFloat.deFloat(); + return v1.construct(1, exp, 1 << 23 | mask).getValue() - v2.construct(1, exp, 1 << 23).getValue(); + }; + + /** + * @param {number} numAccurateBits + * @return {number} + */ + tcuTexVerifierUtil.computeFixedPointError = function(numAccurateBits) { + return tcuTexVerifierUtil.computeFloatingPointError(1.0, numAccurateBits); + }; + + /** + * @param {Array<number>} numAccurateBits + * @return {Array<number>} + */ + tcuTexVerifierUtil.computeFixedPointError_Vector = function(numAccurateBits) { + /** @type {Array<number>} */ var res = []; + for (var ndx = 0; ndx < numAccurateBits.length; ndx++) + res[ndx] = tcuTexVerifierUtil.computeFixedPointError(numAccurateBits[ndx]); + return res; + }; + + /** + * @param {Array<number>} value + * @param {Array<number>} numAccurateBits + * @return {Array<number>} + */ + tcuTexVerifierUtil.computeFloatingPointError_Vector = function(value, numAccurateBits) { + assertMsgOptions(value.length === numAccurateBits.length, '', false, true); + /** @type {Array<number>} */ var res = []; + for (var ndx = 0; ndx < value.length; ndx++) + res[ndx] = tcuTexVerifierUtil.computeFloatingPointError(value[ndx], numAccurateBits[ndx]); + return res; + }; + + // Sampler introspection + + /** + * @param {tcuTexture.FilterMode} mode + * @return {boolean} + */ + tcuTexVerifierUtil.isNearestMipmapFilter = function(mode) { + return mode == tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST || mode == tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST; + }; + + /** + * @param {tcuTexture.FilterMode} mode + * @return {boolean} + */ + tcuTexVerifierUtil.isLinearMipmapFilter = function(mode) { + return mode == tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR || mode == tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR; + }; + + /** + * @param {tcuTexture.FilterMode} mode + * @return {boolean} + */ + tcuTexVerifierUtil.isMipmapFilter = function(mode) { + return tcuTexVerifierUtil.isNearestMipmapFilter(mode) || tcuTexVerifierUtil.isLinearMipmapFilter(mode); + }; + + /** + * @param {tcuTexture.FilterMode} mode + * @return {boolean} + */ + tcuTexVerifierUtil.isLinearFilter = function(mode) { + return mode == tcuTexture.FilterMode.LINEAR || mode == tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST || mode == tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR; + }; + + /** + * @param {tcuTexture.FilterMode} mode + * @return {boolean} + */ + tcuTexVerifierUtil.isNearestFilter = function(mode) { + return !tcuTexVerifierUtil.isLinearFilter(mode); + }; + + /** + * @param {tcuTexture.FilterMode} mode + * @return {tcuTexture.FilterMode} + */ + tcuTexVerifierUtil.getLevelFilter = function(mode) { + return tcuTexVerifierUtil.isLinearFilter(mode) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + }; + + /** + * @param {tcuTexture.WrapMode} mode + * @return {boolean} + */ + tcuTexVerifierUtil.isWrapModeSupported = function(mode) { + return mode != tcuTexture.WrapMode.MIRRORED_REPEAT_CL && mode != tcuTexture.WrapMode.REPEAT_CL; + }; + + /** + * + * @param {boolean} normalizedCoords + * @param {number} dim + * @param {number} coord + * @param {number} coordBits + * @param {number} uvBits + * @return {Array<number>} + */ + tcuTexVerifierUtil.computeNonNormalizedCoordBounds = function(normalizedCoords, dim, coord, coordBits, uvBits) { + /** @type {number} */ var coordErr = tcuTexVerifierUtil.computeFloatingPointError(coord, coordBits); + /** @type {number} */ var minN = coord - coordErr; + /** @type {number} */ var maxN = coord + coordErr; + /** @type {number} */ var minA = normalizedCoords ? minN * dim : minN; + /** @type {number} */ var maxA = normalizedCoords ? maxN * dim : maxN; + /** @type {number} */ var minC = minA - tcuTexVerifierUtil.computeFixedPointError(uvBits); + /** @type {number} */ var maxC = maxA + tcuTexVerifierUtil.computeFixedPointError(uvBits); + assertMsgOptions(minC <= maxC, '', false, true); + return [minC, maxC]; + }; + + /** + * @param {Array<number>} coord + * @param {Array<number>} bits + * @return {?Array<tcuTexture.CubeFace>} + */ + tcuTexVerifierUtil.getPossibleCubeFaces = function(coord, bits) { + + /** @type {Array<tcuTexture.CubeFace>} */ var faces = []; + + /** @type {number} */ var x = coord[0]; + /** @type {number} */ var y = coord[1]; + /** @type {number} */ var z = coord[2]; + /** @type {number} */ var ax = Math.abs(x); + /** @type {number} */ var ay = Math.abs(y); + /** @type {number} */ var az = Math.abs(z); + /** @type {number} */ var ex = tcuTexVerifierUtil.computeFloatingPointError(x, bits[0]); + /** @type {number} */ var ey = tcuTexVerifierUtil.computeFloatingPointError(y, bits[1]); + /** @type {number} */ var ez = tcuTexVerifierUtil.computeFloatingPointError(z, bits[2]); + /** @type {number} */ var numFaces = 0; + + if (ay + ey < ax - ex && az + ez < ax - ex) { + if (x >= ex) faces.push(tcuTexture.CubeFace.CUBEFACE_POSITIVE_X); + if (x <= ex) faces.push(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X); + } else if (ax + ex < ay - ey && az + ez < ay - ey) { + if (y >= ey) faces.push(tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y); + if (y <= ey) faces.push(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y); + } else if (ax + ex < az - ez && ay + ey < az - ez) { + if (z >= ez) faces.push(tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z); + if (z <= ez) faces.push(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z); + } else { + // One or more components are equal (or within error bounds). Allow all faces where major axis is not zero. + if (ax > ex) { + faces.push(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X); + faces.push(tcuTexture.CubeFace.CUBEFACE_POSITIVE_X); + } + + if (ay > ey) { + faces.push(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y); + faces.push(tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y); + } + + if (az > ez) { + faces.push(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z); + faces.push(tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z); + } + } + + return faces.length == 0 ? null : faces; + }; + + /** + * @param {tcuTexture.Sampler} sampler + * @return {tcuTexture.Sampler} + */ + tcuTexVerifierUtil.getUnnormalizedCoordSampler = function(sampler) { + var copy = /** @type {tcuTexture.Sampler} */ (deUtil.clone(sampler)); + copy.normalizedCoords = false; + return copy; + }; + + /** + * @param {number} a + * @param {number} b + * @return {number} + */ + tcuTexVerifierUtil.imod = function(a, b) { + return deMath.imod(a, b); + }; + + /** + * @param {number} a + * @return {number} + */ + tcuTexVerifierUtil.mirror = function(a) { + return deMath.mirror(a); + }; + + /** + * @param {tcuTexture.WrapMode} mode + * @param {number} c + * @param {number} size + * @return {number} + */ + tcuTexVerifierUtil.wrap = function(mode, c, size) { + switch (mode) { + // \note CL and GL modes are handled identically here, as verification process accounts for + // accuracy differences caused by different methods (wrapping vs. denormalizing first). + case tcuTexture.WrapMode.CLAMP_TO_EDGE: + return deMath.clamp(c, 0, size - 1); + + case tcuTexture.WrapMode.REPEAT_GL: + case tcuTexture.WrapMode.REPEAT_CL: + return deMath.imod(c, size); + + case tcuTexture.WrapMode.MIRRORED_REPEAT_GL: + case tcuTexture.WrapMode.MIRRORED_REPEAT_CL: + return (size - 1) - deMath.mirror(deMath.imod(c, 2 * size) - size); + + default: + throw new Error('Wrap mode not supported.'); + } + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexture.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexture.js new file mode 100644 index 000000000..8a3a2ed1d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTexture.js @@ -0,0 +1,3636 @@ +/*------------------------------------------------------------------------- + * 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('framework.common.tcuTexture'); +goog.require('framework.common.tcuFloat'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.delibs.debase.deString'); +goog.require('framework.delibs.debase.deUtil'); + +goog.scope(function() { + +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var tcuFloat = framework.common.tcuFloat; +var deString = framework.delibs.debase.deString; +var deUtil = framework.delibs.debase.deUtil; + +var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); +}; + +/** + * Texture tcuTexture.channel order + * @enum + */ +tcuTexture.ChannelOrder = { + R: 0, + A: 1, + I: 2, + L: 3, + LA: 4, + RG: 5, + RA: 6, + RGB: 7, + RGBA: 8, + ARGB: 9, + BGRA: 10, + + sRGB: 11, + sRGBA: 12, + + D: 13, + S: 14, + DS: 15 +}; + +/** + * Texture tcuTexture.channel type + * @enum + */ +tcuTexture.ChannelType = { + SNORM_INT8: 0, + SNORM_INT16: 1, + SNORM_INT32: 2, + UNORM_INT8: 3, + UNORM_INT16: 4, + UNORM_INT32: 5, + UNORM_SHORT_565: 6, + UNORM_SHORT_555: 7, + UNORM_SHORT_4444: 8, + UNORM_SHORT_5551: 9, + UNORM_INT_101010: 10, + UNORM_INT_1010102_REV: 11, + UNSIGNED_INT_1010102_REV: 12, + UNSIGNED_INT_11F_11F_10F_REV: 13, + UNSIGNED_INT_999_E5_REV: 14, + UNSIGNED_INT_24_8: 15, + SIGNED_INT8: 16, + SIGNED_INT16: 17, + SIGNED_INT32: 18, + UNSIGNED_INT8: 19, + UNSIGNED_INT16: 20, + UNSIGNED_INT32: 21, + HALF_FLOAT: 22, + FLOAT: 23, + FLOAT_UNSIGNED_INT_24_8_REV: 24 +}; + +/** + * Enums for tcuTexture.TextureChannelClass + * @enum {number} + */ +tcuTexture.TextureChannelClass = { + + SIGNED_FIXED_POINT: 0, + UNSIGNED_FIXED_POINT: 1, + SIGNED_INTEGER: 2, + UNSIGNED_INTEGER: 3, + FLOATING_POINT: 4 +}; + +/** + * @param {?tcuTexture.ChannelType} channelType + * @return {tcuTexture.TextureChannelClass} + */ +tcuTexture.getTextureChannelClass = function(channelType) { + + switch (channelType) { + case tcuTexture.ChannelType.SNORM_INT8: return tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT; + case tcuTexture.ChannelType.SNORM_INT16: return tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_INT8: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_INT16: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_SHORT_565: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_SHORT_555: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_SHORT_4444: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_SHORT_5551: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_INT_101010: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: return tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: return tcuTexture.TextureChannelClass.UNSIGNED_INTEGER; + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: return tcuTexture.TextureChannelClass.FLOATING_POINT; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: return tcuTexture.TextureChannelClass.FLOATING_POINT; + case tcuTexture.ChannelType.SIGNED_INT8: return tcuTexture.TextureChannelClass.SIGNED_INTEGER; + case tcuTexture.ChannelType.SIGNED_INT16: return tcuTexture.TextureChannelClass.SIGNED_INTEGER; + case tcuTexture.ChannelType.SIGNED_INT32: return tcuTexture.TextureChannelClass.SIGNED_INTEGER; + case tcuTexture.ChannelType.UNSIGNED_INT8: return tcuTexture.TextureChannelClass.UNSIGNED_INTEGER; + case tcuTexture.ChannelType.UNSIGNED_INT16: return tcuTexture.TextureChannelClass.UNSIGNED_INTEGER; + case tcuTexture.ChannelType.UNSIGNED_INT32: return tcuTexture.TextureChannelClass.UNSIGNED_INTEGER; + case tcuTexture.ChannelType.HALF_FLOAT: return tcuTexture.TextureChannelClass.FLOATING_POINT; + case tcuTexture.ChannelType.FLOAT: return tcuTexture.TextureChannelClass.FLOATING_POINT; + default: return /** @type {tcuTexture.TextureChannelClass<number>} */ (Object.keys(tcuTexture.ChannelType).length); + } +}; + +/** + * @param {tcuTexture.TextureFormat} format + */ +tcuTexture.isFixedPointDepthTextureFormat = function(format) { + var channelClass = tcuTexture.getTextureChannelClass(format.type); + + if (format.order == tcuTexture.ChannelOrder.D) { + // depth internal formats cannot be non-normalized integers + return channelClass != tcuTexture.TextureChannelClass.FLOATING_POINT; + } else if (format.order == tcuTexture.ChannelOrder.DS) { + // combined formats have no single channel class, detect format manually + switch (format.type) { + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: return false; + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: return true; + + default: + // unknown format + DE_ASSERT(false); + return true; + } + } + return false; +}; + +/** + * @param {Array<number>} color + * @param {tcuTexture.CompareMode} compare + * @param {number} chanNdx + * @param {number} ref_ + * @param {boolean} isFixedPoint + */ +tcuTexture.execCompare = function(color, compare, chanNdx, ref_, isFixedPoint) { + var clampValues = isFixedPoint; + var cmp = clampValues ? deMath.clamp(color[chanNdx], 0.0, 1.0) : color[chanNdx]; + var ref = clampValues ? deMath.clamp(ref_, 0.0, 1.0) : ref_; + var res = false; + + switch (compare) { + case tcuTexture.CompareMode.COMPAREMODE_LESS: res = ref < cmp; break; + case tcuTexture.CompareMode.COMPAREMODE_LESS_OR_EQUAL: res = ref <= cmp; break; + case tcuTexture.CompareMode.COMPAREMODE_GREATER: res = ref > cmp; break; + case tcuTexture.CompareMode.COMPAREMODE_GREATER_OR_EQUAL: res = ref >= cmp; break; + case tcuTexture.CompareMode.COMPAREMODE_EQUAL: res = ref == cmp; break; + case tcuTexture.CompareMode.COMPAREMODE_NOT_EQUAL: res = ref != cmp; break; + case tcuTexture.CompareMode.COMPAREMODE_ALWAYS: res = true; break; + case tcuTexture.CompareMode.COMPAREMODE_NEVER: res = false; break; + default: + DE_ASSERT(false); + } + + return res ? 1.0 : 0.0; +}; + +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} levels + * @param {number} numLevels + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {number} s + * @param {number} t + * @param {number} lod + * @param {Array<number>} offset + */ +tcuTexture.sampleLevelArray2DCompare = function(levels, numLevels, sampler, ref, s, t, lod, offset) { + var magnified = lod <= sampler.lodThreshold; + var filterMode = magnified ? sampler.magFilter : sampler.minFilter; + + switch (filterMode) { + case tcuTexture.FilterMode.NEAREST: return levels[0].sample2DCompare(sampler, filterMode, ref, s, t, offset); + case tcuTexture.FilterMode.LINEAR: return levels[0].sample2DCompare(sampler, filterMode, ref, s, t, offset); + + case tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST: + case tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST: { + var maxLevel = numLevels - 1; + var level = deMath.clamp(Math.ceil(lod + 0.5) - 1, 0, maxLevel); + var levelFilter = filterMode == tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + + return levels[level].sample2DCompare(sampler, levelFilter, ref, s, t, offset); + } + + case tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR: + case tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR: { + var maxLevel = numLevels - 1; + var level0 = deMath.clamp(Math.floor(lod), 0, maxLevel); + var level1 = Math.min(maxLevel, level0 + 1); + var levelFilter = filterMode == tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + var f = deMath.deFloatFrac(lod); + var t0 = levels[level0].sample2DCompare(sampler, levelFilter, ref, s, t, offset); + var t1 = levels[level1].sample2DCompare(sampler, levelFilter, ref, s, t, offset); + + return t0 * (1.0 - f) + t1 * f; + } + + default: + DE_ASSERT(false); + return 0.0; + } +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {number} u + * @param {number} v + * @param {Array<number>} offset + * @param {boolean} isFixedPointDepthFormat + * @return {number} + */ +tcuTexture.sampleLinear2DCompare = function(access, sampler, ref, u, v, offset, isFixedPointDepthFormat) { + var w = access.getWidth(); + var h = access.getHeight(); + + var x0 = Math.floor(u - 0.5) + offset[0]; + var x1 = x0 + 1; + var y0 = Math.floor(v - 0.5) + offset[1]; + var y1 = y0 + 1; + + var i0 = tcuTexture.wrap(sampler.wrapS, x0, w); + var i1 = tcuTexture.wrap(sampler.wrapS, x1, w); + var j0 = tcuTexture.wrap(sampler.wrapT, y0, h); + var j1 = tcuTexture.wrap(sampler.wrapT, y1, h); + + var a = deMath.deFloatFrac(u - 0.5); + var b = deMath.deFloatFrac(v - 0.5); + + var i0UseBorder = sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(i0, 0, w); + var i1UseBorder = sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(i1, 0, w); + var j0UseBorder = sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(j0, 0, h); + var j1UseBorder = sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(j1, 0, h); + + // Border color for out-of-range coordinates if using CLAMP_TO_BORDER, otherwise execute lookups. + var p00Clr = (i0UseBorder || j0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j0, offset[2]); + var p10Clr = (i1UseBorder || j0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j0, offset[2]); + var p01Clr = (i0UseBorder || j1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j1, offset[2]); + var p11Clr = (i1UseBorder || j1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j1, offset[2]); + + // Execute comparisons. + var p00 = tcuTexture.execCompare(p00Clr, sampler.compare, sampler.compareChannel, ref, isFixedPointDepthFormat); + var p10 = tcuTexture.execCompare(p10Clr, sampler.compare, sampler.compareChannel, ref, isFixedPointDepthFormat); + var p01 = tcuTexture.execCompare(p01Clr, sampler.compare, sampler.compareChannel, ref, isFixedPointDepthFormat); + var p11 = tcuTexture.execCompare(p11Clr, sampler.compare, sampler.compareChannel, ref, isFixedPointDepthFormat); + + // Interpolate. + return (p00 * (1.0 - a) * (1.0 - b)) + + (p10 * (a) * (1.0 - b)) + + (p01 * (1.0 - a) * (b)) + + (p11 * (a) * (b)); +}; + +/** + * Construct texture format + * @param {?tcuTexture.ChannelOrder} order + * @param {?tcuTexture.ChannelType} type + * + * @constructor + */ +tcuTexture.TextureFormat = function(order, type) { + this.order = order; + this.type = type; +}; + +/** + * Compare two formats + * @param {tcuTexture.TextureFormat} format Format to compare with + * @return {boolean} + */ +tcuTexture.TextureFormat.prototype.isEqual = function(format) { + return this.order === format.order && this.type === format.type; +}; + +tcuTexture.TextureFormat.prototype.toString = function() { + return 'TextureFormat(' + deString.enumToString(tcuTexture.ChannelOrder, this.order) + ', ' + + deString.enumToString(tcuTexture.ChannelType, this.type) + ')'; +}; + +/** + * Is format sRGB? + * @return {boolean} + */ +tcuTexture.TextureFormat.prototype.isSRGB = function() { + return this.order === tcuTexture.ChannelOrder.sRGB || this.order === tcuTexture.ChannelOrder.sRGBA; +}; + +tcuTexture.TextureFormat.prototype.getNumStencilBits = function() { + switch (this.order) { + case tcuTexture.ChannelOrder.S: + switch (this.type) { + case tcuTexture.ChannelType.UNSIGNED_INT8: return 8; + case tcuTexture.ChannelType.UNSIGNED_INT16: return 16; + case tcuTexture.ChannelType.UNSIGNED_INT32: return 32; + default: + throw new Error('Wrong type: ' + this.type); + } + + case tcuTexture.ChannelOrder.DS: + switch (this.type) { + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: return 8; + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: return 8; + default: + throw new Error('Wrong type: ' + this.type); + } + + default: + throw new Error('Wrong order: ' + this.order); + } +}; + +/** + * Get TypedArray type that can be used to access texture. + * @param {?tcuTexture.ChannelType} type + * @return TypedArray that supports the tcuTexture.channel type. + */ +tcuTexture.getTypedArray = function(type) { + switch (type) { + case tcuTexture.ChannelType.SNORM_INT8: return Int8Array; + case tcuTexture.ChannelType.SNORM_INT16: return Int16Array; + case tcuTexture.ChannelType.SNORM_INT32: return Int32Array; + case tcuTexture.ChannelType.UNORM_INT8: return Uint8Array; + case tcuTexture.ChannelType.UNORM_INT16: return Uint16Array; + case tcuTexture.ChannelType.UNORM_INT32: return Uint32Array; + case tcuTexture.ChannelType.UNORM_SHORT_565: return Uint16Array; + case tcuTexture.ChannelType.UNORM_SHORT_555: return Uint16Array; + case tcuTexture.ChannelType.UNORM_SHORT_4444: return Uint16Array; + case tcuTexture.ChannelType.UNORM_SHORT_5551: return Uint16Array; + case tcuTexture.ChannelType.UNORM_INT_101010: return Uint32Array; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: return Uint32Array; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: return Uint32Array; + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: return Uint32Array; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: return Uint32Array; + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: return Uint32Array; + case tcuTexture.ChannelType.FLOAT: return Float32Array; + case tcuTexture.ChannelType.SIGNED_INT8: return Int8Array; + case tcuTexture.ChannelType.SIGNED_INT16: return Int16Array; + case tcuTexture.ChannelType.SIGNED_INT32: return Int32Array; + case tcuTexture.ChannelType.UNSIGNED_INT8: return Uint8Array; + case tcuTexture.ChannelType.UNSIGNED_INT16: return Uint16Array; + case tcuTexture.ChannelType.UNSIGNED_INT32: return Uint32Array; + case tcuTexture.ChannelType.HALF_FLOAT: return Uint16Array; + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: return Float32Array; /* this type is a special case */ + } + + throw new Error('Unrecognized type ' + type); +}; + +/** + * @return {number} pixel size in bytes + */ +tcuTexture.TextureFormat.prototype.getPixelSize = function() { + if (this.type == null || this.order == null) { + // Invalid/empty format. + return 0; + } else if (this.type == tcuTexture.ChannelType.UNORM_SHORT_565 || + this.type == tcuTexture.ChannelType.UNORM_SHORT_555 || + this.type == tcuTexture.ChannelType.UNORM_SHORT_4444 || + this.type == tcuTexture.ChannelType.UNORM_SHORT_5551) { + DE_ASSERT(this.order == tcuTexture.ChannelOrder.RGB || this.order == tcuTexture.ChannelOrder.RGBA); + return 2; + } else if (this.type == tcuTexture.ChannelType.UNORM_INT_101010 || + this.type == tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV || + this.type == tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV) { + DE_ASSERT(this.order == tcuTexture.ChannelOrder.RGB); + return 4; + } else if (this.type == tcuTexture.ChannelType.UNORM_INT_1010102_REV || + this.type == tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV) { + DE_ASSERT(this.order == tcuTexture.ChannelOrder.RGBA); + return 4; + } else if (this.type == tcuTexture.ChannelType.UNSIGNED_INT_24_8) { + DE_ASSERT(this.order == tcuTexture.ChannelOrder.D || this.order == tcuTexture.ChannelOrder.DS); + return 4; + } else if (this.type == tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV) { + DE_ASSERT(this.order == tcuTexture.ChannelOrder.DS); + return 8; + } else { + var numChannels; + var channelSize; + + switch (this.order) { + case tcuTexture.ChannelOrder.R: numChannels = 1; break; + case tcuTexture.ChannelOrder.A: numChannels = 1; break; + case tcuTexture.ChannelOrder.I: numChannels = 1; break; + case tcuTexture.ChannelOrder.L: numChannels = 1; break; + case tcuTexture.ChannelOrder.LA: numChannels = 2; break; + case tcuTexture.ChannelOrder.RG: numChannels = 2; break; + case tcuTexture.ChannelOrder.RA: numChannels = 2; break; + case tcuTexture.ChannelOrder.RGB: numChannels = 3; break; + case tcuTexture.ChannelOrder.RGBA: numChannels = 4; break; + case tcuTexture.ChannelOrder.ARGB: numChannels = 4; break; + case tcuTexture.ChannelOrder.BGRA: numChannels = 4; break; + case tcuTexture.ChannelOrder.sRGB: numChannels = 3; break; + case tcuTexture.ChannelOrder.sRGBA: numChannels = 4; break; + case tcuTexture.ChannelOrder.D: numChannels = 1; break; + case tcuTexture.ChannelOrder.S: numChannels = 1; break; + case tcuTexture.ChannelOrder.DS: numChannels = 2; break; + default: DE_ASSERT(false); + } + + switch (this.type) { + case tcuTexture.ChannelType.SNORM_INT8: channelSize = 1; break; + case tcuTexture.ChannelType.SNORM_INT16: channelSize = 2; break; + case tcuTexture.ChannelType.SNORM_INT32: channelSize = 4; break; + case tcuTexture.ChannelType.UNORM_INT8: channelSize = 1; break; + case tcuTexture.ChannelType.UNORM_INT16: channelSize = 2; break; + case tcuTexture.ChannelType.UNORM_INT32: channelSize = 4; break; + case tcuTexture.ChannelType.SIGNED_INT8: channelSize = 1; break; + case tcuTexture.ChannelType.SIGNED_INT16: channelSize = 2; break; + case tcuTexture.ChannelType.SIGNED_INT32: channelSize = 4; break; + case tcuTexture.ChannelType.UNSIGNED_INT8: channelSize = 1; break; + case tcuTexture.ChannelType.UNSIGNED_INT16: channelSize = 2; break; + case tcuTexture.ChannelType.UNSIGNED_INT32: channelSize = 4; break; + case tcuTexture.ChannelType.HALF_FLOAT: channelSize = 2; break; + case tcuTexture.ChannelType.FLOAT: channelSize = 4; break; + default: DE_ASSERT(false); + } + + return numChannels * channelSize; + } +}; + +/** + * @enum + */ +tcuTexture.CubeFace = { + CUBEFACE_NEGATIVE_X: 0, + CUBEFACE_POSITIVE_X: 1, + CUBEFACE_NEGATIVE_Y: 2, + CUBEFACE_POSITIVE_Y: 3, + CUBEFACE_NEGATIVE_Z: 4, + CUBEFACE_POSITIVE_Z: 5 +}; + +/** + * Renamed from ArrayBuffer due to name clash + * Wraps ArrayBuffer. + * @constructor + * @param {number=} numElements + */ +tcuTexture.DeqpArrayBuffer = function(numElements) { + if (numElements) + this.m_ptr = new ArrayBuffer(numElements); +}; + +/** + * Set array size + * @param {number} numElements Size in bytes + */ +tcuTexture.DeqpArrayBuffer.prototype.setStorage = function(numElements) { + this.m_ptr = new ArrayBuffer(numElements); +}; + +/** + * @return {number} Buffer size + */ +tcuTexture.DeqpArrayBuffer.prototype.size = function() { + if (this.m_ptr) + return this.m_ptr.byteLength; + + return 0; +}; + +/** + * Is the buffer empty (zero size)? + * @return {boolean} + */ +tcuTexture.DeqpArrayBuffer.prototype.empty = function() { + if (!this.m_ptr) + return true; + return this.size() == 0; +}; + +/** + * @enum + * The values are negative to avoid conflict with channels 0 - 3 + */ +tcuTexture.channel = { + ZERO: -1, + ONE: -2 +}; + +/** + * @param {tcuTexture.ChannelOrder} order + * @return {Array<Number|tcuTexture.channel>} + */ +tcuTexture.getChannelReadMap = function(order) { + switch (order) { + /*static const Channel INV[] = { tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, tcuTexture.channel.ONE }; */ + + case tcuTexture.ChannelOrder.R: return [0, tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, tcuTexture.channel.ONE]; + case tcuTexture.ChannelOrder.A: return [tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, 0]; + case tcuTexture.ChannelOrder.I: return [0, 0, 0, 0]; + case tcuTexture.ChannelOrder.L: return [0, 0, 0, tcuTexture.channel.ONE]; + case tcuTexture.ChannelOrder.LA: return [0, 0, 0, 1]; + case tcuTexture.ChannelOrder.RG: return [0, 1, tcuTexture.channel.ZERO, tcuTexture.channel.ONE]; + case tcuTexture.ChannelOrder.RA: return [0, tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, 1]; + case tcuTexture.ChannelOrder.RGB: return [0, 1, 2, tcuTexture.channel.ONE]; + case tcuTexture.ChannelOrder.RGBA: return [0, 1, 2, 3]; + case tcuTexture.ChannelOrder.BGRA: return [2, 1, 0, 3]; + case tcuTexture.ChannelOrder.ARGB: return [1, 2, 3, 0]; + case tcuTexture.ChannelOrder.sRGB: return [0, 1, 2, tcuTexture.channel.ONE]; + case tcuTexture.ChannelOrder.sRGBA: return [0, 1, 2, 3]; + case tcuTexture.ChannelOrder.D: return [0, tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, tcuTexture.channel.ONE]; + case tcuTexture.ChannelOrder.S: return [tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, 0]; + case tcuTexture.ChannelOrder.DS: return [0, tcuTexture.channel.ZERO, tcuTexture.channel.ZERO, 1]; + } + + throw new Error('Unrecognized order ' + order); +}; + +/** + * @param {tcuTexture.ChannelOrder} order + * @return {Array<number>} + */ +tcuTexture.getChannelWriteMap = function(order) { + switch (order) { + case tcuTexture.ChannelOrder.R: return [0]; + case tcuTexture.ChannelOrder.A: return [3]; + case tcuTexture.ChannelOrder.I: return [0]; + case tcuTexture.ChannelOrder.L: return [0]; + case tcuTexture.ChannelOrder.LA: return [0, 3]; + case tcuTexture.ChannelOrder.RG: return [0, 1]; + case tcuTexture.ChannelOrder.RA: return [0, 3]; + case tcuTexture.ChannelOrder.RGB: return [0, 1, 2]; + case tcuTexture.ChannelOrder.RGBA: return [0, 1, 2, 3]; + case tcuTexture.ChannelOrder.ARGB: return [3, 0, 1, 2]; + case tcuTexture.ChannelOrder.BGRA: return [2, 1, 0, 3]; + case tcuTexture.ChannelOrder.sRGB: return [0, 1, 2]; + case tcuTexture.ChannelOrder.sRGBA: return [0, 1, 2, 3]; + case tcuTexture.ChannelOrder.D: return [0]; + case tcuTexture.ChannelOrder.S: return [3]; + case tcuTexture.ChannelOrder.DS: return [0, 3]; + } + throw new Error('Unrecognized order ' + order); +}; + +/** + * @param {tcuTexture.ChannelType} type + * @return {number} + */ +tcuTexture.getChannelSize = function(type) { + switch (type) { + case tcuTexture.ChannelType.SNORM_INT8: return 1; + case tcuTexture.ChannelType.SNORM_INT16: return 2; + case tcuTexture.ChannelType.SNORM_INT32: return 4; + case tcuTexture.ChannelType.UNORM_INT8: return 1; + case tcuTexture.ChannelType.UNORM_INT16: return 2; + case tcuTexture.ChannelType.UNORM_INT32: return 4; + case tcuTexture.ChannelType.SIGNED_INT8: return 1; + case tcuTexture.ChannelType.SIGNED_INT16: return 2; + case tcuTexture.ChannelType.SIGNED_INT32: return 4; + case tcuTexture.ChannelType.UNSIGNED_INT8: return 1; + case tcuTexture.ChannelType.UNSIGNED_INT16: return 2; + case tcuTexture.ChannelType.UNSIGNED_INT32: return 4; + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: return 4; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: return 4; + case tcuTexture.ChannelType.HALF_FLOAT: return 2; + case tcuTexture.ChannelType.FLOAT: return 4; + + } + throw new Error('Unrecognized type ' + deString.enumToString(tcuTexture.ChannelType, type)); +}; + +/** + * @param {number} src Source value + * @param {number} bits Source value size in bits + * @return {number} Normalized value + */ +tcuTexture.channelToNormFloat = function(src, bits) { + var maxVal = (1 << bits) - 1; + return src / maxVal; +}; + +/** + * @param {number} value Source value + * @param {tcuTexture.ChannelType} type + * @return {number} Source value converted to float + */ +tcuTexture.channelToFloat = function(value, type) { + switch (type) { + case tcuTexture.ChannelType.SNORM_INT8: return Math.max(-1, value / 127); + case tcuTexture.ChannelType.SNORM_INT16: return Math.max(-1, value / 32767); + case tcuTexture.ChannelType.SNORM_INT32: return Math.max(-1, value / 2147483647); + case tcuTexture.ChannelType.UNORM_INT8: return value / 255; + case tcuTexture.ChannelType.UNORM_INT16: return value / 65535; + case tcuTexture.ChannelType.UNORM_INT32: return value / 4294967295; + case tcuTexture.ChannelType.SIGNED_INT8: return value; + case tcuTexture.ChannelType.SIGNED_INT16: return value; + case tcuTexture.ChannelType.SIGNED_INT32: return value; + case tcuTexture.ChannelType.UNSIGNED_INT8: return value; + case tcuTexture.ChannelType.UNSIGNED_INT16: return value; + case tcuTexture.ChannelType.UNSIGNED_INT32: return value; + case tcuTexture.ChannelType.HALF_FLOAT: return tcuFloat.halfFloatToNumber(value); + case tcuTexture.ChannelType.FLOAT: return value; + } + throw new Error('Unrecognized tcuTexture.channel type ' + type); +}; + +/** + * @param {number} value Source value + * @param {tcuTexture.ChannelType} type + * @return {number} Source value converted to int + */ +tcuTexture.channelToInt = function(value, type) { + switch (type) { + case tcuTexture.ChannelType.HALF_FLOAT: return Math.round(tcuFloat.halfFloatToNumber(value)); + case tcuTexture.ChannelType.FLOAT: return Math.round(value); + default: + return value; + } +}; + +/** + * @param {tcuTexture.ChannelOrder} order + * @return {number} + */ +tcuTexture.getNumUsedChannels = function(order) { + switch (order) { + case tcuTexture.ChannelOrder.R: return 1; + case tcuTexture.ChannelOrder.A: return 1; + case tcuTexture.ChannelOrder.I: return 1; + case tcuTexture.ChannelOrder.L: return 1; + case tcuTexture.ChannelOrder.LA: return 2; + case tcuTexture.ChannelOrder.RG: return 2; + case tcuTexture.ChannelOrder.RA: return 2; + case tcuTexture.ChannelOrder.RGB: return 3; + case tcuTexture.ChannelOrder.RGBA: return 4; + case tcuTexture.ChannelOrder.ARGB: return 4; + case tcuTexture.ChannelOrder.BGRA: return 4; + case tcuTexture.ChannelOrder.sRGB: return 3; + case tcuTexture.ChannelOrder.sRGBA: return 4; + case tcuTexture.ChannelOrder.D: return 1; + case tcuTexture.ChannelOrder.S: return 1; + case tcuTexture.ChannelOrder.DS: return 2; + } + throw new Error('Unrecognized tcuTexture.channel order ' + order); +}; + +/** + * @enum + */ +tcuTexture.WrapMode = { + CLAMP_TO_EDGE: 0, //! Clamp to edge + CLAMP_TO_BORDER: 1, //! Use border color at edge + REPEAT_GL: 2, //! Repeat with OpenGL semantics + REPEAT_CL: 3, //! Repeat with OpenCL semantics + MIRRORED_REPEAT_GL: 4, //! Mirrored repeat with OpenGL semantics + MIRRORED_REPEAT_CL: 5 //! Mirrored repeat with OpenCL semantics +}; + +/** + * @enum + */ +tcuTexture.FilterMode = { + NEAREST: 0, + LINEAR: 1, + + NEAREST_MIPMAP_NEAREST: 2, + NEAREST_MIPMAP_LINEAR: 3, + LINEAR_MIPMAP_NEAREST: 4, + LINEAR_MIPMAP_LINEAR: 5 +}; + +/** + * @enum + */ +tcuTexture.CompareMode = { + COMPAREMODE_NONE: 0, + COMPAREMODE_LESS: 1, + COMPAREMODE_LESS_OR_EQUAL: 2, + COMPAREMODE_GREATER: 3, + COMPAREMODE_GREATER_OR_EQUAL: 4, + COMPAREMODE_EQUAL: 5, + COMPAREMODE_NOT_EQUAL: 6, + COMPAREMODE_ALWAYS: 7, + COMPAREMODE_NEVER: 8 +}; + +/** + * @constructor + * @param {!tcuTexture.WrapMode} wrapS + * @param {!tcuTexture.WrapMode} wrapT + * @param {!tcuTexture.WrapMode} wrapR + * @param {!tcuTexture.FilterMode} minFilter + * @param {!tcuTexture.FilterMode} magFilter + * @param {number=} lodThreshold + * @param {boolean=} normalizedCoords + * @param {tcuTexture.CompareMode=} compare + * @param {number=} compareChannel + * @param {Array<number>=} borderColor + * @param {boolean=} seamlessCubeMap + */ +tcuTexture.Sampler = function(wrapS, wrapT, wrapR, minFilter, magFilter, lodThreshold, normalizedCoords, compare, compareChannel, borderColor, seamlessCubeMap) { + /** @type {!tcuTexture.WrapMode} */ this.wrapS = wrapS; + /** @type {!tcuTexture.WrapMode} */ this.wrapT = wrapT; + /** @type {!tcuTexture.WrapMode} */ this.wrapR = wrapR; + /** @type {!tcuTexture.FilterMode} */ this.minFilter = minFilter; + /** @type {!tcuTexture.FilterMode} */ this.magFilter = magFilter; + this.lodThreshold = lodThreshold || 0; + this.normalizedCoords = normalizedCoords === undefined ? true : normalizedCoords; + /** @type {tcuTexture.CompareMode} */ this.compare = compare || tcuTexture.CompareMode.COMPAREMODE_NONE; + this.compareChannel = compareChannel || 0; + this.borderColor = borderColor || [0, 0, 0, 0]; + this.seamlessCubeMap = seamlessCubeMap || false; +}; + +/** + * Special unnormalization for REPEAT_CL and MIRRORED_REPEAT_CL tcuTexture.wrap modes; otherwise ordinary unnormalization. + * @param {tcuTexture.WrapMode} mode + * @param {number} c Value to tcuTexture.unnormalize + * @param {number} size Unnormalized type size (integer) + * @return {number} + */ +tcuTexture.unnormalize = function(mode, c, size) { + switch (mode) { + case tcuTexture.WrapMode.CLAMP_TO_EDGE: + case tcuTexture.WrapMode.CLAMP_TO_BORDER: + case tcuTexture.WrapMode.REPEAT_GL: + case tcuTexture.WrapMode.MIRRORED_REPEAT_GL: // Fall-through (ordinary case). + return size * c; + + case tcuTexture.WrapMode.REPEAT_CL: + return size * (c - Math.floor(c)); + + case tcuTexture.WrapMode.MIRRORED_REPEAT_CL: + return size * Math.abs(c - 2 * deMath.rint(0.5 * c)); + } + throw new Error('Unrecognized tcuTexture.wrap mode ' + mode); +}; + +/** + * @param {tcuTexture.WrapMode} mode + * @param {number} c Source value (integer) + * @param {number} size Type size (integer) + * @return {number} + */ +tcuTexture.wrap = function(mode, c, size) { + switch (mode) { + case tcuTexture.WrapMode.CLAMP_TO_BORDER: + return deMath.clamp(c, -1, size); + + case tcuTexture.WrapMode.CLAMP_TO_EDGE: + return deMath.clamp(c, 0, size - 1); + + case tcuTexture.WrapMode.REPEAT_GL: + return deMath.imod(c, size); + + case tcuTexture.WrapMode.REPEAT_CL: + return deMath.imod(c, size); + + case tcuTexture.WrapMode.MIRRORED_REPEAT_GL: + return (size - 1) - deMath.mirror(deMath.imod(c, 2 * size) - size); + + case tcuTexture.WrapMode.MIRRORED_REPEAT_CL: + return deMath.clamp(c, 0, size - 1); // \note Actual mirroring done already in unnormalization function. + } + throw new Error('Unrecognized tcuTexture.wrap mode ' + mode); +}; + +/** + * @param {number} cs + * @return {number} + */ +tcuTexture.sRGBChannelToLinear = function(cs) { + if (cs <= 0.04045) + return cs / 12.92; + else + return Math.pow((cs + 0.055) / 1.055, 2.4); +}; + +/** + * Convert sRGB to linear colorspace + * @param {Array<number>} cs Vec4 + * @return {Array<number>} Vec4 + */ +tcuTexture.sRGBToLinear = function(cs) { + return [ + tcuTexture.sRGBChannelToLinear(cs[0]), + tcuTexture.sRGBChannelToLinear(cs[1]), + tcuTexture.sRGBChannelToLinear(cs[2]), + cs[3] + ]; +}; + +/** + * Texel tcuTexture.lookup with color conversion. + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {number} i + * @param {number} j + * @param {number} k + * @return {Array<number>} Vec4 pixel color + */ +tcuTexture.lookup = function(access, i, j, k) { + var p = access.getPixel(i, j, k); + return access.getFormat().isSRGB() ? tcuTexture.sRGBToLinear(p) : p; +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {number} u + * @param {number} v + * @param {(number|Array<number>)} depthOrOffset depth (int) or offset (ivec3) + * @return {Array<number>} Vec4 pixel color + */ +tcuTexture.sampleLinear2D = function(access, sampler, u, v, depthOrOffset) { + /** @type {number} */ var xOffset = 0; + /** @type {number} */ var yOffset = 0; + /** @type {number} */ var value; + if (Array.isArray(depthOrOffset)) { + xOffset = depthOrOffset[0]; + yOffset = depthOrOffset[1]; + value = depthOrOffset[2]; + } else { + value = /** @type {number} */ (depthOrOffset); + } + + /** + * @param {Array<number>} p00 + * @param {Array<number>} p10 + * @param {Array<number>} p01 + * @param {Array<number>} p11 + * @param {number} a + * @param {number} b + */ + var interpolateQuad = function(p00, p10, p01, p11, a, b) { + var s00 = (1 - a) * (1 - b); + var s10 = a * (1 - b); + var s01 = (1 - a) * b; + var s11 = a * b; + + return [ + (p00[0] * s00) + (p10[0] * s10) + (p01[0] * s01) + (p11[0] * s11), + (p00[1] * s00) + (p10[1] * s10) + (p01[1] * s01) + (p11[1] * s11), + (p00[2] * s00) + (p10[2] * s10) + (p01[2] * s01) + (p11[2] * s11), + (p00[3] * s00) + (p10[3] * s10) + (p01[3] * s01) + (p11[3] * s11) + ]; + }; + + var w = access.getWidth(); + var h = access.getHeight(); + + var x0 = Math.floor(u - 0.5) + xOffset; + var x1 = x0 + 1; + var y0 = Math.floor(v - 0.5) + yOffset; + var y1 = y0 + 1; + + var i0 = tcuTexture.wrap(sampler.wrapS, x0, w); + var i1 = tcuTexture.wrap(sampler.wrapS, x1, w); + var j0 = tcuTexture.wrap(sampler.wrapT, y0, h); + var j1 = tcuTexture.wrap(sampler.wrapT, y1, h); + + var a = deMath.deFloatFrac(u - 0.5); + var b = deMath.deFloatFrac(v - 0.5); + + var i0UseBorder = sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(i0, 0, w); + var i1UseBorder = sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(i1, 0, w); + var j0UseBorder = sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(j0, 0, h); + var j1UseBorder = sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(j1, 0, h); + + // Border color for out-of-range coordinates if using CLAMP_TO_BORDER, otherwise execute lookups. + var p00 = (i0UseBorder || j0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j0, value); + var p10 = (i1UseBorder || j0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j0, value); + var p01 = (i0UseBorder || j1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j1, value); + var p11 = (i1UseBorder || j1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j1, value); + + // Interpolate. + return interpolateQuad(p00, p10, p01, p11, a, b); +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {number} u + * @param {number} v + * @param {number} w + * @param {Array<number>=} offset + * @return {Array<number>} Vec4 pixel color + */ +tcuTexture.sampleLinear3D = function(access, sampler, u, v, w, offset) { + /** + * @param {Array<number>} p000 + * @param {Array<number>} p100 + * @param {Array<number>} p010 + * @param {Array<number>} p110 + * @param {Array<number>} p001 + * @param {Array<number>} p101 + * @param {Array<number>} p011 + * @param {Array<number>} p111 + * @param {number} a + * @param {number} b + * @param {number} c + */ + var interpolateCube = function(p000, p100, p010, p110, p001, p101, p011, p111, a, b, c) { + var s000 = (1 - a) * (1 - b) * (1 - c); + var s100 = a * (1 - b) * (1 - c); + var s010 = (1 - a) * b * (1 - c); + var s110 = a * b * (1 - c); + var s001 = (1 - a) * (1 - b) * c; + var s101 = a * (1 - b) * c; + var s011 = (1 - a) * b * c; + var s111 = a * b * c; + + return [ + (p000[0] * s000) + (p100[0] * s100) + (p010[0] * s010) + (p110[0] * s110) + (p001[0] * s001) + (p101[0] * s101) + (p011[0] * s011) + (p111[0] * s111), + (p000[1] * s000) + (p100[1] * s100) + (p010[1] * s010) + (p110[1] * s110) + (p001[1] * s001) + (p101[1] * s101) + (p011[1] * s011) + (p111[1] * s111), + (p000[2] * s000) + (p100[2] * s100) + (p010[2] * s010) + (p110[2] * s110) + (p001[2] * s001) + (p101[2] * s101) + (p011[2] * s011) + (p111[2] * s111), + (p000[3] * s000) + (p100[3] * s100) + (p010[3] * s010) + (p110[3] * s110) + (p001[3] * s001) + (p101[3] * s101) + (p011[3] * s011) + (p111[3] * s111) + ]; + }; + + var width = access.getWidth(); + var height = access.getHeight(); + var depth = access.getDepth(); + + /** @type {number} */ var xOffset = 0; + /** @type {number} */ var yOffset = 0; + /** @type {number} */ var zOffset = 0; + + if (offset !== undefined && offset.length === 3) { + xOffset = offset[0]; + yOffset = offset[1]; + zOffset = offset[2]; + } + + var x0 = Math.floor(u - 0.5) + xOffset; + var x1 = x0 + 1; + var y0 = Math.floor(v - 0.5) + yOffset; + var y1 = y0 + 1; + var z0 = Math.floor(w - 0.5) + zOffset; + var z1 = z0 + 1; + + var i0 = tcuTexture.wrap(sampler.wrapS, x0, width); + var i1 = tcuTexture.wrap(sampler.wrapS, x1, width); + var j0 = tcuTexture.wrap(sampler.wrapT, y0, height); + var j1 = tcuTexture.wrap(sampler.wrapT, y1, height); + var k0 = tcuTexture.wrap(sampler.wrapR, z0, depth); + var k1 = tcuTexture.wrap(sampler.wrapR, z1, depth); + + var a = deMath.deFloatFrac(u - 0.5); + var b = deMath.deFloatFrac(v - 0.5); + var c = deMath.deFloatFrac(w - 0.5); + + var i0UseBorder = sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(i0, 0, width); + var i1UseBorder = sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(i1, 0, width); + var j0UseBorder = sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(j0, 0, height); + var j1UseBorder = sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(j1, 0, height); + var k0UseBorder = sampler.wrapR == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(k0, 0, depth); + var k1UseBorder = sampler.wrapR == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(k1, 0, depth); + + // Border color for out-of-range coordinates if using CLAMP_TO_BORDER, otherwise execute lookups. + var p000 = (i0UseBorder || j0UseBorder || k0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j0, k0); + var p100 = (i1UseBorder || j0UseBorder || k0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j0, k0); + var p010 = (i0UseBorder || j1UseBorder || k0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j1, k0); + var p110 = (i1UseBorder || j1UseBorder || k0UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j1, k0); + var p001 = (i0UseBorder || j0UseBorder || k1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j0, k1); + var p101 = (i1UseBorder || j0UseBorder || k1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j0, k1); + var p011 = (i0UseBorder || j1UseBorder || k1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i0, j1, k1); + var p111 = (i1UseBorder || j1UseBorder || k1UseBorder) ? sampler.borderColor : tcuTexture.lookup(access, i1, j1, k1); + + // Interpolate. + return interpolateCube(p000, p100, p010, p110, p001, p101, p011, p111, a, b, c); +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {number} u + * @param {number} v + * @param {(number|Array<number>)} depthOrOffset depth (integer) or offset (ivec3) + * @return {Array<number>} Vec4 pixel color + */ +tcuTexture.sampleNearest2D = function(access, sampler, u, v, depthOrOffset) { + /** @type {number} */ var xOffset = 0; + /** @type {number} */ var yOffset = 0; + /** @type {number} */ var value; + if (Array.isArray(depthOrOffset)) { + xOffset = depthOrOffset[0]; + yOffset = depthOrOffset[1]; + value = depthOrOffset[2]; + } else { + value = /** @type {number} */ (depthOrOffset); + } + + var width = access.getWidth(); + var height = access.getHeight(); + + var x = Math.floor(u) + xOffset; + var y = Math.floor(v) + yOffset; + + // Check for CLAMP_TO_BORDER. + if ((sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(x, 0, width)) || + (sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(y, 0, height))) + return sampler.borderColor; + + var i = tcuTexture.wrap(sampler.wrapS, x, width); + var j = tcuTexture.wrap(sampler.wrapT, y, height); + + return tcuTexture.lookup(access, i, j, value); +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @param {tcuTexture.Sampler} sampler + * @param {number} u + * @param {number} v + * @param {number} w + * @param {Array<number>=} offset + * @return {Array<number>} Vec4 pixel color + */ +tcuTexture.sampleNearest3D = function(access, sampler, u, v, w, offset) { + var width = access.getWidth(); + var height = access.getHeight(); + var depth = access.getDepth(); + /** @type {number} */ var xOffset = 0; + /** @type {number} */ var yOffset = 0; + /** @type {number} */ var zOffset = 0; + + if (offset !== undefined && offset.length === 3) { + xOffset = offset[0]; + yOffset = offset[1]; + zOffset = offset[2]; + } + + var x = Math.floor(u) + xOffset; + var y = Math.floor(v) + yOffset; + var z = Math.floor(w) + zOffset; + + // Check for CLAMP_TO_BORDER. + if ((sampler.wrapS == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(x, 0, width)) || + (sampler.wrapT == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(y, 0, height)) || + (sampler.wrapR == tcuTexture.WrapMode.CLAMP_TO_BORDER && !deMath.deInBounds32(z, 0, depth))) + return sampler.borderColor; + + var i = tcuTexture.wrap(sampler.wrapS, x, width); + var j = tcuTexture.wrap(sampler.wrapT, y, height); + var k = tcuTexture.wrap(sampler.wrapR, z, depth); + + return tcuTexture.lookup(access, i, j, k); +}; + +/** + * @param {Array<number>} color Vec4 color + * @return {number} The color in packed 32 bit format + */ +tcuTexture.packRGB999E5 = function(color) { + /** @const */ var mBits = 9; + /** @const */ var eBits = 5; + /** @const */ var eBias = 15; + /** @const */ var eMax = (1 << eBits) - 1; + /** @const */ var maxVal = (((1 << mBits) - 1) * (1 << (eMax - eBias))) / (1 << mBits); + + var rc = deMath.clamp(color[0], 0, maxVal); + var gc = deMath.clamp(color[1], 0, maxVal); + var bc = deMath.clamp(color[2], 0, maxVal); + var maxc = Math.max(rc, gc, bc); + var expp = Math.max(-eBias - 1, Math.floor(Math.log2(maxc))) + 1 + eBias; + var e = Math.pow(2, expp - eBias - mBits); + var maxs = Math.floor(maxc / e + 0.5); + + var exps = maxs == (1 << mBits) ? expp + 1 : expp; + var rs = deMath.clamp(Math.floor(rc / e + 0.5), 0, (1 << 9) - 1); + var gs = deMath.clamp(Math.floor(gc / e + 0.5), 0, (1 << 9) - 1); + var bs = deMath.clamp(Math.floor(bc / e + 0.5), 0, (1 << 9) - 1); + + DE_ASSERT((exps & ~((1 << 5) - 1)) == 0); + DE_ASSERT((rs & ~((1 << 9) - 1)) == 0); + DE_ASSERT((gs & ~((1 << 9) - 1)) == 0); + DE_ASSERT((bs & ~((1 << 9) - 1)) == 0); + + return rs | (gs << 9) | (bs << 18) | (exps << 27); +}; + +/** + * @param {number} color Color in packed 32 bit format + * @return {Array<number>} The color in unpacked format + */ +tcuTexture.unpackRGB999E5 = function(color) { + var mBits = 9; + var eBias = 15; + + var exp = (color >> 27) & ((1 << 5) - 1); + var bs = (color >> 18) & ((1 << 9) - 1); + var gs = (color >> 9) & ((1 << 9) - 1); + var rs = color & ((1 << 9) - 1); + + var e = Math.pow(2, (exp - eBias - mBits)); + var r = rs * e; + var g = gs * e; + var b = bs * e; + + return [r, g, b, 1]; +}; + +/** + * \brief Read-only pixel data access + * + * tcuTexture.ConstPixelBufferAccess encapsulates pixel data pointer along with + * format and layout information. It can be used for read-only access + * to arbitrary pixel buffers. + * + * Access objects are like iterators or pointers. They can be passed around + * as values and are valid as long as the storage doesn't change. + * @constructor + */ +tcuTexture.ConstPixelBufferAccess = function(descriptor) { + if (descriptor) { + this.m_offset = descriptor.offset || 0; + this.m_format = descriptor.format || new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.FLOAT); + this.m_width = descriptor.width; + this.m_height = descriptor.height; + if (descriptor.depth) + this.m_depth = descriptor.depth; + else + this.m_depth = 1; + this.m_data = descriptor.data; + if (descriptor.rowPitch) + this.m_rowPitch = descriptor.rowPitch; + else + this.m_rowPitch = this.m_width * this.m_format.getPixelSize(); + + if (descriptor.slicePitch) + this.m_slicePitch = descriptor.slicePitch; + else + this.m_slicePitch = this.m_rowPitch * this.m_height; + + if (this.m_format.isEqual(new tcuTexture.TextureFormat( + tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8))) + this.m_rgba8View = new tcuTexture.RGBA8View(this); + else if (this.m_format.isEqual(new tcuTexture.TextureFormat( + tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT8))) + this.m_rgb8View = new tcuTexture.RGBA8View(this); + + } + + this.m_dataPtrType = null; + this.m_dataPtr = null; +}; + +tcuTexture.ConstPixelBufferAccess.prototype.toString = function() { + var str = 'BufferAccess(format: ' + this.m_format + + ', width: ' + this.m_width + + ', height: ' + this.m_height; + if (this.m_depth > 1) + str += ', depth: ' + this.m_depth; + if (this.m_rowPitch != this.m_width * this.m_format.getPixelSize()) + str += ', row pitch: ' + this.m_rowPitch; + if (this.m_slicePitch != this.m_rowPitch * this.m_height) + str += ', slice pitch: ' + this.m_slicePitch; + if (this.m_offset > 0) + str += ', offset: ' + this.m_offset; + str += ')'; + return str; +}; + +/** @return {number} */ +tcuTexture.ConstPixelBufferAccess.prototype.getDataSize = function() { return this.m_depth * this.m_slicePitch; }; +tcuTexture.ConstPixelBufferAccess.prototype.isEmpty = function() { return this.m_width == 0 || this.m_height == 0 || this.m_depth == 0; }; +/** @return {goog.TypedArray} */ +tcuTexture.ConstPixelBufferAccess.prototype.getDataPtr = function() { + if (this.m_dataPtrType != this.m_format.type) { + this.m_dataPtrType = this.m_format.type; + var arrayType = tcuTexture.getTypedArray(this.m_format.type); + this.m_dataPtr = new arrayType(this.m_data, this.m_offset); + } + return this.m_dataPtr; +}; +/** @return {ArrayBuffer} */ +tcuTexture.ConstPixelBufferAccess.prototype.getBuffer = function() { + return this.m_data; +}; +/** @return {number} */ +tcuTexture.ConstPixelBufferAccess.prototype.getRowPitch = function() { return this.m_rowPitch; }; +/** @return {number} */ +tcuTexture.ConstPixelBufferAccess.prototype.getWidth = function() { return this.m_width; }; +/** @return {number} */ +tcuTexture.ConstPixelBufferAccess.prototype.getHeight = function() { return this.m_height; }; +/** @return {number} */ +tcuTexture.ConstPixelBufferAccess.prototype.getDepth = function() { return this.m_depth; }; +/** @return {number} */ +tcuTexture.ConstPixelBufferAccess.prototype.getSlicePitch = function() { return this.m_slicePitch; }; +/** @return {tcuTexture.TextureFormat} */ +tcuTexture.ConstPixelBufferAccess.prototype.getFormat = function() { return this.m_format; }; + +/** + * @param {number} x + * @param {number} y + * @param {number=} z + * @return {number} stencil value + */ +tcuTexture.ConstPixelBufferAccess.prototype.getPixStencil = function(x, y, z) { + z = z || 0; + + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + x = Math.round(x); + y = Math.round(y); + z = Math.round(z); + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + case tcuTexture.ChannelOrder.S: return (pixelPtr[pixelPtrOffset] >> 8) & 0xff; + case tcuTexture.ChannelOrder.DS: return pixelPtr[pixelPtrOffset] & 0xff; + + default: + DE_ASSERT(false); + return 0; + } + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.DS); + var u32array = new Uint32Array(this.m_data, offset + this.m_offset + 4, 1); + return u32array[0] & 0xff; + + default: { + if (this.m_format.order == tcuTexture.ChannelOrder.S) + return tcuTexture.channelToInt(pixelPtr[pixelPtrOffset], this.m_format.type); + else { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.DS); + var stencilChannelIndex = 3; + return tcuTexture.channelToInt(pixelPtr[stencilChannelIndex + pixelPtrOffset], this.m_format.type); + } + } + } +}; + +/** + * @param {number} x + * @param {number} y + * @param {number=} z + * @return {number} + */ +tcuTexture.ConstPixelBufferAccess.prototype.getPixDepth = function(x, y, z) { + if (z == null) + z = 0; + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + x = Math.round(x); + y = Math.round(y); + z = Math.round(z); + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + + var ub = function(pixel, offset, count) { + return (pixel >> offset) & ((1 << count) - 1); + }; + var nb = function(pixel, offset, count) { + return tcuTexture.channelToNormFloat(ub(pixel, offset, count), count); + }; + + // Packed formats. + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + case tcuTexture.ChannelOrder.D: // fall-through + case tcuTexture.ChannelOrder.DS: + return nb(pixelPtr[pixelPtrOffset], 8, 24); + default: + throw new Error('Unsupported tcuTexture.channel order ' + this.m_format.order); + } + break; + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.DS); + return pixelPtr[pixelPtrOffset]; + break; + } + + default: { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.D || this.m_format.order == tcuTexture.ChannelOrder.DS); + return tcuTexture.channelToFloat(pixelPtr[pixelPtrOffset], this.m_format.type); + } + } +}; + +/** + * @param {number} x + * @param {number} y + * @param {number=} z + * @return {Array<number>} Pixel value as Vec4 + */ +tcuTexture.ConstPixelBufferAccess.prototype.getPixel = function(x, y, z) { + z = z || 0; + + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + return this._getPixelInternal(Math.round(x), Math.round(y), Math.round(z)); +}; + +// NOTE: getPixel has been broken into getPixel, _getPixelInternal, and _getPixelPacked +// because having them combined previously was causing V8 depots +tcuTexture.ConstPixelBufferAccess.prototype._getPixelInternal = function(x, y, z) { + // Quick paths + if (z == 0) { + if (this.m_rgba8View) { + var color = this.m_rgba8View.read(x, y, 4); + color[0] /= 255; + color[1] /= 255; + color[2] /= 255; + color[3] /= 255; + return color; + } else if (this.m_rgb8View) { + var color = this.m_rgb8View.read(x, y, 3); + color[0] /= 255; + color[1] /= 255; + color[2] /= 255; + color[3] = 1; + return color; + } + } + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + + return this._getPixelPacked(pixelPtr, pixelPtrOffset); +}; + +tcuTexture.ConstPixelBufferAccess.prototype._getPixelPacked = (function() { + + var ub = function(pixel, offset, count) { + return (pixel >> offset) & ((1 << count) - 1); + }; + var nb = function(pixel, offset, count) { + var maxVal = (1 << count) - 1; + return ((pixel >> offset) & ((1 << count) - 1)) / maxVal; + }; + var f11 = tcuFloat.float11ToNumber; + var f10 = tcuFloat.float10ToNumber; + + return function tcuTexture_ConstPixelBufferAccess_getPixelPacked(pixelPtr, pixelPtrOffset) { + var pixel = pixelPtr[pixelPtrOffset]; + + // Packed formats. + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNORM_SHORT_565: return [nb(pixel, 11, 5), nb(pixel, 5, 6), nb(pixel, 0, 5), 1]; + case tcuTexture.ChannelType.UNORM_SHORT_555: return [nb(pixel, 10, 5), nb(pixel, 5, 5), nb(pixel, 0, 5), 1]; + case tcuTexture.ChannelType.UNORM_SHORT_4444: return [nb(pixel, 12, 4), nb(pixel, 8, 4), nb(pixel, 4, 4), nb(pixel, 0, 4)]; + case tcuTexture.ChannelType.UNORM_SHORT_5551: return [nb(pixel, 11, 5), nb(pixel, 6, 5), nb(pixel, 1, 5), nb(pixel, 0, 1)]; + case tcuTexture.ChannelType.UNORM_INT_101010: return [nb(pixel, 22, 10), nb(pixel, 12, 10), nb(pixel, 2, 10), 1]; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: return [nb(pixel, 0, 10), nb(pixel, 10, 10), nb(pixel, 20, 10), nb(pixel, 30, 2)]; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: return [ub(pixel, 0, 10), ub(pixel, 10, 10), ub(pixel, 20, 10), ub(pixel, 30, 2)]; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: return tcuTexture.unpackRGB999E5(pixel); + + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + // \note Stencil is always ignored. + case tcuTexture.ChannelOrder.D: return [nb(pixel, 8, 24), 0, 0, 1]; + case tcuTexture.ChannelOrder.DS: return [nb(pixel, 8, 24), 0, 0, 1 /* (float)ub(0, 8) */]; + default: + DE_ASSERT(false); + } + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.DS); + // \note Stencil is ignored. + return [pixel, 0, 0, 1]; + } + + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: { + return [f11(ub(pixel, 0, 11)), f11(ub(pixel, 11, 11)), f10(ub(pixel, 22, 10)), 1]; + } + + default: + break; + } + + // Generic path. + var result = [0, 0, 0, 0]; + var channelMap = tcuTexture.getChannelReadMap(this.m_format.order); + var channelSize = tcuTexture.getChannelSize(this.m_format.type); + + for (var c = 0; c < 4; c++) { + var map = channelMap[c]; + if (map == tcuTexture.channel.ZERO) + result[c] = 0; + else if (map == tcuTexture.channel.ONE) + result[c] = 1; + else + result[c] = tcuTexture.channelToFloat(pixelPtr[map + pixelPtrOffset], this.m_format.type); + } + + return result; + }; +})(); + +/** + * @param {number} x + * @param {number} y + * @param {number=} z + * @return {Array<number>} Pixel value as Vec4 + */ +tcuTexture.ConstPixelBufferAccess.prototype.getPixelInt = function(x, y, z) { + z = z || 0; + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + x = Math.round(x); + y = Math.round(y); + z = Math.round(z); + + // Quick paths + if (z == 0) { + if (this.m_rgba8View) + return this.m_rgba8View.read(x, y, 4); + else if (this.m_rgb8View) + return this.m_rgb8View.read(x, y, 3); + } + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + var pixel = pixelPtr[pixelPtrOffset]; + + var ub = function(pixel, offset, count) { + return (pixel >> offset) & ((1 << count) - 1); + }; + + // Packed formats. + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNORM_SHORT_565: return [ub(pixel, 11, 5), ub(pixel, 5, 6), ub(pixel, 0, 5), 1]; + case tcuTexture.ChannelType.UNORM_SHORT_555: return [ub(pixel, 10, 5), ub(pixel, 5, 5), ub(pixel, 0, 5), 1]; + case tcuTexture.ChannelType.UNORM_SHORT_4444: return [ub(pixel, 12, 4), ub(pixel, 8, 4), ub(pixel, 4, 4), ub(pixel, 0, 4)]; + case tcuTexture.ChannelType.UNORM_SHORT_5551: return [ub(pixel, 11, 5), ub(pixel, 6, 5), ub(pixel, 1, 5), ub(pixel, 0, 1)]; + case tcuTexture.ChannelType.UNORM_INT_101010: return [ub(pixel, 22, 10), ub(pixel, 12, 10), ub(pixel, 2, 10), 1]; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: return [ub(pixel, 0, 10), ub(pixel, 10, 10), ub(pixel, 20, 10), ub(pixel, 30, 2)]; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: return [ub(pixel, 0, 10), ub(pixel, 10, 10), ub(pixel, 20, 10), ub(pixel, 30, 2)]; + + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + case tcuTexture.ChannelOrder.D: return [ub(pixel, 8, 24), 0, 0, 1]; + case tcuTexture.ChannelOrder.S: return [0, 0, 0, ub(pixel, 8, 24)]; + case tcuTexture.ChannelOrder.DS: return [ub(pixel, 8, 24), 0, 0, ub(pixel, 0, 8)]; + default: + DE_ASSERT(false); + } + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.DS); + var u32array = new Uint32Array(this.m_data, this.m_offset + offset + 4, 1); + return [pixel, 0, 0, ub(u32array[0], 0, 8)]; + } + + default: + break; + } + + // Generic path. + var result = []; + result.length = 4; + var channelMap = tcuTexture.getChannelReadMap(this.m_format.order); + var channelSize = tcuTexture.getChannelSize(this.m_format.type); + + for (var c = 0; c < 4; c++) { + var map = channelMap[c]; + if (map == tcuTexture.channel.ZERO) + result[c] = 0; + else if (map == tcuTexture.channel.ONE) + result[c] = 1; + else + result[c] = tcuTexture.channelToInt(pixelPtr[map + pixelPtrOffset], this.m_format.type); + } + + return result; +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {?tcuTexture.FilterMode} filter + * @param {number} s + * @param {number} t + * @param {number} depth (integer) + * @return {Array<number>} Sample color + */ +tcuTexture.ConstPixelBufferAccess.prototype.sample2D = function(sampler, filter, s, t, depth) { + DE_ASSERT(deMath.deInBounds32(depth, 0, this.m_depth)); + + // Non-normalized coordinates. + var u = s; + var v = t; + + if (sampler.normalizedCoords) { + u = tcuTexture.unnormalize(sampler.wrapS, s, this.m_width); + v = tcuTexture.unnormalize(sampler.wrapT, t, this.m_height); + } + + switch (filter) { + case tcuTexture.FilterMode.NEAREST: return tcuTexture.sampleNearest2D(this, sampler, u, v, depth); + case tcuTexture.FilterMode.LINEAR: return tcuTexture.sampleLinear2D(this, sampler, u, v, depth); + default: + throw new Error('Invalid filter:' + filter); + } + throw new Error('Unimplemented'); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {?tcuTexture.FilterMode} filter + * @param {number} s + * @param {number} t + * @param {Array<number>} offset + * @return {Array<number>} Sample color + */ +tcuTexture.ConstPixelBufferAccess.prototype.sample2DOffset = function(sampler, filter, s, t, offset) { + DE_ASSERT(deMath.deInBounds32(offset[2], 0, this.m_depth)); + + // Non-normalized coordinates. + var u = s; + var v = t; + + if (sampler.normalizedCoords) { + u = tcuTexture.unnormalize(sampler.wrapS, s, this.m_width); + v = tcuTexture.unnormalize(sampler.wrapT, t, this.m_height); + } + + switch (filter) { + case tcuTexture.FilterMode.NEAREST: return tcuTexture.sampleNearest2D(this, sampler, u, v, offset); + case tcuTexture.FilterMode.LINEAR: return tcuTexture.sampleLinear2D(this, sampler, u, v, offset); + default: + throw new Error('Invalid filter:' + filter); + } +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {?tcuTexture.FilterMode} filter + * @param {number} s + * @param {number} t + * @param {number} r + * @param {Array<number>} offset + * @return {Array<number>} Sample color + */ +tcuTexture.ConstPixelBufferAccess.prototype.sample3DOffset = function(sampler, filter, s, t, r, offset) { + // Non-normalized coordinates. + /** @type {number} */ var u = s; + /** @type {number} */ var v = t; + /** @type {number} */ var w = r; + + if (sampler.normalizedCoords) { + u = tcuTexture.unnormalize(sampler.wrapS, s, this.m_width); + v = tcuTexture.unnormalize(sampler.wrapT, t, this.m_height); + w = tcuTexture.unnormalize(sampler.wrapR, r, this.m_depth); + } + + switch (filter) { + case tcuTexture.FilterMode.NEAREST: return tcuTexture.sampleNearest3D(this, sampler, u, v, w, offset); + case tcuTexture.FilterMode.LINEAR: return tcuTexture.sampleLinear3D(this, sampler, u, v, w, offset); + default: + throw new Error('Invalid filter:' + filter); + } +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filter + * @param {number} ref + * @param {number} s + * @param {number} t + * @param {Array<number>} offset + * @return {number} + */ +tcuTexture.ConstPixelBufferAccess.prototype.sample2DCompare = function(sampler, filter, ref, s, t, offset) { + DE_ASSERT(deMath.deInBounds32(offset[2], 0, this.m_depth)); + + // Format information for comparison function + var isFixedPointDepth = tcuTexture.isFixedPointDepthTextureFormat(this.m_format); + + // Non-normalized coordinates. + var u = s; + var v = t; + + if (sampler.normalizedCoords) { + u = tcuTexture.unnormalize(sampler.wrapS, s, this.m_width); + v = tcuTexture.unnormalize(sampler.wrapT, t, this.m_height); + } + + switch (filter) { + case tcuTexture.FilterMode.NEAREST: return tcuTexture.execCompare(tcuTexture.sampleNearest2D(this, sampler, u, v, offset), sampler.compare, sampler.compareChannel, ref, isFixedPointDepth); + case tcuTexture.FilterMode.LINEAR: return tcuTexture.sampleLinear2DCompare(this, sampler, ref, u, v, offset, isFixedPointDepth); + default: + DE_ASSERT(false); + return 0.0; + } +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {tcuTexture.FilterMode} filter + * @param {number} s + * @param {number} t + * @param {number} r + * @return {Array<number>} Sample color + */ +tcuTexture.ConstPixelBufferAccess.prototype.sample3D = function(sampler, filter, s, t, r) { + // Non-normalized coordinates. + var u = s; + var v = t; + var w = r; + + if (sampler.normalizedCoords) { + u = tcuTexture.unnormalize(sampler.wrapS, s, this.m_width); + v = tcuTexture.unnormalize(sampler.wrapT, t, this.m_height); + w = tcuTexture.unnormalize(sampler.wrapR, r, this.m_depth); + } + + switch (filter) { + case tcuTexture.FilterMode.NEAREST: return tcuTexture.sampleNearest3D(this, sampler, u, v, w); + case tcuTexture.FilterMode.LINEAR: return tcuTexture.sampleLinear3D(this, sampler, u, v, w); + default: + throw new Error('Invalid filter:' + filter); + } + throw new Error('Unimplemented'); +}; + + /* TODO: do we need any of these? */ { + // template<typename T> + // Vector<T, 4> getPixelT (int x, int y, int z = 0) const; + + // Vec4 sample3D (const tcuTexture.Sampler& sampler, tcuTexture.tcuTexture.Sampler.tcuTexture.FilterMode filter, float s, float t, float r) const; + + // Vec4 sample2DOffset (const tcuTexture.Sampler& sampler, tcuTexture.Sampler::tcuTexture.FilterMode filter, float s, float t, const IVec3& offset) const; + // Vec4 sample3DOffset (const tcuTexture.Sampler& sampler, tcuTexture.Sampler::tcuTexture.FilterMode filter, float s, float t, float r, const IVec3& offset) const; + + // float sample2DCompare (const tcuTexture.Sampler& sampler, tcuTexture.Sampler::tcuTexture.FilterMode filter, float ref, float s, float t, const IVec3& offset) const; + }; + +/** Common type limits + * + */ +tcuTexture.deTypes = { + deInt8: {min: -(1 << 7), max: (1 << 7) - 1}, + deInt16: {min: -(1 << 15), max: (1 << 15) - 1}, + deInt32: {min: -2147483648, max: 2147483647}, + deUint8: {min: 0, max: (1 << 8) - 1}, + deUint16: {min: 0, max: (1 << 16) - 1}, + deUint32: {min: 0, max: 4294967295} +}; + +/** + * Round to even and saturate + * @param {{max: number, min: number}} deType from tcuTexture.deTypes + * @param {number} value + * @return {number} + */ +tcuTexture.convertSatRte = function(deType, value) { + var minVal = deType.min; + var maxVal = deType.max; + var floor = Math.floor(value); + var frac = value - floor; + if (frac == 0.5) { + if (floor % 2 != 0) + floor += 1; + } else if (frac > 0.5) { + floor += 1; + } + + return Math.max(minVal, Math.min(maxVal, floor)); +}; + +/** + * Saturate value to type range + * @param { {max: number, min: number}} deType from tcuTexture.deTypes + * @param {number} src + * @return {number} + */ +tcuTexture.convertSat = function(deType, src) { + var minVal = deType.min; + var maxVal = deType.max; + if (src < minVal) + return minVal; + else if (src > maxVal) + return maxVal; + else + return src; +}; + +/** + * @param {number} src Input integer value + * @param {tcuTexture.ChannelType} type + * @return {number} + */ +tcuTexture.intToChannel = function(src, type) { + var dst; + switch (type) { + case tcuTexture.ChannelType.SNORM_INT8: dst = tcuTexture.convertSat(tcuTexture.deTypes.deInt8, src); break; + case tcuTexture.ChannelType.SNORM_INT16: dst = tcuTexture.convertSat(tcuTexture.deTypes.deInt16, src); break; + case tcuTexture.ChannelType.UNORM_INT8: dst = tcuTexture.convertSat(tcuTexture.deTypes.deUint8, src); break; + case tcuTexture.ChannelType.UNORM_INT16: dst = tcuTexture.convertSat(tcuTexture.deTypes.deUint16, src); break; + case tcuTexture.ChannelType.SIGNED_INT8: dst = tcuTexture.convertSat(tcuTexture.deTypes.deInt8, src); break; + case tcuTexture.ChannelType.SIGNED_INT16: dst = tcuTexture.convertSat(tcuTexture.deTypes.deInt16, src); break; + case tcuTexture.ChannelType.SIGNED_INT32: dst = tcuTexture.convertSat(tcuTexture.deTypes.deInt32, src); break; + case tcuTexture.ChannelType.UNSIGNED_INT8: dst = tcuTexture.convertSat(tcuTexture.deTypes.deUint8, src); break; + case tcuTexture.ChannelType.UNSIGNED_INT16: dst = tcuTexture.convertSat(tcuTexture.deTypes.deUint16, src); break; + case tcuTexture.ChannelType.UNSIGNED_INT32: dst = tcuTexture.convertSat(tcuTexture.deTypes.deUint32, src); break; + case tcuTexture.ChannelType.HALF_FLOAT: dst = tcuFloat.numberToHalfFloat(src); break; + case tcuTexture.ChannelType.FLOAT: dst = src; break; + default: + throw new Error('Unrecognized tcuTexture.channel type: ' + type); + } + return dst; +}; + +/** + * @param {number} src + * @param {number} bits + * @return {number} + */ +tcuTexture.normFloatToChannel = function(src, bits) { + var maxVal = (1 << bits) - 1; + var intVal = tcuTexture.convertSatRte(tcuTexture.deTypes.deUint32, src * maxVal); + return Math.min(maxVal, intVal); +}; + +/** + * @param {number} src + * @param {number} bits + * @return {number} + */ +tcuTexture.uintToChannel = function(src, bits) { + var maxVal = (1 << bits) - 1; + return Math.min(maxVal, src); +}; + +/** + * @param {number} src + * @param {tcuTexture.ChannelType} type + * @return {number} Converted src color value + */ +tcuTexture.floatToChannel = function(src, type) { + switch (type) { + case tcuTexture.ChannelType.SNORM_INT8: return tcuTexture.convertSatRte(tcuTexture.deTypes.deInt8, src * 127); + case tcuTexture.ChannelType.SNORM_INT16: return tcuTexture.convertSatRte(tcuTexture.deTypes.deInt16, src * 32767); + case tcuTexture.ChannelType.SNORM_INT32: return tcuTexture.convertSatRte(tcuTexture.deTypes.deInt32, src * 2147483647); + case tcuTexture.ChannelType.UNORM_INT8: return tcuTexture.convertSatRte(tcuTexture.deTypes.deUint8, src * 255); + case tcuTexture.ChannelType.UNORM_INT16: return tcuTexture.convertSatRte(tcuTexture.deTypes.deUint16, src * 65535); + case tcuTexture.ChannelType.UNORM_INT32: return tcuTexture.convertSatRte(tcuTexture.deTypes.deUint32, src * 4294967295); + case tcuTexture.ChannelType.SIGNED_INT8: return tcuTexture.convertSatRte(tcuTexture.deTypes.deInt8, src); + case tcuTexture.ChannelType.SIGNED_INT16: return tcuTexture.convertSatRte(tcuTexture.deTypes.deInt16, src); + case tcuTexture.ChannelType.SIGNED_INT32: return tcuTexture.convertSatRte(tcuTexture.deTypes.deInt32, src); + case tcuTexture.ChannelType.UNSIGNED_INT8: return tcuTexture.convertSatRte(tcuTexture.deTypes.deUint8, src); + case tcuTexture.ChannelType.UNSIGNED_INT16: return tcuTexture.convertSatRte(tcuTexture.deTypes.deUint16, src); + case tcuTexture.ChannelType.UNSIGNED_INT32: return tcuTexture.convertSatRte(tcuTexture.deTypes.deUint32, src); + case tcuTexture.ChannelType.HALF_FLOAT: return tcuFloat.numberToHalfFloat(src); + case tcuTexture.ChannelType.FLOAT: return src; + } + throw new Error('Unrecognized type ' + type); +}; + +/** + * \brief Read-write pixel data access + * + * This class extends read-only access object by providing write functionality. + * + * \note tcuTexture.PixelBufferAccess may not have any data members nor add any + * virtual functions. It must be possible to reinterpret_cast<> + * tcuTexture.PixelBufferAccess to tcuTexture.ConstPixelBufferAccess. + * @constructor + * @extends {tcuTexture.ConstPixelBufferAccess} + * + */ +tcuTexture.PixelBufferAccess = function(descriptor) { + tcuTexture.ConstPixelBufferAccess.call(this, descriptor); +}; + +tcuTexture.PixelBufferAccess.prototype = Object.create(tcuTexture.ConstPixelBufferAccess.prototype); +tcuTexture.PixelBufferAccess.prototype.constructor = tcuTexture.PixelBufferAccess; + +/** + * @param {Array<number>} color Vec4 color to set + * @param {number} x + * @param {number} y + * @param {number=} z + */ +tcuTexture.PixelBufferAccess.prototype.setPixel = function(color, x, y, z) { + z = z || 0; + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + this._setPixelInternal(color, Math.round(x), Math.round(y), Math.round(z)); +}; + +// NOTE: setPixel has been broken into setPixel, _setPixelInternal, and _setPixelPacked +// because having them combined previously was causing V8 depots +tcuTexture.PixelBufferAccess.prototype._setPixelInternal = function(color, x, y, z) { + // Quick paths + if (z == 0) { + if (this.m_rgba8View) { + color = deMath.toIVec(color); + this.m_rgba8View.write(x, y, color, 4); + return; + } else if (this.m_rgb8View) { + color = deMath.toIVec(color); + this.m_rgb8View.write(x, y, color, 3); + return; + } + } + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + + return this._setPixelPacked(color, pixelPtr, pixelPtrOffset); +}; + +tcuTexture.PixelBufferAccess.prototype._setPixelPacked = (function () { + var pn = function(val, offs, bits) { + return tcuTexture.normFloatToChannel(val, bits) << offs; + }; + + var pu = function(val, offs, bits) { + return tcuTexture.uintToChannel(val, bits) << offs; + }; + + return function tcuTexture_PixelBufferAccess_setPixelPacked(color, pixelPtr, pixelPtrOffset) { + // Packed formats. + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNORM_SHORT_565: pixelPtr[pixelPtrOffset] = pn(color[0], 11, 5) | pn(color[1], 5, 6) | pn(color[2], 0, 5); break; + case tcuTexture.ChannelType.UNORM_SHORT_555: pixelPtr[pixelPtrOffset] = pn(color[0], 10, 5) | pn(color[1], 5, 5) | pn(color[2], 0, 5); break; + case tcuTexture.ChannelType.UNORM_SHORT_4444: pixelPtr[pixelPtrOffset] = pn(color[0], 12, 4) | pn(color[1], 8, 4) | pn(color[2], 4, 4) | pn(color[3], 0, 4); break; + case tcuTexture.ChannelType.UNORM_SHORT_5551: pixelPtr[pixelPtrOffset] = pn(color[0], 11, 5) | pn(color[1], 6, 5) | pn(color[2], 1, 5) | pn(color[3], 0, 1); break; + case tcuTexture.ChannelType.UNORM_INT_101010: pixelPtr[pixelPtrOffset] = pn(color[0], 22, 10) | pn(color[1], 12, 10) | pn(color[2], 2, 10); break; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: pixelPtr[pixelPtrOffset] = pn(color[0], 0, 10) | pn(color[1], 10, 10) | pn(color[2], 20, 10) | pn(color[3], 30, 2); break; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: pixelPtr[pixelPtrOffset] = pu(color[0], 0, 10) | pu(color[1], 10, 10) | pu(color[2], 20, 10) | pu(color[3], 30, 2); break; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: pixelPtr[pixelPtrOffset] = tcuTexture.packRGB999E5(color); break; + + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + // \note Stencil is always ignored. + case tcuTexture.ChannelOrder.D: pixelPtr[pixelPtrOffset] = pn(color[0], 8, 24); break; + case tcuTexture.ChannelOrder.S: pixelPtr[pixelPtrOffset] = pn(color[3], 8, 24); break; + case tcuTexture.ChannelOrder.DS: pixelPtr[pixelPtrOffset] = pn(color[0], 8, 24) | pu(color[3], 0, 8); break; + default: + throw new Error('Unsupported tcuTexture.channel order ' + this.m_format.order); + } + break; + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: { + pixelPtr[pixelPtrOffset] = color[0]; + var u32array = new Uint32Array(this.m_data, (pixelPtrOffset * pixelPtr.BYTES_PER_ELEMENT) + this.m_offset + 4, 1); + u32array[0] = pu(color[3], 0, 8); + break; + } + + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: { + var f11 = function(value) { + return tcuFloat.numberToFloat11(value); + }; + var f10 = function(value) { + return tcuFloat.numberToFloat10(value); + }; + + pixelPtr[pixelPtrOffset] = f11(color[0]) | (f11(color[1]) << 11) | (f10(color[2]) << 22); + break; + } + case tcuTexture.ChannelType.FLOAT: + if (this.m_format.order == tcuTexture.ChannelOrder.D) { + pixelPtr[pixelPtrOffset] = color[0]; + break; + } + // else fall-through to default case! + + default: { + // Generic path. + var numChannels = tcuTexture.getNumUsedChannels(this.m_format.order); + var map = tcuTexture.getChannelWriteMap(this.m_format.order); + + for (var c = 0; c < numChannels; c++) + pixelPtr[c + pixelPtrOffset] = tcuTexture.floatToChannel(color[map[c]], this.m_format.type); + } + } + }; +})(); + +/** + * @param {Array<number>} color Vec4 color to set (unnormalized) + * @param {number} x + * @param {number} y + * @param {number=} z + */ +tcuTexture.PixelBufferAccess.prototype.setPixelInt = function(color, x, y, z) { + z = z || 0; + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + x = Math.round(x); + y = Math.round(y); + z = Math.round(z); + + // Quick paths + if (z == 0) { + if (this.m_rgba8View) { + this.m_rgba8View.write(x, y, color, 4); + return; + } else if (this.m_rgb8View) { + this.m_rgb8View.write(x, y, color, 3); + return; + } + } + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + + var pu = function(val, offs, bits) { + return tcuTexture.uintToChannel(val, bits) << offs; + }; + + // Packed formats. + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNORM_SHORT_565: pixelPtr[pixelPtrOffset] = pu(color[0], 11, 5) | pu(color[1], 5, 6) | pu(color[2], 0, 5); break; + case tcuTexture.ChannelType.UNORM_SHORT_555: pixelPtr[pixelPtrOffset] = pu(color[0], 10, 5) | pu(color[1], 5, 5) | pu(color[2], 0, 5); break; + case tcuTexture.ChannelType.UNORM_SHORT_4444: pixelPtr[pixelPtrOffset] = pu(color[0], 12, 4) | pu(color[1], 8, 4) | pu(color[2], 4, 4) | pu(color[3], 0, 4); break; + case tcuTexture.ChannelType.UNORM_SHORT_5551: pixelPtr[pixelPtrOffset] = pu(color[0], 11, 5) | pu(color[1], 6, 5) | pu(color[2], 1, 5) | pu(color[3], 0, 1); break; + case tcuTexture.ChannelType.UNORM_INT_101010: pixelPtr[pixelPtrOffset] = pu(color[0], 22, 10) | pu(color[1], 12, 10) | pu(color[2], 2, 10); break; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: pixelPtr[pixelPtrOffset] = pu(color[0], 0, 10) | pu(color[1], 10, 10) | pu(color[2], 20, 10) | pu(color[3], 30, 2); break; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: pixelPtr[pixelPtrOffset] = pu(color[0], 0, 10) | pu(color[1], 10, 10) | pu(color[2], 20, 10) | pu(color[3], 30, 2); break; + + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + // \note Stencil is always ignored. + case tcuTexture.ChannelOrder.D: pixelPtr[pixelPtrOffset] = pu(color[0], 8, 24); break; + case tcuTexture.ChannelOrder.S: pixelPtr[pixelPtrOffset] = pu(color[3], 8, 24); break; + case tcuTexture.ChannelOrder.DS: pixelPtr[pixelPtrOffset] = pu(color[0], 8, 24) | pu(color[3], 0, 8); break; + default: + throw new Error('Unsupported tcuTexture.channel order ' + this.m_format.order); + } + break; + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: { + pixelPtr[pixelPtrOffset] = color[0]; + var u32array = new Uint32Array(this.m_data, offset + this.m_offset + 4, 1); + u32array[pixelPtrOffset] = pu(color[3], 0, 8); + break; + } + + default: { + // Generic path. + var numChannels = tcuTexture.getNumUsedChannels(this.m_format.order); + var map = tcuTexture.getChannelWriteMap(this.m_format.order); + + for (var c = 0; c < numChannels; c++) + pixelPtr[c + pixelPtrOffset] = tcuTexture.intToChannel(color[map[c]], this.m_format.type); + } + } +}; + +/** + * @param {Array<number>=} color Vec4 color to set, optional. + * @param {Array<number>=} x Range in x axis, optional. + * @param {Array<number>=} y Range in y axis, optional. + * @param {Array<number>=} z Range in z axis, optional. + */ +tcuTexture.PixelBufferAccess.prototype.clear = function(color, x, y, z) { + var c = color || [0, 0, 0, 0]; + var arrayType = tcuTexture.getTypedArray(this.m_format.type); + var range_x = x || [0, this.m_width]; + var range_y = y || [0, this.m_height]; + var range_z = z || [0, this.m_depth]; + var pixelSize = this.m_format.getPixelSize(); + var numElements = pixelSize / arrayType.BYTES_PER_ELEMENT; + var width = range_x[1] - range_x[0]; + var height = range_y[1] - range_y[0]; + var depth = range_z[1] - range_z[0]; + if (x === undefined && y === undefined && z === undefined && + c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0) { + var pixelPtr = new arrayType(this.m_data, this.m_offset); + pixelPtr.fill(0); + return; + } + + //copy first pixel over other pixels in the row + var fillRow = function(pixelPtr, numElements, width) { + for (var i = 1; i < width; i++) + for (var c = 0; c < numElements; c++) + pixelPtr[i * numElements + c] = pixelPtr[c]; + }; + // copy first row to other rows in all planes + var fillPlanes = function(buffer, arrayType, src, offset, rowStride, planeStride, width, height, depth) { + for (var j = 0; j < depth; j++) + for (var i = (j == 0 ? 1 : 0); i < height; i++) { + var dst = new arrayType(buffer, offset + i * rowStride + j * planeStride, width); + dst.set(src); + } + }; + + this.setPixel(c, range_x[0], range_y[0], range_z[0]); + + var offset = range_z[0] * this.m_slicePitch + range_y[0] * this.m_rowPitch + range_x[0] * pixelSize; + var pixelPtr = new arrayType(this.m_data, offset + this.m_offset, width * numElements); + + fillRow(pixelPtr, numElements, width); + fillPlanes(this.m_data, arrayType, pixelPtr, offset + this.m_offset, this.m_rowPitch, this.m_slicePitch, width * numElements, height, depth); +}; + +/** + * @param {number} depth to set + * @param {number} x + * @param {number} y + * @param {number=} z + */ +tcuTexture.PixelBufferAccess.prototype.setPixDepth = function(depth, x, y, z) { + if (z == null) + z = 0; + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + x = Math.round(x); + y = Math.round(y); + z = Math.round(z); + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + + var pn = function(val, offs, bits) { + return tcuTexture.normFloatToChannel(val, bits) << offs; + }; + + // Packed formats. + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + case tcuTexture.ChannelOrder.D: pixelPtr[pixelPtrOffset] = pn(depth, 8, 24); break; + case tcuTexture.ChannelOrder.DS: pixelPtr[pixelPtrOffset] = pn(depth, 8, 24) | (pixelPtr[pixelPtrOffset] & 0xFF); break; + default: + throw new Error('Unsupported tcuTexture.channel order ' + this.m_format.order); + } + break; + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.DS); + pixelPtr[pixelPtrOffset] = depth; + break; + } + + default: { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.D || this.m_format.order == tcuTexture.ChannelOrder.DS); + pixelPtr[pixelPtrOffset] = tcuTexture.floatToChannel(depth, this.m_format.type); + } + } +}; + +/** + * @param {number} stencil to set + * @param {number} x + * @param {number} y + * @param {number=} z + */ +tcuTexture.PixelBufferAccess.prototype.setPixStencil = function(stencil, x, y, z) { + if (z == null) + z = 0; + DE_ASSERT(deMath.deInBounds32(x, 0, this.m_width)); + DE_ASSERT(deMath.deInBounds32(y, 0, this.m_height)); + DE_ASSERT(deMath.deInBounds32(z, 0, this.m_depth)); + + // Make sure that the position is 'integer' + x = Math.round(x); + y = Math.round(y); + z = Math.round(z); + + var pixelSize = this.m_format.getPixelSize(); + var offset = z * this.m_slicePitch + y * this.m_rowPitch + x * pixelSize; + var pixelPtr = this.getDataPtr(); + var pixelPtrOffset = offset / pixelPtr.BYTES_PER_ELEMENT; + + var pu = function(val, offs, bits) { + return tcuTexture.uintToChannel(val, bits) << offs; + }; + + // Packed formats. + switch (this.m_format.type) { + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + switch (this.m_format.order) { + case tcuTexture.ChannelOrder.S: pixelPtr[pixelPtrOffset] = pu(stencil, 8, 24); break; + case tcuTexture.ChannelOrder.DS: pixelPtr[pixelPtrOffset] = pu(stencil, 0, 8) | (pixelPtr[pixelPtrOffset] & 0xFFFFFF00); break; + default: + throw new Error('Unsupported tcuTexture.channel order ' + this.m_format.order); + } + break; + + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: { + var u32array = new Uint32Array(this.m_data, this.m_offset + offset + 4, 1); + u32array[0] = pu(stencil, 0, 8); + break; + } + + default: { + if (this.m_format.order == tcuTexture.ChannelOrder.S) + pixelPtr[pixelPtrOffset] = tcuTexture.floatToChannel(stencil, this.m_format.type); + else { + DE_ASSERT(this.m_format.order == tcuTexture.ChannelOrder.DS); + pixelPtr[3 + pixelPtrOffset] = tcuTexture.floatToChannel(stencil, this.m_format.type); + } + } + } +}; + +/** + * newFromTextureLevel + * @param {tcuTexture.TextureLevel} level + * @return {tcuTexture.PixelBufferAccess} + */ +tcuTexture.PixelBufferAccess.newFromTextureLevel = function(level) { + var descriptor = new Object(); + descriptor.format = level.getFormat(); + descriptor.width = level.getWidth(); + descriptor.height = level.getHeight(); + descriptor.depth = level.m_depth; + descriptor.data = level.m_data.m_ptr; + + return new tcuTexture.PixelBufferAccess(descriptor); +}; + +/** + * newFromTextureFormat + * @param {tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + * @param {number} depth + * @param {number} rowPitch + * @param {number} slicePitch + * @param {ArrayBuffer} data + */ +tcuTexture.PixelBufferAccess.newFromTextureFormat = function(format, width, height, depth, rowPitch, slicePitch, data) { + var descriptor = new Object(); + descriptor.format = format; + descriptor.width = width; + descriptor.height = height; + descriptor.depth = depth; + descriptor.rowPitch = rowPitch; + descriptor.slicePitch = slicePitch; + descriptor.data = data; + + return new tcuTexture.PixelBufferAccess(descriptor); +}; + +/* TODO: Port */ +// { +// public: +// tcuTexture.PixelBufferAccess (void) {} +// tcuTexture.PixelBufferAccess (const tcuTexture.TextureFormat& format, int width, int height, int depth, void* data); + +// void* getDataPtr (void) const { return m_data; } + +// void setPixels (const void* buf, int bufSize) const; +// void setPixel (const tcu::Vec4& color, int x, int y, int z = 0) const; +// void setPixel (const tcu::IVec4& color, int x, int y, int z = 0) const; +// void setPixel (const tcu::UVec4& color, int x, int y, int z = 0) const { setPixel(color.cast<int>(), x, y, z); } + +// void setPixDepth (float depth, int x, int y, int z = 0) const; +// void setPixStencil (int stencil, int x, int y, int z = 0) const; +// }; + +/** + * @constructor + * @param {tcuTexture.TextureFormat} format + * @param {number} numLevels + */ +tcuTexture.TextureLevelPyramid = function(format, numLevels) { + /* tcuTexture.TextureFormat */this.m_format = format; + /* LevelData */ this.m_data = []; + for (var i = 0; i < numLevels; i++) + this.m_data.push(new tcuTexture.DeqpArrayBuffer()); + /* {Array<tcuTexture.PixelBufferAccess>} */ this.m_access = []; + this.m_access.length = numLevels; +}; + +/** @return {boolean} */ +tcuTexture.TextureLevelPyramid.prototype.isLevelEmpty = function(levelNdx) { return this.m_data[levelNdx].empty(); }; +/** @return {tcuTexture.TextureFormat} */ +tcuTexture.TextureLevelPyramid.prototype.getFormat = function() { return this.m_format; }; +/** @return {number} */ +tcuTexture.TextureLevelPyramid.prototype.getNumLevels = function() { return this.m_access.length; }; +/** @return {tcuTexture.PixelBufferAccess} */ +tcuTexture.TextureLevelPyramid.prototype.getLevel = function(ndx) { return this.m_access[ndx]; }; +/** @return {Array<tcuTexture.PixelBufferAccess>} */ +tcuTexture.TextureLevelPyramid.prototype.getLevels = function() { return this.m_access; }; + +/** + * @param {number} levelNdx + * @param {number} width + * @param {number} height + * @param {number} depth + */ +tcuTexture.TextureLevelPyramid.prototype.allocLevel = function(levelNdx, width, height, depth) { + var size = this.m_format.getPixelSize() * width * height * depth; + + DE_ASSERT(this.isLevelEmpty(levelNdx)); + + this.m_data[levelNdx].setStorage(size); + this.m_access[levelNdx] = new tcuTexture.PixelBufferAccess({ + format: this.m_format, + width: width, + height: height, + depth: depth, + data: this.m_data[levelNdx].m_ptr + }); +}; + +tcuTexture.TextureLevelPyramid.prototype.clearLevel = function(levelNdx) { + /* TODO: Implement */ + throw new Error('Not implemented'); +}; + +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} levels + * @param {number} numLevels + * @param {tcuTexture.Sampler} sampler + * @param {number} s + * @param {number} t + * @param {number} depth (integer) + * @param {number=} lod + * @return {Array<number>} Vec4 pixel color + */ +tcuTexture.sampleLevelArray2D = function(levels, numLevels, sampler, s, t, depth, lod) { + // z-offset in 2D textures is layer selector + return tcuTexture.sampleLevelArray2DOffset(levels, numLevels, sampler, [s, t], lod, [0, 0, depth]); +}; + +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} levels + * @param {number} numLevels + * @param {tcuTexture.Sampler} sampler + * @param {number} s + * @param {number} t + * @param {number} r + * @param {number} lod + * @return {Array<number>} Vec4 pixel color + */ +tcuTexture.sampleLevelArray3D = function(levels, numLevels, sampler, s, t, r, lod) { + return tcuTexture.sampleLevelArray3DOffset(levels, numLevels, sampler, s, t, r, lod, [0, 0, 0]); +}; + +/** + * @constructor + * @param {tcuTexture.CubeFace} face + * @param {Array<number>} coords + */ +tcuTexture.CubeFaceCoords = function(face, coords) { + this.face = face; + this.s = coords[0]; + this.t = coords[1]; +}; + +/** + * \brief 2D Texture View + * @constructor + * @param {number} numLevels + * @param {?Array<tcuTexture.ConstPixelBufferAccess>} levels + */ +tcuTexture.Texture2DView = function(numLevels, levels) { + this.m_numLevels = numLevels; + this.m_levels = levels; +}; + +/** @return {number} */ +tcuTexture.Texture2DView.prototype.getNumLevels = function() { return this.m_numLevels; }; +/** @return {number} */ +tcuTexture.Texture2DView.prototype.getWidth = function() { return this.m_numLevels > 0 ? this.m_levels[0].getWidth() : 0; }; +/** @return {number} */ +tcuTexture.Texture2DView.prototype.getHeight = function() { return this.m_numLevels > 0 ? this.m_levels[0].getHeight() : 0; }; +/** + * @param {number} ndx + * @return {tcuTexture.ConstPixelBufferAccess} + */ +tcuTexture.Texture2DView.prototype.getLevel = function(ndx) { DE_ASSERT(deMath.deInBounds32(ndx, 0, this.m_numLevels)); return this.m_levels[ndx]; }; +/** @return {Array<tcuTexture.ConstPixelBufferAccess>} */ +tcuTexture.Texture2DView.prototype.getLevels = function() { return this.m_levels; }; + +/** + * @param {number} baseLevel + * @param {number} maxLevel + * return {tcuTexture.Texture2DView} + */ +tcuTexture.Texture2DView.prototype.getSubView = function(baseLevel, maxLevel) { + var clampedBase = deMath.clamp(baseLevel, 0, this.m_numLevels - 1); + var clampedMax = deMath.clamp(maxLevel, clampedBase, this.m_numLevels - 1); + var numLevels = clampedMax - clampedBase + 1; + return new tcuTexture.Texture2DView(numLevels, this.m_levels.slice(clampedBase, numLevels)); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number=} lod + * @return {Array<number>} Pixel color + */ +tcuTexture.Texture2DView.prototype.sample = function(sampler, texCoord, lod) { + return tcuTexture.sampleLevelArray2D(this.m_levels, this.m_numLevels, sampler, texCoord[0], texCoord[1], 0 /* depth */, lod); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number} lod + * @param {Array<number>} offset + * @return {Array<number>} Pixel color + */ +tcuTexture.Texture2DView.prototype.sampleOffset = function(sampler, texCoord, lod, offset) { + return tcuTexture.sampleLevelArray2DOffset(this.m_levels, this.m_numLevels, sampler, texCoord, lod, [offset[0], offset[1], 0]); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {Array<number>} texCoord + * @param {number} lod + * @return {number} + */ +tcuTexture.Texture2DView.prototype.sampleCompare = function(sampler, ref, texCoord, lod) { + return tcuTexture.sampleLevelArray2DCompare(this.m_levels, this.m_numLevels, sampler, ref, texCoord[0], texCoord[1], lod, [0, 0, 0]); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {Array<number>} texCoord + * @param {number} lod + * @param {Array<number>} offset + * @return {number} + */ +tcuTexture.Texture2DView.prototype.sampleCompareOffset = function(sampler, ref, texCoord, lod, offset) { + return tcuTexture.sampleLevelArray2DCompare(this.m_levels, this.m_numLevels, sampler, ref, texCoord[0], texCoord[1], lod, [offset[0], offset[1], 0]); +}; + + /* TODO: Port + Vec4 sample (const tcuTexture.Sampler& sampler, float s, float t, float lod) const; + Vec4 sampleOffset (const tcuTexture.Sampler& sampler, float s, float t, float lod, const IVec2& offset) const; + float sampleCompare (const tcuTexture.Sampler& sampler, float ref, float s, float t, float lod) const; + float sampleCompareOffset (const tcuTexture.Sampler& sampler, float ref, float s, float t, float lod, const IVec2& offset) const; + + Vec4 gatherOffsets (const tcuTexture.Sampler& sampler, float s, float t, int componentNdx, const IVec2 (&offsets)[4]) const; + Vec4 gatherOffsetsCompare(const tcuTexture.Sampler& sampler, float ref, float s, float t, const IVec2 (&offsets)[4]) const; + */ + +/** + * @constructor + * @param {number} numLevels + * @param {Array<tcuTexture.ConstPixelBufferAccess>} levels + */ +tcuTexture.Texture2DArrayView = function(numLevels, levels) { + this.m_numLevels = numLevels; + this.m_levels = levels; +}; + +/** @return {number} */ +tcuTexture.Texture2DArrayView.prototype.getNumLevels = function() { return this.m_numLevels; }; +/** @return {number} */ +tcuTexture.Texture2DArrayView.prototype.getWidth = function() { return this.m_numLevels > 0 ? this.m_levels[0].getWidth() : 0; }; +/** @return {number} */ +tcuTexture.Texture2DArrayView.prototype.getHeight = function() { return this.m_numLevels > 0 ? this.m_levels[0].getHeight() : 0; }; +/** @return {number} */ +tcuTexture.Texture2DArrayView.prototype.getNumLayers = function() { return this.m_numLevels > 0 ? this.m_levels[0].getDepth() : 0; }; +/** + * @param {number} ndx + * @return {tcuTexture.ConstPixelBufferAccess} + */ +tcuTexture.Texture2DArrayView.prototype.getLevel = function(ndx) { DE_ASSERT(deMath.deInBounds32(ndx, 0, this.m_numLevels)); return this.m_levels[ndx]; }; +/** @return {Array<tcuTexture.ConstPixelBufferAccess>} */ +tcuTexture.Texture2DArrayView.prototype.getLevels = function() { return this.m_levels; }; + +/** + * @param {number} r + * @return {number} layer corresponding to requested sampling 'r' coordinate + */ +tcuTexture.Texture2DArrayView.prototype.selectLayer = function(r) { + DE_ASSERT(this.m_numLevels > 0 && this.m_levels); + return deMath.clamp(Math.round(r), 0, this.m_levels[0].getDepth() - 1); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number=} lod + * @return {Array<number>} Pixel color + */ +tcuTexture.Texture2DArrayView.prototype.sample = function(sampler, texCoord, lod) { + lod = lod || 0; + return tcuTexture.sampleLevelArray2D(this.m_levels, this.m_numLevels, sampler, texCoord[0], texCoord[1], this.selectLayer(texCoord[2]), lod); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number} lod + * @param {Array<number>} offset + * @return {Array<number>} + */ +tcuTexture.Texture2DArrayView.prototype.sampleOffset = function(sampler, texCoord, lod, offset) { + return tcuTexture.sampleLevelArray2DOffset(this.m_levels, this.m_numLevels, sampler, texCoord, lod, [offset[0], offset[1], this.selectLayer(texCoord[2])]); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {Array<number>} texCoord + * @param {number} lod + * @param {Array<number>} offset + * @return {number} + */ +tcuTexture.Texture2DArrayView.prototype.sampleCompareOffset = function(sampler, ref, texCoord, lod, offset) { + return tcuTexture.sampleLevelArray2DCompare(this.m_levels, this.m_numLevels, sampler, ref, texCoord[0], texCoord[1], lod, [offset[0], offset[1], this.selectLayer(texCoord[2])]); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {Array<number>} texCoord + * @param {number} lod + * @return {number} + */ +tcuTexture.Texture2DArrayView.prototype.sampleCompare = function(sampler, ref, texCoord, lod) { + return tcuTexture.sampleLevelArray2DCompare(this.m_levels, this.m_numLevels, sampler, ref, texCoord[0], texCoord[1], lod, [0, 0, this.selectLayer(texCoord[2])]); +}; + +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} levels + * @param {number} numLevels + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number} lod + * @param {Array<number>} offset + * @return {Array<number>} + */ +tcuTexture.sampleLevelArray2DOffset = function(levels, numLevels, sampler, texCoord, lod, offset) { + /** @type {boolean} */ var magnified = lod <= sampler.lodThreshold; + /** @type {tcuTexture.FilterMode} */ var filterMode = magnified ? sampler.magFilter : sampler.minFilter; + /** @type {number} */ var maxLevel; + /** @type {tcuTexture.FilterMode} */ var levelFilter; + switch (filterMode) { + case tcuTexture.FilterMode.NEAREST: return levels[0].sample2DOffset(sampler, filterMode, texCoord[0], texCoord[1], offset); + case tcuTexture.FilterMode.LINEAR: return levels[0].sample2DOffset(sampler, filterMode, texCoord[0], texCoord[1], offset); + + case tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST: + case tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST: + maxLevel = numLevels - 1; + /** @type {number} */ var level = deMath.clamp(Math.ceil(lod + 0.5) - 1, 0, maxLevel); + levelFilter = (filterMode === tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + + return levels[level].sample2DOffset(sampler, levelFilter, texCoord[0], texCoord[1], offset); + + case tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR: + case tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR: + maxLevel = numLevels - 1; + /** @type {number} */ var level0 = deMath.clamp(Math.floor(lod), 0, maxLevel); + /** @type {number} */ var level1 = Math.min(maxLevel, level0 + 1); + levelFilter = (filterMode === tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + /** @type {number} */ var f = deMath.deFloatFrac(lod); + /** @type {Array<number>} */ var t0 = levels[level0].sample2DOffset(sampler, levelFilter, texCoord[0], texCoord[1], offset); + /** @type {Array<number>} */ var t1 = levels[level1].sample2DOffset(sampler, levelFilter, texCoord[0], texCoord[1], offset); + + return deMath.add(deMath.scale(t0, (1.0 - f)), deMath.scale(t1, f)); + + default: + return [0.0, 0.0, 0.0, 0.0]; + } +}; + +/** + * @constructor + * @param {number} numLevels + * @param {Array<tcuTexture.ConstPixelBufferAccess>} levels + */ +tcuTexture.Texture3DView = function(numLevels, levels) { + this.m_numLevels = numLevels; + this.m_levels = levels; +}; + +/** @return {number} */ +tcuTexture.Texture3DView.prototype.getNumLevels = function() { return this.m_numLevels; }; +/** @return {number} */ +tcuTexture.Texture3DView.prototype.getWidth = function() { return this.m_numLevels > 0 ? this.m_levels[0].getWidth() : 0; }; +/** @return {number} */ +tcuTexture.Texture3DView.prototype.getHeight = function() { return this.m_numLevels > 0 ? this.m_levels[0].getHeight() : 0; }; +/** @return {number} */ +tcuTexture.Texture3DView.prototype.getDepth = function() { return this.m_numLevels > 0 ? this.m_levels[0].getDepth() : 0; }; +/** + * @param {number} ndx + * @return {tcuTexture.ConstPixelBufferAccess} + */ +tcuTexture.Texture3DView.prototype.getLevel = function(ndx) { DE_ASSERT(deMath.deInBounds32(ndx, 0, this.m_numLevels)); return this.m_levels[ndx]; }; +/** @return {Array<tcuTexture.ConstPixelBufferAccess>} */ +tcuTexture.Texture3DView.prototype.getLevels = function() { return this.m_levels; }; + +/** + * @param {number} baseLevel + * @param {number} maxLevel + * return {tcuTexture.Texture3DView} + */ +tcuTexture.Texture3DView.prototype.getSubView = function(baseLevel, maxLevel) { + var clampedBase = deMath.clamp(baseLevel, 0, this.m_numLevels - 1); + var clampedMax = deMath.clamp(maxLevel, clampedBase, this.m_numLevels - 1); + var numLevels = clampedMax - clampedBase + 1; + return new tcuTexture.Texture3DView(numLevels, this.m_levels.slice(clampedBase, numLevels)); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number=} lod + * @return {Array<number>} Pixel color + */ +tcuTexture.Texture3DView.prototype.sample = function(sampler, texCoord, lod) { + lod = lod || 0; + return tcuTexture.sampleLevelArray3D(this.m_levels, this.m_numLevels, sampler, texCoord[0], texCoord[1], texCoord[2], lod); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {Array<number>} texCoord + * @param {number} lod + * @return {number} + */ +tcuTexture.Texture3DView.prototype.sampleCompare = function(sampler, ref, texCoord, lod) { + throw new Error('Unimplemented'); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number} lod + * @param {Array<number>} offset + * @return {Array<number>} + */ +tcuTexture.Texture3DView.prototype.sampleOffset = function(sampler, texCoord, lod, offset) { + return tcuTexture.sampleLevelArray3DOffset(this.m_levels, this.m_numLevels, sampler, texCoord[0], texCoord[1], texCoord[2], lod, offset); +}; + +/* TODO: All view classes are very similar. They should have a common base class */ + +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} levels + * @param {number} numLevels + * @param {tcuTexture.Sampler} sampler + * @param {number} s + * @param {number} t + * @param {number} r + * @param {number} lod + * @param {Array<number>} offset + * @return {Array<number>} + */ +tcuTexture.sampleLevelArray3DOffset = function(levels, numLevels, sampler, s, t, r, lod, offset) { + /** @type {boolean} */ var magnified = lod <= sampler.lodThreshold; + /** @type {tcuTexture.FilterMode} */ var filterMode = magnified ? sampler.magFilter : sampler.minFilter; + /** @type {number} */ var maxLevel; + /** @type {tcuTexture.FilterMode} */ var levelFilter; + switch (filterMode) { + case tcuTexture.FilterMode.NEAREST: return levels[0].sample3DOffset(sampler, filterMode, s, t, r, offset); + case tcuTexture.FilterMode.LINEAR: return levels[0].sample3DOffset(sampler, filterMode, s, t, r, offset); + + case tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST: + case tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST: + maxLevel = numLevels - 1; + /** @type {number} */ var level = deMath.clamp(Math.ceil(lod + 0.5) - 1, 0, maxLevel); + levelFilter = (filterMode === tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + + return levels[level].sample3DOffset(sampler, levelFilter, s, t, r, offset); + + case tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR: + case tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR: + maxLevel = numLevels - 1; + /** @type {number} */ var level0 = deMath.clamp(Math.floor(lod), 0, maxLevel); + /** @type {number} */ var level1 = Math.min(maxLevel, level0 + 1); + levelFilter = (filterMode === tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + /** @type {number} */ var f = deMath.deFloatFrac(lod); + /** @type {Array<number>} */ var t0 = levels[level0].sample3DOffset(sampler, levelFilter, s, t, r, offset); + /** @type {Array<number>} */ var t1 = levels[level1].sample3DOffset(sampler, levelFilter, s, t, r, offset); + + return deMath.add(deMath.scale(t0, (1.0 - f)), deMath.scale(t1, f)); + + default: + throw new Error('Filter mode not supported'); + } +}; + +/** + * @param {number} width + * @param {number=} height + * @param {number=} depth + * @return {number} Number of pyramid levels + */ +tcuTexture.computeMipPyramidLevels = function(width, height, depth) { + if (depth !== undefined) + return Math.floor(Math.log2(Math.max(width, Math.max(height, depth)))) + 1; + else if (height !== undefined) + return Math.floor(Math.log2(Math.max(width, height))) + 1; + else + return Math.floor(Math.log2(width)) + 1; +}; + +/** + * @param {number} baseLevelSize + * @param {number} levelNdx + */ +tcuTexture.getMipPyramidLevelSize = function(baseLevelSize, levelNdx) { + return Math.max(baseLevelSize >> levelNdx, 1); +}; + +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faceAccesses + * @param {tcuTexture.CubeFace} baseFace + * @param {number} u + * @param {number} v + * @param {number} depth + * @return {Array<Array<number>>} + */ +tcuTexture.getCubeLinearSamples = function(faceAccesses, baseFace, u, v, depth) { + DE_ASSERT(faceAccesses[0].getWidth() == faceAccesses[0].getHeight()); + /** @type {Array<Array<number>>} */ var dst = []; + var size = faceAccesses[0].getWidth(); + var x0 = Math.floor(u - 0.5); + var x1 = x0 + 1; + var y0 = Math.floor(v - 0.5); + var y1 = y0 + 1; + var baseSampleCoords = + [ + [x0, y0], + [x1, y0], + [x0, y1], + [x1, y1] + ]; + /** @type {Array<Array<number>>} */ var sampleColors = []; + /** @type {Array<boolean>} */ var hasBothCoordsOutOfBounds = []; //!< Whether correctCubeFace() returns CUBEFACE_LAST, i.e. both u and v are out of bounds. + + // Find correct faces and coordinates for out-of-bounds sample coordinates. + + for (var i = 0; i < 4; i++) { + /** @type {tcuTexture.CubeFaceCoords} */ var coords = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(baseFace, baseSampleCoords[i]), size); + hasBothCoordsOutOfBounds[i] = coords == null; + if (!hasBothCoordsOutOfBounds[i]) + sampleColors[i] = tcuTexture.lookup(faceAccesses[coords.face], coords.s, coords.t, depth); + } + + // If a sample was out of bounds in both u and v, we get its color from the average of the three other samples. + // \note This averaging behavior is not required by the GLES3 spec (though it is recommended). GLES3 spec only + // requires that if the three other samples all have the same color, then the doubly-out-of-bounds sample + // must have this color as well. + + var bothOutOfBoundsNdx = -1; + for (var i = 0; i < 4; i++) { + if (hasBothCoordsOutOfBounds[i]) { + DE_ASSERT(bothOutOfBoundsNdx < 0); // Only one sample can be out of bounds in both u and v. + bothOutOfBoundsNdx = i; + } + } + if (bothOutOfBoundsNdx != -1) { + sampleColors[bothOutOfBoundsNdx] = [0, 0, 0, 0]; + for (var i = 0; i < 4; i++) + if (i != bothOutOfBoundsNdx) + sampleColors[bothOutOfBoundsNdx] = deMath.add(sampleColors[bothOutOfBoundsNdx], sampleColors[i]); + + sampleColors[bothOutOfBoundsNdx] = deMath.scale(sampleColors[bothOutOfBoundsNdx], (1.0 / 3.0)); + } + + for (var i = 0; i < sampleColors.length; i++) + dst[i] = sampleColors[i]; + + return dst; +}; + +// \todo [2014-02-19 pyry] Optimize faceAccesses +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faceAccesses + * @param {tcuTexture.CubeFace} baseFace + * @param {tcuTexture.Sampler} sampler + * @param {number} s + * @param {number} t + * @param {number} depth + * @return {Array<number>} + */ +tcuTexture.sampleCubeSeamlessLinear = function(faceAccesses, baseFace, sampler, s, t, depth) { + DE_ASSERT(faceAccesses[0].getWidth() == faceAccesses[0].getHeight()); + + var size = faceAccesses[0].getWidth(); + // Non-normalized coordinates. + var u = s; + var v = t; + + if (sampler.normalizedCoords) { + u = tcuTexture.unnormalize(sampler.wrapS, s, size); + v = tcuTexture.unnormalize(sampler.wrapT, t, size); + } + + // Get sample colors. + + /** @type {Array<Array<number>>} */ var sampleColors = tcuTexture.getCubeLinearSamples(faceAccesses, baseFace, u, v, depth); + + // Interpolate. + + var a = deMath.deFloatFrac(u - 0.5); + var b = deMath.deFloatFrac(v - 0.5); + + return deMath.add((deMath.scale(deMath.scale(sampleColors[0], (1.0 - a)), (1.0 - b))), + deMath.add((deMath.scale(deMath.scale(sampleColors[1], (a)), (1.0 - b))), + deMath.add((deMath.scale(deMath.scale(sampleColors[2], (1.0 - a)), (b))), + (deMath.scale(deMath.scale(sampleColors[3], (a)), (b)))))); +}; + +/** + * @param {Array<Array<tcuTexture.ConstPixelBufferAccess>>} faces + * @param {number} numLevels + * @param {tcuTexture.CubeFace} face + * @param {tcuTexture.Sampler} sampler + * @param {number} s + * @param {number} t + * @param {number} depth + * @param {number=} lod + * @return {Array<number>} + */ +tcuTexture.sampleLevelArrayCubeSeamless = function(faces, numLevels, face, sampler, s, t, depth, lod) { + lod = lod || 0; + var magnified = lod <= sampler.lodThreshold; + /** @type {tcuTexture.FilterMode} */ var filterMode = magnified ? sampler.magFilter : sampler.minFilter; + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faceAccesses = []; + /** @type {tcuTexture.FilterMode}*/ var levelFilter; + + switch (filterMode) { + case tcuTexture.FilterMode.NEAREST: + return tcuTexture.sampleCubeSeamlessNearest(faces[face][0], sampler, s, t, depth); + + case tcuTexture.FilterMode.LINEAR: { + faceAccesses = []; + for (var i = 0; i < Object.keys(tcuTexture.CubeFace).length; i++) + faceAccesses[i] = faces[i][0]; + + return tcuTexture.sampleCubeSeamlessLinear(faceAccesses, face, sampler, s, t, depth); + } + + case tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST: + case tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST: { + var maxLevel = numLevels - 1; + var level = deMath.clamp(Math.ceil(lod + 0.5) - 1, 0, maxLevel); + levelFilter = (filterMode == tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + + if (levelFilter == tcuTexture.FilterMode.NEAREST) + return tcuTexture.sampleCubeSeamlessNearest(faces[face][level], sampler, s, t, depth); + else { + DE_ASSERT(levelFilter == tcuTexture.FilterMode.LINEAR); + + faceAccesses = []; + for (var i = 0; i < Object.keys(tcuTexture.CubeFace).length; i++) + faceAccesses[i] = faces[i][level]; + + return tcuTexture.sampleCubeSeamlessLinear(faceAccesses, face, sampler, s, t, depth); + } + } + + case tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR: + case tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR: { + var maxLevel = numLevels - 1; + var level0 = deMath.clamp(Math.floor(lod), 0, maxLevel); + var level1 = Math.min(maxLevel, level0 + 1); + levelFilter = (filterMode == tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + var f = deMath.deFloatFrac(lod); + var t0 = []; + var t1 = []; + + if (levelFilter == tcuTexture.FilterMode.NEAREST) { + t0 = tcuTexture.sampleCubeSeamlessNearest(faces[face][level0], sampler, s, t, depth); + t1 = tcuTexture.sampleCubeSeamlessNearest(faces[face][level1], sampler, s, t, depth); + } else { + DE_ASSERT(levelFilter == tcuTexture.FilterMode.LINEAR); + + /** @type {Array<tcuTexture.ConstPixelBufferAccess>}*/ var faceAccesses0 = []; + /** @type {Array<tcuTexture.ConstPixelBufferAccess>}*/ var faceAccesses1 = []; + for (var i = 0; i < Object.keys(tcuTexture.CubeFace).length; i++) { + faceAccesses0[i] = faces[i][level0]; + faceAccesses1[i] = faces[i][level1]; + } + + t0 = tcuTexture.sampleCubeSeamlessLinear(faceAccesses0, face, sampler, s, t, depth); + t1 = tcuTexture.sampleCubeSeamlessLinear(faceAccesses1, face, sampler, s, t, depth); + } + + return deMath.add(deMath.scale(t0, (1.0 - f)), deMath.scale(t1, f)); + } + + default: + throw new Error('Unsupported filter mode'); + } +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} faceAccess + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {number} s + * @param {number} t + * @param {number=} depth + * @return {number} + */ +tcuTexture.sampleCubeSeamlessNearestCompare = function(faceAccess, sampler, ref, s, t, depth) { + depth = depth ? depth : 0; + /** @type {tcuTexture.Sampler} */ var clampingSampler = deUtil.clone(sampler); + clampingSampler.wrapS = tcuTexture.WrapMode.CLAMP_TO_EDGE; + clampingSampler.wrapT = tcuTexture.WrapMode.CLAMP_TO_EDGE; + return faceAccess.sample2DCompare(clampingSampler, tcuTexture.FilterMode.NEAREST, ref, s, t, [0, 0, depth]); +}; + +/** + * @param {Array<tcuTexture.ConstPixelBufferAccess>} faceAccesses + * @param {tcuTexture.CubeFace} baseFace + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {number} s + * @param {number} t + * @return {number} + */ +tcuTexture.sampleCubeSeamlessLinearCompare = function(faceAccesses, baseFace, sampler, ref, s, t) { + DE_ASSERT(faceAccesses[0].getWidth() == faceAccesses[0].getHeight()); + + var size = faceAccesses[0].getWidth(); + // Non-normalized coordinates. + var u = s; + var v = t; + + if (sampler.normalizedCoords) { + u = tcuTexture.unnormalize(sampler.wrapS, s, size); + v = tcuTexture.unnormalize(sampler.wrapT, t, size); + } + + var x0 = Math.floor(u - 0.5); + var x1 = x0 + 1; + var y0 = Math.floor(v - 0.5); + var y1 = y0 + 1; + var baseSampleCoords = [ + [x0, y0], + [x1, y0], + [x0, y1], + [x1, y1] + ]; + var sampleRes = []; + var hasBothCoordsOutOfBounds = []; //!< Whether correctCubeFace() returns CUBEFACE_LAST, i.e. both u and v are out of bounds. + + // Find correct faces and coordinates for out-of-bounds sample coordinates. + + for (var i = 0; i < 4; i++) { + /** @type {tcuTexture.CubeFaceCoords} */ var coords = tcuTexture.remapCubeEdgeCoords(new tcuTexture.CubeFaceCoords(baseFace, baseSampleCoords[i]), size); + hasBothCoordsOutOfBounds[i] = coords == null; + + if (!hasBothCoordsOutOfBounds[i]) { + var isFixedPointDepth = tcuTexture.isFixedPointDepthTextureFormat(faceAccesses[coords.face].getFormat()); + + sampleRes[i] = tcuTexture.execCompare(faceAccesses[coords.face].getPixel(coords.s, coords.t), sampler.compare, sampler.compareChannel, ref, isFixedPointDepth); + } + } + + // If a sample was out of bounds in both u and v, we get its color from the average of the three other samples. + // \note This averaging behavior is not required by the GLES3 spec (though it is recommended). GLES3 spec only + // requires that if the three other samples all have the same color, then the doubly-out-of-bounds sample + // must have this color as well. + + var bothOutOfBoundsNdx = -1; + for (var i = 0; i < 4; i++) { + if (hasBothCoordsOutOfBounds[i]) { + DE_ASSERT(bothOutOfBoundsNdx < 0); // Only one sample can be out of bounds in both u and v. + bothOutOfBoundsNdx = i; + } + } + if (bothOutOfBoundsNdx != -1) { + sampleRes[bothOutOfBoundsNdx] = 0.0; + for (var i = 0; i < 4; i++) + if (i != bothOutOfBoundsNdx) + sampleRes[bothOutOfBoundsNdx] += sampleRes[i]; + + sampleRes[bothOutOfBoundsNdx] = sampleRes[bothOutOfBoundsNdx] * (1.0 / 3.0); + } + + // Interpolate. + + var a = deMath.deFloatFrac(u - 0.5); + var b = deMath.deFloatFrac(v - 0.5); + + return (sampleRes[0] * (1.0 - a) * (1.0 - b)) + + (sampleRes[1] * (a) * (1.0 - b)) + + (sampleRes[2] * (1.0 - a) * (b)) + + (sampleRes[3] * (a) * (b)); +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} faceAccess + * @param {tcuTexture.Sampler} sampler + * @param {number} s + * @param {number} t + * @param {number} depth + * @return {Array<number>} + */ +tcuTexture.sampleCubeSeamlessNearest = function(faceAccess, sampler, s, t, depth) { + /** @type {tcuTexture.Sampler} */ var clampingSampler = sampler; + clampingSampler.wrapS = tcuTexture.WrapMode.CLAMP_TO_EDGE; + clampingSampler.wrapT = tcuTexture.WrapMode.CLAMP_TO_EDGE; + return faceAccess.sample2D(clampingSampler, tcuTexture.FilterMode.NEAREST, s, t, depth); +}; + +/** + * @param {Array<number>} coords Vec3 cube coordinates + * @return {tcuTexture.CubeFaceCoords} + */ +tcuTexture.getCubeFaceCoords = function(coords) { + var face = tcuTexture.selectCubeFace(coords); + return new tcuTexture.CubeFaceCoords(face, tcuTexture.projectToFace(face, coords)); +}; + +/** + * @param {Array<Array<tcuTexture.ConstPixelBufferAccess>>} faces + * @param {number} numLevels + * @param {tcuTexture.CubeFace} face + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {number} s + * @param {number} t + * @param {number} lod + * @return {number} + */ +tcuTexture.sampleLevelArrayCubeSeamlessCompare = function(faces, numLevels, face, sampler, ref, s, t, lod) { + var magnified = lod <= sampler.lodThreshold; + /** @type {tcuTexture.FilterMode}*/ var filterMode = magnified ? sampler.magFilter : sampler.minFilter; + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faceAccesses = []; + /** @type {tcuTexture.FilterMode} */ var levelFilter; + + switch (filterMode) { + case tcuTexture.FilterMode.NEAREST: + return tcuTexture.sampleCubeSeamlessNearestCompare(faces[face][0], sampler, ref, s, t); + + case tcuTexture.FilterMode.LINEAR: { + faceAccesses = []; + for (var i = 0; i < Object.keys(tcuTexture.CubeFace).length; i++) + faceAccesses[i] = faces[i][0]; + + return tcuTexture.sampleCubeSeamlessLinearCompare(faceAccesses, face, sampler, ref, s, t); + } + + case tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST: + case tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST: { + var maxLevel = numLevels - 1; + var level = deMath.clamp(Math.ceil(lod + 0.5) - 1, 0, maxLevel); + levelFilter = filterMode == tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + + if (levelFilter == tcuTexture.FilterMode.NEAREST) + return tcuTexture.sampleCubeSeamlessNearestCompare(faces[face][level], sampler, ref, s, t); + else { + DE_ASSERT(levelFilter == tcuTexture.FilterMode.LINEAR); + + faceAccesses = []; + for (var i = 0; i < Object.keys(tcuTexture.CubeFace).length; i++) + faceAccesses[i] = faces[i][level]; + + return tcuTexture.sampleCubeSeamlessLinearCompare(faceAccesses, face, sampler, ref, s, t); + } + } + + case tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR: + case tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR: { + var maxLevel = numLevels - 1; + var level0 = deMath.clamp(Math.floor(lod), 0, maxLevel); + var level1 = Math.min(maxLevel, level0 + 1); + levelFilter = (filterMode == tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + var f = deMath.deFloatFrac(lod); + var t0; + var t1; + + if (levelFilter == tcuTexture.FilterMode.NEAREST) { + t0 = tcuTexture.sampleCubeSeamlessNearestCompare(faces[face][level0], sampler, ref, s, t); + t1 = tcuTexture.sampleCubeSeamlessNearestCompare(faces[face][level1], sampler, ref, s, t); + } else { + DE_ASSERT(levelFilter == tcuTexture.FilterMode.LINEAR); + + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faceAccesses0 = []; + /** @type {Array<tcuTexture.ConstPixelBufferAccess>} */ var faceAccesses1 = []; + for (var i = 0; i < Object.keys(tcuTexture.CubeFace).length; i++) { + faceAccesses0[i] = faces[i][level0]; + faceAccesses1[i] = faces[i][level1]; + } + + t0 = tcuTexture.sampleCubeSeamlessLinearCompare(faceAccesses0, face, sampler, ref, s, t); + t1 = tcuTexture.sampleCubeSeamlessLinearCompare(faceAccesses1, face, sampler, ref, s, t); + } + + return t0 * (1.0 - f) + t1 * f; + } + + default: + throw new Error('Unsupported filter mode'); + } +}; + +/** + * @constructor + * @extends {tcuTexture.TextureLevelPyramid} + * @param {tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + */ +tcuTexture.Texture2D = function(format, width, height) { + tcuTexture.TextureLevelPyramid.call(this, format, tcuTexture.computeMipPyramidLevels(width, height)); + this.m_width = width; + this.m_height = height; + this.m_view = new tcuTexture.Texture2DView(this.getNumLevels(), this.getLevels()); +}; + +tcuTexture.Texture2D.prototype = Object.create(tcuTexture.TextureLevelPyramid.prototype); +tcuTexture.Texture2D.prototype.constructor = tcuTexture.Texture2D; + +tcuTexture.Texture2D.prototype.getWidth = function() { return this.m_width; }; +tcuTexture.Texture2D.prototype.getHeight = function() { return this.m_height; }; +/** @return {tcuTexture.Texture2DView} */ +tcuTexture.Texture2D.prototype.getView = function() { return this.m_view; }; + +/** + * @param {number} baseLevel + * @param {number} maxLevel + * @return {tcuTexture.Texture2DView} + */ +tcuTexture.Texture2D.prototype.getSubView = function(baseLevel, maxLevel) { return this.m_view.getSubView(baseLevel, maxLevel); }; + +/** + * @param {number} levelNdx + */ +tcuTexture.Texture2D.prototype.allocLevel = function(levelNdx) { + DE_ASSERT(deMath.deInBounds32(levelNdx, 0, this.getNumLevels())); + + var width = tcuTexture.getMipPyramidLevelSize(this.m_width, levelNdx); + var height = tcuTexture.getMipPyramidLevelSize(this.m_height, levelNdx); + + tcuTexture.TextureLevelPyramid.prototype.allocLevel.call(this, levelNdx, width, height, 1); +}; + +/** + * @constructor + * @extends {tcuTexture.TextureLevelPyramid} + * @param {tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + * @param {number} numLayers + */ +tcuTexture.Texture2DArray = function(format, width, height, numLayers) { + tcuTexture.TextureLevelPyramid.call(this, format, tcuTexture.computeMipPyramidLevels(width, height)); + this.m_width = width; + this.m_height = height; + this.m_numLayers = numLayers; + this.m_view = new tcuTexture.Texture2DArrayView(this.getNumLevels(), this.getLevels()); +}; + +tcuTexture.Texture2DArray.prototype = Object.create(tcuTexture.TextureLevelPyramid.prototype); +tcuTexture.Texture2DArray.prototype.constructor = tcuTexture.Texture2DArray; +/** @return {tcuTexture.Texture2DArrayView} */ +tcuTexture.Texture2DArray.prototype.getView = function() { return this.m_view; }; + +/** @return {number} */ +tcuTexture.Texture2DArray.prototype.getWidth = function() { return this.m_width; }; + +/** @return {number} */ +tcuTexture.Texture2DArray.prototype.getHeight = function() { return this.m_height; }; + +/** + * @param {number} levelNdx + */ +tcuTexture.Texture2DArray.prototype.allocLevel = function(levelNdx) { + DE_ASSERT(deMath.deInBounds32(levelNdx, 0, this.getNumLevels())); + + var width = tcuTexture.getMipPyramidLevelSize(this.m_width, levelNdx); + var height = tcuTexture.getMipPyramidLevelSize(this.m_height, levelNdx); + + tcuTexture.TextureLevelPyramid.prototype.allocLevel.call(this, levelNdx, width, height, this.m_numLayers); +}; + +/** + * @constructor + * @extends {tcuTexture.TextureLevelPyramid} + * @param {tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + * @param {number} depth + */ +tcuTexture.Texture3D = function(format, width, height, depth) { + tcuTexture.TextureLevelPyramid.call(this, format, tcuTexture.computeMipPyramidLevels(width, height, depth)); + this.m_width = width; + this.m_height = height; + this.m_depth = depth; + this.m_view = new tcuTexture.Texture3DView(this.getNumLevels(), this.getLevels()); +}; + +tcuTexture.Texture3D.prototype = Object.create(tcuTexture.TextureLevelPyramid.prototype); +tcuTexture.Texture3D.prototype.constructor = tcuTexture.Texture3D; + +tcuTexture.Texture3D.prototype.getWidth = function() { return this.m_width; }; +tcuTexture.Texture3D.prototype.getHeight = function() { return this.m_height; }; +tcuTexture.Texture3D.prototype.getDepth = function() { return this.m_depth; }; +tcuTexture.Texture3D.prototype.getView = function() { return this.m_view; }; +/** + * @param {number} baseLevel + * @param {number} maxLevel + * @return {tcuTexture.Texture3DView} + */ +tcuTexture.Texture3D.prototype.getSubView = function(baseLevel, maxLevel) { return this.m_view.getSubView(baseLevel, maxLevel); }; + +/** + * @param {number} levelNdx + */ +tcuTexture.Texture3D.prototype.allocLevel = function(levelNdx) { + DE_ASSERT(deMath.deInBounds32(levelNdx, 0, this.getNumLevels())); + + var width = tcuTexture.getMipPyramidLevelSize(this.m_width, levelNdx); + var height = tcuTexture.getMipPyramidLevelSize(this.m_height, levelNdx); + var depth = tcuTexture.getMipPyramidLevelSize(this.m_depth, levelNdx); + + tcuTexture.TextureLevelPyramid.prototype.allocLevel.call(this, levelNdx, width, height, depth); +}; + +/** + * @constructor + * @param {number} numLevels + * @param {Array<Array<tcuTexture.ConstPixelBufferAccess>>} levels + */ +tcuTexture.TextureCubeView = function(numLevels, levels) { + this.m_numLevels = numLevels; + this.m_levels = levels; +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number=} lod + * @return {Array<number>} Pixel color + */ +tcuTexture.TextureCubeView.prototype.sample = function(sampler, texCoord, lod) { + DE_ASSERT(sampler.compare == tcuTexture.CompareMode.COMPAREMODE_NONE); + + // Computes (face, s, t). + var coords = tcuTexture.getCubeFaceCoords(texCoord); + if (sampler.seamlessCubeMap) + return tcuTexture.sampleLevelArrayCubeSeamless(this.m_levels, this.m_numLevels, coords.face, sampler, coords.s, coords.t, 0 /* depth */, lod); + else + return tcuTexture.sampleLevelArray2D(this.m_levels[coords.face], this.m_numLevels, sampler, coords.s, coords.t, 0 /* depth */, lod); +}; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {number} ref + * @param {Array<number>} texCoord + * @param {number} lod + * @return {number} + */ +tcuTexture.TextureCubeView.prototype.sampleCompare = function(sampler, ref, texCoord, lod) { + DE_ASSERT(sampler.compare != tcuTexture.CompareMode.COMPAREMODE_NONE); + + // Computes (face, s, t). + var coords = tcuTexture.getCubeFaceCoords(texCoord); + if (sampler.seamlessCubeMap) + return tcuTexture.sampleLevelArrayCubeSeamlessCompare(this.m_levels, this.m_numLevels, coords.face, sampler, ref, coords.s, coords.t, lod); + else + return tcuTexture.sampleLevelArray2DCompare(this.m_levels[coords.face], this.m_numLevels, sampler, ref, coords.s, coords.t, lod, [0, 0, 0]); +}; + +/** + * @param {tcuTexture.CubeFace} face + * @return {Array<tcuTexture.ConstPixelBufferAccess>} + */ +tcuTexture.TextureCubeView.prototype.getFaceLevels = function(face) { return this.m_levels[face]; }; +/** @return {number} */ +tcuTexture.TextureCubeView.prototype.getSize = function() { return this.m_numLevels > 0 ? this.m_levels[0][0].getWidth() : 0; }; + +/** @return {number} */ +tcuTexture.TextureCubeView.prototype.getNumLevels = function() { return this.m_numLevels; }; + +/** + * @param {number} ndx + * @param {tcuTexture.CubeFace} face + * @return {tcuTexture.ConstPixelBufferAccess} + */ +tcuTexture.TextureCubeView.prototype.getLevelFace = function(ndx, face) { + assertMsgOptions(0 <= ndx && ndx < this.m_numLevels, '', false, true); + return this.m_levels[face][ndx]; +}; + +/** + * @param {number} baseLevel + * @param {number} maxLevel + * @return {tcuTexture.TextureCubeView} + */ +tcuTexture.TextureCubeView.prototype.getSubView = function(baseLevel, maxLevel) { + var clampedBase = deMath.clamp(baseLevel, 0, this.m_numLevels - 1); + var clampedMax = deMath.clamp(maxLevel, clampedBase, this.m_numLevels - 1); + var numLevels = clampedMax - clampedBase + 1; + var levels = []; + for (var face in tcuTexture.CubeFace) + levels.push(this.getFaceLevels(tcuTexture.CubeFace[face]).slice(clampedBase, numLevels)); + + return new tcuTexture.TextureCubeView(numLevels, levels); +}; + +/** + * @constructor + * @param {tcuTexture.TextureFormat} format + * @param {number} size + */ +tcuTexture.TextureCube = function(format, size) { + this.m_format = format; + this.m_size = size; + this.m_data = []; + this.m_data.length = Object.keys(tcuTexture.CubeFace).length; + this.m_access = []; + this.m_access.length = Object.keys(tcuTexture.CubeFace).length; + + var numLevels = tcuTexture.computeMipPyramidLevels(this.m_size); + var levels = []; + levels.length = Object.keys(tcuTexture.CubeFace).length; + + for (var face in tcuTexture.CubeFace) { + this.m_data[tcuTexture.CubeFace[face]] = []; + for (var i = 0; i < numLevels; i++) + this.m_data[tcuTexture.CubeFace[face]].push(new tcuTexture.DeqpArrayBuffer()); + this.m_access[tcuTexture.CubeFace[face]] = []; + this.m_access[tcuTexture.CubeFace[face]].length = numLevels; + levels[tcuTexture.CubeFace[face]] = this.m_access[tcuTexture.CubeFace[face]]; + } + + this.m_view = new tcuTexture.TextureCubeView(numLevels, levels); +}; + +/** @return {tcuTexture.TextureFormat} */ +tcuTexture.TextureCube.prototype.getFormat = function() { return this.m_format; }; +/** @return {number} */ +tcuTexture.TextureCube.prototype.getSize = function() { return this.m_size; }; +/** @return {tcuTexture.TextureCubeView} */ +tcuTexture.TextureCube.prototype.getView = function() { return this.m_view; }; +/** + * @param {number} ndx Level index + * @param {tcuTexture.CubeFace} face + * @return {tcuTexture.PixelBufferAccess} + */ +tcuTexture.TextureCube.prototype.getLevelFace = function(ndx, face) { return this.m_access[face][ndx]; }; +/** @return {number} */ +tcuTexture.TextureCube.prototype.getNumLevels = function() { return this.m_access[0].length; }; + +/** + * @param {tcuTexture.Sampler} sampler + * @param {Array<number>} texCoord + * @param {number} lod + * @return {Array<number>} Pixel color + */ +tcuTexture.TextureCube.prototype.sample = function(sampler, texCoord, lod) { + return this.m_view.sample(sampler, texCoord, lod); +}; + +/** + * @param {number} baseLevel + * @param {number} maxLevel + * @return {tcuTexture.TextureCubeView} + */ +tcuTexture.TextureCube.prototype.getSubView = function(baseLevel, maxLevel) { return this.m_view.getSubView(baseLevel, maxLevel); }; + +/** + * @param {tcuTexture.CubeFace} face + * @param {number} levelNdx + * @return {boolean} + */ +tcuTexture.TextureCube.prototype.isLevelEmpty = function(face, levelNdx) { + return this.m_data[face][levelNdx].empty(); +}; + +/** + * @param {tcuTexture.CubeFace} face + * @param {number} levelNdx + */ +tcuTexture.TextureCube.prototype.allocLevel = function(face, levelNdx) { + /** @const */ var size = tcuTexture.getMipPyramidLevelSize(this.m_size, levelNdx); + /** @const*/ var dataSize = this.m_format.getPixelSize() * size * size; + DE_ASSERT(this.isLevelEmpty(face, levelNdx)); + + this.m_data[face][levelNdx].setStorage(dataSize); + this.m_access[face][levelNdx] = new tcuTexture.PixelBufferAccess({ + format: this.m_format, + width: size, + height: size, + depth: 1, + data: this.m_data[face][levelNdx].m_ptr + }); +}; + +/** + * @param {Array<number>} coords Cube coordinates + * @return {tcuTexture.CubeFace} + */ +tcuTexture.selectCubeFace = function(coords) { + var x = coords[0]; + var y = coords[1]; + var z = coords[2]; + var ax = Math.abs(x); + var ay = Math.abs(y); + var az = Math.abs(z); + + if (ay < ax && az < ax) + return x >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_X : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X; + else if (ax < ay && az < ay) + return y >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y; + else if (ax < az && ay < az) + return z >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z; + else { + // Some of the components are equal. Use tie-breaking rule. + if (ax == ay) { + if (ax < az) + return z >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z; + else + return x >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_X : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X; + } else if (ax == az) { + if (az < ay) + return y >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y; + else + return z >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z; + } else if (ay == az) { + if (ay < ax) + return x >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_X : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X; + else + return y >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y; + } else + return x >= 0 ? tcuTexture.CubeFace.CUBEFACE_POSITIVE_X : tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X; + } +}; + +/** + * @param {tcuTexture.CubeFace} face + * @param {Array<number>} coord Cube coordinates (Vec3) + * @return {Array<number>} face coordinates (Vec2) + */ +tcuTexture.projectToFace = function(face, coord) { + var rx = coord[0]; + var ry = coord[1]; + var rz = coord[2]; + var sc = 0; + var tc = 0; + var ma = 0; + + switch (face) { + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X: sc = +rz; tc = -ry; ma = -rx; break; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_X: sc = -rz; tc = -ry; ma = +rx; break; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y: sc = +rx; tc = -rz; ma = -ry; break; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y: sc = +rx; tc = +rz; ma = +ry; break; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z: sc = -rx; tc = -ry; ma = -rz; break; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z: sc = +rx; tc = -ry; ma = +rz; break; + default: + throw new Error('Unrecognized face ' + face); + } + + // Compute s, t + var s = ((sc / ma) + 1) / 2; + var t = ((tc / ma) + 1) / 2; + + return [s, t]; +}; + +/** + * @constructor + * @param {tcuTexture.TextureFormat} format + * @param {number=} width + * @param {number=} height + * @param {number=} depth + */ +tcuTexture.TextureLevel = function(format, width, height, depth) { + this.m_format = format; + this.m_width = width || 0; + this.m_height = height || 0; + this.m_depth = depth === undefined ? 1 : depth; + this.m_data = new tcuTexture.DeqpArrayBuffer(); + this.setSize(this.m_width, this.m_height, this.m_depth); +}; + +tcuTexture.TextureLevel.prototype.constructor = tcuTexture.TextureLevel; + +/** + * @param {tcuTexture.TextureFormat} format + * @param {number=} width + * @param {number=} height + * @param {number=} depth + */ +tcuTexture.TextureLevel.prototype.setStorage = function(format, width, height, depth) { + this.m_format = format; + this.setSize(width, height, depth); +}; + +/** + * @param {number=} width + * @param {number=} height + * @param {number=} depth + */ +tcuTexture.TextureLevel.prototype.setSize = function(width, height, depth) { + var pixelSize = this.m_format.getPixelSize(); + + this.m_width = width || 0; + this.m_height = height || 0; + this.m_depth = depth === undefined ? 1 : depth; + + this.m_data.setStorage(this.m_width * this.m_height * this.m_depth * pixelSize); +}; + +/** + * @return {tcuTexture.PixelBufferAccess} + */ +tcuTexture.TextureLevel.prototype.getAccess = function() { + return new tcuTexture.PixelBufferAccess({ + format: this.m_format, + width: this.m_width, + height: this.m_height, + depth: this.m_depth, + data: this.m_data.m_ptr + }); + +}; + +/** + * @return {number} + */ +tcuTexture.TextureLevel.prototype.getWidth = function() { + return this.m_width; +}; + +/** + * @return {number} + */ +tcuTexture.TextureLevel.prototype.getHeight = function() { + return this.m_height; +}; + +/** + * @return {number} + */ +tcuTexture.TextureLevel.prototype.getDepth = function() { + return this.m_depth; +}; + +/** + * @return {?tcuTexture.TextureFormat} + */ +tcuTexture.TextureLevel.prototype.getFormat = function() { + return this.m_format; +}; + +/** + * Checks if origCoords.coords is in bounds defined by size; if not, return a CubeFaceCoords with face set to the appropriate neighboring face and coords transformed accordingly. + * \note If both x and y in origCoords.coords are out of bounds, this returns with face CUBEFACE_LAST, signifying that there is no unique neighboring face. + * @param {tcuTexture.CubeFaceCoords} origCoords + * @param {number} size + * @return {tcuTexture.CubeFaceCoords} + */ +tcuTexture.remapCubeEdgeCoords = function(origCoords, size) { + var uInBounds = deMath.deInBounds32(origCoords.s, 0, size); + var vInBounds = deMath.deInBounds32(origCoords.t, 0, size); + + if (uInBounds && vInBounds) + return origCoords; + + if (!uInBounds && !vInBounds) + return null; + + var coords = [ + tcuTexture.wrap(tcuTexture.WrapMode.CLAMP_TO_BORDER, origCoords.s, size), + tcuTexture.wrap(tcuTexture.WrapMode.CLAMP_TO_BORDER, origCoords.t, size)]; + var canonizedCoords = []; + + // Map the uv coordinates to canonized 3d coordinates. + + switch (origCoords.face) { + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X: canonizedCoords = [0, size - 1 - coords[1], coords[0]]; break; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_X: canonizedCoords = [size - 1, size - 1 - coords[1], size - 1 - coords[0]]; break; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y: canonizedCoords = [coords[0], 0, size - 1 - coords[1]]; break; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y: canonizedCoords = [coords[0], size - 1, coords[1]]; break; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z: canonizedCoords = [size - 1 - coords[0], size - 1 - coords[1], 0]; break; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z: canonizedCoords = [coords[0], size - 1 - coords[1], size - 1]; break; + default: throw new Error('Invalid cube face:' + origCoords.face); + } + + // Find an appropriate face to re-map the coordinates to. + + if (canonizedCoords[0] == -1) + return new tcuTexture.CubeFaceCoords(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X, [canonizedCoords[2], size - 1 - canonizedCoords[1]]); + + if (canonizedCoords[0] == size) + return new tcuTexture.CubeFaceCoords(tcuTexture.CubeFace.CUBEFACE_POSITIVE_X, [size - 1 - canonizedCoords[2], size - 1 - canonizedCoords[1]]); + + if (canonizedCoords[1] == -1) + return new tcuTexture.CubeFaceCoords(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y, [canonizedCoords[0], size - 1 - canonizedCoords[2]]); + + if (canonizedCoords[1] == size) + return new tcuTexture.CubeFaceCoords(tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y, [canonizedCoords[0], canonizedCoords[2]]); + + if (canonizedCoords[2] == -1) + return new tcuTexture.CubeFaceCoords(tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z, [size - 1 - canonizedCoords[0], size - 1 - canonizedCoords[1]]); + + if (canonizedCoords[2] == size) + return new tcuTexture.CubeFaceCoords(tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z, [canonizedCoords[0], size - 1 - canonizedCoords[1]]); + + throw new Error('Cannot remap cube coordinates'); +}; + +/** + * @constructor + * @param {tcuTexture.ConstPixelBufferAccess} src + */ +tcuTexture.RGBA8View = function(src) { + this.src = src; + this.data = new Uint8Array(src.getBuffer(), src.m_offset); + this.stride = src.getRowPitch(); + this.width = src.getWidth(); + this.height = src.getHeight(); + this.pixelSize = src.getFormat().getPixelSize(); +}; + +/** + * @return {tcuTexture.TextureFormat} + */ +tcuTexture.RGBA8View.prototype.getFormat = function() { return this.src.getFormat(); }; + +/** + * Read a pixel + * @param {number} x + * @param {number} y + * @param {number=} numChannels + * @return {Array<number>} + */ +tcuTexture.RGBA8View.prototype.read = function(x, y, numChannels) { + numChannels = numChannels || 4; + var offset = y * this.stride + x * this.pixelSize; + /* Always return a vec4 */ + var result = [0, 0, 0, 255]; + for (var i = 0; i < numChannels; i++) + result[i] = this.data[offset + i]; + return result; +}; + +/** + * Read a pixel into a Uint32 + * @param {number} x + * @param {number} y + * @return {number} + */ +tcuTexture.RGBA8View.prototype.readUintRGBA8 = function(x, y) { + var offset = y * this.stride + x * this.pixelSize; + return ((this.data[offset] & 0xff) << 24) + + ((this.data[offset + 1] & 0xff) << 16) + + ((this.data[offset + 2] & 0xff) << 8) + + (this.data[offset + 3] & 0xff); +}; + +/** + * Write a pixel + * @param {number} x + * @param {number} y + * @param {Array<number>} value + * @param {number=} numChannels + */ +tcuTexture.RGBA8View.prototype.write = function(x, y, value, numChannels) { + numChannels = numChannels || 4; + var offset = y * this.stride + x * this.pixelSize; + for (var i = 0; i < numChannels; i++) + this.data[offset + i] = value[i]; +}; + +tcuTexture.RGBA8View.prototype.getWidth = function() { return this.width; }; + +tcuTexture.RGBA8View.prototype.getHeight = function() { return this.height; }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTextureUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTextureUtil.js new file mode 100644 index 000000000..40450ab38 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTextureUtil.js @@ -0,0 +1,725 @@ +/*------------------------------------------------------------------------- + * 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 tcuTextureUtil.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('framework.common.tcuTextureUtil'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.delibs.debase.deRandom'); + +goog.scope(function() { + +var tcuTextureUtil = framework.common.tcuTextureUtil; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var deRandom = framework.delibs.debase.deRandom; + +var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); +}; + +/** + * @param {number} t + * @param {number} minVal + * @param {number} maxVal + * @return {number} + */ +tcuTextureUtil.linearInterpolate = function(t, minVal, maxVal) { + return minVal + (maxVal - minVal) * t; +}; + +/** tcuTextureUtil.linearChannelToSRGB + * @param {number} cl + * @return {number} + */ +tcuTextureUtil.linearChannelToSRGB = function(cl) { + if (cl <= 0.0) + return 0.0; + else if (cl < 0.0031308) + return 12.92 * cl; + else if (cl < 1.0) + return 1.055 * Math.pow(cl, 0.41666) - 0.055; + else + return 1.0; +}; + +/** + * Convert sRGB to linear colorspace + * @param {Array<number>} cs + * @return {Array<number>} + */ +tcuTextureUtil.sRGBToLinear = function(cs) { + return [tcuTextureUtil.sRGBChannelToLinear(cs[0]), + tcuTextureUtil.sRGBChannelToLinear(cs[1]), + tcuTextureUtil.sRGBChannelToLinear(cs[2]), + cs[3]]; +}; + +/** + * @param {number} cs + * @return {number} + */ + tcuTextureUtil.sRGBChannelToLinear = function(cs) { + if (cs <= 0.04045) + return cs / 12.92; + else + return Math.pow((cs + 0.055) / 1.055, 2.4); +}; + +/** tcuTextureUtil.linearToSRGB + * @param {Array<number>} cl + * @return {Array<number>} + */ +tcuTextureUtil.linearToSRGB = function(cl) { + return [tcuTextureUtil.linearChannelToSRGB(cl[0]), + tcuTextureUtil.linearChannelToSRGB(cl[1]), + tcuTextureUtil.linearChannelToSRGB(cl[2]), + cl[3] + ]; +}; + +/** + * tcuTextureUtil.getSubregion + * @param {tcuTexture.PixelBufferAccess} access + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} width + * @param {number} height + * @param {number} depth + * @return {tcuTexture.PixelBufferAccess} + */ +tcuTextureUtil.getSubregion = function(access, x, y, z, width, height, depth) { + + DE_ASSERT(deMath.deInBounds32(x, 0, access.getWidth()) && deMath.deInRange32(x + width, x, access.getWidth())); + DE_ASSERT(deMath.deInBounds32(y, 0, access.getHeight()) && deMath.deInRange32(y + height, y, access.getHeight())); + DE_ASSERT(deMath.deInBounds32(z, 0, access.getDepth()) && deMath.deInRange32(z + depth, z, access.getDepth())); + + return new tcuTexture.PixelBufferAccess({ + format: access.getFormat(), + width: width, + height: height, + depth: depth, + rowPitch: access.getRowPitch(), + slicePitch: access.getSlicePitch(), + offset: access.m_offset + access.getFormat().getPixelSize() * x + access.getRowPitch() * y + access.getSlicePitch() * z, + data: access.getBuffer() + }); +}; + +/** + * @param {tcuTexture.PixelBufferAccess} access + * @param {Array<number>} minVal + * @param {Array<number>} maxVal + */ +tcuTextureUtil.fillWithComponentGradients1D = function(access, minVal, maxVal) { + DE_ASSERT(access.getHeight() == 1); + for (var x = 0; x < access.getWidth(); x++) { + var s = (x + 0.5) / access.getWidth(); + + var r = tcuTextureUtil.linearInterpolate(s, minVal[0], maxVal[0]); + var g = tcuTextureUtil.linearInterpolate(s, minVal[1], maxVal[1]); + var b = tcuTextureUtil.linearInterpolate(s, minVal[2], maxVal[2]); + var a = tcuTextureUtil.linearInterpolate(s, minVal[3], maxVal[3]); + + access.setPixel([r, g, b, a], x, 0); + } +}; + +/** + * @param {tcuTexture.PixelBufferAccess} access + * @param {Array<number>} minVal + * @param {Array<number>} maxVal + */ +tcuTextureUtil.fillWithComponentGradients2D = function(access, minVal, maxVal) { + for (var y = 0; y < access.getHeight(); y++) { + var t = (y + 0.5) / access.getHeight(); + for (var x = 0; x < access.getWidth(); x++) { + var s = (x + 0.5) / access.getWidth(); + + var r = tcuTextureUtil.linearInterpolate((s + t) * 0.5, minVal[0], maxVal[0]); + var g = tcuTextureUtil.linearInterpolate((s + (1 - t)) * 0.5, minVal[1], maxVal[1]); + var b = tcuTextureUtil.linearInterpolate(((1 - s) + t) * 0.5, minVal[2], maxVal[2]); + var a = tcuTextureUtil.linearInterpolate(((1 - s) + (1 - t)) * 0.5, minVal[3], maxVal[3]); + + access.setPixel([r, g, b, a], x, y); + } + } +}; + +/** + * @param {tcuTexture.PixelBufferAccess} dst + * @param {Array<number>} minVal + * @param {Array<number>} maxVal + */ +tcuTextureUtil.fillWithComponentGradients3D = function(dst, minVal, maxVal) { + for (var z = 0; z < dst.getDepth(); z++) { + var p = (z + 0.5) / dst.getDepth(); + var b = tcuTextureUtil.linearInterpolate(p, minVal[2], maxVal[2]); + for (var y = 0; y < dst.getHeight(); y++) { + var t = (y + 0.5) / dst.getHeight(); + var g = tcuTextureUtil.linearInterpolate(t, minVal[1], maxVal[1]); + for (var x = 0; x < dst.getWidth(); x++) { + var s = (x + 0.5) / dst.getWidth(); + var r = tcuTextureUtil.linearInterpolate(s, minVal[0], maxVal[0]); + var a = tcuTextureUtil.linearInterpolate(1 - (s + t + p) / 3, minVal[3], maxVal[3]); + dst.setPixel([r, g, b, a], x, y, z); + } + } + } +}; + +/** + * @param {tcuTexture.PixelBufferAccess} access + * @param {Array<number>} minVal + * @param {Array<number>} maxVal + */ +tcuTextureUtil.fillWithComponentGradients = function(access, minVal, maxVal) { + if (access.getHeight() == 1 && access.getDepth() == 1) + tcuTextureUtil.fillWithComponentGradients1D(access, minVal, maxVal); + else if (access.getDepth() == 1) + tcuTextureUtil.fillWithComponentGradients2D(access, minVal, maxVal); + else + tcuTextureUtil.fillWithComponentGradients3D(access, minVal, maxVal); +}; + +/** + * @param {tcuTexture.PixelBufferAccess} dst + */ +tcuTextureUtil.fillWithRGBAQuads = function(dst) { + checkMessage(dst.getDepth() == 1, 'Depth must be 1'); + var width = dst.getWidth(); + var height = dst.getHeight(); + var left = width / 2; + var top = height / 2; + + tcuTextureUtil.getSubregion(dst, 0, 0, 0, left, top, 1).clear([1.0, 0.0, 0.0, 1.0]); + tcuTextureUtil.getSubregion(dst, left, 0, 0, width - left, top, 1).clear([0.0, 1.0, 0.0, 1.0]); + tcuTextureUtil.getSubregion(dst, 0, top, 0, left, height - top, 1).clear([0.0, 0.0, 1.0, 0.0]); + tcuTextureUtil.getSubregion(dst, left, top, 0, width - left, height - top, 1).clear([0.5, 0.5, 0.5, 1.0]); +}; + +// \todo [2012-11-13 pyry] There is much better metaballs code in CL SIR value generators. +/** + * @param {tcuTexture.PixelBufferAccess} dst + * @param {number} numBalls + * @param {number} seed + */ +tcuTextureUtil.fillWithMetaballs = function(dst, numBalls, seed) { + checkMessage(dst.getDepth() == 1, 'Depth must be 1'); + var points = []; + var rnd = new deRandom.Random(seed); + + for (var i = 0; i < numBalls; i++) { + var x = rnd.getFloat(); + var y = rnd.getFloat(); + points[i] = [x, y]; + } + + for (var y = 0; y < dst.getHeight(); y++) + for (var x = 0; x < dst.getWidth(); x++) { + var p = [x / dst.getWidth(), y / dst.getHeight()]; + + var sum = 0.0; + for (var pointNdx = 0; pointNdx < points.length; pointNdx++) { + var d = deMath.subtract(p, points[pointNdx]); + var f = 0.01 / (d[0] * d[0] + d[1] * d[1]); + + sum += f; + } + + dst.setPixel([sum, sum, sum, sum], x, y); + } +}; + +/** + * Create tcuTextureUtil.TextureFormatInfo. + * @constructor + * @param {Array<number>} valueMin + * @param {Array<number>} valueMax + * @param {Array<number>} lookupScale + * @param {Array<number>} lookupBias + */ +tcuTextureUtil.TextureFormatInfo = function(valueMin, valueMax, lookupScale, lookupBias) { + /** @type {Array<number>} */ this.valueMin = valueMin; + /** @type {Array<number>} */ this.valueMax = valueMax; + /** @type {Array<number>} */ this.lookupScale = lookupScale; + /** @type {Array<number>} */ this.lookupBias = lookupBias; +}; + +/** + * @param {?tcuTexture.ChannelType} channelType + * @return {Array<number>} + */ +tcuTextureUtil.getChannelValueRange = function(channelType) { + var cMin = 0; + var cMax = 0; + + switch (channelType) { + // Signed normalized formats. + case tcuTexture.ChannelType.SNORM_INT8: + case tcuTexture.ChannelType.SNORM_INT16: cMin = -1; cMax = 1; break; + + // Unsigned normalized formats. + case tcuTexture.ChannelType.UNORM_INT8: + case tcuTexture.ChannelType.UNORM_INT16: + case tcuTexture.ChannelType.UNORM_SHORT_565: + case tcuTexture.ChannelType.UNORM_SHORT_4444: + case tcuTexture.ChannelType.UNORM_INT_101010: + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: cMin = 0; cMax = 1; break; + + // Misc formats. + case tcuTexture.ChannelType.SIGNED_INT8: cMin = -128; cMax = 127; break; + case tcuTexture.ChannelType.SIGNED_INT16: cMin = -32768; cMax = 32767; break; + case tcuTexture.ChannelType.SIGNED_INT32: cMin = -2147483648; cMax = 2147483647; break; + case tcuTexture.ChannelType.UNSIGNED_INT8: cMin = 0; cMax = 255; break; + case tcuTexture.ChannelType.UNSIGNED_INT16: cMin = 0; cMax = 65535; break; + case tcuTexture.ChannelType.UNSIGNED_INT32: cMin = 0; cMax = 4294967295; break; + case tcuTexture.ChannelType.HALF_FLOAT: cMin = -1e3; cMax = 1e3; break; + case tcuTexture.ChannelType.FLOAT: cMin = -1e5; cMax = 1e5; break; + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: cMin = 0; cMax = 1e4; break; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: cMin = 0; cMax = 1e5; break; + + default: + DE_ASSERT(false); + } + + return [cMin, cMax]; +}; + +/** + * Creates an array by choosing between 'a' and 'b' based on 'cond' array. + * @param {Array | number} a + * @param {Array | number} b + * @param {Array<boolean>} cond Condtions + * @return {Array} + */ +tcuTextureUtil.select = function(a, b, cond) { + + /*DE_ASSERT(!(a.length && !b.length) + || !(!a.length && b.length) + || !((a.length && b.length) && ((a.length != b.length) || (b.length != cond.length) || (a.length != cond.length))));*/ + + if (a.length && !b.length) throw new Error('second input parameter is not a vector'); + if (!a.length && b.length) throw new Error('first input parameter is not a vector'); + if ((a.length && b.length) && ((a.length != b.length) || (b.length != cond.length) || (a.length != cond.length))) throw new Error('different size vectors'); + + var dst = []; + for (var i = 0; i < cond.length; i++) + if (cond[i]) { + if (a.length) dst.push(a[i]); + else dst.push(a); + } else { + if (b.length) dst.push(b[i]); + else dst.push(b); + } + return dst; +}; + +/** + * Get standard parameters for testing texture format + * + * Returns tcuTextureUtil.TextureFormatInfo that describes good parameters for exercising + * given TextureFormat. Parameters include value ranges per channel and + * suitable lookup scaling and bias in order to reduce result back to + * 0..1 range. + * + * @param {tcuTexture.TextureFormat} format + * @return {tcuTextureUtil.TextureFormatInfo} + */ +tcuTextureUtil.getTextureFormatInfo = function(format) { + // Special cases. + if (format.isEqual(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV))) + return new tcuTextureUtil.TextureFormatInfo([0, 0, 0, 0], + [1023, 1023, 1023, 3], + [1 / 1023, 1 / 1023, 1 / 1023, 1 / 3], + [0, 0, 0, 0]); + else if (format.order == tcuTexture.ChannelOrder.D || format.order == tcuTexture.ChannelOrder.DS) + return new tcuTextureUtil.TextureFormatInfo([0, 0, 0, 0], + [1, 1, 1, 0], + [1, 1, 1, 1], + [0, 0, 0, 0]); // Depth / stencil formats. + else if (format.isEqual(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_SHORT_5551))) + return new tcuTextureUtil.TextureFormatInfo([0, 0, 0, 0.5], + [1, 1, 1, 1.5], + [1, 1, 1, 1], + [0, 0, 0, 0]); + + var cRange = tcuTextureUtil.getChannelValueRange(format.type); + var chnMask = null; + + switch (format.order) { + case tcuTexture.ChannelOrder.R: chnMask = [true, false, false, false]; break; + case tcuTexture.ChannelOrder.A: chnMask = [false, false, false, true]; break; + case tcuTexture.ChannelOrder.L: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.LA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.RG: chnMask = [true, true, false, false]; break; + case tcuTexture.ChannelOrder.RGB: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.RGBA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.sRGB: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.sRGBA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.D: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.DS: chnMask = [true, true, true, true]; break; + default: + DE_ASSERT(false); + } + + var scale = 1 / (cRange[1] - cRange[0]); + var bias = -cRange[0] * scale; + + return new tcuTextureUtil.TextureFormatInfo(tcuTextureUtil.select(cRange[0], 0, chnMask), + tcuTextureUtil.select(cRange[1], 0, chnMask), + tcuTextureUtil.select(scale, 1, chnMask), + tcuTextureUtil.select(bias, 0, chnMask)); +}; + +/** tcuTextureUtil.getChannelBitDepth + * @param {?tcuTexture.ChannelType} channelType + * @return {Array<number>} + */ +tcuTextureUtil.getChannelBitDepth = function(channelType) { + + switch (channelType) { + case tcuTexture.ChannelType.SNORM_INT8: return [8, 8, 8, 8]; + case tcuTexture.ChannelType.SNORM_INT16: return [16, 16, 16, 16]; + case tcuTexture.ChannelType.SNORM_INT32: return [32, 32, 32, 32]; + case tcuTexture.ChannelType.UNORM_INT8: return [8, 8, 8, 8]; + case tcuTexture.ChannelType.UNORM_INT16: return [16, 16, 16, 16]; + case tcuTexture.ChannelType.UNORM_INT32: return [32, 32, 32, 32]; + case tcuTexture.ChannelType.UNORM_SHORT_565: return [5, 6, 5, 0]; + case tcuTexture.ChannelType.UNORM_SHORT_4444: return [4, 4, 4, 4]; + case tcuTexture.ChannelType.UNORM_SHORT_555: return [5, 5, 5, 0]; + case tcuTexture.ChannelType.UNORM_SHORT_5551: return [5, 5, 5, 1]; + case tcuTexture.ChannelType.UNORM_INT_101010: return [10, 10, 10, 0]; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: return [10, 10, 10, 2]; + case tcuTexture.ChannelType.SIGNED_INT8: return [8, 8, 8, 8]; + case tcuTexture.ChannelType.SIGNED_INT16: return [16, 16, 16, 16]; + case tcuTexture.ChannelType.SIGNED_INT32: return [32, 32, 32, 32]; + case tcuTexture.ChannelType.UNSIGNED_INT8: return [8, 8, 8, 8]; + case tcuTexture.ChannelType.UNSIGNED_INT16: return [16, 16, 16, 16]; + case tcuTexture.ChannelType.UNSIGNED_INT32: return [32, 32, 32, 32]; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: return [10, 10, 10, 2]; + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: return [24, 0, 0, 8]; + case tcuTexture.ChannelType.HALF_FLOAT: return [16, 16, 16, 16]; + case tcuTexture.ChannelType.FLOAT: return [32, 32, 32, 32]; + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: return [11, 11, 10, 0]; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: return [9, 9, 9, 0]; + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: return [32, 0, 0, 8]; + default: + DE_ASSERT(false); + return [0, 0, 0, 0]; + } +}; + +/** tcuTextureUtil.getTextureFormatBitDepth + * @param {tcuTexture.TextureFormat} format + * @return {Array<number>} + */ +tcuTextureUtil.getTextureFormatBitDepth = function(format) { + + /** @type {Array<number>} */ var chnBits = tcuTextureUtil.getChannelBitDepth(format.type); // IVec4 + /** @type {Array<boolean>} */ var chnMask = [false, false, false, false]; // BVec4 + /** @type {Array<number>} */ var chnSwz = [0, 1, 2, 3]; // IVec4 + + switch (format.order) { + case tcuTexture.ChannelOrder.R: chnMask = [true, false, false, false]; break; + case tcuTexture.ChannelOrder.A: chnMask = [false, false, false, true]; break; + case tcuTexture.ChannelOrder.RA: chnMask = [true, false, false, true]; break; + case tcuTexture.ChannelOrder.L: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.I: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.LA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.RG: chnMask = [true, true, false, false]; break; + case tcuTexture.ChannelOrder.RGB: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.RGBA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.BGRA: chnMask = [true, true, true, true]; chnSwz = [2, 1, 0, 3]; break; + case tcuTexture.ChannelOrder.ARGB: chnMask = [true, true, true, true]; chnSwz = [1, 2, 3, 0]; break; + case tcuTexture.ChannelOrder.sRGB: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.sRGBA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.D: chnMask = [true, false, false, false]; break; + case tcuTexture.ChannelOrder.DS: chnMask = [true, false, false, true]; break; + case tcuTexture.ChannelOrder.S: chnMask = [false, false, false, true]; break; + default: + DE_ASSERT(false); + } + + return tcuTextureUtil.select(deMath.swizzle(chnBits, [chnSwz[0], chnSwz[1], chnSwz[2], chnSwz[3]]), [0, 0, 0, 0], chnMask); + +}; + +/** tcuTextureUtil.fillWithGrid + * @const @param {tcuTexture.PixelBufferAccess} access + * @param {number} cellSize + * @param {Array<number>} colorA + * @param {Array<number>} colorB + */ +tcuTextureUtil.fillWithGrid = function(access, cellSize, colorA, colorB) { + if (access.getHeight() == 1 && access.getDepth() == 1) + tcuTextureUtil.fillWithGrid1D(access, cellSize, colorA, colorB); + else if (access.getDepth() == 1) + tcuTextureUtil.fillWithGrid2D(access, cellSize, colorA, colorB); + else + tcuTextureUtil.fillWithGrid3D(access, cellSize, colorA, colorB); +}; + +/** tcuTextureUtil.fillWithGrid1D + * @const @param {tcuTexture.PixelBufferAccess} access + * @param {number} cellSize + * @param {Array<number>} colorA + * @param {Array<number>} colorB + */ +tcuTextureUtil.fillWithGrid1D = function(access, cellSize, colorA, colorB) { + for (var x = 0; x < access.getWidth(); x++) { + var mx = Math.floor(x / cellSize) % 2; + + if (mx) + access.setPixel(colorB, x, 0); + else + access.setPixel(colorA, x, 0); + } +}; + +/** tcuTextureUtil.fillWithGrid2D + * @const @param {tcuTexture.PixelBufferAccess} access + * @param {number} cellSize + * @param {Array<number>} colorA + * @param {Array<number>} colorB + */ +tcuTextureUtil.fillWithGrid2D = function(access, cellSize, colorA, colorB) { + for (var y = 0; y < access.getHeight(); y++) + for (var x = 0; x < access.getWidth(); x++) { + var mx = Math.floor(x / cellSize) % 2; + var my = Math.floor(y / cellSize) % 2; + + if (mx ^ my) + access.setPixel(colorB, x, y); + else + access.setPixel(colorA, x, y); + } +}; + +/** tcuTextureUtil.fillWithGrid3D + * @const @param {tcuTexture.PixelBufferAccess} access + * @param {number} cellSize + * @param {Array<number>} colorA + * @param {Array<number>} colorB + */ +tcuTextureUtil.fillWithGrid3D = function(access, cellSize, colorA, colorB) { + for (var z = 0; z < access.getDepth(); z++) + for (var y = 0; y < access.getHeight(); y++) + for (var x = 0; x < access.getWidth(); x++) { + var mx = Math.floor(x / cellSize) % 2; + var my = Math.floor(y / cellSize) % 2; + var mz = Math.floor(z / cellSize) % 2; + + if (mx ^ my ^ mz) + access.setPixel(colorB, x, y, z); + else + access.setPixel(colorA, x, y, z); + } +}; + +/** + * @const @param {tcuTexture.TextureFormat} format + * @return {Array<number>} + */ +tcuTextureUtil.getTextureFormatMantissaBitDepth = function(format) { + /** @type {Array<number>} */ var chnBits = tcuTextureUtil.getChannelMantissaBitDepth(format.type); + /** @type {Array<boolean>} */ var chnMask = [false, false, false, false]; + /** @type {Array<number>} */ var chnSwz = [0, 1, 2, 3]; + + switch (format.order) { + case tcuTexture.ChannelOrder.R: chnMask = [true, false, false, false]; break; + case tcuTexture.ChannelOrder.A: chnMask = [false, false, false, true]; break; + case tcuTexture.ChannelOrder.RA: chnMask = [true, false, false, true]; break; + case tcuTexture.ChannelOrder.L: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.I: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.LA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.RG: chnMask = [true, true, false, false]; break; + case tcuTexture.ChannelOrder.RGB: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.RGBA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.BGRA: chnMask = [true, true, true, true]; chnSwz = [2, 1, 0, 3]; break; + case tcuTexture.ChannelOrder.ARGB: chnMask = [true, true, true, true]; chnSwz = [1, 2, 3, 0]; break; + case tcuTexture.ChannelOrder.sRGB: chnMask = [true, true, true, false]; break; + case tcuTexture.ChannelOrder.sRGBA: chnMask = [true, true, true, true]; break; + case tcuTexture.ChannelOrder.D: chnMask = [true, false, false, false]; break; + case tcuTexture.ChannelOrder.DS: chnMask = [true, false, false, true]; break; + case tcuTexture.ChannelOrder.S: chnMask = [false, false, false, true]; break; + default: + DE_ASSERT(false); + } + return tcuTextureUtil.select(deMath.swizzle(chnBits, [chnSwz[0], chnSwz[1], chnSwz[2], chnSwz[3]]), [0, 0, 0, 0], chnMask); +}; + +/** + * @param {?tcuTexture.ChannelType} channelType + * @return {Array<number>} + */ +tcuTextureUtil.getChannelMantissaBitDepth = function(channelType) { + switch (channelType) { + case tcuTexture.ChannelType.SNORM_INT8: + case tcuTexture.ChannelType.SNORM_INT16: + case tcuTexture.ChannelType.SNORM_INT32: + case tcuTexture.ChannelType.UNORM_INT8: + case tcuTexture.ChannelType.UNORM_INT16: + case tcuTexture.ChannelType.UNORM_INT32: + case tcuTexture.ChannelType.UNORM_SHORT_565: + case tcuTexture.ChannelType.UNORM_SHORT_4444: + case tcuTexture.ChannelType.UNORM_SHORT_555: + case tcuTexture.ChannelType.UNORM_SHORT_5551: + case tcuTexture.ChannelType.UNORM_INT_101010: + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: + case tcuTexture.ChannelType.SIGNED_INT8: + case tcuTexture.ChannelType.SIGNED_INT16: + case tcuTexture.ChannelType.SIGNED_INT32: + case tcuTexture.ChannelType.UNSIGNED_INT8: + case tcuTexture.ChannelType.UNSIGNED_INT16: + case tcuTexture.ChannelType.UNSIGNED_INT32: + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: + return tcuTextureUtil.getChannelBitDepth(channelType); + case tcuTexture.ChannelType.HALF_FLOAT: return [10, 10, 10, 10]; + case tcuTexture.ChannelType.FLOAT: return [23, 23, 23, 23]; + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: return [6, 6, 5, 0]; + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: return [23, 0, 0, 8]; + default: + throw new Error('Invalid channelType: ' + channelType); + } +}; + +/** + * @param {tcuTexture.PixelBufferAccess} dst + * @param {tcuTexture.ConstPixelBufferAccess} src + */ +tcuTextureUtil.copy = function(dst, src) { + var width = dst.getWidth(); + var height = dst.getHeight(); + var depth = dst.getDepth(); + + DE_ASSERT(src.getWidth() == width && src.getHeight() == height && src.getDepth() == depth); + + if (src.getFormat().isEqual(dst.getFormat())) { + var srcData = src.getDataPtr(); + var dstData = dst.getDataPtr(); + + if (srcData.length == dstData.length) { + dstData.set(srcData); + return; + } + } + var srcClass = tcuTexture.getTextureChannelClass(src.getFormat().type); + var dstClass = tcuTexture.getTextureChannelClass(dst.getFormat().type); + var srcIsInt = srcClass == tcuTexture.TextureChannelClass.SIGNED_INTEGER || srcClass == tcuTexture.TextureChannelClass.UNSIGNED_INTEGER; + var dstIsInt = dstClass == tcuTexture.TextureChannelClass.SIGNED_INTEGER || dstClass == tcuTexture.TextureChannelClass.UNSIGNED_INTEGER; + + if (srcIsInt && dstIsInt) { + for (var z = 0; z < depth; z++) + for (var y = 0; y < height; y++) + for (var x = 0; x < width; x++) + dst.setPixelInt(src.getPixelInt(x, y, z), x, y, z); + } else { + for (var z = 0; z < depth; z++) + for (var y = 0; y < height; y++) + for (var x = 0; x < width; x++) + dst.setPixel(src.getPixel(x, y, z), x, y, z); + } +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} access + */ +tcuTextureUtil.estimatePixelValueRange = function(access) { + var format = access.getFormat(); + + switch (format.type) { + case tcuTexture.ChannelType.UNORM_INT8: + case tcuTexture.ChannelType.UNORM_INT16: + // Normalized unsigned formats. + return [ + [0, 0, 0, 0], + [1, 1, 1, 1] + ]; + + case tcuTexture.ChannelType.SNORM_INT8: + case tcuTexture.ChannelType.SNORM_INT16: + // Normalized signed formats. + return [ + [-1, -1, -1, -1], + [1, 1, 1, 1] + ]; + + default: + // \note Samples every 4/8th pixel. + var minVal = [Infinity, Infinity, Infinity, Infinity]; + var maxVal = [-Infinity, -Infinity, -Infinity, -Infinity]; + + for (var z = 0; z < access.getDepth(); z += 2) { + for (var y = 0; y < access.getHeight(); y += 2) { + for (var x = 0; x < access.getWidth(); x += 2) { + var p = access.getPixel(x, y, z); + + minVal[0] = Math.min(minVal[0], p[0]); + minVal[1] = Math.min(minVal[1], p[1]); + minVal[2] = Math.min(minVal[2], p[2]); + minVal[3] = Math.min(minVal[3], p[3]); + + maxVal[0] = Math.max(maxVal[0], p[0]); + maxVal[1] = Math.max(maxVal[1], p[1]); + maxVal[2] = Math.max(maxVal[2], p[2]); + maxVal[3] = Math.max(maxVal[3], p[3]); + } + } + } + return [minVal, maxVal]; + } +}; + +/** + * @param {tcuTexture.ConstPixelBufferAccess} access + * @return {{scale: Array<number>, bias: Array<number>}} + */ +tcuTextureUtil.computePixelScaleBias = function(access) { + var limits = tcuTextureUtil.estimatePixelValueRange(access); + var minVal = limits[0]; + var maxVal = limits[1]; + + var scale = [1, 1, 1, 1]; + var bias = [0, 0, 0, 0]; + + var eps = 0.0001; + + for (var c = 0; c < 4; c++) { + if (maxVal[c] - minVal[c] < eps) { + scale[c] = (maxVal[c] < eps) ? 1 : (1 / maxVal[c]); + bias[c] = (c == 3) ? (1 - maxVal[c] * scale[c]) : (0 - minVal[c] * scale[c]); + } else { + scale[c] = 1 / (maxVal[c] - minVal[c]); + bias[c] = 0 - minVal[c] * scale[c]; + } + } + + return { + scale: scale, + bias: bias + }; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deMath.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deMath.js new file mode 100644 index 000000000..7551d6c1b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deMath.js @@ -0,0 +1,1061 @@ +/*------------------------------------------------------------------------- + * 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('framework.delibs.debase.deMath'); + +/** @typedef { (Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array) } */ +goog.TypedArray; + +/** @typedef { (Array<number>|Array<boolean>|goog.TypedArray) } */ +goog.NumberArray; + +goog.scope(function() { + +var deMath = framework.delibs.debase.deMath; + +/** @const */ deMath.INT32_SIZE = 4; + +deMath.deInRange32 = function(a, mn, mx) { + return (a >= mn) && (a <= mx); +}; + +deMath.deInBounds32 = function(a, mn, mx) { + return (a >= mn) && (a < mx); +}; + +/** + * @param {number} a + * @return {number} + */ +deMath.deFloatFrac = function(a) { return a - Math.floor(a); }; + +/** + * Transform a 64-bit float number into a 32-bit float number. + * Native dEQP uses 32-bit numbers, so sometimes 64-bit floating numbers in JS should be transformed into 32-bit ones to ensure the correctness of the result. + * @param {number} a + * @return {number} + */ +deMath.toFloat32 = (function() { + var FLOAT32ARRAY1 = new Float32Array(1); + return function(a) { + FLOAT32ARRAY1[0] = a; + return FLOAT32ARRAY1[0]; + }; +})(); + +/** @const */ deMath.INV_LOG_2_FLOAT32 = deMath.toFloat32(1.44269504089); /** 1.0 / log_e(2.0) */ + +/** + * Check if a value is a power-of-two. + * @param {number} a Input value. + * @return {boolean} return True if input is a power-of-two value, false otherwise. + * (Also returns true for zero). + */ +deMath.deIsPowerOfTwo32 = function(a) { + return ((a & (a - 1)) == 0); +}; + +/** + * Align an integer to given power-of-two size. + * @param {number} val The number to align. + * @param {number} align The size to align to. + * @return {number} The aligned value + */ +deMath.deAlign32 = function(val, align) { + if (!deMath.deIsPowerOfTwo32(align)) + throw new Error('Not a power of 2: ' + align); + return ((val + align - 1) & ~(align - 1)) & 0xFFFFFFFF; //0xFFFFFFFF make sure it returns a 32 bit calculation in 64 bit browsers. +}; + +/** + * Compute the bit population count of an integer. + * @param {number} a + * @return {number} The number of one bits in + */ +deMath.dePop32 = function(a) { + /** @type {number} */ var mask0 = 0x55555555; /* 1-bit values. */ + /** @type {number} */ var mask1 = 0x33333333; /* 2-bit values. */ + /** @type {number} */ var mask2 = 0x0f0f0f0f; /* 4-bit values. */ + /** @type {number} */ var mask3 = 0x00ff00ff; /* 8-bit values. */ + /** @type {number} */ var mask4 = 0x0000ffff; /* 16-bit values. */ + /** @type {number} */ var t = a & 0xFFFFFFFF; /* Crop to 32-bit value */ + t = (t & mask0) + ((t >> 1) & mask0); + t = (t & mask1) + ((t >> 2) & mask1); + t = (t & mask2) + ((t >> 4) & mask2); + t = (t & mask3) + ((t >> 8) & mask3); + t = (t & mask4) + (t >> 16); + return t; +}; + +deMath.clamp = function(val, minParm, maxParm) { + return Math.min(Math.max(val, minParm), maxParm); +}; + +/** + * @param {Array<number>} values + * @param {number} minParm + * @param {number} maxParm + * @return {Array<number>} + */ +deMath.clampVector = function(values, minParm, maxParm) { + var result = []; + for (var i = 0; i < values.length; i++) + result.push(deMath.clamp(values[i], minParm, maxParm)); + return result; +}; + +deMath.imod = function(a, b) { + var m = a % b; + return m < 0 ? m + b : m; +}; + +deMath.mirror = function(a) { + return a >= 0 ? a : -(1 + a); +}; + +/** + * @param {goog.NumberArray} a Source array + * @param {goog.NumberArray} indices + * @return {Array<number>} Swizzled array + */ +deMath.swizzle = function(a, indices) { + if (!indices.length) + throw new Error('Argument must be an array'); + var dst = []; + for (var i = 0; i < indices.length; i++) + dst.push(a[indices[i]]); + return dst; +}; + +/** + * Shift left elements of array a by elements of array b + * dst[n] a[n] << b[n] + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} Result array + */ +deMath.arrayShiftLeft = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] << b[i]); + return dst; +}; + +/** + * Multiply two vectors, element by element + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} Result array + */ + +deMath.multiply = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] * b[i]); + return dst; +}; + +/** + * Divide two vectors, element by element + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} Result array + * @throws {Error} + */ + +deMath.divide = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) { + if (b[i] === 0) + throw new Error('Division by 0'); + dst.push(a[i] / b[i]); + } + return dst; +}; + +/** + * Divide vector by a scalar + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} Result array + */ +deMath.divideScale = function(a, b) { + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] / b); + return dst; +}; + +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +deMath.mod = function(a, b) { + return a - b * Math.floor(a / b); +}; + +/** + * Modulus vector by a scalar + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} Result array + */ +deMath.modScale = function(a, b) { + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(deMath.mod(a[i], b)); + return dst; +}; + +/** + * Multiply vector by a scalar + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} Result array + */ +deMath.scale = function(a, b) { + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] * b); + return dst; +}; + +/** + * Add vector and scalar, element by element + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} Result array + */ +deMath.addScalar = function(a, b) { + if (!Array.isArray(a)) + throw new Error('First argument must be an array.'); + if (typeof b !== 'number') + throw new Error('Second argument must be a number.'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] + b); + return dst; +}; + +/** + * Add two vectors, element by element + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} Result array + */ +deMath.add = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] + b[i]); + return dst; +}; + +/** + * Subtract two vectors, element by element + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} Result array + */ + +deMath.subtract = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] - b[i]); + return dst; +}; + +/** + * Subtract vector and scalar, element by element + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} Result array + */ +deMath.subScalar = function(a, b) { + if (!Array.isArray(a)) + throw new Error('First argument must be an array.'); + if (typeof b !== 'number') + throw new Error('Second argument must be a number.'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] - b); + return dst; +}; + +/** + * Calculate absolute difference between two vectors + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} abs(diff(a - b)) + */ +deMath.absDiff = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(Math.abs(a[i] - b[i])); + return dst; +}; + +/** + * Calculate absolute value of a vector + * @param {goog.NumberArray} a + * @return {Array<number>} abs(a) + */ +deMath.abs = function(a) { + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(Math.abs(a[i])); + return dst; +}; + +/** + * Is a <= b (element by element)? + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<boolean>} Result array of booleans + */ +deMath.lessThanEqual = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i] <= b[i]); + return dst; +}; + +/** + * Is a === b (element by element)? + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {boolean} Result + */ +deMath.equal = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + for (var i = 0; i < a.length; i++) { + if (a[i] !== b[i]) + return false; + } + return true; +}; + +/** + * Are all values in the array true? + * @param {Array<boolean>} a + * @return {boolean} + */ +deMath.boolAll = function(a) { + for (var i = 0; i < a.length; i++) + if (a[i] == false) + return false; + return true; +}; + +/** + * deMath.assign(a, b) element by element + * @param {goog.NumberArray} a + * @return {Array<number>} + */ +deMath.assign = function(a) { + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(a[i]); + return dst; +}; + +/** + * deMath.max(a, b) element by element + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} + */ +deMath.max = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(Math.max(a[i], b[i])); + return dst; +}; + +/** + * deMath.min(a, b) element by element + * @param {goog.NumberArray} a + * @param {goog.NumberArray} b + * @return {Array<number>} + */ +deMath.min = function(a, b) { + if (a.length != b.length) + throw new Error('Arrays must have the same size'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(Math.min(a[i], b[i])); + return dst; +}; + +// Nearest-even rounding in case of tie (fractional part 0.5), otherwise ordinary rounding. +deMath.rint = function(a) { + var floorVal = Math.floor(a); + var fracVal = a - floorVal; + + if (fracVal != 0.5) + return Math.round(a); // Ordinary case. + + var roundUp = (floorVal % 2) != 0; + + return floorVal + (roundUp ? 1 : 0); +}; + +/** + * wrap the number, so that it fits in the range [minValue, maxValue] + * @param {number} v + * @param {number} minValue + * @param {number} maxValue + * @return {number} + */ +deMath.wrap = function(v, minValue, maxValue) { + var range = maxValue - minValue + 1; + + if (v < minValue) { + v += range * (Math.floor((minValue - v) / range) + 1); + } + return minValue + Math.floor((v - minValue) % range); +}; + +/** + * Round number to int by dropping fractional part + * it is equivalent of GLSL int() constructor + * @param {number} a + * @return {number} + */ +deMath.intCast = function(a) { + var v; + if (a >= 0) + v = Math.floor(a); + else + v = Math.ceil(a); + return deMath.wrap(v, -0x80000000, 0x7FFFFFFF); +}; + +/** + * Round number to uint by dropping fractional part + * it is equivalent of GLSL uint() constructor + * @param {number} a + * @return {number} + */ +deMath.uintCast = function(a) { + var v; + if (a >= 0) + v = Math.floor(a); + else + v = Math.ceil(a); + return deMath.wrap(v, 0, 0xFFFFFFFF); +}; + +/** + * @param {number} a + * @return {number} + */ +deMath.logToFloor = function(a) { + assertMsgOptions(a > 0, 'Value is less or equal than zero', false, true); + return 31 - deMath.clz32(a); +}; + +/** + * Find intersection of two rectangles + * @param {goog.NumberArray} a Array [x, y, width, height] + * @param {goog.NumberArray} b Array [x, y, width, height] + * @return {Array<number>} + */ +deMath.intersect = function(a, b) { + if (a.length != 4) + throw new Error('Array "a" must have length 4 but has length: ' + a.length); + if (b.length != 4) + throw new Error('Array "b" must have length 4 but has length: ' + b.length); + var x0 = Math.max(a[0], b[0]); + var y0 = Math.max(a[1], b[1]); + var x1 = Math.min(a[0] + a[2], b[0] + b[2]); + var y1 = Math.min(a[1] + a[3], b[1] + b[3]); + var w = Math.max(0, x1 - x0); + var h = Math.max(0, y1 - y0); + + return [x0, y0, w, h]; +}; + +/** deMath.deMathHash + * @param {number} a + * @return {number} + */ +deMath.deMathHash = function(a) { + var key = a; + key = (key ^ 61) ^ (key >> 16); + key = key + (key << 3); + key = key ^ (key >> 4); + key = key * 0x27d4eb2d; /* prime/odd constant */ + key = key ^ (key >> 15); + return key; +}; + +/** + * Converts a byte array to a number + * @param {Uint8Array} array + * @return {number} + */ +deMath.arrayToNumber = function(array) { + /** @type {number} */ var result = 0; + + for (var ndx = 0; ndx < array.length; ndx++) { + result += array[ndx] * Math.pow(256, ndx); + } + + return result; +}; + +/** + * Fills a byte array with a number + * @param {Uint8Array} array Output array (already resized) + * @param {number} number + */ +deMath.numberToArray = function(array, number) { + for (var byteNdx = 0; byteNdx < array.length; byteNdx++) { + /** @type {number} */ var acumzndx = !byteNdx ? number : Math.floor(number / Math.pow(256, byteNdx)); + array[byteNdx] = acumzndx & 0xFF; + } +}; + +/** + * Obtains the bit fragment from a number + * @param {number} x + * @param {number} firstNdx + * @param {number} lastNdx + * @return {number} + */ +deMath.getBitRange = function(x, firstNdx, lastNdx) { + var shifted = deMath.shiftRight(x, firstNdx); + var bitSize = lastNdx - firstNdx; + var mask; + if (bitSize < 32) + mask = (1 << bitSize) - 1; + else + mask = Math.pow(2, bitSize) - 1; + var masked = deMath.binaryAnd(shifted, mask); + return masked; +}; + +/** + * Split a large signed number into low and high 32bit dwords. + * @param {number} x + * @return {Array<number>} + */ +deMath.split32 = function(x) { + var ret = []; + ret[1] = Math.floor(x / 0x100000000); + ret[0] = x - ret[1] * 0x100000000; + return ret; +}; + +/** + * Split a signed number's low 32bit dwords into low and high 16bit dwords. + * @param {number} x + * @return {Array<number>} + */ +deMath.split16 = function(x) { + var ret = []; + x = x & 0xffffffff; + ret[1] = Math.floor(x / 0x10000); + ret[0] = x - ret[1] * 0x10000; + return ret; +}; + +/** + * Recontruct a number from high and low 32 bit dwords + * @param {Array<number>} x + * @return {number} + */ +deMath.join32 = function(x) { + var v0 = x[0] >= 0 ? x[0] : 0x100000000 + x[0]; + var v1 = x[1]; + var val = v1 * 0x100000000 + v0; + return val; +}; + +//Bit operations with the help of arrays + +/** + * @enum + */ +deMath.BinaryOp = { + AND: 0, + OR: 1, + XOR: 2 +}; + +/** + * Performs a normal (native) binary operation + * @param {number} valueA First operand + * @param {number} valueB Second operand + * @param {deMath.BinaryOp} operation The desired operation to perform + * @return {number} + */ +deMath.doNativeBinaryOp = function(valueA, valueB, operation) { + switch (operation) { + case deMath.BinaryOp.AND: + return valueA & valueB; + case deMath.BinaryOp.OR: + return valueA | valueB; + case deMath.BinaryOp.XOR: + return valueA ^ valueB; + default: + throw new Error('Unknown operation: ' + operation); + } +}; + +/** + * Performs a binary operation between two operands + * with the help of arrays to avoid losing the internal binary representation. + * @param {number} valueA First operand + * @param {number} valueB Second operand + * @param {deMath.BinaryOp} binaryOpParm The desired operation to perform + * @return {number} + */ +deMath.binaryOp = function(valueA, valueB, binaryOpParm) { + //quick path if values fit in signed 32 bit range + if (deMath.deInRange32(valueA, -0x80000000, 0x7FFFFFFF) && deMath.deInRange32(valueB, -0x80000000, 0x7FFFFFFF)) + return deMath.doNativeBinaryOp(valueA, valueB, binaryOpParm); + + var x = deMath.split32(valueA); + var y = deMath.split32(valueB); + var z = []; + for (var i = 0; i < 2; i++) + z[i] = deMath.doNativeBinaryOp(x[i], y[i], binaryOpParm); + var ret = deMath.join32(z); + return ret; +}; + +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +deMath.binaryAnd = function(a, b) { + return deMath.binaryOp(a, b, deMath.BinaryOp.AND); +}; + +/** + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} + */ +deMath.binaryAndVecScalar = function(a, b) { + if (!Array.isArray(a)) + throw new Error('First argument must be an array.'); + if (typeof b !== 'number') + throw new Error('Second argument must be a number.'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.AND)); + return dst; +}; + +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +deMath.binaryOr = function(a, b) { + return deMath.binaryOp(a, b, deMath.BinaryOp.OR); +}; + +/** + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} + */ +deMath.binaryOrVecScalar = function(a, b) { + if (!Array.isArray(a)) + throw new Error('First argument must be an array.'); + if (typeof b !== 'number') + throw new Error('Second argument must be a number.'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.OR)); + return dst; +}; + +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +deMath.binaryXor = function(a, b) { + return deMath.binaryOp(a, b, deMath.BinaryOp.XOR); +}; + +/** + * @param {goog.NumberArray} a + * @param {number} b + * @return {Array<number>} + */ +deMath.binaryXorVecScalar = function(a, b) { + if (!Array.isArray(a)) + throw new Error('First argument must be an array.'); + if (typeof b !== 'number') + throw new Error('Second argument must be a number.'); + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.XOR)); + return dst; +}; + +/** + * Performs a binary NOT operation on an operand + * @param {number} value Operand + * @return {number} + */ +deMath.binaryNot = function(value) { + //quick path if value fits in signed 32 bit range + if (deMath.deInRange32(value, -0x80000000, 0x7FFFFFFF)) + return ~value; + + var x = deMath.split32(value); + x[0] = ~x[0]; + x[1] = ~x[1]; + var ret = deMath.join32(x); + return ret; +}; + +/** + * Shifts the given value 'steps' bits to the left. Replaces << operator + * This function should be used if the expected value will be wider than 32-bits. + * @param {number} value + * @param {number} steps + * @return {number} + */ +deMath.shiftLeft = function(value, steps) { + //quick path + if (steps < 31) { + var v = value * (1 << steps); + if (deMath.deInRange32(v, -0x80000000, 0x7FFFFFFF)) + return v; + } + + if (steps == 0) + return value; + else if (steps < 32) { + var mask = (1 << 32 - steps) - 1; + var x = deMath.split32(value); + var highBits = x[0] & (~mask); + var y = highBits >> (32 - steps); + if (highBits < 0) { + var m = (1 << steps) - 1; + y &= m; + } + var result = []; + result[0] = x[0] << steps; + result[1] = x[1] << steps; + result[1] |= y; + + return deMath.join32(result); + } else { + var x = deMath.split32(value); + var result = []; + result[0] = 0; + result[1] = x[0] << steps - 32; + return deMath.join32(result); + } +}; + +/** + * @param {Array<number>} a + * @param {number} b + */ +deMath.shiftLeftVecScalar = function(a, b) { + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(deMath.shiftLeft(a[i], b)); + return dst; +}; + +/** + * Shifts the given value 'steps' bits to the right. Replaces >> operator + * This function should be used if the value is wider than 32-bits + * @param {number} value + * @param {number} steps + * @return {number} + */ +deMath.shiftRight = function(value, steps) { + //quick path + if (deMath.deInRange32(value, -0x80000000, 0x7FFFFFFF) && steps < 32) + return value >> steps; + + if (steps == 0) + return value; + else if (steps < 32) { + if (steps == 0) + return value; + var mask = (1 << steps) - 1; + var x = deMath.split32(value); + var lowBits = x[1] & mask; + var result = []; + var m = (1 << 32 - steps) - 1; + result[0] = (x[0] >> steps) & m; + result[1] = x[1] >> steps; + result[0] |= lowBits << 32 - steps; + return deMath.join32(result); + } else { + var x = deMath.split32(value); + var result = []; + result[0] = x[1] >> steps - 32; + result[1] = value < 0 ? -1 : 0; + return deMath.join32(result); + } +}; + +/** + * @param {Array<number>} a + * @param {number} b + */ +deMath.shiftRightVecScalar = function(a, b) { + var dst = []; + for (var i = 0; i < a.length; i++) + dst.push(deMath.shiftRight(a[i], b)); + return dst; +}; + +/** deMath.logicalAndBool over two arrays of booleans + * @param {Array<boolean>} a + * @param {Array<boolean>} b + * @return {Array<boolean>} + */ +deMath.logicalAndBool = function(a, b) { + if (!Array.isArray(a)) + throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); + if (!Array.isArray(b)) + throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); + if (a.length != b.length) + throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); + + /** @type {Array<boolean>} */ var result = []; + for (var i = 0; i < a.length; i++) { + if (a[i] & b[i]) + result.push(true); + else + result.push(false); + } + return result; +}; + +/** deMath.logicalOrBool over two arrays of booleans + * @param {Array<boolean>} a + * @param {Array<boolean>} b + * @return {Array<boolean>} + */ +deMath.logicalOrBool = function(a, b) { + if (!Array.isArray(a)) + throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); + if (!Array.isArray(b)) + throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); + if (a.length != b.length) + throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); + + /** @type {Array<boolean>} */ var result = []; + for (var i = 0; i < a.length; i++) { + if (a[i] | b[i]) + result.push(true); + else + result.push(false); + } + return result; +}; + +/** deMath.logicalNotBool over an array of booleans + * @param {Array<boolean>} a + * @return {Array<boolean>} + */ +deMath.logicalNotBool = function(a) { + if (!Array.isArray(a)) + throw new Error('The passed value is not an array: (' + typeof(a) + ')' + a); + + /** @type {Array<boolean>} */ var result = []; + for (var i = 0; i < a.length; i++) + result.push(!a[i]); + return result; +}; + +/** deMath.greaterThan over two arrays of booleans + * @param {Array<number>} a + * @param {Array<number>} b + * @return {Array<boolean>} + */ +deMath.greaterThan = function(a, b) { + if (!Array.isArray(a)) + throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); + if (!Array.isArray(b)) + throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); + if (a.length != b.length) + throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); + + /** @type {Array<boolean>} */ var result = []; + for (var i = 0; i < a.length; i++) + result.push(a[i] > b[i]); + return result; +}; + +/** deMath.greaterThan over two arrays of booleans + * @param {Array<number>} a + * @param {Array<number>} b + * @return {Array<boolean>} + */ +deMath.greaterThanEqual = function(a, b) { + if (!Array.isArray(a)) + throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); + if (!Array.isArray(b)) + throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); + if (a.length != b.length) + throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); + + /** @type {Array<boolean>} */ var result = []; + for (var i = 0; i < a.length; i++) + result.push(a[i] >= b[i]); + return result; +}; + +/** + * Array of float to array of int (0, 255) + * @param {Array<number>} a + * @return {Array<number>} + */ + +deMath.toIVec = function(a) { + /** @type {Array<number>} */ var res = []; + for (var i = 0; i < a.length; i++) + res.push(deMath.clamp(Math.floor(a[i] * 255), 0, 255)); + return res; +}; + +/** + * @param {number} a + * @return {number} + */ + deMath.clz32 = function(a) { + /** @type {number} */ var maxValue = 2147483648; // max 32 bit number + /** @type {number} */ var leadingZeros = 0; + while (a < maxValue) { + maxValue = maxValue >>> 1; + leadingZeros++; + } + return leadingZeros; +}; + +/** + * @param {number} a + * @param {number} exponent + * @return {number} + */ +deMath.deLdExp = function(a, exponent) { + return deMath.ldexp(a, exponent); +}; + +/** + * @param {number} a + * @param {number} exponent + * @return {number} + */ +deMath.deFloatLdExp = function(a, exponent) { + return deMath.ldexp(a, exponent); +}; + +/** + * @param {number} value + * @return {Array<number>} + */ +deMath.frexp = (function() { + var data = new DataView(new ArrayBuffer(8)); + + return function(value) { + if (value === 0) return [value, 0]; + data.setFloat64(0, value); + var bits = (data.getUint32(0) >>> 20) & 0x7FF; + if (bits === 0) { + data.setFloat64(0, value * Math.pow(2, 64)); + bits = ((data.getUint32(0) >>> 20) & 0x7FF) - 64; + } + var exponent = bits - 1022, + mantissa = deMath.ldexp(value, -exponent); + return [mantissa, exponent]; + } +})(); + +/** + * @param {number} mantissa + * @param {number} exponent + * @return {number} + */ +deMath.ldexp = function(mantissa, exponent) { + return exponent > 1023 ? // avoid multiplying by infinity + mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023) : + exponent < -1074 ? // avoid multiplying by zero + mantissa * Math.pow(2, -1074) * Math.pow(2, exponent + 1074) : + mantissa * Math.pow(2, exponent); +}; + +/** + * @param {number} a + * @return {number} + */ +deMath.deCbrt = function(a) { + return deMath.deSign(a) * Math.pow(Math.abs(a), 1.0 / 3.0); +}; + +/** + * @param {number} x + * @return {number} + */ +deMath.deSign = function(x) { + return isNaN(x) ? x : ((x > 0.0) - (x < 0.0)); +}; + +deMath.deFractExp = function(x) { + var result = { + significand: x, + exponent: 0 + }; + + if (isFinite(x)) { + var r = deMath.frexp(x); + result.exponent = r[1] - 1; + result.significand = r[0] * 2; + } + return result; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deRandom.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deRandom.js new file mode 100644 index 000000000..2246a2e9d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deRandom.js @@ -0,0 +1,260 @@ +/*------------------------------------------------------------------------- + * 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. + * + */ + +/** + * This class allows one to create a random integer, floating point number or boolean (TODO, deRandom.choose random items from a list and deRandom.shuffle an array) + */ +'use strict'; +goog.provide('framework.delibs.debase.deRandom'); + +goog.scope(function() { + +var deRandom = framework.delibs.debase.deRandom; + +/** + * Array of pseudo random numbers based on seed + * @constructor + * @struct + */ +deRandom.deRandom = function() { + /** @type {number} */ this.x = 0; + /** @type {number} */ this.y = 0; + /** @type {number} */ this.z = 0; + /** @type {number} */ this.w = 0; +}; + +/** + * deRandom.Random number generator init + * @param {deRandom.deRandom} rnd Array to store random numbers + * @param {number} seed Number for seed + */ +deRandom.deRandom_init = function(rnd, seed) { + rnd.x = (-seed ^ 123456789); + rnd.y = (362436069 * seed); + rnd.z = (521288629 ^ (seed >> 7)); + rnd.w = (88675123 ^ (seed << 3)); +}; + +/** + * Function to get random int + * @param {deRandom.deRandom} rnd Initialised array of random numbers + * @param {Array<number>=} opts Min and max for range + * @return {number} deRandom.Random int + */ +deRandom.deRandom_getInt = function(rnd, opts) { + if (opts != undefined && opts[0] != undefined && opts[1] != undefined) { + if (opts[0] == 0x80000000 && opts[1] == 0x7fffffff) { + return deRandom.deRandom_getInt(rnd); + } else { + return opts[0] + (deRandom.deRandom_getInt(rnd) % (opts[1] - opts[0] + 1)); + } + } + var w = rnd.w; + var t; + + t = rnd.x ^ (rnd.x << 11); + rnd.x = rnd.y; + rnd.y = rnd.z; + rnd.z = w; + rnd.w = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); + return w; +}; + +/** + * Function to get random float + * @param {deRandom.deRandom} rnd Initialised array of random numbers + * @param {Array<number>=} opts Min and max for range + * @return {number} deRandom.Random float + */ +deRandom.deRandom_getFloat = function(rnd, opts) { + if (opts != undefined && opts[0] != undefined && opts[1] != undefined) { + if (opts[0] <= opts[1]) { + return opts[0] + (opts[1] - opts[0]) * deRandom.deRandom_getFloat(rnd); + } + } else { + return (deRandom.deRandom_getInt(rnd) & 0xFFFFFFF) / (0xFFFFFFF + 1); + } + throw new Error('Invalid arguments'); +}; + +/** + * Function to get random boolean + * @param {deRandom.deRandom} rnd Initialised array of random numbers + * @return {boolean} deRandom.Random boolean + */ +deRandom.deRandom_getBool = function(rnd) { + var val; + val = deRandom.deRandom_getInt(rnd); + return ((val & 0xFFFFFF) < 0x800000); +}; + +/** + * Function to get a common base seed + * @return {number} constant + */ +deRandom.getBaseSeed = function() { + return 42; +}; + +/** + * TODO Function to deRandom.choose random items from a list + * @template T + * @param {deRandom.deRandom} rnd Initialised array of random numbers + * @param {Array<T>} elements Array segment already defined + * @param {Array<T>=} resultOut Array where to store the elements in. If undefined, default to array of (num) elements. + * @param {number=} num Number of items to store in resultOut. If undefined, default to 1. + * @return {Array<T>} Even though result is stored in resultOut, return it here as well. + */ +deRandom.choose = function(rnd, elements, resultOut, num) { + var items = num || 1; + var temp = elements.slice(); + if (!resultOut) + resultOut = []; + + while (items-- > 0) { + var index = deRandom.deRandom_getInt(rnd, [0, temp.length - 1]); + resultOut.push(temp[index]); + temp.splice(index, 1); + } + return resultOut; +}; + +/** + * TODO Function to deRandom.choose weighted random items from a list + * @param {deRandom.deRandom} rnd Initialised randomizer + * @param {Array<number>} array Array to choose items from + * @param {Array<number>} weights Weights array + * @return {number} Result output + */ +deRandom.chooseWeighted = function(rnd, array, weights) { + // Compute weight sum + /** @type {number} */ var weightSum = 0.0; + /** @type {number} */ var ndx; + for (ndx = 0; ndx < array.length; ndx++) + weightSum += weights[ndx]; + + // Random point in 0..weightSum + /** @type {number} */ var p = deRandom.deRandom_getFloat(rnd, [0.0, weightSum]); + + // Find item in range + /** @type {number} */ var lastNonZero = array.length; + /** @type {number} */ var curWeight = 0.0; + for (ndx = 0; ndx != array.length; ndx++) { + /** @type {number} */ var w = weights[ndx]; + + curWeight += w; + + if (p < curWeight) + return array[ndx]; + else if (w > 0.0) + lastNonZero = ndx; + } + + assertMsgOptions(lastNonZero != array.length, 'Index went out of bounds', false, true); + return array[lastNonZero]; +}; + +/** + * TODO Function to deRandom.shuffle an array + * @param {deRandom.deRandom} rnd Initialised array of random numbers + * @param {Array} elements Array to deRandom.shuffle + * @return {Array} Shuffled array + */ +deRandom.shuffle = function(rnd, elements) { + var index = elements.length; + + while (index > 0) { + var random = deRandom.deRandom_getInt(rnd, [0, index - 1]); + index -= 1; + var elem = elements[index]; + elements[index] = elements[random]; + elements[random] = elem; + } + return elements; +}; + +/** + * This function is used to create the deRandom.Random object and + * initialise the random number with a seed. + * It contains functions for generating random numbers in a variety of formats + * @constructor + * @param {number} seed Number to use as a seed + */ +deRandom.Random = function(seed) { + /** + * Instance of array of pseudo random numbers based on seeds + */ + this.m_rnd = new deRandom.deRandom(); + + //initialise the random numbers based on seed + deRandom.deRandom_init(this.m_rnd, seed); +}; + +/** + * Function to get random boolean + * @return {boolean} deRandom.Random boolean + */ +deRandom.Random.prototype.getBool = function() { return deRandom.deRandom_getBool(this.m_rnd) == true; }; +/** + * Function to get random float + * @param {number=} min Min for range + * @param {number=} max Max for range + * @return {number} deRandom.Random float + */ +deRandom.Random.prototype.getFloat = function(min, max) { return deRandom.deRandom_getFloat(this.m_rnd, [min, max]) }; +/** + * Function to get random int + * @param {number=} min Min for range + * @param {number=} max Max for range + * @return {number} deRandom.Random int + */ +deRandom.Random.prototype.getInt = function(min, max) {return deRandom.deRandom_getInt(this.m_rnd, [min, max])}; +/** + * TODO Function to deRandom.choose random items from a list + * @template T + * @param {Array<T>} elements Array segment already defined + * @param {Array<T>=} resultOut Array where to store the elements in. If undefined, default to array of (num) elements. + * @param {number=} num Number of items to store in resultOut. If undefined, default to 1. + * @return {Array<T>} Even though result is stored in resultOut, return it here as well. + */ +deRandom.Random.prototype.choose = function(elements, resultOut, num) {return deRandom.choose(this.m_rnd, elements, resultOut, num)}; +/** + * choose weighted random items from a list + * @param {Array<number>} array Array to choose items from + * @param {Array<number>} weights Weights array + * @return {number} Result output + */ +deRandom.Random.prototype.chooseWeighted = function(array, weights) {return deRandom.chooseWeighted(this.m_rnd, array, weights)}; +/** + * TODO Function to deRandom.shuffle an array + * @param {Array} elements Array to deRandom.shuffle + * @return {Array} Shuffled array + */ +deRandom.Random.prototype.shuffle = function(elements) {return deRandom.shuffle(this.m_rnd, elements)}; + +/** + * Function to get a common base seed + * @return {number} constant + */ +deRandom.Random.prototype.getBaseSeed = function() { + return deRandom.getBaseSeed(); +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deString.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deString.js new file mode 100644 index 000000000..fc84a7232 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deString.js @@ -0,0 +1,111 @@ +/*------------------------------------------------------------------------- + * 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. + * + */ + +/** + * This class allows one to create a random integer, floating point number or boolean (TODO, choose random items from a list and shuffle an array) + */ +'use strict'; +goog.provide('framework.delibs.debase.deString'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var deString = framework.delibs.debase.deString; +var deMath = framework.delibs.debase.deMath; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * Compute hash from string. + * @param {?string} str String to compute hash value for. + * @return {number} Computed hash value. + */ + deString.deStringHash = function(str) { + /* \note [pyry] This hash is used in DT_GNU_HASH and is proven + to be robust for symbol hashing. */ + /* \see http://sources.redhat.com/ml/binutils/2006-06/msg00418.html */ + /** @type {number} */ var hash = 5381; + /** @type {number} */ var c; + + DE_ASSERT(str != undefined); + if (str !== null) { + var i = 0; + while (i < str.length) { //(c = (unsigned int)*str++) != 0) + c = str.charCodeAt(i); //trunc to 8-bit + hash = (hash << 5) + hash + c; + i++; + } + } + return hash; + }; + + /** + * Checks if a JS string is either empty or undefined + * @param {string} str + * @return {boolean} + */ + deString.deIsStringEmpty = function(str) { + if (str === undefined || str.length == 0) + return true; + return false; + }; + + /** + * @private + * @param {Object} enumType + * @param {?} value + * @return {string} + */ + deString.getString = function(enumType, value) { + for (var p in enumType) + if (enumType[p] == value) + return p; + + if (typeof value === 'undefined') + return 'undefined'; + + if (!value) + return 'null'; + + return value.toString(10); + }; + + /** + * @param {Object} enumType + * @param {?} value + * @return {string} + */ + deString.enumToString = function(enumType, value) { + if (typeof deString.enumToString[enumType] === 'undefined') + deString.enumToString[enumType] = {}; + + var table = deString.enumToString[enumType]; + if (typeof table[value] === 'undefined') { + var v = deString.getString(enumType, value); + table[value] = v; + } + + return table[value]; + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deUtil.js new file mode 100644 index 000000000..56a90b6ff --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/delibs/debase/deUtil.js @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- + * 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('framework.delibs.debase.deUtil'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + + var deUtil = framework.delibs.debase.deUtil; + var deMath = framework.delibs.debase.deMath; + + //! Get an element of an array with a specified size. + /** + * @param {Array} array + * @param {number} offset + * @return {*} + */ + deUtil.getArrayElement = function(array, offset) { + assertMsgOptions(deMath.deInBounds32(offset, 0, array.length), 'Array element out of bounds', false, true); + return array[offset]; + }; + + /** + * clone - If you need to pass/assign an object by value, call this + * @param {*} obj + * @return {*} + */ + deUtil.clone = function(obj) { + if (obj == null || typeof(obj) != 'object') + return obj; + + var temp = {}; + if (ArrayBuffer.isView(obj)) { + temp = new obj.constructor(obj); + } else if (obj instanceof Array) { + temp = new Array(obj.length); + for (var akey in obj) + temp[akey] = deUtil.clone(obj[akey]); + } else if (obj instanceof ArrayBuffer) { + temp = new ArrayBuffer(obj.byteLength); + var dst = new Uint8Array(temp); + var src = new Uint8Array(obj); + dst.set(src); + } else { + temp = Object.create(obj.constructor.prototype); + temp.constructor = obj.constructor; + for (var key in obj) + temp[key] = deUtil.clone(obj[key]); + } + return temp; + }; + + /** + * Add a push_unique function to Array. Will insert only if there is no equal element. + * @template T + * @param {Array<T>} array Any array + * @param {T} object Any object + */ + deUtil.dePushUniqueToArray = function(array, object) { + //Simplest implementation + for (var i = 0; i < array.length; i++) { + if (object.equals !== undefined) + if (object.equals(array[i])) + return undefined; + else if (object === array[i]) + return undefined; + } + + array.push(object); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluDrawUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluDrawUtil.js new file mode 100644 index 000000000..baa05a970 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluDrawUtil.js @@ -0,0 +1,510 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluDrawUtil'); +goog.require('framework.opengl.gluShaderProgram'); + +goog.scope(function() { + +var gluDrawUtil = framework.opengl.gluDrawUtil; +var gluShaderProgram = framework.opengl.gluShaderProgram; + +/** + * Description of a vertex array binding + * @constructor + * @param {number} type GL gluDrawUtil.Type of data + * @param {(number|undefined)} location Binding location + * @param {number} components Number of components per vertex + * @param {number} elements Number of elements in the array + * @param {Array<number>} data Source data + * @param {number=} stride + * @param {number=} offset + */ +gluDrawUtil.VertexArrayBinding = function(type, location, components, elements, data, stride, offset) { + this.type = type; + this.location = location === undefined ? -1 : location; + this.components = components; + this.elements = elements; + this.data = data; + /** @type {?string} */ this.name = null; + this.stride = stride || 0; + this.offset = offset || 0; +}; + +/** + * Description of a vertex array binding + * @param {gluDrawUtil.BindingPoint} binding + * @param {gluDrawUtil.VertexArrayPointer} pointer + * @param {number=} dataType GL Data Type + * @return {gluDrawUtil.VertexArrayBinding} + */ +gluDrawUtil.vabFromBindingPointAndArrayPointer = function(binding, pointer, dataType) { + var type = dataType === undefined ? gl.FLOAT : dataType; + var location = binding.location; + var components = pointer.numComponents; + var elements = pointer.numElements; + var data = pointer.data; + var vaBinding = new gluDrawUtil.VertexArrayBinding(type, location, components, elements, data); + vaBinding.componentType = pointer.componentType; + vaBinding.name = binding.name; + vaBinding.convert = pointer.convert; + vaBinding.stride = pointer.stride; + return vaBinding; +}; + +/** + * ! Lower named bindings to locations and eliminate bindings that are not used by program. + * @param {WebGL2RenderingContext} gl WebGL context + * @param {WebGLProgram} program + * @param {Array} inputArray - Array with the named binding locations + * @param {Array=} outputArray - Array with the lowered locations + * @return {Array} outputArray + */ +gluDrawUtil.namedBindingsToProgramLocations = function(gl, program, inputArray, outputArray) { + outputArray = outputArray || []; + + for (var i = 0; i < inputArray.length; i++) { + var cur = inputArray[i]; + if (cur.name) { + //assert(binding.location >= 0); + var location = gl.getAttribLocation(program, cur.name); + if (location >= 0) { + if (cur.location >= 0) + location += cur.location; + // Add binding.location as an offset to accomodate matrices. + outputArray.push(new gluDrawUtil.VertexArrayBinding(cur.type, location, cur.components, cur.elements, cur.data, cur.stride, cur.offset)); + } + } else { + outputArray.push(cur); + } + } + + return outputArray; +}; + +/** + * Creates vertex buffer, binds it and draws elements + * @param {WebGL2RenderingContext} gl WebGL context + * @param {WebGLProgram} program ID, vertexProgramID + * @param {Array<gluDrawUtil.VertexArrayBinding>} vertexArrays + * @param {gluDrawUtil.PrimitiveList} primitives to gluDrawUtil.draw + * @param { {beforeDrawCall:function(), afterDrawCall:function()}=} callback + */ +gluDrawUtil.draw = function(gl, program, vertexArrays, primitives, callback) { + /** TODO: finish implementation */ + /** @type {Array<WebGLBuffer>} */ var objects = []; + + // Lower bindings to locations + vertexArrays = gluDrawUtil.namedBindingsToProgramLocations(gl, program, vertexArrays); + + for (var i = 0; i < vertexArrays.length; i++) { + /** @type {WebGLBuffer} */ var buffer = gluDrawUtil.vertexBuffer(gl, vertexArrays[i]); + objects.push(buffer); + } + + if (primitives.indices) { + /** @type {WebGLBuffer} */ var elemBuffer = gluDrawUtil.indexBuffer(gl, primitives); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elemBuffer); + + if (callback) + callback.beforeDrawCall(); + + gluDrawUtil.drawIndexed(gl, primitives, 0); + + if (callback) + callback.afterDrawCall(); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + } else { + if (callback) + callback.beforeDrawCall(); + + gl.drawArrays(gluDrawUtil.getPrimitiveGLType(gl, primitives.type), 0, primitives.numElements); + + if (callback) + callback.afterDrawCall(); + } + + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'drawArrays', false, true); + for (var i = 0; i < vertexArrays.length; i++) { + gl.disableVertexAttribArray(vertexArrays[i].location); + } + gl.bindBuffer(gl.ARRAY_BUFFER, null); +}; + +/** + * Creates vertex buffer, binds it and draws elements + * @param {WebGL2RenderingContext} gl WebGL context + * @param {gluDrawUtil.PrimitiveList} primitives Primitives to gluDrawUtil.draw + * @param {number} offset + */ +gluDrawUtil.drawIndexed = function(gl, primitives, offset) { +/** @type {number} */ var mode = gluDrawUtil.getPrimitiveGLType(gl, primitives.type); + /** TODO: C++ implementation supports different index types, we use only int16. + Could it cause any issues? + + deUint32 indexGLType = getIndexGLType(primitives.indexType); + */ + + gl.drawElements(mode, primitives.indices.length, gl.UNSIGNED_SHORT, offset); +}; + +/** + * Enums for primitive types + * @enum + */ +gluDrawUtil.primitiveType = { + TRIANGLES: 0, + TRIANGLE_STRIP: 1, + TRIANGLE_FAN: 2, + + LINES: 3, + LINE_STRIP: 4, + LINE_LOOP: 5, + + POINTS: 6, + + PATCHES: 7 +}; + +/** + * get GL type from primitive type + * @param {WebGL2RenderingContext} gl WebGL context + * @param {gluDrawUtil.primitiveType} type gluDrawUtil.primitiveType + * @return {number} GL primitive type + */ +gluDrawUtil.getPrimitiveGLType = function(gl, type) { + switch (type) { + case gluDrawUtil.primitiveType.TRIANGLES: return gl.TRIANGLES; + case gluDrawUtil.primitiveType.TRIANGLE_STRIP: return gl.TRIANGLE_STRIP; + case gluDrawUtil.primitiveType.TRIANGLE_FAN: return gl.TRIANGLE_FAN; + case gluDrawUtil.primitiveType.LINES: return gl.LINES; + case gluDrawUtil.primitiveType.LINE_STRIP: return gl.LINE_STRIP; + case gluDrawUtil.primitiveType.LINE_LOOP: return gl.LINE_LOOP; + case gluDrawUtil.primitiveType.POINTS: return gl.POINTS; +// case gluDrawUtil.primitiveType.PATCHES: return gl.PATCHES; + default: + throw new Error('Unknown primitive type ' + type); + } +}; + +/** + * Calls gluDrawUtil.newPrimitiveListFromIndices() to create primitive list for Points + * @param {number} numElements + */ +gluDrawUtil.pointsFromElements = function(numElements) { + return new gluDrawUtil.PrimitiveList(gluDrawUtil.primitiveType.POINTS, numElements); +}; + +/** + * Calls gluDrawUtil.newPrimitiveListFromIndices() to create primitive list for Triangles + * @param {Array<number>} indices + */ +gluDrawUtil.triangles = function(indices) { + return gluDrawUtil.newPrimitiveListFromIndices(gluDrawUtil.primitiveType.TRIANGLES, indices); +}; + +/** + * Calls gluDrawUtil.newPrimitiveListFromIndices() to create primitive list for Patches + * @param {Array<number>} indices + */ +gluDrawUtil.patches = function(indices) { + return gluDrawUtil.newPrimitiveListFromIndices(gluDrawUtil.primitiveType.PATCHES, indices); +}; + +/** + * Creates primitive list for Triangles or Patches, depending on type + * @param {gluDrawUtil.primitiveType} type gluDrawUtil.primitiveType + * @param {number} numElements + * @constructor + */ +gluDrawUtil.PrimitiveList = function(type, numElements) { + this.type = type; + this.indices = 0; + this.numElements = numElements; +}; + +/** + * @param {gluDrawUtil.primitiveType} type + * @param {Array<number>} indices + * @return {gluDrawUtil.PrimitiveList} + */ +gluDrawUtil.newPrimitiveListFromIndices = function(type, indices) { + /** @type {gluDrawUtil.PrimitiveList} */ var primitiveList = new gluDrawUtil.PrimitiveList(type, 0); + primitiveList.indices = indices; + return primitiveList; +}; + +/** + * Create Element Array Buffer + * @param {WebGL2RenderingContext} gl WebGL context + * @param {gluDrawUtil.PrimitiveList} primitives to construct the buffer from + * @return {WebGLBuffer} indexObject buffer with elements + */ +gluDrawUtil.indexBuffer = function(gl, primitives) { + /** @type {WebGLBuffer} */ var indexObject = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'bindBuffer', false, true); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(primitives.indices), gl.STATIC_DRAW); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'bufferData', false, true); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + return indexObject; +}; + +/** + * Create Array Buffer + * @param {WebGL2RenderingContext} gl WebGL context + * @param {gluDrawUtil.VertexArrayBinding} vertexArray primitives, Array buffer descriptor + * @return {WebGLBuffer} buffer of vertices + */ +gluDrawUtil.vertexBuffer = function(gl, vertexArray) { + /** @type {goog.TypedArray} */ var typedArray; + switch (vertexArray.type) { + case gl.BYTE: typedArray = new Int8Array(vertexArray.data); break; + case gl.UNSIGNED_BYTE: typedArray = new Uint8Array(vertexArray.data); break; + case gl.SHORT: typedArray = new Int16Array(vertexArray.data); break; + case gl.UNSIGNED_SHORT: typedArray = new Uint16Array(vertexArray.data); break; + case gl.INT: typedArray = new Int32Array(vertexArray.data); break; + case gl.UNSIGNED_INT: typedArray = new Uint32Array(vertexArray.data); break; + default: typedArray = new Float32Array(vertexArray.data); break; + } + + /** @type {WebGLBuffer} */ var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'bindBuffer', false, true); + gl.bufferData(gl.ARRAY_BUFFER, typedArray, gl.STATIC_DRAW); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'bufferData', false, true); + gl.enableVertexAttribArray(vertexArray.location); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'enableVertexAttribArray', false, true); + if (vertexArray.type === gl.FLOAT) { + gl.vertexAttribPointer(vertexArray.location, vertexArray.components, vertexArray.type, false, vertexArray.stride, vertexArray.offset); + } else { + gl.vertexAttribIPointer(vertexArray.location, vertexArray.components, vertexArray.type, vertexArray.stride, vertexArray.offset); + } + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'vertexAttribPointer', false, true); + return buffer; +}; + +/** + * @param {Array<number>} rgba + * @constructor + */ +gluDrawUtil.Pixel = function(rgba) { + this.rgba = rgba; +}; + +gluDrawUtil.Pixel.prototype.getRed = function() { + return this.rgba[0]; +}; +gluDrawUtil.Pixel.prototype.getGreen = function() { + return this.rgba[1]; +}; +gluDrawUtil.Pixel.prototype.getBlue = function() { + return this.rgba[2]; +}; +gluDrawUtil.Pixel.prototype.getAlpha = function() { + return this.rgba[3]; +}; +gluDrawUtil.Pixel.prototype.equals = function(otherPixel) { + return this.rgba[0] == otherPixel.rgba[0] && + this.rgba[1] == otherPixel.rgba[1] && + this.rgba[2] == otherPixel.rgba[2] && + this.rgba[3] == otherPixel.rgba[3]; +}; + +/** + * @constructor + */ +gluDrawUtil.Surface = function() { +}; + +gluDrawUtil.Surface.prototype.readSurface = function(gl, x, y, width, height) { + this.buffer = new Uint8Array(width * height * 4); + gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, this.buffer); + this.x = x; + this.y = y; + this.width = width; + this.height = height; + return this.buffer; +}; + +gluDrawUtil.Surface.prototype.getPixel = function(x, y) { + /** @type {number} */ var base = (x + y * this.width) * 4; + /** @type {Array<number>} */ + var rgba = [ + this.buffer[base], + this.buffer[base + 1], + this.buffer[base + 2], + this.buffer[base + 3] + ]; + return new gluDrawUtil.Pixel(rgba); +}; + +gluDrawUtil.Surface.prototype.getPixelUintRGB8 = function(x, y) { + /** @type {number} */ var base = (x + y * this.width) * 4; + /** @type {number} */ + return (this.buffer[base] << 16) + + (this.buffer[base + 1] << 8) + + this.buffer[base + 2]; +}; + +/** + * @enum + */ +gluDrawUtil.VertexComponentType = { + // Standard types: all conversion types apply. + VTX_COMP_UNSIGNED_INT8: 0, + VTX_COMP_UNSIGNED_INT16: 1, + VTX_COMP_UNSIGNED_INT32: 2, + VTX_COMP_SIGNED_INT8: 3, + VTX_COMP_SIGNED_INT16: 4, + VTX_COMP_SIGNED_INT32: 5, + + // Special types: only CONVERT_NONE is allowed. + VTX_COMP_FIXED: 6, + VTX_COMP_HALF_FLOAT: 7, + VTX_COMP_FLOAT: 8 +}; + +/** + * @enum + */ +gluDrawUtil.VertexComponentConversion = { + VTX_COMP_CONVERT_NONE: 0, //!< No conversion: integer types, or floating-point values. + VTX_COMP_CONVERT_NORMALIZE_TO_FLOAT: 1, //!< Normalize integers to range [0,1] or [-1,1] depending on type. + VTX_COMP_CONVERT_CAST_TO_FLOAT: 2 //!< Convert to floating-point directly. +}; + +/** + * gluDrawUtil.VertexArrayPointer + * @constructor + * @param {gluDrawUtil.VertexComponentType} componentType_ + * @param {gluDrawUtil.VertexComponentConversion} convert_ + * @param {number} numComponents_ + * @param {number} numElements_ + * @param {number} stride_ + * @const @param {Array<number>} data_ + */ +gluDrawUtil.VertexArrayPointer = function(componentType_, convert_, numComponents_, numElements_, stride_, data_) { + this.componentType = componentType_; + this.convert = convert_; + this.numComponents = numComponents_; + this.numElements = numElements_; + this.stride = stride_; + this.data = data_; +}; + +/** + * gluDrawUtil.BindingPoint + * @constructor + * @param {string} name + * @param {number} location + * @param {number=} offset + */ +gluDrawUtil.BindingPoint = function(name, location, offset) { + /** @type {string} */ this.name = name; + /** @type {number} */ this.location = location; + /** @type {number} */ this.offset = offset || 0; +}; + +/** + * bindingPointFromLocation + * @param {number} location + * return {gluDrawUtil.BindingPoint} + */ +gluDrawUtil.bindingPointFromLocation = function(location) { + return new gluDrawUtil.BindingPoint('', location); +}; + +/** + * bindingPointFromName + * @param {string} name + * @param {number=} location + * return {gluDrawUtil.BindingPoint} + */ +gluDrawUtil.bindingPointFromName = function(name, location) { + location = location === undefined ? -1 : location; + return new gluDrawUtil.BindingPoint(name, location); +}; + +/** + * @param {string} name + * @param {number} numComponents + * @param {number} numElements + * @param {number} stride + * @param {Array<number>} data + * @return {gluDrawUtil.VertexArrayBinding} + */ +gluDrawUtil.newInt32VertexArrayBinding = function(name, numComponents, numElements, stride, data) { + var bindingPoint = gluDrawUtil.bindingPointFromName(name); + var arrayPointer = new gluDrawUtil.VertexArrayPointer(gluDrawUtil.VertexComponentType.VTX_COMP_SIGNED_INT32, + gluDrawUtil.VertexComponentConversion.VTX_COMP_CONVERT_NONE, numComponents, numElements, stride, data); + return gluDrawUtil.vabFromBindingPointAndArrayPointer(bindingPoint, arrayPointer, gl.INT); +}; + +/** + * @param {string} name + * @param {number} numComponents + * @param {number} numElements + * @param {number} stride + * @param {Array<number>} data + * @return {gluDrawUtil.VertexArrayBinding} + */ +gluDrawUtil.newUint32VertexArrayBinding = function(name, numComponents, numElements, stride, data) { + var bindingPoint = gluDrawUtil.bindingPointFromName(name); + var arrayPointer = new gluDrawUtil.VertexArrayPointer(gluDrawUtil.VertexComponentType.VTX_COMP_UNSIGNED_INT32, + gluDrawUtil.VertexComponentConversion.VTX_COMP_CONVERT_NONE, numComponents, numElements, stride, data); + return gluDrawUtil.vabFromBindingPointAndArrayPointer(bindingPoint, arrayPointer, gl.UNSIGNED_INT); +}; + +/** + * @param {string} name + * @param {number} numComponents + * @param {number} numElements + * @param {number} stride + * @param {Array<number>} data + * @return {gluDrawUtil.VertexArrayBinding} + */ +gluDrawUtil.newFloatVertexArrayBinding = function(name, numComponents, numElements, stride, data) { + var bindingPoint = gluDrawUtil.bindingPointFromName(name); + var arrayPointer = new gluDrawUtil.VertexArrayPointer(gluDrawUtil.VertexComponentType.VTX_COMP_FLOAT, + gluDrawUtil.VertexComponentConversion.VTX_COMP_CONVERT_NONE, numComponents, numElements, stride, data); + return gluDrawUtil.vabFromBindingPointAndArrayPointer(bindingPoint, arrayPointer); +}; + +/** + * @param {string} name + * @param {number} column + * @param {number} rows + * @param {number} numElements + * @param {number} stride + * @param {Array<number>} data + * @return {gluDrawUtil.VertexArrayBinding} + */ +gluDrawUtil.newFloatColumnVertexArrayBinding = function(name, column, rows, numElements, stride, data) { + var bindingPoint = gluDrawUtil.bindingPointFromName(name); + bindingPoint.location = column; + var arrayPointer = new gluDrawUtil.VertexArrayPointer(gluDrawUtil.VertexComponentType.VTX_COMP_FLOAT, + gluDrawUtil.VertexComponentConversion.VTX_COMP_CONVERT_NONE, rows, numElements, stride, data); + return gluDrawUtil.vabFromBindingPointAndArrayPointer(bindingPoint, arrayPointer); +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluObjectWrapper.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluObjectWrapper.js new file mode 100644 index 000000000..38f8a28f9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluObjectWrapper.js @@ -0,0 +1,117 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluObjectWrapper'); + +goog.scope(function() { + var gluObjectWrapper = framework.opengl.gluObjectWrapper; + + /** + * @typedef {function(this:WebGLRenderingContextBase): WebGLObject} + */ + gluObjectWrapper.funcGenT; + + /** + * @typedef {function(this:WebGLRenderingContextBase, WebGLObject)} + */ + gluObjectWrapper.funcDelT; + + /** + * @typedef {{name: string, funcGen: !gluObjectWrapper.funcGenT, funcDel: !gluObjectWrapper.funcDelT}} + */ + gluObjectWrapper.traitsT; + + /** + * Returns an object containing a configuration for an ObjectWrapper + * @param {string} name + * @param {gluObjectWrapper.funcGenT} funcGen + * @param {gluObjectWrapper.funcDelT} funcDel + * @return {gluObjectWrapper.traitsT} + */ + gluObjectWrapper.traits = function(name, funcGen, funcDel) { + return { + name: name, + funcGen: funcGen, + funcDel: funcDel + }; + }; + + /** + * @constructor + * @param {WebGLRenderingContextBase} gl + * @param {gluObjectWrapper.traitsT} traits + */ + gluObjectWrapper.ObjectWrapper = function(gl, traits) { + /** + * @protected + * @type {WebGLRenderingContextBase} + */ + this.m_gl = gl; + + /** + * @protected + * @type {gluObjectWrapper.traitsT} + */ + this.m_traits = traits; + + /** + * @protected + * @type {WebGLObject} + */ + this.m_object = this.m_traits.funcGen.call(gl); + + }; + + /** + * Destorys the WebGLObject associated with this object. + */ + gluObjectWrapper.ObjectWrapper.prototype.clear = function() { + this.m_traits.funcDel.call(this.m_gl, this.m_object); + }; + + /** + * Returns the WebGLObject associated with this object. + * @return {WebGLObject} + */ + gluObjectWrapper.ObjectWrapper.prototype.get = function() { + return this.m_object; + }; + + /** + * @constructor + * @extends {gluObjectWrapper.ObjectWrapper} + * @param {WebGLRenderingContextBase} gl + */ + gluObjectWrapper.Framebuffer = function(gl) { + gluObjectWrapper.ObjectWrapper.call(this, gl, gluObjectWrapper.traits( + 'framebuffer', + /** @type {gluObjectWrapper.funcGenT} */(gl.createFramebuffer), + /** @type {gluObjectWrapper.funcDelT} */(gl.deleteFramebuffer) + )); + }; + gluObjectWrapper.Framebuffer.prototype = Object.create(gluObjectWrapper.ObjectWrapper.prototype); + gluObjectWrapper.Framebuffer.prototype.constructor = gluObjectWrapper.Framebuffer; + + /** + * @return {WebGLFramebuffer} + */ + gluObjectWrapper.Framebuffer.prototype.get; +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluPixelTransfer.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluPixelTransfer.js new file mode 100644 index 000000000..04b81a2a1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluPixelTransfer.js @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluPixelTransfer'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.opengl.gluTextureUtil'); + +goog.scope(function() { + +var gluPixelTransfer = framework.opengl.gluPixelTransfer; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var gluTextureUtil = framework.opengl.gluTextureUtil; + +gluPixelTransfer.getTransferAlignment = function(format) { + var pixelSize = format.getPixelSize(); + if (deMath.deIsPowerOfTwo32(pixelSize)) + return Math.min(pixelSize, 8); + else + return 1; +}; + +gluPixelTransfer.readPixels = function(ctx, x, y, format, dst) { + var width = dst.getWidth(); + var height = dst.getHeight(); + var arrayType = tcuTexture.getTypedArray(format.type); + var transferFormat = gluTextureUtil.getTransferFormat(format); + ctx.pixelStorei(ctx.PACK_ALIGNMENT, gluPixelTransfer.getTransferAlignment(format)); + var resultPixel = dst.getAccess().getDataPtr(); + resultPixel = new arrayType(dst.getAccess().getBuffer()); + ctx.readPixels(x, y, width, height, transferFormat.format, transferFormat.dataType, resultPixel); +}; + +/* TODO: implement other functions in C++ file */ + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluShaderProgram.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluShaderProgram.js new file mode 100644 index 000000000..0c340ee38 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluShaderProgram.js @@ -0,0 +1,488 @@ +/*------------------------------------------------------------------------- + * drawElements Quality gluShaderProgram.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('framework.opengl.gluShaderProgram'); + +goog.scope(function() { + +var gluShaderProgram = framework.opengl.gluShaderProgram; + +/** + * gluShaderProgram.Shader type enum + * @enum {number} + */ +gluShaderProgram.shaderType = { + VERTEX: 0, + FRAGMENT: 1 +}; + +/** + * gluShaderProgram.Shader type enum name + * @param {gluShaderProgram.shaderType} shaderType + * @return {string} + */ +gluShaderProgram.getShaderTypeName = function(shaderType) { + var s_names = + [ + 'vertex', + 'fragment' + ]; + + return s_names[shaderType]; +}; + +/** + * Get GL shader type from gluShaderProgram.shaderType + * @param {WebGL2RenderingContext} gl WebGL context + * @param {gluShaderProgram.shaderType} type gluShaderProgram.Shader Type + * @return {number} GL shader type + */ +gluShaderProgram.getGLShaderType = function(gl, type) { + var _glShaderType; + switch (type) { + case gluShaderProgram.shaderType.VERTEX: _glShaderType = gl.VERTEX_SHADER; break; + case gluShaderProgram.shaderType.FRAGMENT: _glShaderType = gl.FRAGMENT_SHADER; break; + default: + throw new Error('Unknown shader type ' + type); + } + return _glShaderType; +}; + +/** + * Declares shader information + * @constructor + * @param {gluShaderProgram.shaderType} type + * @param {string=} source + */ +gluShaderProgram.ShaderInfo = function(type, source) { + this.type = type; /** gluShaderProgram.Shader type. */ + this.source = source; /** gluShaderProgram.Shader source. */ + this.infoLog; /** Compile info log. */ + this.compileOk = false; /** Did compilation succeed? */ + this.compileTimeUs = 0; /** Compile time in microseconds (us). */ +}; + +/** + * Generates vertex shader info from source + * @param {string} source + * @return {gluShaderProgram.ShaderInfo} vertex shader info + */ +gluShaderProgram.genVertexSource = function(source) { +/** @type {gluShaderProgram.ShaderInfo} */ var shader = new gluShaderProgram.ShaderInfo(gluShaderProgram.shaderType.VERTEX, source); + return shader; +}; + +/** + * Generates fragment shader info from source + * @param {string} source + * @return {gluShaderProgram.ShaderInfo} fragment shader info + */ +gluShaderProgram.genFragmentSource = function(source) { +/** @type {gluShaderProgram.ShaderInfo} */ var shader = new gluShaderProgram.ShaderInfo(gluShaderProgram.shaderType.FRAGMENT, source); + return shader; +}; + +/** + * Generates shader from WebGL context and type + * @constructor + * @param {WebGL2RenderingContext} gl WebGL context + * @param {gluShaderProgram.shaderType} type gluShaderProgram.Shader Type + */ +gluShaderProgram.Shader = function(gl, type) { + this.gl = gl; + this.info = new gluShaderProgram.ShaderInfo(type); /** Client-side clone of state for debug / perf reasons. */ + this.shader = gl.createShader(gluShaderProgram.getGLShaderType(gl, type)); + assertMsgOptions(gl.getError() == gl.NO_ERROR, 'gl.createShader()', false, true); + + this.setSources = function(source) { + this.gl.shaderSource(this.shader, source); + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'glshaderSource()', false, true); + this.info.source = source; + }; + + this.getCompileStatus = function() { + return this.info.compileOk; + }; + + this.compile = function() { + this.info.compileOk = false; + this.info.compileTimeUs = 0; + this.info.infoLog = ''; + + /** @type {Date} */ var compileStart = new Date(); + this.gl.compileShader(this.shader); + /** @type {Date} */ var compileEnd = new Date(); + this.info.compileTimeUs = 1000 * (compileEnd.getTime() - compileStart.getTime()); + + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.compileShader()', false, true); + + var compileStatus = this.gl.getShaderParameter(this.shader, this.gl.COMPILE_STATUS); + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'glGetShaderParameter()', false, true); + + this.info.compileOk = compileStatus; + this.info.infoLog = this.gl.getShaderInfoLog(this.shader); + }; + + this.getShader = function() { + return this.shader; + }; + + this.destroy = function() { + this.gl.deleteShader(this.shader); + }; + +}; +/** + * Creates gluShaderProgram.ProgramInfo + * @constructor + */ +gluShaderProgram.ProgramInfo = function() { + /** @type {string} */ this.infoLog = ''; /** Link info log. */ + /** @type {boolean} */ this.linkOk = false; /** Did link succeed? */ + /** @type {number} */ this.linkTimeUs = 0; /** Link time in microseconds (us). */ +}; + +/** + * Creates program. + * Inner methods: attach shaders, bind attributes location, link program and transform Feedback Varyings + * @constructor + * @param {WebGL2RenderingContext} gl WebGL context + * @param {WebGLProgram=} programID + */ +gluShaderProgram.Program = function(gl, programID) { + this.gl = gl; + this.program = programID || null; + this.info = new gluShaderProgram.ProgramInfo(); + + if (!programID) { + this.program = gl.createProgram(); + assertMsgOptions(gl.getError() == gl.NO_ERROR, 'gl.createProgram()', false, true); + } +}; + +/** + * @return {WebGLProgram} + */ +gluShaderProgram.Program.prototype.getProgram = function() { return this.program; }; + +/** + * @return {gluShaderProgram.ProgramInfo} + */ +gluShaderProgram.Program.prototype.getInfo = function() { return this.info; }; + +/** + * @param {WebGLShader} shader + */ +gluShaderProgram.Program.prototype.attachShader = function(shader) { + this.gl.attachShader(this.program, shader); + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.attachShader()', false, true); +}; + +/** + * @param {WebGLShader} shader + */ +gluShaderProgram.Program.prototype.detachShader = function(shader) { + this.gl.detachShader(this.program, shader); + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.detachShader()', false, true); +}; + +/** + * @param {number} location + * @param {string} name + */ +gluShaderProgram.Program.prototype.bindAttribLocation = function(location, name) { + this.gl.bindAttribLocation(this.program, location, name); + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.bindAttribLocation()', false, true); +}; + +gluShaderProgram.Program.prototype.link = function() { + this.info.linkOk = false; + this.info.linkTimeUs = 0; + this.info.infoLog = ''; + + /** @type {Date} */ var linkStart = new Date(); + this.gl.linkProgram(this.program); + /** @type {Date} */ var linkEnd = new Date(); + this.info.linkTimeUs = 1000 * (linkEnd.getTime() - linkStart.getTime()); + + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.linkProgram()', false, true); + + var linkStatus = this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS); + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.getProgramParameter()', false, true); + this.info.linkOk = linkStatus; + this.info.infoLog = this.gl.getProgramInfoLog(this.program); + if (!this.info.linkOk) + bufferedLogToConsole("program linking: " + this.info.infoLog); +}; + +/** + * return {boolean} + */ +gluShaderProgram.Program.prototype.getLinkStatus = function() { + return this.info.linkOk; +}; + +/** + * @param {Array<string>} varyings + * @param {number} bufferMode + */ +gluShaderProgram.Program.prototype.transformFeedbackVaryings = function(varyings, bufferMode) { + this.gl.transformFeedbackVaryings(this.program, varyings, bufferMode); + assertMsgOptions(this.gl.getError() == this.gl.NO_ERROR, 'gl.transformFeedbackVaryings()', false, true); +}; + +/** + * Assigns gl WebGL context and programSources. Declares array of shaders and new program() + * @constructor + * @param {WebGL2RenderingContext} gl WebGL context + * @param {gluShaderProgram.ProgramSources} programSources + */ +gluShaderProgram.ShaderProgram = function(gl, programSources) { + this.gl = gl; + this.programSources = programSources; + this.shaders = []; + this.program = new gluShaderProgram.Program(gl); + + /** @type {boolean} */ this.shadersOK = true; + + for (var i = 0; i < programSources.sources.length; i++) { + /** @type {gluShaderProgram.Shader} */ var shader = new gluShaderProgram.Shader(gl, programSources.sources[i].type); + shader.setSources(programSources.sources[i].source); + shader.compile(); + this.shaders.push(shader); + this.shadersOK = this.shadersOK && shader.getCompileStatus(); + if (!shader.getCompileStatus()) { + bufferedLogToConsole('gluShaderProgram.Shader:\n' + programSources.sources[i].source); + bufferedLogToConsole('Compile status: ' + shader.getCompileStatus()); + bufferedLogToConsole('Shader infoLog: ' + shader.info.infoLog); + } + } + + if (this.shadersOK) { + for (var i = 0; i < this.shaders.length; i++) + this.program.attachShader(this.shaders[i].getShader()); + + for (var attrib in programSources.attribLocationBindings) + this.program.bindAttribLocation(programSources.attribLocationBindings[attrib], attrib); + + if (programSources.transformFeedbackBufferMode) + if (programSources.transformFeedbackBufferMode === gl.NONE) + assertMsgOptions(programSources.transformFeedbackVaryings.length === 0, 'Transform feedback sanity check', false, true); + else + this.program.transformFeedbackVaryings(programSources.transformFeedbackVaryings, programSources.transformFeedbackBufferMode); + + /* TODO: GLES 3.1: set separable flag */ + + this.program.link(); + + } +}; + +/** + * return {WebGLProgram} + */ +gluShaderProgram.ShaderProgram.prototype.getProgram = function() { + return this.program.program; + }; + +/** + * @return {gluShaderProgram.ProgramInfo} + */ +gluShaderProgram.ShaderProgram.prototype.getProgramInfo = function() { + return this.program.info; +}; + +gluShaderProgram.ShaderProgram.prototype.isOk = function() { + return this.shadersOK && this.program.getLinkStatus(); +}; + +gluShaderProgram.containerTypes = { + ATTRIB_LOCATION_BINDING: 0, + TRANSFORM_FEEDBACK_MODE: 1, + TRANSFORM_FEEDBACK_VARYING: 2, + TRANSFORM_FEEDBACK_VARYINGS: 3, + SHADER_SOURCE: 4, + PROGRAM_SEPARABLE: 5, + PROGRAM_SOURCES: 6, + + CONTAINER_TYPE_LAST: 7, + ATTACHABLE_BEGIN: 0, // ATTRIB_LOCATION_BINDING + ATTACHABLE_END: 5 + 1 // PROGRAM_SEPARABLE + 1 +}; + +/** + * @constructor + */ +gluShaderProgram.AttribLocationBinding = function(name, location) { + this.name = name; + this.location = location; + + this.getContainerType = function() { + return gluShaderProgram.containerTypes.ATTRIB_LOCATION_BINDING; + }; +}; + +/** + * @constructor + */ +gluShaderProgram.TransformFeedbackMode = function(mode) { + this.mode = mode; + + this.getContainerType = function() { + return gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_MODE; + }; +}; + +/** + * @constructor + * @param {string} name + */ +gluShaderProgram.TransformFeedbackVarying = function(name) { + /** @type {string} */ this.name = name; + + this.getContainerType = function() { + return gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYING; + }; +}; + +/** + * @constructor + * @param {Array<string>} array + */ +gluShaderProgram.TransformFeedbackVaryings = function(array) { + /** @type {Array<string>} */ this.array = array; + + this.getContainerType = function() { + return gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYINGS; + }; +}; + +/** + * @constructor + */ +gluShaderProgram.ProgramSeparable = function(separable) { + this.separable = separable; + + this.getContainerType = function() { + return gluShaderProgram.containerTypes.PROGRAM_SEPARABLE; + }; +}; + +/** + * @constructor + */ +gluShaderProgram.VertexSource = function(str) { + this.shaderType = gluShaderProgram.shaderType.VERTEX; + this.source = str; + + this.getContainerType = function() { + return gluShaderProgram.containerTypes.SHADER_SOURCE; + }; +}; + +/** + * @constructor + */ +gluShaderProgram.FragmentSource = function(str) { + this.shaderType = gluShaderProgram.shaderType.FRAGMENT; + this.source = str; + + this.getContainerType = function() { + return gluShaderProgram.containerTypes.SHADER_SOURCE; + }; +}; + +/** + * Create gluShaderProgram.ProgramSources. + * @constructor + */ +gluShaderProgram.ProgramSources = function() { + /** @type {Array<gluShaderProgram.ShaderInfo>} */ this.sources = []; + this.attribLocationBindings = []; + /** @type {Array<string>} */ this.transformFeedbackVaryings = []; + this.transformFeedbackBufferMode = 0; + this.separable = false; +}; + +gluShaderProgram.ProgramSources.prototype.getContainerType = function() { + return gluShaderProgram.containerTypes.PROGRAM_SOURCES; +}; + +gluShaderProgram.ProgramSources.prototype.add = function(item) { + var type = undefined; + if (typeof(item.getContainerType) == 'function') { + type = item.getContainerType(); + if ( + typeof(type) != 'number' || + type < gluShaderProgram.containerTypes.ATTACHABLE_BEGIN || + type >= gluShaderProgram.containerTypes.ATTACHABLE_END + ) { + type = undefined; + } + } + + switch (type) { + case gluShaderProgram.containerTypes.ATTRIB_LOCATION_BINDING: + this.attribLocationBindings.push(item); + break; + + case gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_MODE: + this.transformFeedbackBufferMode = item.mode; + break; + + case gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYING: + this.transformFeedbackVaryings.push(item.name); + break; + + case gluShaderProgram.containerTypes.TRANSFORM_FEEDBACK_VARYINGS: + this.transformFeedbackVaryings = this.transformFeedbackVaryings.concat(item.array); + break; + + case gluShaderProgram.containerTypes.SHADER_SOURCE: + this.sources.push(new gluShaderProgram.ShaderInfo(item.shaderType, item.source)); + break; + + case gluShaderProgram.containerTypes.PROGRAM_SEPARABLE: + this.separable = item.separable; + break; + + default: + throw new Error('Type \"' + type + '\" cannot be added to gluShaderProgram.ProgramSources.'); + break; + } + + return this; +}; + +/** + * //! Helper for constructing vertex-fragment source pair. + * @param {string} vertexSrc + * @param {string} fragmentSrc + * @return {gluShaderProgram.ProgramSources} + */ +gluShaderProgram.makeVtxFragSources = function(vertexSrc, fragmentSrc) { + /** @type {gluShaderProgram.ProgramSources} */ var sources = new gluShaderProgram.ProgramSources(); + sources.sources.push(gluShaderProgram.genVertexSource(vertexSrc)); + sources.sources.push(gluShaderProgram.genFragmentSource(fragmentSrc)); + return sources; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluShaderUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluShaderUtil.js new file mode 100644 index 000000000..1604dbc61 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluShaderUtil.js @@ -0,0 +1,795 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluShaderUtil'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var gluShaderUtil = framework.opengl.gluShaderUtil; +var deMath = framework.delibs.debase.deMath; + +/** + * ShadingLanguageVersion + * @enum + */ +gluShaderUtil.GLSLVersion = { + V100_ES: 0, //!< GLSL ES 1.0 + V300_ES: 1 //!< GLSL ES 3.0 +}; + +/** + * gluShaderUtil.glslVersionUsesInOutQualifiers + * @param {gluShaderUtil.GLSLVersion} version + * @return {boolean} + */ +gluShaderUtil.glslVersionUsesInOutQualifiers = function(version) { + return version == gluShaderUtil.GLSLVersion.V300_ES; +}; + +/** + * gluShaderUtil.isGLSLVersionSupported + * @param {WebGL2RenderingContext|WebGLRenderingContextBase} ctx + * @param {gluShaderUtil.GLSLVersion} version + * @return {boolean} + */ +gluShaderUtil.isGLSLVersionSupported = function(ctx, version) { + return version <= gluShaderUtil.getGLSLVersion(ctx); +}; + +/** + * gluShaderUtil.getGLSLVersion - Returns a gluShaderUtil.GLSLVersion based on a given webgl context. + * @param {WebGL2RenderingContext|WebGLRenderingContextBase} gl + * @return {gluShaderUtil.GLSLVersion} + */ +gluShaderUtil.getGLSLVersion = function(gl) { + var glslversion = gl.getParameter(gl.SHADING_LANGUAGE_VERSION); + + // TODO: Versions are not yet well implemented... Firefox returns GLSL ES 1.0 in some cases, + // and Chromium returns GLSL ES 2.0 in some cases. Returning the right version for + // testing. + // return gluShaderUtil.GLSLVersion.V300_ES; + + if (glslversion.indexOf('WebGL GLSL ES 1.0') != -1) return gluShaderUtil.GLSLVersion.V100_ES; + if (glslversion.indexOf('WebGL GLSL ES 3.0') != -1) return gluShaderUtil.GLSLVersion.V300_ES; + + throw new Error('Invalid WebGL version'); +}; + +/** + * gluShaderUtil.getGLSLVersionDeclaration - Returns a string declaration for the glsl version in a shader. + * @param {gluShaderUtil.GLSLVersion} version + * @return {string} + */ +gluShaderUtil.getGLSLVersionDeclaration = function(version) { + /** @type {Array<string>} */ var s_decl = + [ + '#version 100', + '#version 300 es' + ]; + + if (version > s_decl.length - 1) + throw new Error('Unsupported GLSL version.'); + + return s_decl[version]; +}; + +/** + * gluShaderUtil.getGLSLVersionString - Returns the same thing as + * getGLSLVersionDeclaration() but without the substring '#version' + * @param {gluShaderUtil.GLSLVersion} version + * @return {string} + */ +gluShaderUtil.getGLSLVersionString = function(version) { + /** @type {Array<string>} */ var s_decl = + [ + '100', + '300 es' + ]; + + if (version > s_decl.length - 1) + throw new Error('Unsupported GLSL version.'); + + return s_decl[version]; +}; + +/** + * @enum + */ +gluShaderUtil.precision = { + PRECISION_LOWP: 0, + PRECISION_MEDIUMP: 1, + PRECISION_HIGHP: 2 +}; + +gluShaderUtil.getPrecisionName = function(prec) { + var s_names = [ + 'lowp', + 'mediump', + 'highp' + ]; + + return s_names[prec]; +}; + +/** + * The Type constants + * @enum {number} + */ +gluShaderUtil.DataType = { + INVALID: 0, + + FLOAT: 1, + FLOAT_VEC2: 2, + FLOAT_VEC3: 3, + FLOAT_VEC4: 4, + FLOAT_MAT2: 5, + FLOAT_MAT2X3: 6, + FLOAT_MAT2X4: 7, + FLOAT_MAT3X2: 8, + FLOAT_MAT3: 9, + FLOAT_MAT3X4: 10, + FLOAT_MAT4X2: 11, + FLOAT_MAT4X3: 12, + FLOAT_MAT4: 13, + + INT: 14, + INT_VEC2: 15, + INT_VEC3: 16, + INT_VEC4: 17, + + UINT: 18, + UINT_VEC2: 19, + UINT_VEC3: 20, + UINT_VEC4: 21, + + BOOL: 22, + BOOL_VEC2: 23, + BOOL_VEC3: 24, + BOOL_VEC4: 25, + + SAMPLER_2D: 26, + SAMPLER_CUBE: 27, + SAMPLER_2D_ARRAY: 28, + SAMPLER_3D: 29, + + SAMPLER_2D_SHADOW: 30, + SAMPLER_CUBE_SHADOW: 31, + SAMPLER_2D_ARRAY_SHADOW: 32, + + INT_SAMPLER_2D: 33, + INT_SAMPLER_CUBE: 34, + INT_SAMPLER_2D_ARRAY: 35, + INT_SAMPLER_3D: 36, + + UINT_SAMPLER_2D: 37, + UINT_SAMPLER_CUBE: 38, + UINT_SAMPLER_2D_ARRAY: 39, + UINT_SAMPLER_3D: 40 +}; + +/** + * Returns type of float scalars + * @param {gluShaderUtil.DataType} dataType + * @return {string} type of float scalar + */ +gluShaderUtil.getDataTypeFloatScalars = function(dataType) { + + switch (dataType) { + case gluShaderUtil.DataType.FLOAT: return 'float'; + case gluShaderUtil.DataType.FLOAT_VEC2: return 'vec2'; + case gluShaderUtil.DataType.FLOAT_VEC3: return 'vec3'; + case gluShaderUtil.DataType.FLOAT_VEC4: return 'vec4'; + case gluShaderUtil.DataType.FLOAT_MAT2: return 'mat2'; + case gluShaderUtil.DataType.FLOAT_MAT2X3: return 'mat2x3'; + case gluShaderUtil.DataType.FLOAT_MAT2X4: return 'mat2x4'; + case gluShaderUtil.DataType.FLOAT_MAT3X2: return 'mat3x2'; + case gluShaderUtil.DataType.FLOAT_MAT3: return 'mat3'; + case gluShaderUtil.DataType.FLOAT_MAT3X4: return 'mat3x4'; + case gluShaderUtil.DataType.FLOAT_MAT4X2: return 'mat4x2'; + case gluShaderUtil.DataType.FLOAT_MAT4X3: return 'mat4x3'; + case gluShaderUtil.DataType.FLOAT_MAT4: return 'mat4'; + case gluShaderUtil.DataType.INT: return 'float'; + case gluShaderUtil.DataType.INT_VEC2: return 'vec2'; + case gluShaderUtil.DataType.INT_VEC3: return 'vec3'; + case gluShaderUtil.DataType.INT_VEC4: return 'vec4'; + case gluShaderUtil.DataType.UINT: return 'float'; + case gluShaderUtil.DataType.UINT_VEC2: return 'vec2'; + case gluShaderUtil.DataType.UINT_VEC3: return 'vec3'; + case gluShaderUtil.DataType.UINT_VEC4: return 'vec4'; + case gluShaderUtil.DataType.BOOL: return 'float'; + case gluShaderUtil.DataType.BOOL_VEC2: return 'vec2'; + case gluShaderUtil.DataType.BOOL_VEC3: return 'vec3'; + case gluShaderUtil.DataType.BOOL_VEC4: return 'vec4'; + } + throw Error('Unrecognized dataType ' + dataType); +}; + +/** + * gluShaderUtil.getDataTypeVector + * @param {gluShaderUtil.DataType} scalarType + * @param {number} size + * @return {gluShaderUtil.DataType} + */ +gluShaderUtil.getDataTypeVector = function(scalarType, size) { + var floats = [gluShaderUtil.DataType.FLOAT, + gluShaderUtil.DataType.FLOAT_VEC2, + gluShaderUtil.DataType.FLOAT_VEC3, + gluShaderUtil.DataType.FLOAT_VEC4]; + var ints = [gluShaderUtil.DataType.INT, + gluShaderUtil.DataType.INT_VEC2, + gluShaderUtil.DataType.INT_VEC3, + gluShaderUtil.DataType.INT_VEC4]; + var uints = [gluShaderUtil.DataType.UINT, + gluShaderUtil.DataType.UINT_VEC2, + gluShaderUtil.DataType.UINT_VEC3, + gluShaderUtil.DataType.UINT_VEC4]; + var bools = [gluShaderUtil.DataType.BOOL, + gluShaderUtil.DataType.BOOL_VEC2, + gluShaderUtil.DataType.BOOL_VEC3, + gluShaderUtil.DataType.BOOL_VEC4]; + + switch (scalarType) { + case gluShaderUtil.DataType.FLOAT: return floats[size - 1]; + case gluShaderUtil.DataType.INT: return ints[size - 1]; + case gluShaderUtil.DataType.UINT: return uints[size - 1]; + case gluShaderUtil.DataType.BOOL: return bools[size - 1]; + default: + throw new Error('Scalar type is not a vectoe:' + scalarType); + } +}; + +/** + * gluShaderUtil.getDataTypeFloatVec + * @param {number} vecSize + * @return {gluShaderUtil.DataType} + */ +gluShaderUtil.getDataTypeFloatVec = function(vecSize) { + return gluShaderUtil.getDataTypeVector(gluShaderUtil.DataType.FLOAT, vecSize); +}; + +/** + * gluShaderUtil.isDataTypeBoolOrBVec + * @param {gluShaderUtil.DataType} dataType + * @return {boolean} + */ +gluShaderUtil.isDataTypeBoolOrBVec = function(dataType) { + return (dataType >= gluShaderUtil.DataType.BOOL) && (dataType <= gluShaderUtil.DataType.BOOL_VEC4); +}; + +/** + * Returns type of scalar + * @param {gluShaderUtil.DataType} dataType shader + * @return {string} type of scalar type + */ +gluShaderUtil.getDataTypeScalarType = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT: return 'float'; + case gluShaderUtil.DataType.FLOAT_VEC2: return 'float'; + case gluShaderUtil.DataType.FLOAT_VEC3: return 'float'; + case gluShaderUtil.DataType.FLOAT_VEC4: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT2: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT2X3: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT2X4: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT3X2: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT3: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT3X4: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT4X2: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT4X3: return 'float'; + case gluShaderUtil.DataType.FLOAT_MAT4: return 'float'; + case gluShaderUtil.DataType.INT: return 'int'; + case gluShaderUtil.DataType.INT_VEC2: return 'int'; + case gluShaderUtil.DataType.INT_VEC3: return 'int'; + case gluShaderUtil.DataType.INT_VEC4: return 'int'; + case gluShaderUtil.DataType.UINT: return 'uint'; + case gluShaderUtil.DataType.UINT_VEC2: return 'uint'; + case gluShaderUtil.DataType.UINT_VEC3: return 'uint'; + case gluShaderUtil.DataType.UINT_VEC4: return 'uint'; + case gluShaderUtil.DataType.BOOL: return 'bool'; + case gluShaderUtil.DataType.BOOL_VEC2: return 'bool'; + case gluShaderUtil.DataType.BOOL_VEC3: return 'bool'; + case gluShaderUtil.DataType.BOOL_VEC4: return 'bool'; + case gluShaderUtil.DataType.SAMPLER_2D: return 'sampler2D'; + case gluShaderUtil.DataType.SAMPLER_CUBE: return 'samplerCube'; + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY: return 'sampler2DArray'; + case gluShaderUtil.DataType.SAMPLER_3D: return 'sampler3D'; + case gluShaderUtil.DataType.SAMPLER_2D_SHADOW: return 'sampler2DShadow'; + case gluShaderUtil.DataType.SAMPLER_CUBE_SHADOW: return 'samplerCubeShadow'; + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY_SHADOW: return 'sampler2DArrayShadow'; + case gluShaderUtil.DataType.INT_SAMPLER_2D: return 'isampler2D'; + case gluShaderUtil.DataType.INT_SAMPLER_CUBE: return 'isamplerCube'; + case gluShaderUtil.DataType.INT_SAMPLER_2D_ARRAY: return 'isampler2DArray'; + case gluShaderUtil.DataType.INT_SAMPLER_3D: return 'isampler3D'; + case gluShaderUtil.DataType.UINT_SAMPLER_2D: return 'usampler2D'; + case gluShaderUtil.DataType.UINT_SAMPLER_CUBE: return 'usamplerCube'; + case gluShaderUtil.DataType.UINT_SAMPLER_2D_ARRAY: return 'usampler2DArray'; + case gluShaderUtil.DataType.UINT_SAMPLER_3D: return 'usampler3D'; + } + throw new Error('Unrecognized datatype:' + dataType); +}; + +/** + * Returns type of scalar + * @param {?gluShaderUtil.DataType} dataType shader + * @return {gluShaderUtil.DataType} type of scalar type + */ +gluShaderUtil.getDataTypeScalarTypeAsDataType = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_VEC2: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_VEC3: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_VEC4: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT2: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT2X3: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT2X4: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT3X2: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT3: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT3X4: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT4X2: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT4X3: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.FLOAT_MAT4: return gluShaderUtil.DataType.FLOAT; + case gluShaderUtil.DataType.INT: return gluShaderUtil.DataType.INT; + case gluShaderUtil.DataType.INT_VEC2: return gluShaderUtil.DataType.INT; + case gluShaderUtil.DataType.INT_VEC3: return gluShaderUtil.DataType.INT; + case gluShaderUtil.DataType.INT_VEC4: return gluShaderUtil.DataType.INT; + case gluShaderUtil.DataType.UINT: return gluShaderUtil.DataType.UINT; + case gluShaderUtil.DataType.UINT_VEC2: return gluShaderUtil.DataType.UINT; + case gluShaderUtil.DataType.UINT_VEC3: return gluShaderUtil.DataType.UINT; + case gluShaderUtil.DataType.UINT_VEC4: return gluShaderUtil.DataType.UINT; + case gluShaderUtil.DataType.BOOL: return gluShaderUtil.DataType.BOOL; + case gluShaderUtil.DataType.BOOL_VEC2: return gluShaderUtil.DataType.BOOL; + case gluShaderUtil.DataType.BOOL_VEC3: return gluShaderUtil.DataType.BOOL; + case gluShaderUtil.DataType.BOOL_VEC4: return gluShaderUtil.DataType.BOOL; + case gluShaderUtil.DataType.SAMPLER_2D: + case gluShaderUtil.DataType.SAMPLER_CUBE: + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY: + case gluShaderUtil.DataType.SAMPLER_3D: + case gluShaderUtil.DataType.SAMPLER_2D_SHADOW: + case gluShaderUtil.DataType.SAMPLER_CUBE_SHADOW: + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY_SHADOW: + case gluShaderUtil.DataType.INT_SAMPLER_2D: + case gluShaderUtil.DataType.INT_SAMPLER_CUBE: + case gluShaderUtil.DataType.INT_SAMPLER_2D_ARRAY: + case gluShaderUtil.DataType.INT_SAMPLER_3D: + case gluShaderUtil.DataType.UINT_SAMPLER_2D: + case gluShaderUtil.DataType.UINT_SAMPLER_CUBE: + case gluShaderUtil.DataType.UINT_SAMPLER_2D_ARRAY: + case gluShaderUtil.DataType.UINT_SAMPLER_3D: + return dataType; + } + throw Error('Unrecognized dataType ' + dataType); +}; + +/** + * Checks if dataType is integer or vectors of integers + * @param {gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType integer or integer vector + */ +gluShaderUtil.isDataTypeIntOrIVec = function(dataType) { + /** @type {boolean} */ var retVal = false; + switch (dataType) { + case gluShaderUtil.DataType.INT: + case gluShaderUtil.DataType.INT_VEC2: + case gluShaderUtil.DataType.INT_VEC3: + case gluShaderUtil.DataType.INT_VEC4: + retVal = true; + } + + return retVal; +}; + +/** + * Checks if dataType is unsigned integer or vectors of unsigned integers + * @param {gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType unsigned integer or unsigned integer vector + */ +gluShaderUtil.isDataTypeUintOrUVec = function(dataType) { + /** @type {boolean} */ var retVal = false; + switch (dataType) { + case gluShaderUtil.DataType.UINT: + case gluShaderUtil.DataType.UINT_VEC2: + case gluShaderUtil.DataType.UINT_VEC3: + case gluShaderUtil.DataType.UINT_VEC4: + retVal = true; + } + + return retVal; +}; + +/** +* Returns type of scalar size +* @param {gluShaderUtil.DataType} dataType shader +* @return {number} with size of the type of scalar +*/ +gluShaderUtil.getDataTypeScalarSize = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT: return 1; + case gluShaderUtil.DataType.FLOAT_VEC2: return 2; + case gluShaderUtil.DataType.FLOAT_VEC3: return 3; + case gluShaderUtil.DataType.FLOAT_VEC4: return 4; + case gluShaderUtil.DataType.FLOAT_MAT2: return 4; + case gluShaderUtil.DataType.FLOAT_MAT2X3: return 6; + case gluShaderUtil.DataType.FLOAT_MAT2X4: return 8; + case gluShaderUtil.DataType.FLOAT_MAT3X2: return 6; + case gluShaderUtil.DataType.FLOAT_MAT3: return 9; + case gluShaderUtil.DataType.FLOAT_MAT3X4: return 12; + case gluShaderUtil.DataType.FLOAT_MAT4X2: return 8; + case gluShaderUtil.DataType.FLOAT_MAT4X3: return 12; + case gluShaderUtil.DataType.FLOAT_MAT4: return 16; + case gluShaderUtil.DataType.INT: return 1; + case gluShaderUtil.DataType.INT_VEC2: return 2; + case gluShaderUtil.DataType.INT_VEC3: return 3; + case gluShaderUtil.DataType.INT_VEC4: return 4; + case gluShaderUtil.DataType.UINT: return 1; + case gluShaderUtil.DataType.UINT_VEC2: return 2; + case gluShaderUtil.DataType.UINT_VEC3: return 3; + case gluShaderUtil.DataType.UINT_VEC4: return 4; + case gluShaderUtil.DataType.BOOL: return 1; + case gluShaderUtil.DataType.BOOL_VEC2: return 2; + case gluShaderUtil.DataType.BOOL_VEC3: return 3; + case gluShaderUtil.DataType.BOOL_VEC4: return 4; + case gluShaderUtil.DataType.SAMPLER_2D: return 1; + case gluShaderUtil.DataType.SAMPLER_CUBE: return 1; + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY: return 1; + case gluShaderUtil.DataType.SAMPLER_3D: return 1; + case gluShaderUtil.DataType.SAMPLER_2D_SHADOW: return 1; + case gluShaderUtil.DataType.SAMPLER_CUBE_SHADOW: return 1; + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY_SHADOW: return 1; + case gluShaderUtil.DataType.INT_SAMPLER_2D: return 1; + case gluShaderUtil.DataType.INT_SAMPLER_CUBE: return 1; + case gluShaderUtil.DataType.INT_SAMPLER_2D_ARRAY: return 1; + case gluShaderUtil.DataType.INT_SAMPLER_3D: return 1; + case gluShaderUtil.DataType.UINT_SAMPLER_2D: return 1; + case gluShaderUtil.DataType.UINT_SAMPLER_CUBE: return 1; + case gluShaderUtil.DataType.UINT_SAMPLER_2D_ARRAY: return 1; + case gluShaderUtil.DataType.UINT_SAMPLER_3D: return 1; + } + throw Error('Unrecognized dataType ' + dataType); +}; + +/** + * Checks if dataType is float or vector + * @param {?gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType float or vector + */ +gluShaderUtil.isDataTypeFloatOrVec = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT: + case gluShaderUtil.DataType.FLOAT_VEC2: + case gluShaderUtil.DataType.FLOAT_VEC3: + case gluShaderUtil.DataType.FLOAT_VEC4: + return true; + } + return false; +}; + +/** + * Checks if dataType is a matrix + * @param {gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType matrix or not + */ +gluShaderUtil.isDataTypeMatrix = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT_MAT2: + case gluShaderUtil.DataType.FLOAT_MAT2X3: + case gluShaderUtil.DataType.FLOAT_MAT2X4: + case gluShaderUtil.DataType.FLOAT_MAT3X2: + case gluShaderUtil.DataType.FLOAT_MAT3: + case gluShaderUtil.DataType.FLOAT_MAT3X4: + case gluShaderUtil.DataType.FLOAT_MAT4X2: + case gluShaderUtil.DataType.FLOAT_MAT4X3: + case gluShaderUtil.DataType.FLOAT_MAT4: + return true; + } + return false; +}; + +/** + * Checks if dataType is a vector + * @param {gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType vector or not + */ +gluShaderUtil.isDataTypeScalar = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT: + case gluShaderUtil.DataType.INT: + case gluShaderUtil.DataType.UINT: + case gluShaderUtil.DataType.BOOL: + return true; + } + return false; +}; + +/** + * Checks if dataType is a vector + * @param {gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType vector or not + */ +gluShaderUtil.isDataTypeVector = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT_VEC2: + case gluShaderUtil.DataType.FLOAT_VEC3: + case gluShaderUtil.DataType.FLOAT_VEC4: + case gluShaderUtil.DataType.INT_VEC2: + case gluShaderUtil.DataType.INT_VEC3: + case gluShaderUtil.DataType.INT_VEC4: + case gluShaderUtil.DataType.UINT_VEC2: + case gluShaderUtil.DataType.UINT_VEC3: + case gluShaderUtil.DataType.UINT_VEC4: + case gluShaderUtil.DataType.BOOL_VEC2: + case gluShaderUtil.DataType.BOOL_VEC3: + case gluShaderUtil.DataType.BOOL_VEC4: + return true; + } + return false; +}; + +/** + * Checks if dataType is a vector or a scalar type + * @param {gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType vector or scalar or not + */ +gluShaderUtil.isDataTypeScalarOrVector = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT: + case gluShaderUtil.DataType.FLOAT_VEC2: + case gluShaderUtil.DataType.FLOAT_VEC3: + case gluShaderUtil.DataType.FLOAT_VEC4: + case gluShaderUtil.DataType.INT: + case gluShaderUtil.DataType.INT_VEC2: + case gluShaderUtil.DataType.INT_VEC3: + case gluShaderUtil.DataType.INT_VEC4: + case gluShaderUtil.DataType.UINT: + case gluShaderUtil.DataType.UINT_VEC2: + case gluShaderUtil.DataType.UINT_VEC3: + case gluShaderUtil.DataType.UINT_VEC4: + case gluShaderUtil.DataType.BOOL: + case gluShaderUtil.DataType.BOOL_VEC2: + case gluShaderUtil.DataType.BOOL_VEC3: + case gluShaderUtil.DataType.BOOL_VEC4: + return true; + } + return false; +}; + +/** + * Checks if dataType is a sampler + * @param {gluShaderUtil.DataType} dataType shader + * @return {boolean} Is dataType vector or scalar or not + */ +gluShaderUtil.isDataTypeSampler = function(dataType) { + return (dataType >= gluShaderUtil.DataType.SAMPLER_2D) && (dataType <= gluShaderUtil.DataType.UINT_SAMPLER_3D); +}; + +/** + * Returns a gluShaderUtil.DataType based on given rows and columns + * @param {number} numCols + * @param {number} numRows + * @return {gluShaderUtil.DataType} + */ +gluShaderUtil.getDataTypeMatrix = function(numCols, numRows) { + if (!(deMath.deInRange32(numCols, 2, 4) && deMath.deInRange32(numRows, 2, 4))) + throw new Error('Out of bounds: (' + numCols + ',' + numRows + ')'); + + var size = numCols.toString() + 'x' + numRows.toString(); + var datatypes = { + '2x2': gluShaderUtil.DataType.FLOAT_MAT2, + '2x3': gluShaderUtil.DataType.FLOAT_MAT2X3, + '2x4': gluShaderUtil.DataType.FLOAT_MAT2X4, + '3x2': gluShaderUtil.DataType.FLOAT_MAT3X2, + '3x3': gluShaderUtil.DataType.FLOAT_MAT3, + '3x4': gluShaderUtil.DataType.FLOAT_MAT3X4, + '4x2': gluShaderUtil.DataType.FLOAT_MAT4X2, + '4x3': gluShaderUtil.DataType.FLOAT_MAT4X3, + '4x4': gluShaderUtil.DataType.FLOAT_MAT4 + }; + return datatypes[size]; +}; + +/** +* Returns number of rows of a gluShaderUtil.DataType Matrix +* @param {gluShaderUtil.DataType} dataType shader +* @return {number} with number of rows depending on gluShaderUtil.DataType Matrix +*/ +gluShaderUtil.getDataTypeMatrixNumRows = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT_MAT2: return 2; + case gluShaderUtil.DataType.FLOAT_MAT2X3: return 3; + case gluShaderUtil.DataType.FLOAT_MAT2X4: return 4; + case gluShaderUtil.DataType.FLOAT_MAT3X2: return 2; + case gluShaderUtil.DataType.FLOAT_MAT3: return 3; + case gluShaderUtil.DataType.FLOAT_MAT3X4: return 4; + case gluShaderUtil.DataType.FLOAT_MAT4X2: return 2; + case gluShaderUtil.DataType.FLOAT_MAT4X3: return 3; + case gluShaderUtil.DataType.FLOAT_MAT4: return 4; + } + throw Error('Unrecognized dataType ' + dataType); +}; + +/** +* Returns number of columns of a gluShaderUtil.DataType Matrix +* @param {gluShaderUtil.DataType} dataType shader +* @return {number} with number of columns depending on gluShaderUtil.DataType Matrix +*/ +gluShaderUtil.getDataTypeMatrixNumColumns = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.FLOAT_MAT2: return 2; + case gluShaderUtil.DataType.FLOAT_MAT2X3: return 2; + case gluShaderUtil.DataType.FLOAT_MAT2X4: return 2; + case gluShaderUtil.DataType.FLOAT_MAT3X2: return 3; + case gluShaderUtil.DataType.FLOAT_MAT3: return 3; + case gluShaderUtil.DataType.FLOAT_MAT3X4: return 3; + case gluShaderUtil.DataType.FLOAT_MAT4X2: return 4; + case gluShaderUtil.DataType.FLOAT_MAT4X3: return 4; + case gluShaderUtil.DataType.FLOAT_MAT4: return 4; + } + throw Error('Unrecognized dataType ' + dataType); +}; + +/** + * @param {gluShaderUtil.DataType} dataType + * @return {number} + */ +gluShaderUtil.getDataTypeNumLocations = function(dataType) { + if (gluShaderUtil.isDataTypeScalarOrVector(dataType)) + return 1; + else if (gluShaderUtil.isDataTypeMatrix(dataType)) + return gluShaderUtil.getDataTypeMatrixNumColumns(dataType); + throw Error('Unrecognized dataType ' + dataType); +}; + +/** + * @param {gluShaderUtil.DataType} dataType + * @return {number} + */ +gluShaderUtil.getDataTypeNumComponents = function(dataType) { + if (gluShaderUtil.isDataTypeScalarOrVector(dataType)) + return gluShaderUtil.getDataTypeScalarSize(dataType); + else if (gluShaderUtil.isDataTypeMatrix(dataType)) + return gluShaderUtil.getDataTypeMatrixNumRows(dataType); + + throw Error('Unrecognized dataType ' + dataType); +}; + +/** + * Returns name of the dataType + * @param {?gluShaderUtil.DataType} dataType shader + * @return {string} dataType name + */ +gluShaderUtil.getDataTypeName = function(dataType) { + switch (dataType) { + case gluShaderUtil.DataType.INVALID: return 'invalid'; + + case gluShaderUtil.DataType.FLOAT: return 'float'; + case gluShaderUtil.DataType.FLOAT_VEC2: return 'vec2'; + case gluShaderUtil.DataType.FLOAT_VEC3: return 'vec3'; + case gluShaderUtil.DataType.FLOAT_VEC4: return 'vec4'; + case gluShaderUtil.DataType.FLOAT_MAT2: return 'mat2'; + case gluShaderUtil.DataType.FLOAT_MAT2X3: return 'mat2x3'; + case gluShaderUtil.DataType.FLOAT_MAT2X4: return 'mat2x4'; + case gluShaderUtil.DataType.FLOAT_MAT3X2: return 'mat3x2'; + case gluShaderUtil.DataType.FLOAT_MAT3: return 'mat3'; + case gluShaderUtil.DataType.FLOAT_MAT3X4: return 'mat3x4'; + case gluShaderUtil.DataType.FLOAT_MAT4X2: return 'mat4x2'; + case gluShaderUtil.DataType.FLOAT_MAT4X3: return 'mat4x3'; + case gluShaderUtil.DataType.FLOAT_MAT4: return 'mat4'; + + case gluShaderUtil.DataType.INT: return 'int'; + case gluShaderUtil.DataType.INT_VEC2: return 'ivec2'; + case gluShaderUtil.DataType.INT_VEC3: return 'ivec3'; + case gluShaderUtil.DataType.INT_VEC4: return 'ivec4'; + + case gluShaderUtil.DataType.UINT: return 'uint'; + case gluShaderUtil.DataType.UINT_VEC2: return 'uvec2'; + case gluShaderUtil.DataType.UINT_VEC3: return 'uvec3'; + case gluShaderUtil.DataType.UINT_VEC4: return 'uvec4'; + + case gluShaderUtil.DataType.BOOL: return 'bool'; + case gluShaderUtil.DataType.BOOL_VEC2: return 'bvec2'; + case gluShaderUtil.DataType.BOOL_VEC3: return 'bvec3'; + case gluShaderUtil.DataType.BOOL_VEC4: return 'bvec4'; + + case gluShaderUtil.DataType.SAMPLER_2D: return 'sampler2D'; + case gluShaderUtil.DataType.SAMPLER_CUBE: return 'samplerCube'; + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY: return 'sampler2DArray'; + case gluShaderUtil.DataType.SAMPLER_3D: return 'sampler3D'; + + case gluShaderUtil.DataType.SAMPLER_2D_SHADOW: return 'sampler2DShadow'; + case gluShaderUtil.DataType.SAMPLER_CUBE_SHADOW: return 'samplerCubeShadow'; + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY_SHADOW: return 'sampler2DArrayShadow'; + + case gluShaderUtil.DataType.INT_SAMPLER_2D: return 'isampler2D'; + case gluShaderUtil.DataType.INT_SAMPLER_CUBE: return 'isamplerCube'; + case gluShaderUtil.DataType.INT_SAMPLER_2D_ARRAY: return 'isampler2DArray'; + case gluShaderUtil.DataType.INT_SAMPLER_3D: return 'isampler3D'; + + case gluShaderUtil.DataType.UINT_SAMPLER_2D: return 'usampler2D'; + case gluShaderUtil.DataType.UINT_SAMPLER_CUBE: return 'usamplerCube'; + case gluShaderUtil.DataType.UINT_SAMPLER_2D_ARRAY: return 'usampler2DArray'; + case gluShaderUtil.DataType.UINT_SAMPLER_3D: return 'usampler3D'; + } + throw Error('Unrecognized dataType ' + dataType); +}; + +/** + * Returns the gluShaderUtil.DataType from the GL type + * @param {number} glType + * @return {gluShaderUtil.DataType} + */ +gluShaderUtil.getDataTypeFromGLType = function(glType) { + switch (glType) { + case gl.FLOAT: return gluShaderUtil.DataType.FLOAT; + case gl.FLOAT_VEC2: return gluShaderUtil.DataType.FLOAT_VEC2; + case gl.FLOAT_VEC3: return gluShaderUtil.DataType.FLOAT_VEC3; + case gl.FLOAT_VEC4: return gluShaderUtil.DataType.FLOAT_VEC4; + + case gl.FLOAT_MAT2: return gluShaderUtil.DataType.FLOAT_MAT2; + case gl.FLOAT_MAT2x3: return gluShaderUtil.DataType.FLOAT_MAT2X3; + case gl.FLOAT_MAT2x4: return gluShaderUtil.DataType.FLOAT_MAT2X4; + + case gl.FLOAT_MAT3x2: return gluShaderUtil.DataType.FLOAT_MAT3X2; + case gl.FLOAT_MAT3: return gluShaderUtil.DataType.FLOAT_MAT3; + case gl.FLOAT_MAT3x4: return gluShaderUtil.DataType.FLOAT_MAT3X4; + + case gl.FLOAT_MAT4x2: return gluShaderUtil.DataType.FLOAT_MAT4X2; + case gl.FLOAT_MAT4x3: return gluShaderUtil.DataType.FLOAT_MAT4X3; + case gl.FLOAT_MAT4: return gluShaderUtil.DataType.FLOAT_MAT4; + + case gl.INT: return gluShaderUtil.DataType.INT; + case gl.INT_VEC2: return gluShaderUtil.DataType.INT_VEC2; + case gl.INT_VEC3: return gluShaderUtil.DataType.INT_VEC3; + case gl.INT_VEC4: return gluShaderUtil.DataType.INT_VEC4; + + case gl.UNSIGNED_INT: return gluShaderUtil.DataType.UINT; + case gl.UNSIGNED_INT_VEC2: return gluShaderUtil.DataType.UINT_VEC2; + case gl.UNSIGNED_INT_VEC3: return gluShaderUtil.DataType.UINT_VEC3; + case gl.UNSIGNED_INT_VEC4: return gluShaderUtil.DataType.UINT_VEC4; + + case gl.BOOL: return gluShaderUtil.DataType.BOOL; + case gl.BOOL_VEC2: return gluShaderUtil.DataType.BOOL_VEC2; + case gl.BOOL_VEC3: return gluShaderUtil.DataType.BOOL_VEC3; + case gl.BOOL_VEC4: return gluShaderUtil.DataType.BOOL_VEC4; + + case gl.SAMPLER_2D: return gluShaderUtil.DataType.SAMPLER_2D; + case gl.SAMPLER_CUBE: return gluShaderUtil.DataType.SAMPLER_CUBE; + case gl.SAMPLER_2D_ARRAY: return gluShaderUtil.DataType.SAMPLER_2D_ARRAY; + case gl.SAMPLER_3D: return gluShaderUtil.DataType.SAMPLER_3D; + + case gl.SAMPLER_2D_SHADOW: return gluShaderUtil.DataType.SAMPLER_2D_SHADOW; + case gl.SAMPLER_CUBE_SHADOW: return gluShaderUtil.DataType.SAMPLER_CUBE_SHADOW; + case gl.SAMPLER_2D_ARRAY_SHADOW: return gluShaderUtil.DataType.SAMPLER_2D_ARRAY_SHADOW; + + case gl.INT_SAMPLER_2D: return gluShaderUtil.DataType.INT_SAMPLER_2D; + case gl.INT_SAMPLER_CUBE: return gluShaderUtil.DataType.INT_SAMPLER_CUBE; + case gl.INT_SAMPLER_2D_ARRAY: return gluShaderUtil.DataType.INT_SAMPLER_2D_ARRAY; + case gl.INT_SAMPLER_3D: return gluShaderUtil.DataType.INT_SAMPLER_3D; + + case gl.UNSIGNED_INT_SAMPLER_2D: return gluShaderUtil.DataType.UINT_SAMPLER_2D; + case gl.UNSIGNED_INT_SAMPLER_CUBE: return gluShaderUtil.DataType.UINT_SAMPLER_CUBE; + case gl.UNSIGNED_INT_SAMPLER_2D_ARRAY: return gluShaderUtil.DataType.UINT_SAMPLER_2D_ARRAY; + case gl.UNSIGNED_INT_SAMPLER_3D: return gluShaderUtil.DataType.UINT_SAMPLER_3D; + + default: + throw new Error('Unrecognized GL type:' + glType); + } +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluStrUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluStrUtil.js new file mode 100644 index 000000000..b554db047 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluStrUtil.js @@ -0,0 +1,166 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluStrUtil'); + +goog.scope(function() { + +var gluStrUtil = framework.opengl.gluStrUtil; + +gluStrUtil.getPixelFormatName = function(value) { + switch (value) { + case gl.LUMINANCE: return 'gl.LUMINANCE'; + case gl.LUMINANCE_ALPHA: return 'gl.LUMINANCE_ALPHA'; + case gl.ALPHA: return 'gl.ALPHA'; + case gl.RGB: return 'gl.RGB'; + case gl.RGBA: return 'gl.RGBA'; + case gl.RGBA4: return 'gl.RGBA4'; + case gl.RGB5_A1: return 'gl.RGB5_A1'; + case gl.RGB565: return 'gl.RGB565'; + case gl.DEPTH_COMPONENT16: return 'gl.DEPTH_COMPONENT16'; + case gl.STENCIL_INDEX8: return 'gl.STENCIL_INDEX8'; + case gl.RG: return 'gl.RG'; + case gl.RED: return 'gl.RED'; + case gl.RGBA_INTEGER: return 'gl.RGBA_INTEGER'; + case gl.RGB_INTEGER: return 'gl.RGB_INTEGER'; + case gl.RG_INTEGER: return 'gl.RG_INTEGER'; + case gl.RED_INTEGER: return 'gl.RED_INTEGER'; + case gl.DEPTH_COMPONENT: return 'gl.DEPTH_COMPONENT'; + case gl.DEPTH_STENCIL: return 'gl.DEPTH_STENCIL'; + case gl.RGBA32F: return 'gl.RGBA32F'; + case gl.RGBA32I: return 'gl.RGBA32I'; + case gl.RGBA32UI: return 'gl.RGBA32UI'; + // case gl.RGBA16: return 'gl.RGBA16'; + // case gl.RGBA16_SNORM: return 'gl.RGBA16_SNORM'; + case gl.RGBA16F: return 'gl.RGBA16F'; + case gl.RGBA16I: return 'gl.RGBA16I'; + case gl.RGBA16UI: return 'gl.RGBA16UI'; + case gl.RGBA8: return 'gl.RGBA8'; + case gl.RGBA8I: return 'gl.RGBA8I'; + case gl.RGBA8UI: return 'gl.RGBA8UI'; + case gl.SRGB8_ALPHA8: return 'gl.SRGB8_ALPHA8'; + case gl.RGB10_A2: return 'gl.RGB10_A2'; + case gl.RGB10_A2UI: return 'gl.RGB10_A2UI'; + case gl.RGBA8_SNORM: return 'gl.RGBA8_SNORM'; + case gl.RGB8: return 'gl.RGB8'; + case gl.R11F_G11F_B10F: return 'gl.R11F_G11F_B10F'; + case gl.RGB32F: return 'gl.RGB32F'; + case gl.RGB32I: return 'gl.RGB32I'; + case gl.RGB32UI: return 'gl.RGB32UI'; + // case gl.RGB16: return 'gl.RGB16'; + // case gl.RGB16_SNORM: return 'gl.RGB16_SNORM'; + case gl.RGB16F: return 'gl.RGB16F'; + case gl.RGB16I: return 'gl.RGB16I'; + case gl.RGB16UI: return 'gl.RGB16UI'; + case gl.RGB8_SNORM: return 'gl.RGB8_SNORM'; + case gl.RGB8I: return 'gl.RGB8I'; + case gl.RGB8UI: return 'gl.RGB8UI'; + case gl.SRGB8: return 'gl.SRGB8'; + case gl.RGB9_E5: return 'gl.RGB9_E5'; + case gl.RG32F: return 'gl.RG32F'; + case gl.RG32I: return 'gl.RG32I'; + case gl.RG32UI: return 'gl.RG32UI'; + // case gl.RG16: return 'gl.RG16'; + // case gl.RG16_SNORM: return 'gl.RG16_SNORM'; + case gl.RG16F: return 'gl.RG16F'; + case gl.RG16I: return 'gl.RG16I'; + case gl.RG16UI: return 'gl.RG16UI'; + case gl.RG8: return 'gl.RG8'; + case gl.RG8I: return 'gl.RG8I'; + case gl.RG8UI: return 'gl.RG8UI'; + case gl.RG8_SNORM: return 'gl.RG8_SNORM'; + case gl.R32F: return 'gl.R32F'; + case gl.R32I: return 'gl.R32I'; + case gl.R32UI: return 'gl.R32UI'; + // case gl.R16: return 'gl.R16'; + // case gl.R16_SNORM: return 'gl.R16_SNORM'; + case gl.R16F: return 'gl.R16F'; + case gl.R16I: return 'gl.R16I'; + case gl.R16UI: return 'gl.R16UI'; + case gl.R8: return 'gl.R8'; + case gl.R8I: return 'gl.R8I'; + case gl.R8UI: return 'gl.R8UI'; + case gl.R8_SNORM: return 'gl.R8_SNORM'; + case gl.DEPTH_COMPONENT32F: return 'gl.DEPTH_COMPONENT32F'; + case gl.DEPTH_COMPONENT24: return 'gl.DEPTH_COMPONENT24'; + case gl.DEPTH32F_STENCIL8: return 'gl.DEPTH32F_STENCIL8'; + case gl.DEPTH24_STENCIL8: return 'gl.DEPTH24_STENCIL8'; + // case gl.RGB10: return 'gl.RGB10'; + // case gl.DEPTH_COMPONENT32: return 'gl.DEPTH_COMPONENT32'; + case gl.SRGB: return 'gl.SRGB'; + // case gl.SRGB_ALPHA: return 'gl.SRGB_ALPHA'; + default: return ''; + } +}; + +gluStrUtil.getTypeName = function(value) { + switch (value) { + case gl.BYTE: return 'gl.BYTE'; + case gl.UNSIGNED_BYTE: return 'gl.UNSIGNED_BYTE'; + case gl.SHORT: return 'gl.SHORT'; + case gl.UNSIGNED_SHORT: return 'gl.UNSIGNED_SHORT'; + case gl.INT: return 'gl.INT'; + case gl.UNSIGNED_INT: return 'gl.UNSIGNED_INT'; + case gl.FLOAT: return 'gl.FLOAT'; + // case gl.FIXED: return 'gl.FIXED'; + case gl.UNSIGNED_SHORT_5_6_5: return 'gl.UNSIGNED_SHORT_5_6_5'; + case gl.UNSIGNED_SHORT_4_4_4_4: return 'gl.UNSIGNED_SHORT_4_4_4_4'; + case gl.UNSIGNED_SHORT_5_5_5_1: return 'gl.UNSIGNED_SHORT_5_5_5_1'; + case gl.HALF_FLOAT: return 'gl.HALF_FLOAT'; + case gl.INT_2_10_10_10_REV: return 'gl.INT_2_10_10_10_REV'; + case gl.UNSIGNED_INT_2_10_10_10_REV: return 'gl.UNSIGNED_INT_2_10_10_10_REV'; + case gl.UNSIGNED_INT_10F_11F_11F_REV: return 'gl.UNSIGNED_INT_10F_11F_11F_REV'; + case gl.UNSIGNED_INT_5_9_9_9_REV: return 'gl.UNSIGNED_INT_5_9_9_9_REV'; + case gl.UNSIGNED_INT_24_8: return 'gl.UNSIGNED_INT_24_8'; + case gl.FLOAT_32_UNSIGNED_INT_24_8_REV: return 'gl.FLOAT_32_UNSIGNED_INT_24_8_REV'; + case gl.SIGNED_NORMALIZED: return 'gl.SIGNED_NORMALIZED'; + case gl.UNSIGNED_NORMALIZED: return 'gl.UNSIGNED_NORMALIZED'; + // case gl.HALF_FLOAT_OES: return 'gl.HALF_FLOAT_OES'; + default: return ''; + } +}; + +gluStrUtil.getErrorName = function(value) { + switch (value) { + case gl.NO_ERROR: return 'gl.NO_ERROR'; + case gl.INVALID_ENUM: return 'gl.INVALID_ENUM'; + case gl.INVALID_VALUE: return 'gl.INVALID_VALUE'; + case gl.INVALID_OPERATION: return 'gl.INVALID_OPERATION'; + case gl.OUT_OF_MEMORY: return 'gl.OUT_OF_MEMORY'; + // case gl.INVALID_FRAMEBUFFER_OPERATION: return 'gl.INVALID_FRAMEBUFFER_OPERATION'; + default: return ''; + } +}; + +gluStrUtil.getFramebufferStatusName = function(value) { + switch (value) { + case gl.FRAMEBUFFER_COMPLETE: return 'gl.FRAMEBUFFER_COMPLETE'; + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: return 'gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: return 'gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: return 'gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: return 'gl.FRAMEBUFFER_UNSUPPORTED'; + case gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: return 'gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE'; + // case: gl.FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: return 'gl.FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS'; + default: return ''; + } +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluTexture.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluTexture.js new file mode 100644 index 000000000..fcc33588e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluTexture.js @@ -0,0 +1,380 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluTexture'); +goog.require('framework.common.tcuCompressedTexture'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.opengl.gluTextureUtil'); + +goog.scope(function() { + +var gluTexture = framework.opengl.gluTexture; +var gluTextureUtil = framework.opengl.gluTextureUtil; +var tcuTexture = framework.common.tcuTexture; +var tcuCompressedTexture = framework.common.tcuCompressedTexture; +var deMath = framework.delibs.debase.deMath; + +var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); +}; + +/** @enum {number} */ +gluTexture.Type = { + TYPE_NONE: 0, + TYPE_2D: 1, + TYPE_CUBE_MAP: 2, + TYPE_2D_ARRAY: 3, + TYPE_3D: 4 +}; + +/** + * @constructor + */ +gluTexture.Texture2D = function(gl, format, isCompressed, refTexture) { + this.gl = gl; + this.m_glTexture = gl.createTexture(); + this.m_isCompressed = isCompressed; + this.m_format = format; // Internal format + this.m_refTexture = refTexture; + this.m_type = gluTexture.Type.TYPE_2D; +}; + +gluTexture.Texture2D.prototype.getType = function() { + return this.m_type; +}; + +gluTexture.Texture2D.prototype.getRefTexture = function() { + return this.m_refTexture; +}; + +gluTexture.Texture2D.prototype.getGLTexture = function() { + return this.m_glTexture; +}; + +gluTexture.texture2DFromFormat = function(gl, format, dataType, width, height) { + var tex = new gluTexture.Texture2D(gl, format, false, new tcuTexture.Texture2D(gluTextureUtil.mapGLTransferFormat(format, dataType), width, height)); + return tex; +}; + +gluTexture.texture2DFromInternalFormat = function(gl, internalFormat, width, height) { + var tex = new gluTexture.Texture2D(gl, internalFormat, false, new tcuTexture.Texture2D(gluTextureUtil.mapGLInternalFormat(internalFormat), width, height)); + return tex; +}; + +/** + * @param {number} numLevels + * @param {Array<tcuCompressedTexture.CompressedTexture>} levels + * @return {gluTexture.Texture2D} + */ +gluTexture.texture2DFromCompressedTexture = function(gl, numLevels, levels) { + var level = levels[0]; + var format = gluTextureUtil.getGLFormat(level.getFormat()); + var refTex = new tcuTexture.Texture2D(level.getUncompressedFormat(), level.getWidth(), level.getHeight()); + /** @type {gluTexture.Texture2D} */ var tex2d = new gluTexture.Texture2D(gl, format, true, refTex); + + tex2d.loadCompressed(numLevels, levels); + + return tex2d; +}; +/** + * @param {number} numLevels + * @param {Array<tcuCompressedTexture.CompressedTexture>} levels + */ +gluTexture.Texture2D.prototype.loadCompressed = function(numLevels, levels) { + /** @type {number} */ var compressedFormat = gluTextureUtil.getGLFormat(levels[0].getFormat()); + + assertMsgOptions(this.m_glTexture, 'm_glTexture not defined', false, true); + gl.bindTexture(gl.TEXTURE_2D, this.m_glTexture); + + for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { + /** @type {tcuCompressedTexture.CompressedTexture} */ var level = levels[levelNdx]; + + // Decompress to reference texture. + this.m_refTexture.allocLevel(levelNdx); + /** @type {tcuTexture.PixelBufferAccess} */ var refLevelAccess = this.m_refTexture.getLevel(levelNdx); + assertMsgOptions(level.getWidth() == refLevelAccess.getWidth() && level.getHeight() == refLevelAccess.getHeight(), 'level and reference sizes not equal', false, true); + level.decompress(refLevelAccess); + + // Upload to GL texture in compressed form. + gl.compressedTexImage2D(gl.TEXTURE_2D, levelNdx, compressedFormat, + level.getWidth(), level.getHeight(), 0, level.getData()); + } +}; + +gluTexture.computePixelStore = function(/*const tcu::TextureFormat&*/ format) { + var pixelSize = format.getPixelSize(); + if (deMath.deIsPowerOfTwo32(pixelSize)) + return Math.min(pixelSize, 8); + else + return 1; +}; + +gluTexture.cubeFaceToGLFace = function(/*tcu::CubeFace*/ face) { + switch (face) { + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X: return gl.TEXTURE_CUBE_MAP_NEGATIVE_X; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_X: return gl.TEXTURE_CUBE_MAP_POSITIVE_X; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y: return gl.TEXTURE_CUBE_MAP_NEGATIVE_Y; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y: return gl.TEXTURE_CUBE_MAP_POSITIVE_Y; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z: return gl.TEXTURE_CUBE_MAP_NEGATIVE_Z; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z: return gl.TEXTURE_CUBE_MAP_POSITIVE_Z; + } + throw new Error('Unrecognized face: ' + face); +}; + +gluTexture.Texture2D.prototype.upload = function() { + DE_ASSERT(!this.m_isCompressed); + + if (this.m_glTexture == null) + testFailedOptions('Failed to create GL texture', true); + + gl.bindTexture(gl.TEXTURE_2D, this.m_glTexture); + gl.pixelStorei(gl.UNPACK_ALIGNMENT, gluTexture.computePixelStore(this.m_refTexture.getFormat())); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Setting pixel store failed', false, true); + + var transferFormat = gluTextureUtil.getTransferFormat(this.m_refTexture.getFormat()); + + for (var levelNdx = 0; levelNdx < this.m_refTexture.getNumLevels(); levelNdx++) { + if (this.m_refTexture.isLevelEmpty(levelNdx)) + continue; // Don't upload. + + var access = this.m_refTexture.getLevel(levelNdx); + DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * access.getWidth()); + var data = access.getDataPtr(); + gl.texImage2D(gl.TEXTURE_2D, levelNdx, this.m_format, access.getWidth(), access.getHeight(), 0 /* border */, transferFormat.format, transferFormat.dataType, access.getDataPtr()); + } + + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); +}; + +/** + * @constructor + * @extends {gluTexture.Texture2D} + */ +gluTexture.TextureCube = function(gl, format, isCompressed, refTexture) { + gluTexture.Texture2D.call(this, gl, format, isCompressed, refTexture); + this.m_type = gluTexture.Type.TYPE_CUBE_MAP; +}; + +gluTexture.TextureCube.prototype = Object.create(gluTexture.Texture2D.prototype); +gluTexture.TextureCube.prototype.constructor = gluTexture.TextureCube; + +gluTexture.TextureCube.prototype.upload = function() { + DE_ASSERT(!this.m_isCompressed); + + if (this.m_glTexture == null) + testFailedOptions('Failed to create GL texture', true); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.m_glTexture); + gl.pixelStorei(gl.UNPACK_ALIGNMENT, gluTexture.computePixelStore(this.m_refTexture.getFormat())); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Setting pixel store failed', false, true); + + var transferFormat = gluTextureUtil.getTransferFormat(this.m_refTexture.getFormat()); + + for (var face in tcuTexture.CubeFace) { + for (var levelNdx = 0; levelNdx < this.m_refTexture.getNumLevels(); levelNdx++) { + if (this.m_refTexture.isLevelEmpty(tcuTexture.CubeFace[face], levelNdx)) + continue; // Don't upload. + + /*tcu::ConstPixelBufferAccess*/ var access = this.m_refTexture.getLevelFace(levelNdx, tcuTexture.CubeFace[face]); + DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * access.getWidth()); + gl.texImage2D(gluTexture.cubeFaceToGLFace(tcuTexture.CubeFace[face]), levelNdx, this.m_format, access.getWidth(), access.getHeight(), 0 /* border */, transferFormat.format, transferFormat.dataType, access.getDataPtr()); + } + } + + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); +}; + +gluTexture.cubeFromFormat = function(gl, format, dataType, size) { + var tex = new gluTexture.TextureCube(gl, format, false, new tcuTexture.TextureCube(gluTextureUtil.mapGLTransferFormat(format, dataType), size)); + return tex; +}; + +gluTexture.cubeFromInternalFormat = function(gl, internalFormat, size) { + var tex = new gluTexture.TextureCube(gl, internalFormat, false, new tcuTexture.TextureCube(gluTextureUtil.mapGLInternalFormat(internalFormat), size)); + return tex; +}; + +/** + * @constructor + * @extends {gluTexture.Texture2D} + */ +gluTexture.Texture2DArray = function(gl, format, isCompressed, refTexture) { + gluTexture.Texture2D.call(this, gl, format, isCompressed, refTexture); + this.m_type = gluTexture.Type.TYPE_2D_ARRAY; +}; + +gluTexture.Texture2DArray.prototype = Object.create(gluTexture.Texture2D.prototype); +gluTexture.Texture2DArray.prototype.constructor = gluTexture.Texture2DArray; + +gluTexture.Texture2DArray.prototype.upload = function() { + if (!gl.texImage3D) + throw new Error('gl.TexImage3D() is not supported'); + + gl.bindTexture(gl.TEXTURE_2D_ARRAY, this.m_glTexture); + gl.pixelStorei(gl.UNPACK_ALIGNMENT, gluTexture.computePixelStore(this.m_refTexture.getFormat())); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); + + var transferFormat = gluTextureUtil.getTransferFormat(this.m_refTexture.getFormat()); + + for (var levelNdx = 0; levelNdx < this.m_refTexture.getNumLevels(); levelNdx++) { + if (this.m_refTexture.isLevelEmpty(levelNdx)) + continue; // Don't upload. + + /*tcu::ConstPixelBufferAccess*/ var access = this.m_refTexture.getLevel(levelNdx); + DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * access.getWidth()); + DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize() * access.getWidth() * access.getHeight()); + gl.texImage3D(gl.TEXTURE_2D_ARRAY, levelNdx, this.m_format, access.getWidth(), access.getHeight(), access.getDepth(), 0 /* border */, transferFormat.format, transferFormat.dataType, access.getDataPtr()); + } + + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); +}; + +gluTexture.texture2DArrayFromFormat = function(gl, format, dataType, width, height, numLayers) { + var tex = new gluTexture.Texture2DArray(gl, format, false, new tcuTexture.Texture2DArray(gluTextureUtil.mapGLTransferFormat(format, dataType), width, height, numLayers)); + return tex; +}; + +gluTexture.texture2DArrayFromInternalFormat = function(gl, internalFormat, width, height, numLayers) { + var tex = new gluTexture.Texture2DArray(gl, internalFormat, false, new tcuTexture.Texture2DArray(gluTextureUtil.mapGLInternalFormat(internalFormat), width, height, numLayers)); + return tex; +}; + +/** + * @constructor + * @extends {gluTexture.Texture2D} + */ +gluTexture.Texture3D = function(gl, format, isCompressed, refTexture) { + gluTexture.Texture2D.call(this, gl, format, isCompressed, refTexture); + this.m_type = gluTexture.Type.TYPE_3D; +}; + +gluTexture.Texture3D.prototype = Object.create(gluTexture.Texture2D.prototype); +gluTexture.Texture3D.prototype.constructor = gluTexture.Texture3D; + +gluTexture.Texture3D.prototype.upload = function() { + if (!gl.texImage3D) + throw new Error('gl.TexImage3D() is not supported'); + + gl.bindTexture(gl.TEXTURE_3D, this.m_glTexture); + gl.pixelStorei(gl.UNPACK_ALIGNMENT, gluTexture.computePixelStore(this.m_refTexture.getFormat())); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); + + var transferFormat = gluTextureUtil.getTransferFormat(this.m_refTexture.getFormat()); + + for (var levelNdx = 0; levelNdx < this.m_refTexture.getNumLevels(); levelNdx++) { + if (this.m_refTexture.isLevelEmpty(levelNdx)) + continue; // Don't upload. + + /*tcu::ConstPixelBufferAccess*/ var access = this.m_refTexture.getLevel(levelNdx); + DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * access.getWidth()); + DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize() * access.getWidth() * access.getHeight()); + gl.texImage3D(gl.TEXTURE_3D, levelNdx, this.m_format, access.getWidth(), access.getHeight(), access.getDepth(), 0 /* border */, transferFormat.format, transferFormat.dataType, access.getDataPtr()); + } + + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); +}; + +gluTexture.texture3DFromFormat = function(gl, format, dataType, width, height, depth) { + var tex = new gluTexture.Texture3D(gl, format, false, new tcuTexture.Texture3D(gluTextureUtil.mapGLTransferFormat(format, dataType), width, height, depth)); + return tex; +}; + +gluTexture.texture3DFromInternalFormat = function(gl, internalFormat, width, height, depth) { + var tex = new gluTexture.Texture3D(gl, internalFormat, false, new tcuTexture.Texture3D(gluTextureUtil.mapGLInternalFormat(internalFormat), width, height, depth)); + return tex; +}; + +/** + * @constructor + * @extends {gluTexture.Texture2D} + */ +gluTexture.Compressed2D = function(gl, format, isCompressed, refTexture) { + gluTexture.Texture2D.call(this, gl, format, isCompressed, refTexture); +}; + +gluTexture.Compressed2D.prototype = Object.create(gluTexture.Texture2D.prototype); +gluTexture.Compressed2D.prototype.constructor = gluTexture.Compressed2D; + +gluTexture.Compressed2D.prototype.uploadLevel = function(level, source) { + DE_ASSERT(this.m_isCompressed); + + if (this.m_glTexture == null) + testFailedOptions('Failed to create GL texture', true); + + gl.bindTexture(gl.TEXTURE_2D, this.m_glTexture); + + gl.compressedTexImage2D(gl.TEXTURE_2D, level, this.m_format, source.m_width, source.m_height, 0 /* border */, source.m_data); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); +}; + +/** + * @constructor + * @extends {gluTexture.Texture2D} + */ +gluTexture.CompressedCube = function(gl, format, isCompressed, refTexture) { + gluTexture.Texture2D.call(this, gl, format, isCompressed, refTexture); +}; + +gluTexture.CompressedCube.prototype = Object.create(gluTexture.Texture2D.prototype); +gluTexture.CompressedCube.prototype.constructor = gluTexture.CompressedCube; + +gluTexture.CompressedCube.prototype.uploadLevel = function(level, source) { + DE_ASSERT(this.m_isCompressed); + + if (this.m_glTexture == null) + testFailedOptions('Failed to create GL texture', true); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.m_glTexture); + + for (var face in tcuTexture.CubeFace) { + + // Upload to GL texture in compressed form. + gl.compressedTexImage2D(gluTexture.cubeFaceToGLFace(tcuTexture.CubeFace[face]), 0, this.m_format, + source.m_width, source.m_height, 0 /* border */, source.m_data); + assertMsgOptions(gl.getError() === gl.NO_ERROR, 'Texture upload failed', false, true); + } + +}; + +gluTexture.compressed2DFromInternalFormat = function(gl, format, width, height, compressed) { + var tex = new gluTexture.Compressed2D(gl, gluTextureUtil.getGLFormat(format), true, new tcuTexture.Texture2D(compressed.getUncompressedFormat(), width, height)); + tex.m_refTexture.allocLevel(0); + compressed.decompress(tex.m_refTexture.getLevel(0)); + tex.uploadLevel(0, compressed); + return tex; +}; + +gluTexture.compressedCubeFromInternalFormat = function(gl, format, size, compressed) { + var tex = new gluTexture.CompressedCube(gl, gluTextureUtil.getGLFormat(format), true, new tcuTexture.TextureCube(compressed.getUncompressedFormat(), size)); + for (var face in tcuTexture.CubeFace) { + tex.m_refTexture.allocLevel(tcuTexture.CubeFace[face], 0); + + /*tcu::ConstPixelBufferAccess*/ var access = tex.m_refTexture.getLevelFace(0, tcuTexture.CubeFace[face]); + DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * access.getWidth()); + compressed.decompress(access); + } + tex.uploadLevel(0, compressed); + return tex; +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluTextureUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluTextureUtil.js new file mode 100644 index 000000000..06f3f5289 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluTextureUtil.js @@ -0,0 +1,1025 @@ +/*------------------------------------------------------------------------- + * 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. + * + */ + +/*--------------------------------------------------------------------*//*! + * \brief Map tcuTexture.TextureFormat to GL pixel transfer format. + * + * Maps generic texture format description to GL pixel transfer format. + * If no mapping is found, throws tcu::InternalError. + * + * \param texFormat Generic texture format. + * \return GL pixel transfer format. + *//*--------------------------------------------------------------------*/ +'use strict'; +goog.provide('framework.opengl.gluTextureUtil'); +goog.require('framework.common.tcuCompressedTexture'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deString'); +goog.require('framework.opengl.gluShaderUtil'); + +goog.scope(function() { + +var gluTextureUtil = framework.opengl.gluTextureUtil; +var deString = framework.delibs.debase.deString; +var tcuTexture = framework.common.tcuTexture; +var tcuTextureUtil = framework.common.tcuTextureUtil; +var tcuCompressedTexture = framework.common.tcuCompressedTexture; +var gluShaderUtil = framework.opengl.gluShaderUtil; + +/** + * @param {number} format + * @param {number} dataType + * @constructor + */ +gluTextureUtil.TransferFormat = function(format, dataType) { + this.format = format; //!< Pixel format. + this.dataType = dataType; //!< Data type. +}; + +/** + * Map tcuTexture.TextureFormat to GL pixel transfer format. + * + * Maps generic texture format description to GL pixel transfer format. + * If no mapping is found, throws tcu::InternalError. + * + * @param {tcuTexture.TextureFormat} texFormat Generic texture format. + * @return {gluTextureUtil.TransferFormat} GL pixel transfer format. + * @throws {Error} + */ +gluTextureUtil.getTransferFormat = function(/* tcuTexture.TextureFormat */ texFormat) { + var format = gl.NONE; + var type = gl.NONE; + /*boolean*/ var isInt = false; + + switch (texFormat.type) { + case tcuTexture.ChannelType.SIGNED_INT8: + case tcuTexture.ChannelType.SIGNED_INT16: + case tcuTexture.ChannelType.SIGNED_INT32: + case tcuTexture.ChannelType.UNSIGNED_INT8: + case tcuTexture.ChannelType.UNSIGNED_INT16: + case tcuTexture.ChannelType.UNSIGNED_INT32: + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: + isInt = true; + break; + + default: + isInt = false; + break; + } + + switch (texFormat.order) { + case tcuTexture.ChannelOrder.A: format = gl.ALPHA; break; + case tcuTexture.ChannelOrder.L: format = gl.LUMINANCE; break; + case tcuTexture.ChannelOrder.LA: format = gl.LUMINANCE_ALPHA; break; + case tcuTexture.ChannelOrder.R: format = isInt ? gl.RED_INTEGER : gl.RED; break; + case tcuTexture.ChannelOrder.RG: format = isInt ? gl.RG_INTEGER : gl.RG; break; + case tcuTexture.ChannelOrder.RGB: format = isInt ? gl.RGB_INTEGER : gl.RGB; break; + case tcuTexture.ChannelOrder.RGBA: format = isInt ? gl.RGBA_INTEGER : gl.RGBA; break; + case tcuTexture.ChannelOrder.sRGB: format = gl.RGB; break; + case tcuTexture.ChannelOrder.sRGBA: format = gl.RGBA; break; + case tcuTexture.ChannelOrder.D: format = gl.DEPTH_COMPONENT; break; + case tcuTexture.ChannelOrder.DS: format = gl.DEPTH_STENCIL; break; + case tcuTexture.ChannelOrder.S: format = gl.STENCIL_INDEX; break; + + default: + throw new Error('Unknown ChannelOrder ' + texFormat.order); + } + + switch (texFormat.type) { + case tcuTexture.ChannelType.SNORM_INT8: type = gl.BYTE; break; + case tcuTexture.ChannelType.SNORM_INT16: type = gl.SHORT; break; + case tcuTexture.ChannelType.UNORM_INT8: type = gl.UNSIGNED_BYTE; break; + case tcuTexture.ChannelType.UNORM_INT16: type = gl.UNSIGNED_SHORT; break; + case tcuTexture.ChannelType.UNORM_SHORT_565: type = gl.UNSIGNED_SHORT_5_6_5; break; + case tcuTexture.ChannelType.UNORM_SHORT_4444: type = gl.UNSIGNED_SHORT_4_4_4_4; break; + case tcuTexture.ChannelType.UNORM_SHORT_5551: type = gl.UNSIGNED_SHORT_5_5_5_1; break; + case tcuTexture.ChannelType.SIGNED_INT8: type = gl.BYTE; break; + case tcuTexture.ChannelType.SIGNED_INT16: type = gl.SHORT; break; + case tcuTexture.ChannelType.SIGNED_INT32: type = gl.INT; break; + case tcuTexture.ChannelType.UNSIGNED_INT8: type = gl.UNSIGNED_BYTE; break; + case tcuTexture.ChannelType.UNSIGNED_INT16: type = gl.UNSIGNED_SHORT; break; + case tcuTexture.ChannelType.UNSIGNED_INT32: type = gl.UNSIGNED_INT; break; + case tcuTexture.ChannelType.FLOAT: type = gl.FLOAT; break; + case tcuTexture.ChannelType.UNORM_INT_101010: type = gl.UNSIGNED_INT_2_10_10_10_REV; break; + case tcuTexture.ChannelType.UNORM_INT_1010102_REV: type = gl.UNSIGNED_INT_2_10_10_10_REV; break; + case tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV: type = gl.UNSIGNED_INT_2_10_10_10_REV; break; + case tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV: type = gl.UNSIGNED_INT_10F_11F_11F_REV; break; + case tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV: type = gl.UNSIGNED_INT_5_9_9_9_REV; break; + case tcuTexture.ChannelType.HALF_FLOAT: type = gl.HALF_FLOAT; break; + case tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV: type = gl.FLOAT_32_UNSIGNED_INT_24_8_REV; break; + case tcuTexture.ChannelType.UNSIGNED_INT_24_8: type = texFormat.order == tcuTexture.ChannelOrder.D ? + gl.UNSIGNED_INT : gl.UNSIGNED_INT_24_8; break; + + default: + throw new Error("Can't map texture format to GL transfer format " + texFormat.type); + } + + return new gluTextureUtil.TransferFormat(format, type); +}; + +/** + * Map tcuTexture.TextureFormat to GL internal sized format. + * + * Maps generic texture format description to GL internal format. + * If no mapping is found, throws Error. + * + * @param {tcuTexture.TextureFormat} texFormat Generic texture format. + * @return {number} GL texture format. + * @throws {Error} + */ +gluTextureUtil.getInternalFormat = function(texFormat) { + + var stringify = function(order, type) { + return '' + order + ' ' + type; + }; + + switch (stringify(texFormat.order, texFormat.type)) { + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_SHORT_5551): return gl.RGB5_A1; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_SHORT_4444): return gl.RGBA4; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_SHORT_565): return gl.RGB565; + case stringify(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNORM_INT16): return gl.DEPTH_COMPONENT16; + case stringify(tcuTexture.ChannelOrder.S, tcuTexture.ChannelType.UNSIGNED_INT8): return gl.STENCIL_INDEX8; + + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.FLOAT): return gl.RGBA32F; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SIGNED_INT32): return gl.RGBA32I; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT32): return gl.RGBA32UI; + // TODO: Check which ones are valid in WebGL 2 - case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT16): return gl.RGBA16; + //case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SNORM_INT16): return gl.RGBA16_SNORM; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.HALF_FLOAT): return gl.RGBA16F; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SIGNED_INT16): return gl.RGBA16I; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT16): return gl.RGBA16UI; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8): return gl.RGBA8; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SIGNED_INT8): return gl.RGBA8I; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT8): return gl.RGBA8UI; + case stringify(tcuTexture.ChannelOrder.sRGBA, tcuTexture.ChannelType.UNORM_INT8): return gl.SRGB8_ALPHA8; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT_1010102_REV): return gl.RGB10_A2; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV): return gl.RGB10_A2UI; + case stringify(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SNORM_INT8): return gl.RGBA8_SNORM; + + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT8): return gl.RGB8; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV): return gl.R11F_G11F_B10F; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.FLOAT): return gl.RGB32F; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SIGNED_INT32): return gl.RGB32I; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT32): return gl.RGB32UI; + //case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT16): return gl.RGB16; + //case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SNORM_INT16): return gl.RGB16_SNORM; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.HALF_FLOAT): return gl.RGB16F; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SIGNED_INT16): return gl.RGB16I; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT16): return gl.RGB16UI; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SNORM_INT8): return gl.RGB8_SNORM; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SIGNED_INT8): return gl.RGB8I; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT8): return gl.RGB8UI; + case stringify(tcuTexture.ChannelOrder.sRGB, tcuTexture.ChannelType.UNORM_INT8): return gl.SRGB8; + case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV): return gl.RGB9_E5; + //case stringify(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT_1010102_REV): return gl.RGB10; + + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.FLOAT): return gl.RG32F; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SIGNED_INT32): return gl.RG32I; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNSIGNED_INT32): return gl.RG32UI; + //case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNORM_INT16): return gl.RG16; + //case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SNORM_INT16): return gl.RG16_SNORM; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.HALF_FLOAT): return gl.RG16F; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SIGNED_INT16): return gl.RG16I; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNSIGNED_INT16): return gl.RG16UI; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNORM_INT8): return gl.RG8; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SIGNED_INT8): return gl.RG8I; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNSIGNED_INT8): return gl.RG8UI; + case stringify(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SNORM_INT8): return gl.RG8_SNORM; + + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.FLOAT): return gl.R32F; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SIGNED_INT32): return gl.R32I; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNSIGNED_INT32): return gl.R32UI; + //case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNORM_INT16): return gl.R16; + //case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SNORM_INT16): return gl.R16_SNORM; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.HALF_FLOAT): return gl.R16F; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SIGNED_INT16): return gl.R16I; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNSIGNED_INT16): return gl.R16UI; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNORM_INT8): return gl.R8; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SIGNED_INT8): return gl.R8I; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNSIGNED_INT8): return gl.R8UI; + case stringify(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SNORM_INT8): return gl.R8_SNORM; + + case stringify(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.FLOAT): return gl.DEPTH_COMPONENT32F; + case stringify(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNSIGNED_INT_24_8): return gl.DEPTH_COMPONENT24; + //case stringify(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNSIGNED_INT32): return gl.DEPTH_COMPONENT32; + case stringify(tcuTexture.ChannelOrder.DS, tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV): return gl.DEPTH32F_STENCIL8; + case stringify(tcuTexture.ChannelOrder.DS, tcuTexture.ChannelType.UNSIGNED_INT_24_8): return gl.DEPTH24_STENCIL8; + + default: + throw new Error("Can't map texture format to GL internal format"); + } +}; + +/** + * Enable WEBGL_compressed_texture_etc support if available, by merging it + * into the WebGL2RenderingContext. + * + * This function may be called many times. + * + * @return {boolean} True if enabled. + */ +gluTextureUtil.enableCompressedTextureETC = (function() { + var enabled = undefined; + return function() { + if (enabled === undefined) { + enabled = false; + + var WEBGL_compressed_texture_etc = gl.getExtension("WEBGL_compressed_texture_etc"); + if (WEBGL_compressed_texture_etc) { + // Extend gl with enums from WEBGL_compressed_texture_etc + // (if it doesn't already have the etc texture formats). + var proto = Object.getPrototypeOf(WEBGL_compressed_texture_etc); + for (var prop in proto) { + if (proto.hasOwnProperty(prop)) { + gl[prop] = proto[prop]; + } + } + enabled = true; + } + } + return enabled; + }; +})(); + +/** + * Map generic compressed format to GL compressed format enum. + * + * Maps generic compressed format to GL compressed format enum value. + * If no mapping is found, throws Error. + + * @param {tcuCompressedTexture.Format} format Generic compressed format. + * @return {number} GL compressed texture format. + * @throws {Error} + */ +gluTextureUtil.getGLFormat = function(/* tcuCompressedTexture.Format */ format) { + switch (format) { + // TODO: check which are available in WebGL 2 - case tcuCompressedTexture.Format.ETC1_RGB8: return gl.ETC1_RGB8_OES; + case tcuCompressedTexture.Format.EAC_R11: return gl.COMPRESSED_R11_EAC; + case tcuCompressedTexture.Format.EAC_SIGNED_R11: return gl.COMPRESSED_SIGNED_R11_EAC; + case tcuCompressedTexture.Format.EAC_RG11: return gl.COMPRESSED_RG11_EAC; + case tcuCompressedTexture.Format.EAC_SIGNED_RG11: return gl.COMPRESSED_SIGNED_RG11_EAC; + case tcuCompressedTexture.Format.ETC2_RGB8: return gl.COMPRESSED_RGB8_ETC2; + case tcuCompressedTexture.Format.ETC2_SRGB8: return gl.COMPRESSED_SRGB8_ETC2; + case tcuCompressedTexture.Format.ETC2_RGB8_PUNCHTHROUGH_ALPHA1: return gl.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case tcuCompressedTexture.Format.ETC2_SRGB8_PUNCHTHROUGH_ALPHA1: return gl.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case tcuCompressedTexture.Format.ETC2_EAC_RGBA8: return gl.COMPRESSED_RGBA8_ETC2_EAC; + case tcuCompressedTexture.Format.ETC2_EAC_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + + /*case tcuCompressedTexture.Format.ASTC_4x4_RGBA: return gl.COMPRESSED_RGBA_ASTC_4x4_KHR; + case tcuCompressedTexture.Format.ASTC_5x4_RGBA: return gl.COMPRESSED_RGBA_ASTC_5x4_KHR; + case tcuCompressedTexture.Format.ASTC_5x5_RGBA: return gl.COMPRESSED_RGBA_ASTC_5x5_KHR; + case tcuCompressedTexture.Format.ASTC_6x5_RGBA: return gl.COMPRESSED_RGBA_ASTC_6x5_KHR; + case tcuCompressedTexture.Format.ASTC_6x6_RGBA: return gl.COMPRESSED_RGBA_ASTC_6x6_KHR; + case tcuCompressedTexture.Format.ASTC_8x5_RGBA: return gl.COMPRESSED_RGBA_ASTC_8x5_KHR; + case tcuCompressedTexture.Format.ASTC_8x6_RGBA: return gl.COMPRESSED_RGBA_ASTC_8x6_KHR; + case tcuCompressedTexture.Format.ASTC_8x8_RGBA: return gl.COMPRESSED_RGBA_ASTC_8x8_KHR; + case tcuCompressedTexture.Format.ASTC_10x5_RGBA: return gl.COMPRESSED_RGBA_ASTC_10x5_KHR; + case tcuCompressedTexture.Format.ASTC_10x6_RGBA: return gl.COMPRESSED_RGBA_ASTC_10x6_KHR; + case tcuCompressedTexture.Format.ASTC_10x8_RGBA: return gl.COMPRESSED_RGBA_ASTC_10x8_KHR; + case tcuCompressedTexture.Format.ASTC_10x10_RGBA: return gl.COMPRESSED_RGBA_ASTC_10x10_KHR; + case tcuCompressedTexture.Format.ASTC_12x10_RGBA: return gl.COMPRESSED_RGBA_ASTC_12x10_KHR; + case tcuCompressedTexture.Format.ASTC_12x12_RGBA: return gl.COMPRESSED_RGBA_ASTC_12x12_KHR; + case tcuCompressedTexture.Format.ASTC_4x4_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; + case tcuCompressedTexture.Format.ASTC_5x4_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR; + case tcuCompressedTexture.Format.ASTC_5x5_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR; + case tcuCompressedTexture.Format.ASTC_6x5_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR; + case tcuCompressedTexture.Format.ASTC_6x6_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR; + case tcuCompressedTexture.Format.ASTC_8x5_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR; + case tcuCompressedTexture.Format.ASTC_8x6_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR; + case tcuCompressedTexture.Format.ASTC_8x8_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR; + case tcuCompressedTexture.Format.ASTC_10x5_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR; + case tcuCompressedTexture.Format.ASTC_10x6_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR; + case tcuCompressedTexture.Format.ASTC_10x8_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR; + case tcuCompressedTexture.Format.ASTC_10x10_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR; + case tcuCompressedTexture.Format.ASTC_12x10_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR; + case tcuCompressedTexture.Format.ASTC_12x12_SRGB8_ALPHA8: return gl.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR;*/ + + default: + throw new Error("Can't map compressed format to GL format"); + } +}; + +/** + * @param {number} dataType + * @param {boolean} normalized + * @return {tcuTexture.ChannelType} + * @throws {Error} + */ +gluTextureUtil.mapGLChannelType = function(/* deMath.deUint32 */ dataType, /*boolean*/ normalized) { + // \note Normalized bit is ignored where it doesn't apply. + + switch (dataType) { + case gl.UNSIGNED_BYTE: return normalized ? tcuTexture.ChannelType.UNORM_INT8 : tcuTexture.ChannelType.UNSIGNED_INT8; + case gl.BYTE: return normalized ? tcuTexture.ChannelType.SNORM_INT8 : tcuTexture.ChannelType.SIGNED_INT8; + case gl.UNSIGNED_SHORT: return normalized ? tcuTexture.ChannelType.UNORM_INT16 : tcuTexture.ChannelType.UNSIGNED_INT16; + case gl.SHORT: return normalized ? tcuTexture.ChannelType.SNORM_INT16 : tcuTexture.ChannelType.SIGNED_INT16; + case gl.UNSIGNED_INT: return normalized ? tcuTexture.ChannelType.UNORM_INT32 : tcuTexture.ChannelType.UNSIGNED_INT32; + case gl.INT: return normalized ? tcuTexture.ChannelType.SNORM_INT32 : tcuTexture.ChannelType.SIGNED_INT32; + case gl.FLOAT: return tcuTexture.ChannelType.FLOAT; + case gl.UNSIGNED_SHORT_4_4_4_4: return tcuTexture.ChannelType.UNORM_SHORT_4444; + case gl.UNSIGNED_SHORT_5_5_5_1: return tcuTexture.ChannelType.UNORM_SHORT_5551; + case gl.UNSIGNED_SHORT_5_6_5: return tcuTexture.ChannelType.UNORM_SHORT_565; + case gl.HALF_FLOAT: return tcuTexture.ChannelType.HALF_FLOAT; + case gl.UNSIGNED_INT_2_10_10_10_REV: return normalized ? tcuTexture.ChannelType.UNORM_INT_1010102_REV : tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV; + case gl.UNSIGNED_INT_10F_11F_11F_REV: return tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV; + case gl.UNSIGNED_INT_24_8: return tcuTexture.ChannelType.UNSIGNED_INT_24_8; + case gl.FLOAT_32_UNSIGNED_INT_24_8_REV: return tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV; + case gl.UNSIGNED_INT_5_9_9_9_REV: return tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV; + + default: + throw new Error('Unsupported dataType ' + dataType); + } +}; + +/** + * @param {number} format Generic compressed format. + * @param {number} dataType + * @return {tcuTexture.TextureFormat} GL texture format. + * @throws {Error} + */ +gluTextureUtil.mapGLTransferFormat = function(format, dataType) { + switch (format) { + case gl.ALPHA: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.A, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.LUMINANCE: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.L, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.LUMINANCE_ALPHA: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.LA, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.RGB: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.RGBA: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, gluTextureUtil.mapGLChannelType(dataType, true)); + //case gl.BGRA: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.BGRA, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.RG: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.RED: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.RGBA_INTEGER: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, gluTextureUtil.mapGLChannelType(dataType, false)); + case gl.RGB_INTEGER: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, gluTextureUtil.mapGLChannelType(dataType, false)); + case gl.RG_INTEGER: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, gluTextureUtil.mapGLChannelType(dataType, false)); + case gl.RED_INTEGER: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, gluTextureUtil.mapGLChannelType(dataType, false)); + + case gl.DEPTH_COMPONENT: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, gluTextureUtil.mapGLChannelType(dataType, true)); + case gl.DEPTH_STENCIL: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.DS, gluTextureUtil.mapGLChannelType(dataType, true)); + + default: + throw new Error("Can't map GL pixel format (" + deString.enumToString(gl, format) + ', ' + deString.enumToString(gl, dataType) + ') to texture format'); + } +}; + + /** + * Map GL internal texture format to tcuTexture.TextureFormat. + * + * If no mapping is found, throws Error. + * @param {number} internalFormat + * @return {tcuTexture.TextureFormat} GL texture format. + * @throws {Error} + */ +gluTextureUtil.mapGLInternalFormat = function(/*deMath.deUint32*/ internalFormat) { + if (internalFormat === undefined) + throw new Error('internalformat is undefined'); + + switch (internalFormat) { + case gl.RGB5_A1: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_SHORT_5551); + case gl.RGBA4: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_SHORT_4444); + case gl.RGB565: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_SHORT_565); + case gl.DEPTH_COMPONENT16: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNORM_INT16); + case gl.STENCIL_INDEX8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.S, tcuTexture.ChannelType.UNSIGNED_INT8); + + case gl.RGBA32F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.FLOAT); + case gl.RGBA32I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SIGNED_INT32); + case gl.RGBA32UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT32); + //TODO: Check which are available in WebGL 2 case gl.RGBA16: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT16); + //case gl.RGBA16_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SNORM_INT16); + case gl.RGBA16F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.HALF_FLOAT); + case gl.RGBA16I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SIGNED_INT16); + case gl.RGBA16UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT16); + case gl.RGBA8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8); + case gl.RGBA8I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SIGNED_INT8); + case gl.RGBA8UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT8); + case gl.SRGB8_ALPHA8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.sRGBA, tcuTexture.ChannelType.UNORM_INT8); + case gl.RGB10_A2: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT_1010102_REV); + case gl.RGB10_A2UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNSIGNED_INT_1010102_REV); + case gl.RGBA8_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.SNORM_INT8); + + case gl.RGB8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT8); + case gl.R11F_G11F_B10F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT_11F_11F_10F_REV); + case gl.RGB32F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.FLOAT); + case gl.RGB32I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SIGNED_INT32); + case gl.RGB32UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT32); + //case gl.RGB16: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT16); + //case gl.RGB16_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SNORM_INT16); + case gl.RGB16F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.HALF_FLOAT); + case gl.RGB16I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SIGNED_INT16); + case gl.RGB16UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT16); + case gl.RGB8_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SNORM_INT8); + case gl.RGB8I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.SIGNED_INT8); + case gl.RGB8UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT8); + case gl.SRGB8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.sRGB, tcuTexture.ChannelType.UNORM_INT8); + case gl.RGB9_E5: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNSIGNED_INT_999_E5_REV); + //case gl.RGB10: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT_1010102_REV); + + case gl.RG32F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.FLOAT); + case gl.RG32I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SIGNED_INT32); + case gl.RG32UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNSIGNED_INT32); + //case gl.RG16: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNORM_INT16); + //case gl.RG16_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SNORM_INT16); + case gl.RG16F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.HALF_FLOAT); + case gl.RG16I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SIGNED_INT16); + case gl.RG16UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNSIGNED_INT16); + case gl.RG8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNORM_INT8); + case gl.RG8I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SIGNED_INT8); + case gl.RG8UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.UNSIGNED_INT8); + case gl.RG8_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SNORM_INT8); + + case gl.R32F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.FLOAT); + case gl.R32I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SIGNED_INT32); + case gl.R32UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNSIGNED_INT32); + //case gl.R16: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNORM_INT16); + //case gl.R16_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SNORM_INT16); + case gl.R16F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.HALF_FLOAT); + case gl.R16I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SIGNED_INT16); + case gl.R16UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNSIGNED_INT16); + case gl.R8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNORM_INT8); + case gl.R8I: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SIGNED_INT8); + case gl.R8UI: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.UNSIGNED_INT8); + case gl.R8_SNORM: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.R, tcuTexture.ChannelType.SNORM_INT8); + + case gl.DEPTH_COMPONENT32F: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.FLOAT); + case gl.DEPTH_COMPONENT24: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNSIGNED_INT_24_8); + //case gl.DEPTH_COMPONENT32: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNSIGNED_INT32); + case gl.DEPTH32F_STENCIL8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.DS, tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV); + case gl.DEPTH24_STENCIL8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.DS, tcuTexture.ChannelType.UNSIGNED_INT_24_8); + + default: + throw new Error("Can't map GL sized internal format (" + internalFormat.toString(16) + ') to texture format'); + } +}; + +/** + * @param {number} format + * @return {boolean} + */ +gluTextureUtil.isGLInternalColorFormatFilterable = function(format) { + switch (format) { + case gl.R8: + case gl.R8_SNORM: + case gl.RG8: + case gl.RG8_SNORM: + case gl.RGB8: + case gl.RGB8_SNORM: + case gl.RGB565: + case gl.RGBA4: + case gl.RGB5_A1: + case gl.RGBA8: + case gl.RGBA8_SNORM: + case gl.RGB10_A2: + case gl.SRGB8: + case gl.SRGB8_ALPHA8: + case gl.R16F: + case gl.RG16F: + case gl.RGB16F: + case gl.RGBA16F: + case gl.R11F_G11F_B10F: + case gl.RGB9_E5: + return true; + + case gl.RGB10_A2UI: + case gl.R32F: + case gl.RG32F: + case gl.RGB32F: + case gl.RGBA32F: + case gl.R8I: + case gl.R8UI: + case gl.R16I: + case gl.R16UI: + case gl.R32I: + case gl.R32UI: + case gl.RG8I: + case gl.RG8UI: + case gl.RG16I: + case gl.RG16UI: + case gl.RG32I: + case gl.RG32UI: + case gl.RGB8I: + case gl.RGB8UI: + case gl.RGB16I: + case gl.RGB16UI: + case gl.RGB32I: + case gl.RGB32UI: + case gl.RGBA8I: + case gl.RGBA8UI: + case gl.RGBA16I: + case gl.RGBA16UI: + case gl.RGBA32I: + case gl.RGBA32UI: + return false; + + default: + throw new Error('Unrecognized format ' + format); + } +}; + +/** + * @param {number} wrapMode + * @return {tcuTexture.WrapMode} + */ +gluTextureUtil.mapGLWrapMode = function(wrapMode) { + switch (wrapMode) { + case gl.CLAMP_TO_EDGE: return tcuTexture.WrapMode.CLAMP_TO_EDGE; + case gl.REPEAT: return tcuTexture.WrapMode.REPEAT_GL; + case gl.MIRRORED_REPEAT: return tcuTexture.WrapMode.MIRRORED_REPEAT_GL; + default: + throw new Error("Can't map GL wrap mode " + deString.enumToString(gl, wrapMode)); + } +}; + +/** + * @param {number} filterMode + * @return {tcuTexture.FilterMode} + * @throws {Error} + */ +gluTextureUtil.mapGLFilterMode = function(filterMode) { + switch (filterMode) { + case gl.NEAREST: return tcuTexture.FilterMode.NEAREST; + case gl.LINEAR: return tcuTexture.FilterMode.LINEAR; + case gl.NEAREST_MIPMAP_NEAREST: return tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST; + case gl.NEAREST_MIPMAP_LINEAR: return tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR; + case gl.LINEAR_MIPMAP_NEAREST: return tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST; + case gl.LINEAR_MIPMAP_LINEAR: return tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR; + default: + throw new Error("Can't map GL filter mode" + filterMode); + } +}; + +/* TODO: Port the code below */ +// /*--------------------------------------------------------------------*//*! +// * \brief Map GL sampler parameters to tcu::Sampler. +// * +// * If no mapping is found, throws tcu::InternalError. +// * +// * \param wrapS S-component wrap mode +// * \param minFilter Minification filter mode +// * \param magFilter Magnification filter mode +// * \return Sampler description. +// *//*--------------------------------------------------------------------*/ +// /*tcu::Sampler mapGLSamplerWrapS (deUint32 wrapS, deUint32 minFilter, deUint32 magFilter) +// { +// return mapGLSampler(wrapS, wrapS, wrapS, minFilter, magFilter); +// } +// */ + +/** + * Map GL sampler parameters to tcu::Sampler. + * + * If no mapping is found, throws tcu::InternalError. + * + * @param {number} wrapS S-component wrap mode + * @param {number} wrapT T-component wrap mode + * @param {number} minFilter Minification filter mode + * @param {number} magFilter Magnification filter mode + * @return {tcuTexture.Sampler} + */ +gluTextureUtil.mapGLSamplerWrapST = function(wrapS, wrapT, minFilter, magFilter) { + return gluTextureUtil.mapGLSampler(wrapS, wrapT, wrapS, minFilter, magFilter); +}; + +/** + * Map GL sampler parameters to tcu::Sampler. + * + * If no mapping is found, throws tcu::InternalError. + * @param {number} wrapS S-component wrap mode + * @param {number} wrapT T-component wrap mode + * @param {number} wrapR R-component wrap mode + * @param {number} minFilter Minification filter mode + * @param {number} magFilter Magnification filter mode + * @return {tcuTexture.Sampler} + */ +gluTextureUtil.mapGLSampler = function(wrapS, wrapT, wrapR, minFilter, magFilter) { + return new tcuTexture.Sampler( + gluTextureUtil.mapGLWrapMode(wrapS), + gluTextureUtil.mapGLWrapMode(wrapT), + gluTextureUtil.mapGLWrapMode(wrapR), + gluTextureUtil.mapGLFilterMode(minFilter), + gluTextureUtil.mapGLFilterMode(magFilter), + 0.0, + true, + tcuTexture.CompareMode.COMPAREMODE_NONE, + 0, + [0.0, 0.0, 0.0, 0.0]); +}; + +// /*--------------------------------------------------------------------*//*! +// * \brief Map GL compare function to tcu::Sampler::CompareMode. +// * +// * If no mapping is found, throws tcu::InternalError. +// * +// * \param mode GL compare mode +// * \return Compare mode +// *//*--------------------------------------------------------------------*/ +/** + * @param {number} mode + */ +gluTextureUtil.mapGLCompareFunc = function(mode) { + switch (mode) { + case gl.LESS: return tcuTexture.CompareMode.COMPAREMODE_LESS; + case gl.LEQUAL: return tcuTexture.CompareMode.COMPAREMODE_LESS_OR_EQUAL; + case gl.GREATER: return tcuTexture.CompareMode.COMPAREMODE_GREATER; + case gl.GEQUAL: return tcuTexture.CompareMode.COMPAREMODE_GREATER_OR_EQUAL; + case gl.EQUAL: return tcuTexture.CompareMode.COMPAREMODE_EQUAL; + case gl.NOTEQUAL: return tcuTexture.CompareMode.COMPAREMODE_NOT_EQUAL; + case gl.ALWAYS: return tcuTexture.CompareMode.COMPAREMODE_ALWAYS; + case gl.NEVER: return tcuTexture.CompareMode.COMPAREMODE_NEVER; + default: + throw new Error("Can't map GL compare mode " + mode); + } +}; + +/** + * Get GL wrap mode. + * + * If no mapping is found, throws tcu::InternalError. + * + * @param {tcuTexture.WrapMode} wrapMode + * @return {number} GL wrap mode + */ +gluTextureUtil.getGLWrapMode = function(wrapMode) { + switch (wrapMode) { + case tcuTexture.WrapMode.CLAMP_TO_EDGE: return gl.CLAMP_TO_EDGE; + case tcuTexture.WrapMode.REPEAT_GL: return gl.REPEAT; + case tcuTexture.WrapMode.MIRRORED_REPEAT_GL: return gl.MIRRORED_REPEAT; + default: + throw new Error("Can't map wrap mode"); + } +}; + +/** + * Get GL filter mode. + * + * If no mapping is found, throws tcu::InternalError. + * + * @param {tcuTexture.FilterMode} filterMode Filter mode + * @return {number} GL filter mode + */ +gluTextureUtil.getGLFilterMode = function(filterMode) { + switch (filterMode) { + case tcuTexture.FilterMode.NEAREST: return gl.NEAREST; + case tcuTexture.FilterMode.LINEAR: return gl.LINEAR; + case tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST: return gl.NEAREST_MIPMAP_NEAREST; + case tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR: return gl.NEAREST_MIPMAP_LINEAR; + case tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST: return gl.LINEAR_MIPMAP_NEAREST; + case tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR: return gl.LINEAR_MIPMAP_LINEAR; + default: + throw new Error("Can't map filter mode"); + } +}; + +/** + * Get GL compare mode. + * + * If no mapping is found, throws tcu::InternalError. + * + * @param {tcuTexture.CompareMode} compareMode Compare mode + * @return {number} GL compare mode + */ +gluTextureUtil.getGLCompareFunc = function(compareMode) { + switch (compareMode) { + case tcuTexture.CompareMode.COMPAREMODE_NONE: return gl.NONE; + case tcuTexture.CompareMode.COMPAREMODE_LESS: return gl.LESS; + case tcuTexture.CompareMode.COMPAREMODE_LESS_OR_EQUAL: return gl.LEQUAL; + case tcuTexture.CompareMode.COMPAREMODE_GREATER: return gl.GREATER; + case tcuTexture.CompareMode.COMPAREMODE_GREATER_OR_EQUAL: return gl.GEQUAL; + case tcuTexture.CompareMode.COMPAREMODE_EQUAL: return gl.EQUAL; + case tcuTexture.CompareMode.COMPAREMODE_NOT_EQUAL: return gl.NOTEQUAL; + case tcuTexture.CompareMode.COMPAREMODE_ALWAYS: return gl.ALWAYS; + case tcuTexture.CompareMode.COMPAREMODE_NEVER: return gl.NEVER; + default: + throw new Error("Can't map compare mode"); + } +}; + +/** + * Get GL cube face. + * + * If no mapping is found, throws tcu::InternalError. + * + * @param {tcuTexture.CubeFace} face Cube face + * @return {number} GL cube face + */ +gluTextureUtil.getGLCubeFace = function(face) { + switch (face) { + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X: + return gl.TEXTURE_CUBE_MAP_NEGATIVE_X; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_X: + return gl.TEXTURE_CUBE_MAP_POSITIVE_X; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y: + return gl.TEXTURE_CUBE_MAP_NEGATIVE_Y; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y: + return gl.TEXTURE_CUBE_MAP_POSITIVE_Y; + case tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z: + return gl.TEXTURE_CUBE_MAP_NEGATIVE_Z; + case tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z: + return gl.TEXTURE_CUBE_MAP_POSITIVE_Z; + default: + throw Error("Can't map cube face"); + } +}; + +// /*--------------------------------------------------------------------*//*! +// * \brief Get GLSL sampler type for texture format. +// * +// * If no mapping is found, glu::TYPE_LAST is returned. +// * +// * \param format Texture format +// * \return GLSL 1D sampler type for format +// *//*--------------------------------------------------------------------*/ +// DataType getSampler1DType (tcu::TextureFormat format) +// { +// using tcu::TextureFormat; + +// if (format.order == tcuTexture.ChannelOrder.D || format.order == tcuTexture.ChannelOrder.DS) +// return TYPE_SAMPLER_1D; + +// if (format.order == tcuTexture.ChannelOrder.S) +// return TYPE_LAST; + +// switch (tcu::getTextureChannelClass(format.type)) +// { +// case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: +// case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: +// case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: +// return glu::TYPE_SAMPLER_1D; + +// case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: +// return glu::TYPE_INT_SAMPLER_1D; + +// case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: +// return glu::TYPE_UINT_SAMPLER_1D; + +// default: +// return glu::TYPE_LAST; +// } +// } + +/** + * Get GLSL sampler type for texture format. + * If no mapping is found, glu::TYPE_LAST is returned. + * + * @param {tcuTexture.TextureFormat} format + * @return {gluShaderUtil.DataType} GLSL 2D sampler type for format + */ +gluTextureUtil.getSampler2DType = function(format) { + if (format.order == tcuTexture.ChannelOrder.D || format.order == tcuTexture.ChannelOrder.DS) + return gluShaderUtil.DataType.SAMPLER_2D; + + if (format.order == tcuTexture.ChannelOrder.S) + return /** @type {gluShaderUtil.DataType} */ (Object.keys(gluShaderUtil.DataType).length); + + switch (tcuTexture.getTextureChannelClass(format.type)) { + case tcuTexture.TextureChannelClass.FLOATING_POINT: + case tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT: + case tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT: + return gluShaderUtil.DataType.SAMPLER_2D; + + case tcuTexture.TextureChannelClass.SIGNED_INTEGER: + return gluShaderUtil.DataType.INT_SAMPLER_2D; + + case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: + return gluShaderUtil.DataType.UINT_SAMPLER_2D; + + default: + return /** @type {gluShaderUtil.DataType} */ (Object.keys(gluShaderUtil.DataType).length); + } +}; + +/** + * + * @param {tcuTexture.TextureFormat} format + * @return {gluShaderUtil.DataType} GLSL 2D sampler type for format + */ +gluTextureUtil.getSampler3DType = function(format) { + if (format.order === tcuTexture.ChannelOrder.D || format.order === tcuTexture.ChannelOrder.DS) + return gluShaderUtil.DataType.SAMPLER_3D; + + if (format.order === tcuTexture.ChannelOrder.S) + return /** @type {gluShaderUtil.DataType} */ (Object.keys(gluShaderUtil.DataType).length); // shouldn't we throw an error instead? + + switch (tcuTexture.getTextureChannelClass(format.type)) { + case tcuTexture.TextureChannelClass.FLOATING_POINT: + case tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT: + case tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT: + return gluShaderUtil.DataType.SAMPLER_3D; + + case tcuTexture.TextureChannelClass.SIGNED_INTEGER: + return gluShaderUtil.DataType.INT_SAMPLER_3D; + + case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: + return gluShaderUtil.DataType.UINT_SAMPLER_3D; + + default: + return /** @type {gluShaderUtil.DataType} */ (Object.keys(gluShaderUtil.DataType).length); + } +}; + +/** + * \brief Get GLSL sampler type for texture format. + * + * @param {tcuTexture.TextureFormat} format + * @return {gluShaderUtil.DataType} GLSL 2D sampler type for format + */ +gluTextureUtil.getSamplerCubeType = function(format) { + if (format.order == tcuTexture.ChannelOrder.D || format.order == tcuTexture.ChannelOrder.DS) + return gluShaderUtil.DataType.SAMPLER_CUBE; + + if (format.order == tcuTexture.ChannelOrder.S) + throw new Error('No cube sampler'); + + switch (tcuTexture.getTextureChannelClass(format.type)) { + case tcuTexture.TextureChannelClass.FLOATING_POINT: + case tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT: + case tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT: + return gluShaderUtil.DataType.SAMPLER_CUBE; + + case tcuTexture.TextureChannelClass.SIGNED_INTEGER: + return gluShaderUtil.DataType.INT_SAMPLER_CUBE; + + case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: + return gluShaderUtil.DataType.UINT_SAMPLER_CUBE; + + default: + throw new Error('No cube sampler'); + } +}; + +/** + * \brief Get GLSL sampler type for texture format. + * + * If no mapping is found, glu::TYPE_LAST is returned. + * + * @param {tcuTexture.TextureFormat} format + * @return {gluShaderUtil.DataType} GLSL 2D sampler type for format + */ +gluTextureUtil.getSampler2DArrayType = function(format) { + + if (format.order == tcuTexture.ChannelOrder.D || format.order == tcuTexture.ChannelOrder.DS) + return gluShaderUtil.DataType.SAMPLER_2D_ARRAY; + + if (format.order == tcuTexture.ChannelOrder.S) + throw new Error('No 2d array sampler'); + + switch (tcuTexture.getTextureChannelClass(format.type)) { + case tcuTexture.TextureChannelClass.FLOATING_POINT: + case tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT: + case tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT: + return gluShaderUtil.DataType.SAMPLER_2D_ARRAY; + + case tcuTexture.TextureChannelClass.SIGNED_INTEGER: + return gluShaderUtil.DataType.INT_SAMPLER_2D_ARRAY; + + case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: + return gluShaderUtil.DataType.UINT_SAMPLER_2D_ARRAY; + + default: + throw new Error('No 2d array sampler'); + } +}; + +/** + * \brief Get GLSL sampler type for texture format. + * + * If no mapping is found, glu::TYPE_LAST is returned. + * + * @param {tcuTexture.TextureFormat} format + * @return {gluShaderUtil.DataType} GLSL 2D sampler type for format + */ +gluTextureUtil.getSampler3D = function(format) { + if (format.order == tcuTexture.ChannelOrder.D || format.order == tcuTexture.ChannelOrder.DS) + return gluShaderUtil.DataType.SAMPLER_3D; + + if (format.order == tcuTexture.ChannelOrder.S) + throw new Error('No 3d sampler'); + + switch (tcuTexture.getTextureChannelClass(format.type)) { + case tcuTexture.TextureChannelClass.FLOATING_POINT: + case tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT: + case tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT: + return gluShaderUtil.DataType.SAMPLER_3D; + + case tcuTexture.TextureChannelClass.SIGNED_INTEGER: + return gluShaderUtil.DataType.INT_SAMPLER_3D; + + case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: + return gluShaderUtil.DataType.UINT_SAMPLER_3D; + + default: + throw new Error('No 3d sampler'); + } +}; + +gluTextureUtil.RenderableType = { + RENDERABLE_COLOR: (1<<0), + RENDERABLE_DEPTH: (1<<1), + RENDERABLE_STENCIL: (1<<2) +}; + +/** + * \brief Get renderable bits. + * \note Works currently only on ES3 context. + * + * @param {number} internalFormat + * @return {gluTextureUtil.RenderableType} + */ +gluTextureUtil.getRenderableBitsES3 = function(internalFormat) +{ + switch (internalFormat) + { + // Color-renderable formats + case gl.RGBA32I: + case gl.RGBA32UI: + case gl.RGBA16I: + case gl.RGBA16UI: + case gl.RGBA8: + case gl.RGBA8I: + case gl.RGBA8UI: + case gl.SRGB8_ALPHA8: + case gl.RGB10_A2: + case gl.RGB10_A2UI: + case gl.RGBA4: + case gl.RGB5_A1: + case gl.RGB8: + case gl.RGB565: + case gl.RG32I: + case gl.RG32UI: + case gl.RG16I: + case gl.RG16UI: + case gl.RG8: + case gl.RG8I: + case gl.RG8UI: + case gl.R32I: + case gl.R32UI: + case gl.R16I: + case gl.R16UI: + case gl.R8: + case gl.R8I: + case gl.R8UI: + return gluTextureUtil.RenderableType.RENDERABLE_COLOR; + + // EXT_color_buffer_float + case gl.RGBA32F: + case gl.R11F_G11F_B10F: + case gl.RG32F: + case gl.R32F: + case gl.RGBA16F: + case gl.RG16F: + case gl.R16F: + if (gl.getExtension("EXT_color_buffer_float")) + return gluTextureUtil.RenderableType.RENDERABLE_COLOR; + else + return 0; + + // Depth formats + case gl.DEPTH_COMPONENT32F: + case gl.DEPTH_COMPONENT24: + case gl.DEPTH_COMPONENT16: + return gluTextureUtil.RenderableType.RENDERABLE_DEPTH; + + // Depth+stencil formats + case gl.DEPTH32F_STENCIL8: + case gl.DEPTH24_STENCIL8: + return gluTextureUtil.RenderableType.RENDERABLE_DEPTH | gluTextureUtil.RenderableType.RENDERABLE_STENCIL; + + // Stencil formats + case gl.STENCIL_INDEX8: + return gluTextureUtil.RenderableType.RENDERABLE_STENCIL; + + default: + return 0; + } +} + +/** + * \brief Check if sized internal format is color-renderable. + * \note Works currently only on ES3 context. + * + * @param {number} sizedFormat + * @return {boolean} + */ +gluTextureUtil.isSizedFormatColorRenderable = function(sizedFormat) +{ + var renderable = 0; + renderable = gluTextureUtil.getRenderableBitsES3(sizedFormat); + return (renderable & gluTextureUtil.RenderableType.RENDERABLE_COLOR) != 0; +} + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluVarType.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluVarType.js new file mode 100644 index 000000000..a05f1c1e5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluVarType.js @@ -0,0 +1,814 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluVarType'); +goog.require('framework.opengl.gluShaderUtil'); + +goog.scope(function() { + + var gluVarType = framework.opengl.gluVarType; + var gluShaderUtil = framework.opengl.gluShaderUtil; + + /** + * gluVarType.VarType types enum + * @enum {number} + */ + gluVarType.Type = { + TYPE_BASIC: 0, + TYPE_ARRAY: 1, + TYPE_STRUCT: 2 + }; + + /** + * gluVarType.TypeArray struct + * @param {gluVarType.VarType} elementType + * @param {number} arraySize + * @constructor + */ + gluVarType.TypeArray = function(elementType, arraySize) { + /** @type {gluVarType.VarType} */ this.elementType = gluVarType.newClone(elementType); + /** @type {number} */ this.size = arraySize; + }; + + /** + * gluVarType.VarType class + * @constructor + */ + gluVarType.VarType = function() { + /** + * @type {gluShaderUtil.precision} + * @private + */ + this.m_flags; + + /** + * @type {number} + * @private + */ + this.m_type = -1; + + /** + * m_data used to be a 'Data' union in C++. Using a var is enough here. + * it will contain any necessary value. + * case TYPE_BASIC: number + * case TYPE_ARRAY: gluVarType.TypeArray + * case TYPE_STRUCT: gluVarType.StructType + * @private + * @type {(number|gluVarType.TypeArray|gluVarType.StructType)} + */ + this.m_data = null; + }; + + gluVarType.VarType.UNSIZED_ARRAY = -1; + + /** + * Creates a basic type gluVarType.VarType. Use this after the constructor call. + * @param {number} basicType + * @param {gluShaderUtil.precision} flags + * @return {gluVarType.VarType} The currently modified object + */ + gluVarType.VarType.prototype.VarTypeBasic = function(basicType, flags) { + this.m_type = gluVarType.Type.TYPE_BASIC; + this.m_flags = flags; + this.m_data = basicType; + + return this; + }; + + /** + * Creates a basic type gluVarType.VarType with type boolean and undefined precision. + * @param {number} basicType + * @return {gluVarType.VarType} The currently modified object + */ + gluVarType.VarType.prototype.VarTypeBoolean = function(basicType) { + this.m_type = gluVarType.Type.TYPE_BASIC; + this.m_data = basicType; + + return this; + }; + + /** + * Creates an array type gluVarType.VarType. Use this after the constructor call. + * @param {gluVarType.VarType} elementType + * @param {number} arraySize + * @return {gluVarType.VarType} The currently modified object + */ + gluVarType.VarType.prototype.VarTypeArray = function(elementType, arraySize) { + this.m_type = gluVarType.Type.TYPE_ARRAY; + if (!(arraySize >= 0 || arraySize == gluVarType.VarType.UNSIZED_ARRAY)) + throw new Error('Illegal array size: ' + arraySize); + this.m_data = new gluVarType.TypeArray(elementType, arraySize); + + return this; + }; + + /** + * Creates a struct type gluVarType.VarType. Use this after the constructor call. + * @param {gluVarType.StructType} structPtr + * @return {gluVarType.VarType} The currently modified object + */ + gluVarType.VarType.prototype.VarTypeStruct = function(structPtr) { + this.m_type = gluVarType.Type.TYPE_STRUCT; + this.m_data = structPtr; + + return this; + }; + + /** + * Creates a gluVarType.VarType, the same type as the passed in object. + * Use this after the constructor call. + * @param {gluVarType.VarType} object + * @return {gluVarType.VarType} The currently modified object + */ + gluVarType.VarType.prototype.VarTypeClone = function(object) { + + this.m_type = object.m_type; + + switch (this.m_type) { + case gluVarType.Type.TYPE_BASIC: + this.m_flags = object.m_flags; + this.m_data = object.m_data; + break; + case gluVarType.Type.TYPE_BASIC: + this.m_data = new gluVarType.TypeArray(object.m_data.elementType, object.m_data.size); + break; + case gluVarType.Type.TYPE_STRUCT: + this.m_data = object.m_data; + break; + default: + throw new Error('unknown type: ' + this.m_type); + } + + return this; + }; + + /** isBasicType + * @return {boolean} true if the gluVarType.VarType represents a basic type. + */ + gluVarType.VarType.prototype.isBasicType = function() { + return this.m_type == gluVarType.Type.TYPE_BASIC; + }; + + /** isArrayType + * @return {boolean} true if the gluVarType.VarType represents an array. + */ + gluVarType.VarType.prototype.isArrayType = function() { + return this.m_type == gluVarType.Type.TYPE_ARRAY; + }; + + /** isStructType + * @return {boolean} true if the gluVarType.VarType represents a struct. + */ + gluVarType.VarType.prototype.isStructType = function() { + return this.m_type == gluVarType.Type.TYPE_STRUCT; + }; + + /** getFlags + * @return {number} returns the flags of the gluVarType.VarType. + */ + gluVarType.VarType.prototype.getFlags = function() { + return this.m_flags; + }; + + /** getBasicType + * @return {gluShaderUtil.DataType<number>} returns the basic data type of the gluVarType.VarType. + */ + gluVarType.VarType.prototype.getBasicType = function() { + if (!this.isBasicType()) + throw new Error('VarType is not a basic type.'); + return /** @type {gluShaderUtil.DataType<number>} */ (this.m_data); + }; + + /** getPrecision + * @return {gluShaderUtil.precision} returns the precision flag. + */ + gluVarType.VarType.prototype.getPrecision = function() { + if (!this.isBasicType()) + throw new Error('VarType is not a basic type.'); + return this.m_flags; + }; + + /** getElementType + * @return {gluVarType.VarType} returns the gluVarType.VarType of the element in case of an Array. + */ + gluVarType.VarType.prototype.getElementType = function() { + if (!this.isArrayType()) + throw new Error('VarType is not an array type.'); + return this.m_data.elementType; + }; + + /** getArraySize + * (not to be confused with a javascript array) + * @return {number} returns the size of the array in case it is an array. + */ + gluVarType.VarType.prototype.getArraySize = function() { + if (!this.isArrayType()) + throw new Error('VarType is not an array type.'); + return this.m_data.size; + }; + + /** getStruct + * @return {gluVarType.StructType} returns the structure when it is a gluVarType.StructType. + */ + gluVarType.VarType.prototype.getStruct = function() { + if (!this.isStructType()) + throw new Error('VarType is not a struct type.'); + return /** @type {gluVarType.StructType} */ (this.m_data); + }; + + /** + * getScalarSize + * @return {number} size of the scalar + */ + gluVarType.VarType.prototype.getScalarSize = function() { + switch (this.m_type) { + case gluVarType.Type.TYPE_BASIC: { + return gluShaderUtil.getDataTypeScalarSize(/** @type {gluShaderUtil.DataType} */(this.getBasicType())); + } + + // TODO: check implementation below: return m_data.array.elementType->getScalarSize()*m_data.array.size; + case gluVarType.Type.TYPE_ARRAY: { + var m_data = /** @type {gluVarType.TypeArray} */(this.m_data); + return m_data.elementType.getScalarSize() * m_data.size; + } + + case gluVarType.Type.TYPE_STRUCT: { + var size = 0; + + var struct = /** @type {gluVarType.StructType} */ (this.m_data); + + // TODO: check loop conditions below + // for (gluVarType.StructType::ConstIterator iter = m_data.structPtr->begin(); iter != m_data.structPtr->end(); iter++) + for (var iter = 0; struct.m_members[iter] < struct.getSize(); iter++) + size += struct.getMember(iter).m_type.getScalarSize(); + return size; + } + + default: + // throw new Error('Unexpected type.'); + return 0; + } + }; + + /** + * is + * @return {boolean} returns true if the current object is equivalent to other. + */ + gluVarType.VarType.prototype.is = function(other) { + if (this.m_type != other.m_type) + return false; + + switch (this.m_type) { + case gluVarType.Type.TYPE_BASIC: + return this.m_data == other.m_data && + this.m_flags == other.m_flags; + + case gluVarType.Type.TYPE_ARRAY: + return this.m_data.elementType == other.m_data.elementType && + this.m_data.size == other.m_data.size; + + case gluVarType.Type.TYPE_STRUCT: + return this.m_data === other.m_data; + + default: + // throw new Error('Unexpected type.'); + return false; + } + }; + + /** + * isnt + * @return {boolean} returns true if the current object is not equivalent to other. + */ + gluVarType.VarType.prototype.isnt = function(other) { + return !(this.is(other)); + }; + + /** + * Creates a basic type gluVarType.VarType. + * @param {gluShaderUtil.DataType} basicType + * @param {framework.opengl.gluShaderUtil.precision=} flags + * @return {gluVarType.VarType} + */ + gluVarType.newTypeBasic = function(basicType, flags) { + if (!gluShaderUtil.isDataTypeBoolOrBVec(basicType)) + return new gluVarType.VarType().VarTypeBasic(basicType, /** @type {framework.opengl.gluShaderUtil.precision}*/ (flags)); + else + return new gluVarType.VarType().VarTypeBoolean(basicType); + }; + + /** + * Creates an array type gluVarType.VarType. + * @param {gluVarType.VarType} elementType + * @param {number} arraySize + * @return {gluVarType.VarType} + */ + gluVarType.newTypeArray = function(elementType, arraySize) { + return new gluVarType.VarType().VarTypeArray(elementType, arraySize); + }; + + /** + * Creates a struct type gluVarType.VarType. + * @param {gluVarType.StructType} structPtr + * @return {gluVarType.VarType} + */ + gluVarType.newTypeStruct = function(structPtr) { + return new gluVarType.VarType().VarTypeStruct(structPtr); + }; + + /** + * Creates a struct type gluVarType.VarType. + * @param {gluVarType.VarType} object + * @return {gluVarType.VarType} + */ + gluVarType.newClone = function(object) { + return new gluVarType.VarType().VarTypeClone(object); + }; + + /** + * gluVarType.StructMember class + * @constructor + */ + gluVarType.StructMember = function() { + /** @type {string} */ this.m_name; + /** @type {gluVarType.VarType} */ this.m_type; + /** @type {number} */ // this.m_flags = 0; // only in glsUniformBlockCase + }; + + /** + * Creates a gluVarType.StructMember. Use this after the constructor call. + * @param {string} name + * @param {gluVarType.VarType} type + * @return {gluVarType.StructMember} The currently modified object + */ + gluVarType.StructMember.prototype.Constructor = function(name, type) { + this.m_type = type; + this.m_name = name; + + return this; + }; + + /** getName + * @return {string} name of the gluVarType.StructMember object. + */ + gluVarType.StructMember.prototype.getName = function() { + return this.m_name; + }; + + /** getType + * @return {gluVarType.VarType} type of the gluVarType.StructMember object. + */ + gluVarType.StructMember.prototype.getType = function() { + return this.m_type; + }; + + /** + * Creates a gluVarType.StructMember. + * @param {string} name + * @param {gluVarType.VarType} type + * @return {gluVarType.StructMember} + */ + gluVarType.newStructMember = function(name, type) { + return new gluVarType.StructMember().Constructor(name, type); + }; + + /** + * gluVarType.StructType class + * @constructor + */ + gluVarType.StructType = function() { + /** @type {string} */ this.m_typeName = ''; + /** @type {Array<gluVarType.StructMember>} */ this.m_members = []; + }; + + /** + * Creates a gluVarType.StructType. Use this after the constructor call. + * @param {string} name + * @return {gluVarType.StructType} The currently modified object + */ + gluVarType.StructType.prototype.Constructor = function(name) { + /** @type {string}*/ this.m_typeName = this.setTypeName(name); + return this; + }; + + /** hasTypeName + * Checks if the gluVarType.StructType m_typeName is defined + * @return {boolean} + */ + gluVarType.StructType.prototype.hasTypeName = function() { + return (this.m_typeName !== 'undefined'); + }; + + /** setTypeName + * @param {string} name + * @return {string} returns gluVarType.StructType.m_typeName + */ + gluVarType.StructType.prototype.setTypeName = function(name) { + return this.m_typeName = name; + }; + + /** getTypeName + * @return {string} + */ + gluVarType.StructType.prototype.getTypeName = function() { + return this.m_typeName; + }; + + /** getNumMembers + * @return {number} + */ + gluVarType.StructType.prototype.getNumMembers = function() { + return this.m_members.length; + }; + + /** getMember + * @param {number} memberNdx The index of the member to retrieve. + * @return {gluVarType.StructMember} + */ + gluVarType.StructType.prototype.getMember = function(memberNdx) { + if (memberNdx >= 0 && memberNdx < this.m_members.length) + return this.m_members[memberNdx]; + else { + throw new Error('Invalid member index for StructTypes members'); + } + }; + + /** getSize + * @return {number} The size of the m_members array. + */ + gluVarType.StructType.prototype.getSize = function() { + return this.m_members.length; + }; + + /** addMember + * @param {string} name + * @param {gluVarType.VarType} type + */ + gluVarType.StructType.prototype.addMember = function(name, type) { + var member = gluVarType.newStructMember(name, type); + this.m_members.push(member); + }; + + /** + * Creates a gluVarType.StructType. + * @param {string} name + * @return {gluVarType.StructType} + */ + gluVarType.newStructType = function(name) { + return new gluVarType.StructType().Constructor(name); + }; + + /** + * @param {number} level + * @return {string} + */ + gluVarType.indent = function(level) { + /** @type {string} */ var str = ''; + for (var i = 0; i < level; i++) + str += '\t'; + return str; + }; + + /** + * @param {gluVarType.VarType} varType + * @param {string} name + * @param {number=} level + * @return {string} + */ + gluVarType.declareVariable = function(varType, name, level) { + /** @type {string} */ var str = ''; + /** @type {gluVarType.VarType} */ var type = varType; + /** @type {gluVarType.VarType} */ var curType = type; + /** @type {Array<number>} */ var arraySizes = []; + + // Handle arrays. + while (curType.isArrayType()) { + arraySizes.push(curType.getArraySize()); + curType = curType.getElementType(); + } + + if (curType.isBasicType()) { + if (curType.getPrecision() !== undefined) + str += gluShaderUtil.getPrecisionName(curType.getPrecision()) + ' '; + str += gluShaderUtil.getDataTypeName(/** @type {gluShaderUtil.DataType} */(curType.getBasicType())); + } else if (curType.isStructType()) { + /** @type {gluVarType.StructType} */ var structPtr = curType.getStruct(); + + if (structPtr.hasTypeName()) + str += structPtr.getTypeName(); + else + str += gluVarType.declareStructType(structPtr, level); // Generate inline declaration. + } else + throw new Error('Unexpected Array typed VarType.'); + + str += ' ' + name; + + // Print array sizes. + for (var size = 0; size < arraySizes.length; size++) { //std::vector<int>::const_iterator sizeIter = arraySizes.begin(); sizeIter != arraySizes.end(); sizeIter++) { + /** @type {number} */ var arrSize = arraySizes[size]; + if (arrSize == gluVarType.VarType.UNSIZED_ARRAY) + str += '[]'; + else + str += '[' + arrSize + ']'; + } + + return str; + }; + + /** + * @param {gluVarType.StructType} structType + * @param {number=} level + * @return {string} + */ + gluVarType.declareStructType = function(structType, level) { + /** @type {string} */ var str = 'struct'; + level = level || 0; + + // gluVarType.Type name is optional. + if (structType.hasTypeName()) + str += ' ' + structType.getTypeName(); + + str += '\n' + gluVarType.indent(level) + ' {\n'; + + for (var memberNdx = 0; memberNdx < structType.getSize(); memberNdx++) { //gluVarType.StructType::ConstIterator memberIter = decl.structPtr->begin(); memberIter != decl.structPtr->end(); memberIter++) { + /** @type {gluVarType.StructMember} */ var memberIter = structType.getMember(memberNdx); + str += gluVarType.indent(level + 1); + str += gluVarType.declareVariable(memberIter.getType(), memberIter.getName(), level + 1) + ';\n'; + } + + str += gluVarType.indent(level) + '}'; + + return str; + }; + + /** + * @param {*} T + * @param {number=} size + * @param {gluShaderUtil.precision=} precision + * @return {gluVarType.VarType} + */ + gluVarType.getVarTypeOf = function(T, size, precision) { + size = size || 1; + precision = precision || gluShaderUtil.precision.PRECISION_LOWP; + switch (size) { + case 4: return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC4, precision); + case 3: return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC3, precision); + case 2: return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, precision); + } + switch (T) { + case 'float' : return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT, precision); + case 'vec4': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC4, precision); + case 'vec3': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC3, precision); + case 'vec2': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_VEC2, precision); + case 'mat2': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT2, precision); + case 'mat2x3': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT2X3, precision); + case 'mat2x4': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT2X4, precision); + case 'mat3x2': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT3X2, precision); + case 'mat3': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT3, precision); + case 'mat3x4': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT3X4, precision); + case 'mat4x2': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT4X2, precision); + case 'mat4x3': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT4X3, precision); + case 'mat4': return gluVarType.newTypeBasic(gluShaderUtil.DataType.FLOAT_MAT4, precision); + } + throw new Error('Invalid input type ' + T + ' or size ' + size); + }; + + /** + * @enum + */ + gluVarType.Storage = { + STORAGE_IN: 0, + STORAGE_OUT: 1, + STORAGE_CONST: 2, + STORAGE_UNIFORM: 3, + STORAGE_BUFFER: 4 + }; + + /** + * @param {gluVarType.Storage} storage + * @return {string} + */ + gluVarType.getStorageName = function(storage) { + switch (storage) { + case gluVarType.Storage.STORAGE_IN: return 'in'; + case gluVarType.Storage.STORAGE_OUT: return 'out'; + case gluVarType.Storage.STORAGE_CONST: return 'const'; + case gluVarType.Storage.STORAGE_UNIFORM: return 'uniform'; + case gluVarType.Storage.STORAGE_BUFFER: return 'buffer'; + default: + throw new Error('Unknown storage: ' + storage); + } + }; + + /** + * @enum + */ + gluVarType.Interpolation = { + INTERPOLATION_SMOOTH: 0, + INTERPOLATION_FLAT: 1, + INTERPOLATION_CENTROID: 2 + }; + + /** + * @param {gluVarType.Interpolation} interpolation + * @return {string} + */ + gluVarType.getInterpolationName = function(interpolation) { + switch (interpolation) { + case gluVarType.Interpolation.INTERPOLATION_SMOOTH: return 'smooth'; + case gluVarType.Interpolation.INTERPOLATION_FLAT: return 'flat'; + case gluVarType.Interpolation.INTERPOLATION_CENTROID: return 'centrod'; + default: + throw new Error('Unknown interpolation: ' + interpolation); + } + }; + + /** + * @enum + */ + gluVarType.FormatLayout = { + FORMATLAYOUT_RGBA32F: 0, + FORMATLAYOUT_RGBA16F: 1, + FORMATLAYOUT_R32F: 2, + FORMATLAYOUT_RGBA8: 3, + FORMATLAYOUT_RGBA8_SNORM: 4, + + FORMATLAYOUT_RGBA32I: 5, + FORMATLAYOUT_RGBA16I: 6, + FORMATLAYOUT_RGBA8I: 7, + FORMATLAYOUT_R32I: 8, + + FORMATLAYOUT_RGBA32UI: 9, + FORMATLAYOUT_RGBA16UI: 10, + FORMATLAYOUT_RGBA8UI: 11, + FORMATLAYOUT_R32UI: 12 + }; + + /** + * @param {gluVarType.FormatLayout} layout + * @return {string} + */ + gluVarType.getFormatLayoutName = function(layout) { + switch (layout) { + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA32F: return 'rgba32f'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA16F: return 'rgba16f'; + case gluVarType.FormatLayout.FORMATLAYOUT_R32F: return 'r32f'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA8: return 'rgba8'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA8_SNORM: return 'rgba8_snorm'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA32I: return 'rgba32i'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA16I: return 'rgba16i'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA8I: return 'rgba8i'; + case gluVarType.FormatLayout.FORMATLAYOUT_R32I: return 'r32i'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA32UI: return 'rgba32ui'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA16UI: return 'rgba16ui'; + case gluVarType.FormatLayout.FORMATLAYOUT_RGBA8UI: return 'rgba8ui'; + case gluVarType.FormatLayout.FORMATLAYOUT_R32UI: return 'r32ui'; + default: + throw new Error('Unknown layout: ' + layout); + } + }; + + /** + * @enum + */ + gluVarType.MatrixOrder = { + MATRIXORDER_COLUMN_MAJOR: 0, + MATRIXORDER_ROW_MAJOR: 1 + }; + + /** + * @param {gluVarType.MatrixOrder} qualifier + * @return {string} + */ + gluVarType.getMatrixOrderName = function(qualifier) { + switch (qualifier) { + case gluVarType.MatrixOrder.MATRIXORDER_COLUMN_MAJOR: return 'column_major'; + case gluVarType.MatrixOrder.MATRIXORDER_ROW_MAJOR: return 'row_major'; + default: + throw new Error('Unknown qualifier: ' + qualifier); + } + }; + + gluVarType.MemoryAccessQualifier = { + MEMORYACCESSQUALIFIER_COHERENT_BIT: 0x01, + MEMORYACCESSQUALIFIER_VOLATILE_BIT: 0x02, + MEMORYACCESSQUALIFIER_RESTRICT_BIT: 0x04, + MEMORYACCESSQUALIFIER_READONLY_BIT: 0x08, + MEMORYACCESSQUALIFIER_WRITEONLY_BIT: 0x10 + }; + gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_MASK = (gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_WRITEONLY_BIT << 1) - 1; + + /** + * @param {number} qualifier + * @return {string} + */ + gluVarType.getMemoryAccessQualifierName = function(qualifier) { + switch (qualifier) { + case gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_COHERENT_BIT: return 'coherent'; + case gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_VOLATILE_BIT: return 'volatile'; + case gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_RESTRICT_BIT: return 'restrict'; + case gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_READONLY_BIT: return 'readonly'; + case gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_WRITEONLY_BIT: return 'writeonly'; + default: + throw new Error('Unknown qualifier: ' + qualifier); + } + }; + + /** + * @constructor + * @param {number=} location + * @param {number=} binding + * @param {number=} offset + * @param {gluVarType.FormatLayout=} format + * @param {gluVarType.MatrixOrder=} matrixOrder + */ + gluVarType.Layout = function(location, binding, offset, format, matrixOrder) { + this.location = location; + this.binding = binding; + this.offset = offset; + this.format = format; + this.matrixOrder = matrixOrder; + }; + + gluVarType.Layout.prototype.toString = function() { + var strings = []; + var str = ''; + if (typeof this.location !== 'undefined') + strings.push('location=' + this.location); + if (typeof this.binding !== 'undefined') + strings.push('binding=' + this.binding); + if (typeof this.offset !== 'undefined') + strings.push('offset=' + this.offset); + if (typeof this.format !== 'undefined') + strings.push(gluVarType.getFormatLayoutName(this.format)); + if (typeof this.matrixOrder !== 'undefined') + strings.push(gluVarType.getMatrixOrderName(this.matrixOrder)); + + if (strings.length > 0) { + str += 'layout(' + strings[0]; + + for (var i = 1; i < strings.length; i++) + str += ', ' + strings[i]; + str += ')'; + } + + return str; + }; + + /** + * @constructor + * @param {gluVarType.VarType} varType + * @param {string} name + * @param {gluVarType.Storage=} storage + * @param {gluVarType.Interpolation=} interpolation + * @param {gluVarType.Layout=} layout + * @param {number=} memoryAccessQualifierBits + */ + gluVarType.VariableDeclaration = function(varType, name, storage, interpolation, layout, memoryAccessQualifierBits) { + this.varType = varType; + this.name = name; + this.storage = storage; + this.interpolation = interpolation; + this.layout = layout; + this.memoryAccessQualifierBits = memoryAccessQualifierBits || 0; + }; + + gluVarType.VariableDeclaration.prototype.toString = function() { + var str = ''; + if (typeof this.layout !== 'undefined') + str += this.layout.toString() + ' '; + + for (var bitNdx = 0; (1 << bitNdx) & gluVarType.MemoryAccessQualifier.MEMORYACCESSQUALIFIER_MASK; ++bitNdx) + if (this.memoryAccessQualifierBits & (1 << bitNdx)) + str += gluVarType.getMemoryAccessQualifierName((1 << bitNdx)) + ' '; + + if (typeof this.interpolation !== 'undefined') + str += gluVarType.getInterpolationName(this.interpolation) + ' '; + + if (typeof this.storage !== 'undefined') + str += gluVarType.getStorageName(this.storage) + ' '; + + str += gluVarType.declareVariable(this.varType, this.name); + + return str; + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluVarTypeUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluVarTypeUtil.js new file mode 100644 index 000000000..30e198a60 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/gluVarTypeUtil.js @@ -0,0 +1,693 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.gluVarTypeUtil'); +goog.require('framework.opengl.gluShaderUtil'); +goog.require('framework.opengl.gluVarType'); + +goog.scope(function() { + + var gluVarTypeUtil = framework.opengl.gluVarTypeUtil; + var gluVarType = framework.opengl.gluVarType; + var gluShaderUtil = framework.opengl.gluShaderUtil; + + gluVarTypeUtil.isNum = function(c) { return /^[0-9]$/.test(c); }; + gluVarTypeUtil.isAlpha = function(c) { return /^[a-zA-Z]$/.test(c); }; + gluVarTypeUtil.isIdentifierChar = function(c) { return /^[a-zA-Z0-9_]$/.test(c); }; + gluVarTypeUtil.array_op_equivalent = function(arr1, arr2) { + if (arr1.length != arr2.length) return false; + for (var i = 0; i < arr1.length; ++i) { + if (arr1[i].isnt(arr2[1])) return false; + } + return true; + }; + + /** + * gluVarTypeUtil.VarTokenizer class. + * @param {string} str + * @constructor + */ + gluVarTypeUtil.VarTokenizer = function(str) { + + /** @private */ + this.m_str = str; + /** @private */ + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.length; + /** @private */ + this.m_tokenStart = 0; + /** @private */ + this.m_tokenLen = 0; + + this.advance(); + + }; + gluVarTypeUtil.VarTokenizer.s_Token = { + IDENTIFIER: 0, + LEFT_BRACKET: 1, + RIGHT_BRACKET: 2, + PERIOD: 3, + NUMBER: 4, + END: 5 + }; + gluVarTypeUtil.VarTokenizer.s_Token.length = Object.keys(gluVarTypeUtil.VarTokenizer.s_Token).length; + + gluVarTypeUtil.VarTokenizer.prototype.getToken = function() { + return this.m_token; + }; + gluVarTypeUtil.VarTokenizer.prototype.getIdentifier = function() { + return this.m_str.substr(this.m_tokenStart, this.m_tokenLen); + }; + gluVarTypeUtil.VarTokenizer.prototype.getNumber = function() { + return parseInt(this.getIdentifier(), 10); + }; + gluVarTypeUtil.VarTokenizer.prototype.getCurrentTokenStartLocation = function() { + return this.m_tokenStart; + }; + gluVarTypeUtil.VarTokenizer.prototype.getCurrentTokenEndLocation = function() { + return this.m_tokenStart + this.m_tokenLen; + }; + + gluVarTypeUtil.VarTokenizer.prototype.advance = function() { + + if (this.m_token == gluVarTypeUtil.VarTokenizer.s_Token.END) { + throw new Error('No more tokens.'); + } + + this.m_tokenStart += this.m_tokenLen; + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.length; + this.m_tokenLen = 1; + + if (this.m_tokenStart >= this.m_str.length) { + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.END; + + } else if (this.m_str[this.m_tokenStart] == '[') { + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.LEFT_BRACKET; + + } else if (this.m_str[this.m_tokenStart] == ']') { + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.RIGHT_BRACKET; + + } else if (this.m_str[this.m_tokenStart] == '.') { + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.PERIOD; + + } else if (gluVarTypeUtil.isNum(this.m_str[this.m_tokenStart])) { + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.NUMBER; + while (gluVarTypeUtil.isNum(this.m_str[this.m_tokenStart + this.m_tokenLen])) { + this.m_tokenLen += 1; + } + + } else if (gluVarTypeUtil.isIdentifierChar(this.m_str[this.m_tokenStart])) { + this.m_token = gluVarTypeUtil.VarTokenizer.s_Token.IDENTIFIER; + while (gluVarTypeUtil.isIdentifierChar(this.m_str[this.m_tokenStart + this.m_tokenLen])) { + this.m_tokenLen += 1; + } + + } else { + throw new Error('Unexpected character'); + } + + }; + + /** + * VarType subtype path utilities class. + * @param {gluVarTypeUtil.VarTypeComponent.s_Type} type + * @param {number} index + * @constructor + */ + gluVarTypeUtil.VarTypeComponent = function(type, index) { + /** @type {gluVarTypeUtil.VarTypeComponent.s_Type} */ this.type = type; + this.index = index || 0; + }; + + gluVarTypeUtil.VarTypeComponent.prototype.is = function(other) { + return this.type == other.type && this.index == other.index; + }; + gluVarTypeUtil.VarTypeComponent.prototype.isnt = function(other) { + return this.type != other.type || this.index != other.index; + }; + + /** + * @enum + */ + gluVarTypeUtil.VarTypeComponent.s_Type = { + STRUCT_MEMBER: 0, + ARRAY_ELEMENT: 1, + MATRIX_COLUMN: 2, + VECTOR_COMPONENT: 3 + }; + + /** + * Type path formatter. + * @param {gluVarType.VarType} type_ + * @param {Array<gluVarTypeUtil.VarTypeComponent>} path_ + * @constructor + */ + gluVarTypeUtil.TypeAccessFormat = function(type_, path_) { + this.type = type_; + this.path = path_; + }; + + gluVarTypeUtil.TypeAccessFormat.prototype.toString = function() { + var curType = this.type; + var str = ''; + + for (var i = 0; i < this.path.length; i++) { + var iter = this.path[i]; + switch (iter.type) { + case gluVarTypeUtil.VarTypeComponent.s_Type.ARRAY_ELEMENT: + curType = curType.getElementType(); // Update current type. + // Fall-through. + + case gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN: + case gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT: + str += '[' + iter.index + ']'; + break; + + case gluVarTypeUtil.VarTypeComponent.s_Type.STRUCT_MEMBER: { + var member = curType.getStruct().getMember(i); + str += '.' + member.getName(); + curType = member.getType(); + break; + } + + default: + throw new Error('Unrecognized type:' + iter.type); + } + } + + return str; + }; + + /** gluVarTypeUtil.SubTypeAccess + * @param {gluVarType.VarType} type + * @constructor + */ + gluVarTypeUtil.SubTypeAccess = function(type) { + + this.m_type = null; // VarType + this.m_path = []; // TypeComponentVector + + }; + + /** @private */ + gluVarTypeUtil.SubTypeAccess.prototype.helper = function(type, ndx) { + this.m_path.push(new gluVarTypeUtil.VarTypeComponent(type, ndx)); + if (!this.isValid()) { + throw new Error; + } + return this; + }; + + gluVarTypeUtil.SubTypeAccess.prototype.member = function(ndx) { + return this.helper(gluVarTypeUtil.VarTypeComponent.s_Type.STRUCT_MEMBER, ndx); + }; + gluVarTypeUtil.SubTypeAccess.prototype.element = function(ndx) { + return this.helper(gluVarTypeUtil.VarTypeComponent.s_Type.ARRAY_ELEMENT, ndx); + }; + gluVarTypeUtil.SubTypeAccess.prototype.column = function(ndx) { + return this.helper(gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN, ndx); + }; + gluVarTypeUtil.SubTypeAccess.prototype.component = function(ndx) { + return this.helper(gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT, ndx); + }; + gluVarTypeUtil.SubTypeAccess.prototype.parent = function() { + if (!this.m_path.length) { + throw new Error; + } + this.m_path.pop(); + return this; + }; + + gluVarTypeUtil.SubTypeAccess.prototype.isValid = function() { + return gluVarTypeUtil.isValidTypePath(this.m_type, this.m_path); + }; + gluVarTypeUtil.SubTypeAccess.prototype.getType = function() { + return gluVarTypeUtil.getVarType(this.m_type, this.m_path); + }; + gluVarTypeUtil.SubTypeAccess.prototype.getPath = function() { + return this.m_path; + }; + gluVarTypeUtil.SubTypeAccess.prototype.empty = function() { + return !this.m_path.length; + }; + gluVarTypeUtil.SubTypeAccess.prototype.is = function(other) { + return ( + gluVarTypeUtil.array_op_equivalent(this.m_path, other.m_path) && + this.m_type.is(other.m_type) + ); + }; + gluVarTypeUtil.SubTypeAccess.prototype.isnt = function(other) { + return ( + !gluVarTypeUtil.array_op_equivalent(this.m_path, other.m_path) || + this.m_type.isnt(other.m_type) + ); + }; + + /** + * Subtype iterator parent class. + * basic usage for all child classes: + * for (var i = new gluVarTypeUtil.BasicTypeIterator(type) ; !i.end() ; i.next()) { + * var j = i.getType(); + * } + * @constructor + */ + gluVarTypeUtil.SubTypeIterator = function(type) { + + /** @private */ + this.m_type = null; // const VarType* + /** @private */ + this.m_path = []; // TypeComponentVector + + if (type) { + this.m_type = type; + this.findNext(); + } + + }; + + gluVarTypeUtil.SubTypeIterator.prototype.isExpanded = function(type) { + throw new Error('This function must be overriden in child class'); + }; + + /** removeTraversed + * @private + */ + gluVarTypeUtil.SubTypeIterator.prototype.removeTraversed = function() { + + while (this.m_path.length) { + var curComp = this.m_path[this.m_path.length - 1]; // gluVarTypeUtil.VarTypeComponent& + var parentType = gluVarTypeUtil.getVarType(this.m_type, this.m_path, 0, this.m_path.length - 1); // VarType + + if (curComp.type == gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN) { + if (!gluShaderUtil.isDataTypeMatrix(parentType.getBasicType())) { + throw new Error('Isn\'t a matrix.'); + } + if (curComp.index + 1 < gluShaderUtil.getDataTypeMatrixNumColumns(parentType.getBasicType())) { + break; + } + + } else if (curComp.type == gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT) { + if (!gluShaderUtil.isDataTypeVector(parentType.getBasicType())) { + throw new Error('Isn\'t a vector.'); + } + if (curComp.index + 1 < gluShaderUtil.getDataTypeScalarSize(parentType.getBasicType())) { + break; + } + + } else if (curComp.type == gluVarTypeUtil.VarTypeComponent.s_Type.ARRAY_ELEMENT) { + if (!parentType.isArrayType()) { + throw new Error('Isn\'t an array.'); + } + if (curComp.index + 1 < parentType.getArraySize()) { + break; + } + + } else if (curComp.type == gluVarTypeUtil.VarTypeComponent.s_Type.STRUCT_MEMBER) { + if (!parentType.isStructType()) { + throw new Error('Isn\'t a struct.'); + } + if (curComp.index + 1 < parentType.getStruct().getNumMembers()) { + break; + } + + } + + this.m_path.pop(); + } + }; + gluVarTypeUtil.SubTypeIterator.prototype.findNext = function() { + + if (this.m_path.length > 0) { + // Increment child counter in current level. + var curComp = this.m_path[this.m_path.length - 1]; // gluVarTypeUtil.VarTypeComponent& + curComp.index += 1; + } + + for (;;) { + + var curType = gluVarTypeUtil.getVarType(this.m_type, this.m_path); // VarType + + if (this.isExpanded(curType)) + break; + + // Recurse into child type. + if (curType.isBasicType()) { + var basicType = curType.getBasicType(); // DataType + + if (gluShaderUtil.isDataTypeMatrix(basicType)) { + this.m_path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN, 0)); + + } else if (gluShaderUtil.isDataTypeVector(basicType)) { + this.m_path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT, 0)); + + } else { + throw new Error('Cant expand scalars - isExpanded() is buggy.'); + } + + } else if (curType.isArrayType()) { + this.m_path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.ARRAY_ELEMENT, 0)); + + } else if (curType.isStructType()) { + this.m_path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.STRUCT_MEMBER, 0)); + + } else { + throw new Error(); + } + } + + }; + gluVarTypeUtil.SubTypeIterator.prototype.end = function() { + return (this.m_type == null); + }; + /** next + * equivelant to operator++(), doesnt return. + */ + gluVarTypeUtil.SubTypeIterator.prototype.next = function() { + if (this.m_path.length > 0) { + // Remove traversed nodes. + this.removeTraversed(); + + if (this.m_path.length > 0) + this.findNext(); + else + this.m_type = null; // Unset type to signal end. + } else { + if (!this.isExpanded(gluVarTypeUtil.getVarType(this.m_type, this.m_path))) { + throw new Error('First type was already expanded.'); + } + this.m_type = null; + } + }; + gluVarTypeUtil.SubTypeIterator.prototype.getType = function() { + return gluVarTypeUtil.getVarType(this.m_type, this.m_path); + }; + gluVarTypeUtil.SubTypeIterator.prototype.getPath = function() { + return this.m_path; + }; + + gluVarTypeUtil.SubTypeIterator.prototype.toString = function() { + var x = new gluVarTypeUtil.TypeAccessFormat(this.m_type, this.m_path); + return x.toString(); + }; + + /** gluVarTypeUtil.BasicTypeIterator + * @param {gluVarType.VarType} type + * @constructor + * @extends {gluVarTypeUtil.SubTypeIterator} + */ + gluVarTypeUtil.BasicTypeIterator = function(type) { + gluVarTypeUtil.SubTypeIterator.call(this, type); + }; + gluVarTypeUtil.BasicTypeIterator.prototype = Object.create(gluVarTypeUtil.SubTypeIterator.prototype); + gluVarTypeUtil.BasicTypeIterator.prototype.constructor = gluVarTypeUtil.BasicTypeIterator; + + gluVarTypeUtil.BasicTypeIterator.prototype.isExpanded = function(type) { + return type.isBasicType(); + }; + + /** gluVarTypeUtil.VectorTypeIterator + * @param {gluVarType.VarType} type + * @constructor + * @extends {gluVarTypeUtil.SubTypeIterator} + */ + gluVarTypeUtil.VectorTypeIterator = function(type) { + gluVarTypeUtil.SubTypeIterator.call(this, type); + }; + gluVarTypeUtil.VectorTypeIterator.prototype = Object.create(gluVarTypeUtil.SubTypeIterator.prototype); + gluVarTypeUtil.VectorTypeIterator.prototype.constructor = gluVarTypeUtil.VectorTypeIterator; + + gluVarTypeUtil.VectorTypeIterator.prototype.isExpanded = function(type) { + return type.isBasicType() && gluShaderUtil.isDataTypeScalarOrVector(type.getBasicType()); + }; + + /** gluVarTypeUtil.ScalarTypeIterator + * @param {gluVarType.VarType} type + * @constructor + * @extends {gluVarTypeUtil.SubTypeIterator} + */ + gluVarTypeUtil.ScalarTypeIterator = function(type) { + gluVarTypeUtil.SubTypeIterator.call(this, type); + }; + gluVarTypeUtil.ScalarTypeIterator.prototype = Object.create(gluVarTypeUtil.SubTypeIterator.prototype); + gluVarTypeUtil.ScalarTypeIterator.prototype.constructor = gluVarTypeUtil.ScalarTypeIterator; + + gluVarTypeUtil.ScalarTypeIterator.prototype.isExpanded = function(type) { + return type.isBasicType() && gluShaderUtil.isDataTypeScalar(type.getBasicType()); + }; + + gluVarTypeUtil.inBounds = (function(x, a, b) { return a <= x && x < b; }); + + /** gluVarTypeUtil.isValidTypePath + * @param {gluVarType.VarType} type + * @param {Array<gluVarTypeUtil.VarTypeComponent>} array + * @param {number=} begin + * @param {number=} end + * @return {boolean} + */ + gluVarTypeUtil.isValidTypePath = function(type, array, begin, end) { + + if (typeof(begin) == 'undefined') {begin = 0;} + if (typeof(end) == 'undefined') {begin = array.length;} + + var curType = type; // const VarType* + var pathIter = begin; // Iterator + + // Process struct member and array element parts of path. + while (pathIter != end) { + var element = array[pathIter]; + + if (element.type == gluVarTypeUtil.VarTypeComponent.s_Type.STRUCT_MEMBER) { + + if (!curType.isStructType() || !gluVarTypeUtil.inBounds(element.index, 0, curType.getStruct().getNumMembers())) { + return false; + } + + curType = curType.getStruct().getMember(element.index).getType(); + + } else if (element.type == gluVarTypeUtil.VarTypeComponent.s_Type.ARRAY_ELEMENT) { + if ( + !curType.isArrayType() || + ( + curType.getArraySize() != gluVarType.VarType.UNSIZED_ARRAY && + !gluVarTypeUtil.inBounds(element.index, 0, curType.getArraySize()) + ) + ) { + return false; + } + + curType = curType.getElementType(); + } else { + break; + } + + ++pathIter; + } + + if (pathIter != end) { + if (!( + array[pathIter].type == gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN || + array[pathIter].type == gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT + )) { + throw new Error('Not a matrix or a vector'); + } + + // Current type should be basic type. + if (!curType.isBasicType()) { + return false; + } + + var basicType = curType.getBasicType(); // DataType + + if (array[pathIter].type == gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN) { + if (!gluShaderUtil.isDataTypeMatrix(basicType)) { + return false; + } + + basicType = gluShaderUtil.getDataTypeFloatVec(gluShaderUtil.getDataTypeMatrixNumRows(basicType)); + ++pathIter; + } + + if (pathIter != end && array[pathIter].type == gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT) { + if (!gluShaderUtil.isDataTypeVector(basicType)) + return false; + + basicType = gluShaderUtil.getDataTypeScalarType(basicType); + ++pathIter; + } + } + + return pathIter == end; + }; + + /** gluVarTypeUtil.getVarType + * @param {gluVarType.VarType} type + * @param {Array<gluVarTypeUtil.VarTypeComponent>} array + * @param {number=} start + * @param {number=} end + * @return {gluVarType.VarType} + */ + gluVarTypeUtil.getVarType = function(type, array, start, end) { + + if (typeof(start) == 'undefined') start = 0; + if (typeof(end) == 'undefined') end = array.length; + + if (!gluVarTypeUtil.isValidTypePath(type, array, start, end)) { + throw new Error('Type is invalid'); + } + + var curType = type; // const VarType* + var element = null; // Iterator + var pathIter = 0; + + // Process struct member and array element parts of path. + for (pathIter = start; pathIter != end; ++pathIter) { + element = array[pathIter]; + + if (element.type == gluVarTypeUtil.VarTypeComponent.s_Type.STRUCT_MEMBER) { + curType = curType.getStruct().getMember(element.index).getType(); + + } else if (element.type == gluVarTypeUtil.VarTypeComponent.s_Type.ARRAY_ELEMENT) { + curType = curType.getElementType(); + + } else { + break; + + } + } + + if (pathIter != end) { + + var basicType = curType.getBasicType(); // DataType + var precision = curType.getPrecision(); // Precision + + if (element.type == gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN) { + basicType = gluShaderUtil.getDataTypeFloatVec(gluShaderUtil.getDataTypeMatrixNumRows(basicType)); + element = array[++pathIter]; + } + + if (pathIter != end && element.type == gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT) { + basicType = gluShaderUtil.getDataTypeScalarTypeAsDataType(basicType); + element = array[++pathIter]; + } + + if (pathIter != end) { + throw new Error(); + } + return gluVarType.newTypeBasic(basicType, precision); + } else { + /* TODO: Original code created an object copy. We are returning reference to the same object */ + return curType; + } + }; + + gluVarTypeUtil.parseVariableName = function(nameWithPath) { + var tokenizer = new gluVarTypeUtil.VarTokenizer(nameWithPath); + if (tokenizer.getToken() != gluVarTypeUtil.VarTokenizer.s_Token.IDENTIFIER) { + throw new Error('Not an identifier.'); + } + return tokenizer.getIdentifier(); + }; + + // returns an array (TypeComponentVector& path) + // params: const char*, const VarType& + gluVarTypeUtil.parseTypePath = function(nameWithPath, type) { + + var tokenizer = new gluVarTypeUtil.VarTokenizer(nameWithPath); + + if (tokenizer.getToken() == gluVarTypeUtil.VarTokenizer.s_Token.IDENTIFIER) { + tokenizer.advance(); + } + + var path = []; + + while (tokenizer.getToken() != gluVarTypeUtil.VarTokenizer.s_Token.END) { + + var curType = gluVarTypeUtil.getVarType(type, path); + + if (tokenizer.getToken() == gluVarTypeUtil.VarTokenizer.s_Token.PERIOD) { + + tokenizer.advance(); + if (tokenizer.getToken() != gluVarTypeUtil.VarTokenizer.s_Token.IDENTIFIER) { + throw new Error(); + } + if (!curType.isStructType()) { + throw new Error('Invalid field selector'); + } + + // Find member. + var memberName = tokenizer.getIdentifier(); + var ndx = 0; + for (; ndx < curType.getStruct().getSize(); ++ndx) { + + if (memberName == curType.getStruct().getMember(ndx).getName()) { + break; + } + + } + if (ndx >= curType.getStruct().getSize()) { + throw new Error('Member not found in type: ' + memberName); + } + + path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.STRUCT_MEMBER, ndx)); + tokenizer.advance(); + + } else if (tokenizer.getToken() == gluVarTypeUtil.VarTokenizer.s_Token.LEFT_BRACKET) { + + tokenizer.advance(); + if (tokenizer.getToken() != gluVarTypeUtil.VarTokenizer.s_Token.NUMBER) { + throw new Error(); + } + + var ndx = tokenizer.getNumber(); + + if (curType.isArrayType()) { + if (!gluVarTypeUtil.inBounds(ndx, 0, curType.getArraySize())) throw new Error; + path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.ARRAY_ELEMENT, ndx)); + + } else if (curType.isBasicType() && gluShaderUtil.isDataTypeMatrix(curType.getBasicType())) { + if (!gluVarTypeUtil.inBounds(ndx, 0, gluShaderUtil.getDataTypeMatrixNumColumns(curType.getBasicType()))) throw new Error; + path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.MATRIX_COLUMN, ndx)); + + } else if (curType.isBasicType() && gluShaderUtil.isDataTypeVector(curType.getBasicType())) { + if (!gluVarTypeUtil.inBounds(ndx, 0, gluShaderUtil.getDataTypeScalarSize(curType.getBasicType()))) throw new Error; + path.push(new gluVarTypeUtil.VarTypeComponent(gluVarTypeUtil.VarTypeComponent.s_Type.VECTOR_COMPONENT, ndx)); + + } else { + //TCU_FAIL + throw new Error('Invalid subscript'); + } + + tokenizer.advance(); + if (tokenizer.getToken() != gluVarTypeUtil.VarTokenizer.s_Token.RIGHT_BRACKET) { + throw new Error('Expected token RIGHT_BRACKET'); + } + tokenizer.advance(); + + } else { + // TCU_FAIL + throw new Error('Unexpected token'); + } + } + + return path; + + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/00_test_list.txt new file mode 100644 index 000000000..7db3d9d5c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/00_test_list.txt @@ -0,0 +1 @@ +referencecontext.html
\ No newline at end of file diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/referencecontext.html b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/referencecontext.html new file mode 100644 index 000000000..f3ba0ed26 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/referencecontext.html @@ -0,0 +1,32 @@ +<html> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +<title>Reference context test</title> +<link rel="stylesheet" href="../../../../resources/js-test-style.css"/> +<script src="../../../../js/js-test-pre.js"></script> +<script src="../../../../js/webgl-test-utils.js"></script> + +<script src="../../../../closure-library/closure/goog/base.js"></script> +<script src="../../../deqp-deps.js"></script> +<script>goog.require('framework.opengl.simplereference.sglrReferenceContextTest');</script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<canvas id="canvas" width="200" height="100"> </canvas> +<script> +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext('canvas', {preserveDrawingBuffer: true}, 2); + + try { + framework.opengl.simplereference.sglrReferenceContextTest.run(gl); + } + catch(err) + { + bufferedLogToConsole(err); + } + +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrGLContext.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrGLContext.js new file mode 100644 index 000000000..13f75e8f5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrGLContext.js @@ -0,0 +1,231 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.simplereference.sglrGLContext'); +goog.require('framework.common.tcuPixelFormat'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.delibs.debase.deUtil'); +goog.require('framework.opengl.gluShaderProgram'); +goog.require('framework.opengl.gluShaderUtil'); +goog.require('framework.opengl.gluTextureUtil'); +goog.require('framework.opengl.simplereference.sglrShaderProgram'); +goog.require('framework.referencerenderer.rrDefs'); +goog.require('framework.referencerenderer.rrMultisamplePixelBufferAccess'); +goog.require('framework.referencerenderer.rrRenderState'); +goog.require('framework.referencerenderer.rrRenderer'); +goog.require('framework.referencerenderer.rrVertexAttrib'); + +goog.scope(function() { + + var sglrGLContext = framework.opengl.simplereference.sglrGLContext; + var tcuTexture = framework.common.tcuTexture; + var deUtil = framework.delibs.debase.deUtil; + var deMath = framework.delibs.debase.deMath; + var tcuTextureUtil = framework.common.tcuTextureUtil; + var tcuPixelFormat = framework.common.tcuPixelFormat; + var gluShaderProgram = framework.opengl.gluShaderProgram; + var gluShaderUtil = framework.opengl.gluShaderUtil; + var gluTextureUtil = framework.opengl.gluTextureUtil; + var sglrShaderProgram = framework.opengl.simplereference.sglrShaderProgram; + var rrDefs = framework.referencerenderer.rrDefs; + var rrMultisamplePixelBufferAccess = framework.referencerenderer.rrMultisamplePixelBufferAccess; + var rrRenderer = framework.referencerenderer.rrRenderer; + var rrRenderState = framework.referencerenderer.rrRenderState; + var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * sglrGLContext.GLContext wraps the standard WebGL context to be able to be used interchangeably with the ReferenceContext + * @constructor + * @extends {WebGL2RenderingContext} + * @param {?WebGL2RenderingContext} context + * @param {Array<number>=} viewport + */ + sglrGLContext.GLContext = function(context, viewport) { + DE_ASSERT(context); + + var functionwrapper = function(context, fname) { + return function() { + return context[fname].apply(context, arguments); + }; + }; + + var wrap = {}; + for (var i in context) { + try { + if (typeof context[i] == 'function') { + wrap[i] = functionwrapper(context, i); + } else { + wrap[i] = context[i]; + } + } catch (e) { + throw new Error('GLContext: Error accessing ' + i); + } + } + if (viewport) + context.viewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + /** + * createProgram + * @override + * @param {sglrShaderProgram.ShaderProgram=} shader + * @return {!WebGLProgram} + */ + this.createProgram = function(shader) { + var program = new gluShaderProgram.ShaderProgram( + context, + gluShaderProgram.makeVtxFragSources( + shader.m_vertSrc, + shader.m_fragSrc + ) + ); + + if (!program.isOk()) { + bufferedLogToConsole(program.toString()); + testFailedOptions('Compile failed', true); + } + return program.getProgram(); + }; + wrap['createProgram'] = this.createProgram; + + /** + * Draws quads from vertex arrays + * @param {number} primitive Primitive type + * @param {number} first First vertex to begin drawing with + * @param {number} count Number of vertices + */ + var drawQuads = function(primitive, first, count) { + context.drawArrays(primitive, first, count); + }; + wrap['drawQuads'] = drawQuads; + + /** + * @return {number} + */ + var getWidth = function() { + if(viewport) + return viewport[2]; + else + return context.drawingBufferWidth; + }; + wrap['getWidth'] = getWidth; + + /** + * @return {number} + */ + var getHeight = function() { + if(viewport) + return viewport[3]; + else + return context.drawingBufferHeight; + }; + wrap['getHeight'] = getHeight; + + /** + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @param {number} format + * @param {number} dataType + * @param {ArrayBuffer|ArrayBufferView} data + */ + var readPixels = function(x, y, width, height, format, dataType, data) { + /** @type {?ArrayBufferView} */ var dataArr; + if (!ArrayBuffer.isView(data)) { + var type = gluTextureUtil.mapGLChannelType(dataType, true); + var dataArrType = tcuTexture.getTypedArray(type); + dataArr = new dataArrType(data); + } else { + dataArr = /** @type {?ArrayBufferView} */ (data); + } + + context.readPixels(x, y, width, height, format, dataType, dataArr); + }; + wrap['readPixels'] = readPixels; + + /** + * @param {number} target + * @param {number} level + * @param {number} internalFormat + * @param {number} width + * @param {number} height + */ + var texImage2DDelegate = function(target, level, internalFormat, width, height) { + var format; + var dataType; + + switch(internalFormat) + { + case gl.ALPHA: + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + case gl.RGB: + case gl.RGBA: + format = internalFormat; + dataType = gl.UNSIGNED_BYTE; + break; + default: + { + var transferFmt = gluTextureUtil.getTransferFormat(gluTextureUtil.mapGLInternalFormat(internalFormat)); + format = transferFmt.format; + dataType = transferFmt.dataType; + break; + } + } + context.texImage2D(target, level, internalFormat, width, height, 0, format, dataType, null); + }; + wrap['texImage2DDelegate'] = texImage2DDelegate; + + return wrap; + }; + + /** + * createProgram - This had to be added here as dummy to remove a warning when the only context used is GLContext (no reference context) + * @override + * @param {sglrShaderProgram.ShaderProgram=} shader + * @return {!WebGLProgram} + */ + sglrGLContext.GLContext.prototype.createProgram = function(shader) {return this.createProgram();}; + + /** + * @param ctx GL-like context + * @param {string} name + * @return {boolean} + */ + sglrGLContext.isExtensionSupported = function(ctx, name) { + var extns = ctx.getSupportedExtensions(); + var found = false; + if (extns) { + var index = extns.indexOf(name); + if (index != -1) + found = true; + } + return found; + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceContext.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceContext.js new file mode 100644 index 000000000..523dbe607 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceContext.js @@ -0,0 +1,4986 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.simplereference.sglrReferenceContext'); +goog.require('framework.common.tcuMatrix'); +goog.require('framework.common.tcuMatrixUtil'); +goog.require('framework.common.tcuPixelFormat'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.opengl.gluShaderUtil'); +goog.require('framework.opengl.gluTextureUtil'); +goog.require('framework.opengl.simplereference.sglrReferenceUtils'); +goog.require('framework.opengl.simplereference.sglrShaderProgram'); +goog.require('framework.referencerenderer.rrDefs'); +goog.require('framework.referencerenderer.rrGenericVector'); +goog.require('framework.referencerenderer.rrMultisamplePixelBufferAccess'); +goog.require('framework.referencerenderer.rrRenderState'); +goog.require('framework.referencerenderer.rrRenderer'); +goog.require('framework.referencerenderer.rrVertexAttrib'); + +goog.scope(function() { + + var sglrReferenceContext = framework.opengl.simplereference.sglrReferenceContext; + var rrMultisamplePixelBufferAccess = framework.referencerenderer.rrMultisamplePixelBufferAccess; + var tcuTexture = framework.common.tcuTexture; + var deMath = framework.delibs.debase.deMath; + var gluTextureUtil = framework.opengl.gluTextureUtil; + var tcuTextureUtil = framework.common.tcuTextureUtil; + var tcuPixelFormat = framework.common.tcuPixelFormat; + var gluShaderUtil = framework.opengl.gluShaderUtil; + var rrRenderer = framework.referencerenderer.rrRenderer; + var rrDefs = framework.referencerenderer.rrDefs; + var rrGenericVector = framework.referencerenderer.rrGenericVector; + var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; + var rrRenderState = framework.referencerenderer.rrRenderState; + var sglrReferenceUtils = framework.opengl.simplereference.sglrReferenceUtils; + var sglrShaderProgram = framework.opengl.simplereference.sglrShaderProgram; + var tcuMatrix = framework.common.tcuMatrix; + var tcuMatrixUtil = framework.common.tcuMatrixUtil; + + sglrReferenceContext.rrMPBA = rrMultisamplePixelBufferAccess; + + //TODO: Implement automatic error checking in sglrReferenceContext, optional on creation. + + /** @typedef {WebGLRenderbuffer|WebGLTexture|sglrReferenceContext.Renderbuffer|sglrReferenceContext.TextureContainer} */ sglrReferenceContext.AnyRenderbuffer; + + /** @typedef {WebGLFramebuffer|sglrReferenceContext.Framebuffer} */ sglrReferenceContext.AnyFramebuffer; + + /** + * @param {number} error + * @param {number} message + * @throws {Error} + */ + sglrReferenceContext.GLU_EXPECT_NO_ERROR = function(error, message) { + if (error !== gl.NONE) { + bufferedLogToConsole('Assertion failed message:' + message); + } + }; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + // /* TODO: remove */ + // /** @type {WebGL2RenderingContext} */ var gl; + + sglrReferenceContext.MAX_TEXTURE_SIZE_LOG2 = 14; + sglrReferenceContext.MAX_TEXTURE_SIZE = 1 << sglrReferenceContext.MAX_TEXTURE_SIZE_LOG2; + + /** + * @param {number} width + * @param {number} height + * @return {number} + */ + sglrReferenceContext.getNumMipLevels2D = function(width, height) { + return Math.floor(Math.log2(Math.max(width, height)) + 1); + }; + + /** + * @param {number} width + * @param {number} height + * @param {number} depth + * @return {number} + */ + sglrReferenceContext.getNumMipLevels3D = function(width, height, depth) { + return Math.floor(Math.log2(Math.max(width, height, depth)) + 1); + }; + + /** + * @param {number} baseLevelSize + * @param {number} levelNdx + * @return {number} + */ + sglrReferenceContext.getMipLevelSize = function(baseLevelSize, levelNdx) { + return Math.max(baseLevelSize >> levelNdx, 1); + }; + + sglrReferenceContext.mapGLCubeFace = function(face) { + switch (face) { + case gl.TEXTURE_CUBE_MAP_NEGATIVE_X: return tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X; + case gl.TEXTURE_CUBE_MAP_POSITIVE_X: return tcuTexture.CubeFace.CUBEFACE_POSITIVE_X; + case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y: return tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y; + case gl.TEXTURE_CUBE_MAP_POSITIVE_Y: return tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y; + case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z: return tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z; + case gl.TEXTURE_CUBE_MAP_POSITIVE_Z: return tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z; + default: throw new Error('Invalid cube face: ' + face); + } + }; + + /** + * @param {tcuTexture.FilterMode} mode + * @return {boolean} + */ + sglrReferenceContext.isMipmapFilter = function(/*const tcu::Sampler::FilterMode*/ mode) { + return mode != tcuTexture.FilterMode.NEAREST && mode != tcuTexture.FilterMode.LINEAR; + }; + + sglrReferenceContext.getNumMipLevels1D = function(size) { + return Math.floor(Math.log2(size)) + 1; + }; + + /** + * @param {?sglrReferenceContext.TextureType} type + * @return {sglrReferenceContext.TexTarget} + */ + sglrReferenceContext.texLayeredTypeToTarget = function(type) { + switch (type) { + case sglrReferenceContext.TextureType.TYPE_2D_ARRAY: return sglrReferenceContext.TexTarget.TEXTARGET_2D_ARRAY; + case sglrReferenceContext.TextureType.TYPE_3D: return sglrReferenceContext.TexTarget.TEXTARGET_3D; + case sglrReferenceContext.TextureType.TYPE_CUBE_MAP_ARRAY: return sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_ARRAY; + default: throw new Error('Invalid texture type: ' + type); + } + }; + + /** + * @param {rrDefs.IndexType} indexType + * @return {number} + * @throws {Error} + */ + sglrReferenceContext.getFixedRestartIndex = function(indexType) { + switch (indexType) { + case rrDefs.IndexType.INDEXTYPE_UINT8: return 0xFF; + case rrDefs.IndexType.INDEXTYPE_UINT16: return 0xFFFF; + case rrDefs.IndexType.INDEXTYPE_UINT32: return 0xFFFFFFFF; + default: + throw new Error('Unrecognized index type: ' + indexType); + } + }; + + /** + * @constructor + * @param {sglrShaderProgram.ShaderProgram} program + */ + sglrReferenceContext.ShaderProgramObjectContainer = function(program) { + this.m_program = program; + /** @type {boolean} */ this.m_deleteFlag = false; + }; + + /** + * @param {WebGL2RenderingContext} gl + * @constructor + */ + sglrReferenceContext.ReferenceContextLimits = function(gl) { + /** @type {number} */ this.maxTextureImageUnits = 16; + /** @type {number} */ this.maxTexture2DSize = 2048; + /** @type {number} */ this.maxTextureCubeSize = 2048; + /** @type {number} */ this.maxTexture2DArrayLayers = 256; + /** @type {number} */ this.maxTexture3DSize = 256; + /** @type {number} */ this.maxRenderbufferSize = 2048; + /** @type {number} */ this.maxVertexAttribs = 16; + + if (gl) { + this.maxTextureImageUnits = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)); + this.maxTexture2DSize = /** @type {number} */ (gl.getParameter(gl.MAX_TEXTURE_SIZE)); + this.maxTextureCubeSize = /** @type {number} */ (gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)); + this.maxRenderbufferSize = /** @type {number} */ (gl.getParameter(gl.MAX_RENDERBUFFER_SIZE)); + this.maxVertexAttribs = /** @type {number} */ (gl.getParameter(gl.MAX_VERTEX_ATTRIBS)); + this.maxTexture2DArrayLayers = /** @type {number} */ (gl.getParameter(gl.MAX_ARRAY_TEXTURE_LAYERS)); + this.maxTexture3DSize = /** @type {number} */ (gl.getParameter(gl.MAX_3D_TEXTURE_SIZE)); + + // Limit texture sizes to supported values + this.maxTexture2DSize = Math.min(this.maxTexture2DSize, sglrReferenceContext.MAX_TEXTURE_SIZE); + this.maxTextureCubeSize = Math.min(this.maxTextureCubeSize, sglrReferenceContext.MAX_TEXTURE_SIZE); + this.maxTexture3DSize = Math.min(this.maxTexture3DSize, sglrReferenceContext.MAX_TEXTURE_SIZE); + + sglrReferenceContext.GLU_EXPECT_NO_ERROR(gl.getError(), gl.NO_ERROR); + } + + /* TODO: Port + // \todo [pyry] Figure out following things: + // + supported fbo configurations + // ... + + // \todo [2013-08-01 pyry] Do we want to make these conditional based on renderCtx? + addExtension("gl.EXT_color_buffer_half_float"); + addExtension("gl.WEBGL_color_buffer_float"); + */ + }; + + /** + * @enum + */ + sglrReferenceContext.TextureType = { + TYPE_2D: 0, + TYPE_CUBE_MAP: 1, + TYPE_2D_ARRAY: 2, + TYPE_3D: 3, + TYPE_CUBE_MAP_ARRAY: 4 + }; + + /** + * @constructor + * @implements {rrDefs.Sampler} + * @param {sglrReferenceContext.TextureType} type + */ + sglrReferenceContext.Texture = function(type) { + // NamedObject.call(this, name); + /** @type {sglrReferenceContext.TextureType} */ this.m_type = type; + /** @type {boolean} */ this.m_immutable = false; + /** @type {number} */ this.m_baseLevel = 0; + /** @type {number} */ this.m_maxLevel = 1000; + /** @type {tcuTexture.Sampler} */ this.m_sampler = new tcuTexture.Sampler( + tcuTexture.WrapMode.REPEAT_GL, + tcuTexture.WrapMode.REPEAT_GL, + tcuTexture.WrapMode.REPEAT_GL, + tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR, + tcuTexture.FilterMode.LINEAR, + 0, + true, + tcuTexture.CompareMode.COMPAREMODE_NONE, + 0, + [0, 0, 0, 0], + true); + }; + + /** + * @param {Array<number>} pos + * @param {number=} lod + * @throws {Error} + */ + sglrReferenceContext.Texture.prototype.sample = function(pos, lod) {throw new Error('Intentionally empty. Call method from child class instead'); }; + + /** + * @param {Array<Array<number>>} packetTexcoords + * @param {number} lodBias + * @throws {Error} + */ + sglrReferenceContext.Texture.prototype.sample4 = function(packetTexcoords, lodBias) {throw new Error('Intentionally empty. Call method from child class instead'); }; + + // sglrReferenceContext.Texture.prototype = Object.create(NamedObject.prototype); + // sglrReferenceContext.Texture.prototype.constructor = sglrReferenceContext.Texture; + + /** + * @return {number} + */ + sglrReferenceContext.Texture.prototype.getType = function() { return this.m_type; }; + + /** + * @return {number} + */ + sglrReferenceContext.Texture.prototype.getBaseLevel = function() { return this.m_baseLevel; }; + + /** + * @return {number} + */ + sglrReferenceContext.Texture.prototype.getMaxLevel = function() { return this.m_maxLevel; }; + + /** + * @return {boolean} + */ + sglrReferenceContext.Texture.prototype.isImmutable = function() { return this.m_immutable; }; + + /** + * @param {number} baseLevel + */ + sglrReferenceContext.Texture.prototype.setBaseLevel = function(baseLevel) { this.m_baseLevel = baseLevel; }; + + /** + * @param {number} maxLevel + */ + sglrReferenceContext.Texture.prototype.setMaxLevel = function(maxLevel) { this.m_maxLevel = maxLevel; }; + + /** + */ + sglrReferenceContext.Texture.prototype.setImmutable = function() { this.m_immutable = true; }; + + /** + * @return {tcuTexture.Sampler} + */ + sglrReferenceContext.Texture.prototype.getSampler = function() { return this.m_sampler; }; + + /** + * @constructor + */ + sglrReferenceContext.TextureLevelArray = function() { + /** @type {Array<ArrayBuffer>} */ this.m_data = []; + /** @type {Array<tcuTexture.PixelBufferAccess>} */ this.m_access = []; + }; + + /** + * @param {number} level + * @return {boolean} + */ + sglrReferenceContext.TextureLevelArray.prototype.hasLevel = function(level) { return this.m_data[level] != null; }; + + /** + * @param {number} level + * @return {tcuTexture.PixelBufferAccess} + * @throws {Error} + */ + sglrReferenceContext.TextureLevelArray.prototype.getLevel = function(level) { + if (!this.hasLevel(level)) + throw new Error('Level: ' + level + ' is not defined.'); + + return this.m_access[level]; + }; + + /** + * @return {Array<tcuTexture.PixelBufferAccess>} + */ + sglrReferenceContext.TextureLevelArray.prototype.getLevels = function() { return this.m_access; }; + + /** + * @param {number} level + * @param {tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + * @param {number} depth + */ + sglrReferenceContext.TextureLevelArray.prototype.allocLevel = function(level, format, width, height, depth) { + /** @type {number} */ var dataSize = format.getPixelSize() * width * height * depth; + if (this.hasLevel(level)) + this.clearLevel(level); + + this.m_data[level] = new ArrayBuffer(dataSize); + this.m_access[level] = new tcuTexture.PixelBufferAccess({ + format: format, + width: width, + height: height, + depth: depth, + data: this.m_data[level]}); + }; + + /** + * @param {number} level + */ + sglrReferenceContext.TextureLevelArray.prototype.clearLevel = function(level) { + delete this.m_data[level]; + delete this.m_access[level]; + }; + + /** + */ + sglrReferenceContext.TextureLevelArray.prototype.clear = function() { + for (var key in this.m_data) + delete this.m_data[key]; + + for (var key in this.m_access) + delete this.m_access[key]; + }; + + /** + * @constructor + * @extends {sglrReferenceContext.Texture} + */ + sglrReferenceContext.Texture2D = function() { + sglrReferenceContext.Texture.call(this, sglrReferenceContext.TextureType.TYPE_2D); + /** @type {tcuTexture.Texture2DView} */ this.m_view = new tcuTexture.Texture2DView(0, null); + /** @type {sglrReferenceContext.TextureLevelArray} */ this.m_levels = new sglrReferenceContext.TextureLevelArray(); + }; + + /** + */ + sglrReferenceContext.Texture2D.prototype = Object.create(sglrReferenceContext.Texture.prototype); + sglrReferenceContext.Texture2D.prototype.constructor = sglrReferenceContext.Texture2D; + + sglrReferenceContext.Texture2D.prototype.clearLevels = function() { this.m_levels.clear(); }; + + /** + * @param {number} level + * @return {boolean} + */ + sglrReferenceContext.Texture2D.prototype.hasLevel = function(level) { return this.m_levels.hasLevel(level); }; + + /** + * @param {number} level + * @return {tcuTexture.PixelBufferAccess} + */ + sglrReferenceContext.Texture2D.prototype.getLevel = function(level) { return this.m_levels.getLevel(level); }; + + /** + * @param {number} level + * @param {?tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.Texture2D.prototype.allocLevel = function(level, format, width, height) { this.m_levels.allocLevel(level, format, width, height, 1); }; + + /** + * @return {boolean} + */ + sglrReferenceContext.Texture2D.prototype.isComplete = function() { + /** @type {number} */ var baseLevel = this.getBaseLevel(); + + if (this.hasLevel(baseLevel)) { + /** @type {tcuTexture.PixelBufferAccess} */ var level0 = this.getLevel(baseLevel); + /** @type {boolean} */ var mipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + + if (mipmap) { + /** @type {tcuTexture.TextureFormat} */ var format = level0.getFormat(); + /** @type {number} */ var w = level0.getWidth(); + /** @type {number} */ var h = level0.getHeight(); + /** @type {number} */ var numLevels = Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels2D(w, h)); + + for (var levelNdx = 1; levelNdx < numLevels; levelNdx++) { + if (this.hasLevel(baseLevel + levelNdx)) { + /** @type {tcuTexture.PixelBufferAccess} */ var level = this.getLevel(baseLevel + levelNdx); + /** @type {number} */ var expectedW = sglrReferenceContext.getMipLevelSize(w, levelNdx); + /** @type {number} */ var expectedH = sglrReferenceContext.getMipLevelSize(h, levelNdx); + + if (level.getWidth() != expectedW || + level.getHeight() != expectedH || + !level.getFormat().isEqual(format)) + return false; + } else + return false; + } + } + + return true; + } else + return false; + }; + + /** + */ + sglrReferenceContext.Texture2D.prototype.updateView = function() { + /** @type {number} */ var baseLevel = this.getBaseLevel(); + + if (this.hasLevel(baseLevel) && !this.getLevel(baseLevel).isEmpty()) { + // Update number of levels in mipmap pyramid. + /** @type {number} */ var width = this.getLevel(baseLevel).getWidth(); + /** @type {number} */ var height = this.getLevel(baseLevel).getHeight(); + /** @type {boolean} */ var isMipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + /** @type {number} */ var numLevels = isMipmap ? Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels2D(width, height)) : 1; + + this.m_view = new tcuTexture.Texture2DView(numLevels, this.m_levels.getLevels().slice(baseLevel)); + } else + this.m_view = new tcuTexture.Texture2DView(0, null); + }; + + /** + * @param {Array<number>} pos + * @param {number=} lod + * @return {Array<number>} + */ + sglrReferenceContext.Texture2D.prototype.sample = function(pos, lod) { + return this.m_view.sample(this.getSampler(), pos, lod); + }; + + /** + * @param {Array<Array<number>>} packetTexcoords 4 vec2 coordinates + * @param {number} lodBias_ + * @return {Array<Array<number>>} 4 vec4 samples + */ + sglrReferenceContext.Texture2D.prototype.sample4 = function(packetTexcoords, lodBias_) { + /** @type {number} */ var lodBias = lodBias_ || 0; + /** @type {number} */ var texWidth = this.m_view.getWidth(); + /** @type {number} */ var texHeight = this.m_view.getHeight(); + /** @type {Array<Array<number>>}*/ var output = []; + + /** @type {Array<number>}*/ var dFdx0 = deMath.subtract(packetTexcoords[1], packetTexcoords[0]); + /** @type {Array<number>}*/ var dFdx1 = deMath.subtract(packetTexcoords[3], packetTexcoords[2]); + /** @type {Array<number>}*/ var dFdy0 = deMath.subtract(packetTexcoords[2], packetTexcoords[0]); + /** @type {Array<number>}*/ var dFdy1 = deMath.subtract(packetTexcoords[3], packetTexcoords[1]); + + for (var fragNdx = 0; fragNdx < 4; ++fragNdx) { + /** @type {Array<number>}*/var dFdx = (fragNdx & 2) ? dFdx1 : dFdx0; + /** @type {Array<number>}*/var dFdy = (fragNdx & 1) ? dFdy1 : dFdy0; + + /** @type {number} */ var mu = Math.max(Math.abs(dFdx[0]), Math.abs(dFdy[0])); + /** @type {number} */ var mv = Math.max(Math.abs(dFdx[1]), Math.abs(dFdy[1])); + /** @type {number} */ var p = Math.max(mu * texWidth, mv * texHeight); + + /** @type {number} */ var lod = Math.log2(p) + lodBias; + + output.push(this.sample([packetTexcoords[fragNdx][0], packetTexcoords[fragNdx][1]], lod)); + } + + return output; + }; + + /** + * @constructor + * @extends {sglrReferenceContext.Texture} + */ + sglrReferenceContext.TextureCube = function() { + sglrReferenceContext.Texture.call(this, sglrReferenceContext.TextureType.TYPE_CUBE_MAP); + /** @type {tcuTexture.TextureCubeView} */ this.m_view = new tcuTexture.TextureCubeView(0, null); + /** @type {Array<sglrReferenceContext.TextureLevelArray>} */ this.m_levels = []; + for (var face in tcuTexture.CubeFace) + this.m_levels[tcuTexture.CubeFace[face]] = new sglrReferenceContext.TextureLevelArray(); + }; + + /** + */ + sglrReferenceContext.TextureCube.prototype = Object.create(sglrReferenceContext.Texture.prototype); + sglrReferenceContext.TextureCube.prototype.constructor = sglrReferenceContext.Texture2D; + + sglrReferenceContext.TextureCube.prototype.clearLevels = function() { + for (var face in tcuTexture.CubeFace) + this.m_levels[tcuTexture.CubeFace[face]].clear(); + }; + + /** + * @param {number} level + * @param {tcuTexture.CubeFace} face + * @return {boolean} + */ + sglrReferenceContext.TextureCube.prototype.hasFace = function(level, face) { return this.m_levels[face].hasLevel(level); }; + + /** + * @param {number} level + * @param {tcuTexture.CubeFace} face + * @return {tcuTexture.PixelBufferAccess} + */ + sglrReferenceContext.TextureCube.prototype.getFace = function(level, face) { return this.m_levels[face].getLevel(level); }; + + /** + * @param {number} level + * @param {tcuTexture.CubeFace} face + * @param {?tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.TextureCube.prototype.allocLevel = function(level, face, format, width, height) { + this.m_levels[face].allocLevel(level, format, width, height, 1); + }; + + sglrReferenceContext.TextureCube.prototype.isComplete = function() { + var baseLevel = this.getBaseLevel(); + + if (this.hasFace(baseLevel, tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X)) { + var level = this.getFace(baseLevel, tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X); + var width = level.getWidth(); + var height = level.getHeight(); + var format = level.getFormat(); + var mipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + var numLevels = mipmap ? Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels2D(width, height)) : 1; + + if (width != height) + return false; // Non-square is not supported. + + // \note Level 0 is always checked for consistency + for (var levelNdx = 0; levelNdx < numLevels; levelNdx++) { + var levelW = sglrReferenceContext.getMipLevelSize(width, levelNdx); + var levelH = sglrReferenceContext.getMipLevelSize(height, levelNdx); + + for (var face in tcuTexture.CubeFace) { + if (this.hasFace(baseLevel + levelNdx, tcuTexture.CubeFace[face])) { + level = this.getFace(baseLevel + levelNdx, tcuTexture.CubeFace[face]); + + if (level.getWidth() != levelW || + level.getHeight() != levelH || + !level.getFormat().isEqual(format)) + return false; + } else + return false; + } + } + + return true; + } else + return false; + }; + + sglrReferenceContext.TextureCube.prototype.updateView = function() { + + var baseLevel = this.getBaseLevel(); + var faces = []; + + if (this.isComplete()) { + var size = this.getFace(baseLevel, tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X).getWidth(); + var isMipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + var numLevels = isMipmap ? Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels1D(size)) : 1; + + for (var face in tcuTexture.CubeFace) + faces[tcuTexture.CubeFace[face]] = this.m_levels[tcuTexture.CubeFace[face]].getLevels().slice(baseLevel); + + this.m_view = new tcuTexture.TextureCubeView(numLevels, faces); + } else + this.m_view = new tcuTexture.TextureCubeView(0, null); + }; + + /** + * @param {Array<number>} pos + * @param {number=} lod + * @return {Array<number>} + */ + sglrReferenceContext.TextureCube.prototype.sample = function(pos, lod) { return this.m_view.sample(this.getSampler(), pos, lod) }; + + /** + * @constructor + * @extends {sglrReferenceContext.Texture} + */ + sglrReferenceContext.Texture2DArray = function() { + sglrReferenceContext.Texture.call(this, sglrReferenceContext.TextureType.TYPE_2D_ARRAY); + /** @type {tcuTexture.Texture2DArrayView} */ this.m_view = new tcuTexture.Texture2DArrayView(0, null); + /** @type {sglrReferenceContext.TextureLevelArray} */ this.m_levels = new sglrReferenceContext.TextureLevelArray(); + }; + + /** + */ + sglrReferenceContext.Texture2DArray.prototype = Object.create(sglrReferenceContext.Texture.prototype); + sglrReferenceContext.Texture2DArray.prototype.constructor = sglrReferenceContext.Texture2DArray; + + sglrReferenceContext.Texture2DArray.prototype.clearLevels = function() { this.m_levels.clear(); }; + + /** + * @param {number} level + * @return {boolean} + */ + sglrReferenceContext.Texture2DArray.prototype.hasLevel = function(level) { return this.m_levels.hasLevel(level); }; + + /** + * @param {number} level + * @return {tcuTexture.PixelBufferAccess} + */ + sglrReferenceContext.Texture2DArray.prototype.getLevel = function(level) { return this.m_levels.getLevel(level); }; + + /** + * @param {number} level + * @param {?tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + * @param {number} numLayers + */ + sglrReferenceContext.Texture2DArray.prototype.allocLevel = function(level, format, width, height, numLayers) { + this.m_levels.allocLevel(level, format, width, height, numLayers); + }; + + /** + * @return {boolean} + */ + sglrReferenceContext.Texture2DArray.prototype.isComplete = function() { + /** @type {number} */ var baseLevel = this.getBaseLevel(); + + if (this.hasLevel(baseLevel)) { + /** @type {tcuTexture.PixelBufferAccess} */ var level0 = this.getLevel(baseLevel); + /** @type {boolean} */ var mipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + + if (mipmap) { + /** @type {tcuTexture.TextureFormat} */ var format = level0.getFormat(); + /** @type {number} */ var w = level0.getWidth(); + /** @type {number} */ var h = level0.getHeight(); + /** @type {number} */ var numLayers = level0.getDepth(); + /** @type {number} */ var numLevels = Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels2D(w, h)); + + for (var levelNdx = 1; levelNdx < numLevels; levelNdx++) { + if (this.hasLevel(baseLevel + levelNdx)) { + /** @type {tcuTexture.PixelBufferAccess} */ var level = this.getLevel(baseLevel + levelNdx); + /** @type {number} */ var expectedW = sglrReferenceContext.getMipLevelSize(w, levelNdx); + /** @type {number} */ var expectedH = sglrReferenceContext.getMipLevelSize(h, levelNdx); + + if (level.getWidth() != expectedW || + level.getHeight() != expectedH || + level.getDepth() != numLayers || + !level.getFormat().isEqual(format)) + return false; + } else + return false; + } + } + + return true; + } else + return false; + }; + + /** + */ + sglrReferenceContext.Texture2DArray.prototype.updateView = function() { + /** @type {number} */ var baseLevel = this.getBaseLevel(); + + if (this.hasLevel(baseLevel) && !this.getLevel(baseLevel).isEmpty()) { + // Update number of levels in mipmap pyramid. + /** @type {number} */ var width = this.getLevel(baseLevel).getWidth(); + /** @type {number} */ var height = this.getLevel(baseLevel).getHeight(); + /** @type {boolean} */ var isMipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + /** @type {number} */ var numLevels = isMipmap ? Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels2D(width, height)) : 1; + + this.m_view = new tcuTexture.Texture2DArrayView(numLevels, this.m_levels.getLevels().slice(baseLevel)); + } else + this.m_view = new tcuTexture.Texture2DArrayView(0, null); + }; + + /** + * @param {Array<number>} pos + * @param {number=} lod + * @return {Array<number>} + */ + sglrReferenceContext.Texture2DArray.prototype.sample = function(pos, lod) { + return this.m_view.sample(this.getSampler(), pos, lod); + }; + + /** + * @constructor + * @extends {sglrReferenceContext.Texture} + */ + sglrReferenceContext.Texture3D = function() { + sglrReferenceContext.Texture.call(this, sglrReferenceContext.TextureType.TYPE_2D_ARRAY); + /** @type {tcuTexture.Texture3DView} */ this.m_view = new tcuTexture.Texture3DView(0, null); + /** @type {sglrReferenceContext.TextureLevelArray} */ this.m_levels = new sglrReferenceContext.TextureLevelArray(); + }; + + /** + */ + sglrReferenceContext.Texture3D.prototype = Object.create(sglrReferenceContext.Texture.prototype); + sglrReferenceContext.Texture3D.prototype.constructor = sglrReferenceContext.Texture3D; + + sglrReferenceContext.Texture3D.prototype.clearLevels = function() { this.m_levels.clear(); }; + + /** + * @param {number} level + * @return {boolean} + */ + sglrReferenceContext.Texture3D.prototype.hasLevel = function(level) { return this.m_levels.hasLevel(level); }; + + /** + * @param {number} level + * @return {tcuTexture.PixelBufferAccess} + */ + sglrReferenceContext.Texture3D.prototype.getLevel = function(level) { return this.m_levels.getLevel(level); }; + + /** + * @param {number} level + * @param {?tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + * @param {number} depth + */ + sglrReferenceContext.Texture3D.prototype.allocLevel = function(level, format, width, height, depth) { + this.m_levels.allocLevel(level, format, width, height, depth); + }; + + /** + * @return {boolean} + */ + sglrReferenceContext.Texture3D.prototype.isComplete = function() { + /** @type {number} */ var baseLevel = this.getBaseLevel(); + + if (this.hasLevel(baseLevel)) { + /** @type {tcuTexture.PixelBufferAccess} */ var level0 = this.getLevel(baseLevel); + /** @type {boolean} */ var mipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + + if (mipmap) { + /** @type {tcuTexture.TextureFormat} */ var format = level0.getFormat(); + /** @type {number} */ var w = level0.getWidth(); + /** @type {number} */ var h = level0.getHeight(); + /** @type {number} */ var d = level0.getDepth(); + /** @type {number} */ var numLevels = Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels3D(w, h, d)); + + for (var levelNdx = 1; levelNdx < numLevels; levelNdx++) { + if (this.hasLevel(baseLevel + levelNdx)) { + /** @type {tcuTexture.PixelBufferAccess} */ var level = this.getLevel(baseLevel + levelNdx); + /** @type {number} */ var expectedW = sglrReferenceContext.getMipLevelSize(w, levelNdx); + /** @type {number} */ var expectedH = sglrReferenceContext.getMipLevelSize(h, levelNdx); + /** @type {number} */ var expectedD = sglrReferenceContext.getMipLevelSize(d, levelNdx); + + if (level.getWidth() != expectedW || + level.getHeight() != expectedH || + level.getDepth() != expectedD || + !level.getFormat().isEqual(format)) + return false; + } else + return false; + } + } + + return true; + } else + return false; + }; + + /** + */ + sglrReferenceContext.Texture3D.prototype.updateView = function() { + /** @type {number} */ var baseLevel = this.getBaseLevel(); + + if (this.hasLevel(baseLevel) && !this.getLevel(baseLevel).isEmpty()) { + // Update number of levels in mipmap pyramid. + /** @type {number} */ var width = this.getLevel(baseLevel).getWidth(); + /** @type {number} */ var height = this.getLevel(baseLevel).getHeight(); + /** @type {boolean} */ var isMipmap = sglrReferenceContext.isMipmapFilter(this.getSampler().minFilter); + /** @type {number} */ var numLevels = isMipmap ? Math.min(this.getMaxLevel() - baseLevel + 1, sglrReferenceContext.getNumMipLevels2D(width, height)) : 1; + + this.m_view = new tcuTexture.Texture3DView(numLevels, this.m_levels.getLevels().slice(baseLevel)); + } else + this.m_view = new tcuTexture.Texture3DView(0, null); + }; + + /** + * @param {Array<number>} pos + * @param {number=} lod + * @return {Array<number>} + */ + sglrReferenceContext.Texture3D.prototype.sample = function(pos, lod) { return this.m_view.sample(this.getSampler(), pos, lod) }; + + /** + * A container object for storing one of texture types; + * @constructor + */ + sglrReferenceContext.TextureContainer = function() { + /** @type {sglrReferenceContext.Texture2D | sglrReferenceContext.TextureCube|sglrReferenceContext.Texture2DArray|sglrReferenceContext.Texture3D} */ + this.texture = null; + /** @type {?sglrReferenceContext.TextureType} */ this.textureType = null; + }; + + /** + * @return {?sglrReferenceContext.TextureType} + */ + sglrReferenceContext.TextureContainer.prototype.getType = function() { return this.textureType; }; + + /** + * @param {number} target + * @throws {Error} + */ + sglrReferenceContext.TextureContainer.prototype.init = function(target) { + switch (target) { + case gl.TEXTURE_2D: + this.texture = new sglrReferenceContext.Texture2D(); + this.textureType = sglrReferenceContext.TextureType.TYPE_2D; + break; + case gl.TEXTURE_CUBE_MAP: + this.texture = new sglrReferenceContext.TextureCube(); + this.textureType = sglrReferenceContext.TextureType.TYPE_CUBE_MAP; + break; + case gl.TEXTURE_2D_ARRAY: + this.texture = new sglrReferenceContext.Texture2DArray(); + this.textureType = sglrReferenceContext.TextureType.TYPE_2D_ARRAY; + break; + case gl.TEXTURE_3D: + this.texture = new sglrReferenceContext.Texture3D(); + this.textureType = sglrReferenceContext.TextureType.TYPE_3D; + break; + /* TODO: Implement other types */ + // case gl.TEXTURE_CUBE_MAP_ARRAY: + // this.textureType = sglrReferenceContext.TextureType.TYPE_CUBE_MAP_ARRAY; + // break; + default: throw new Error('Unrecognized target: ' + target); + } + }; + + /** + * @enum + */ + sglrReferenceContext.AttachmentPoint = { + ATTACHMENTPOINT_COLOR0: 0, + ATTACHMENTPOINT_DEPTH: 1, + ATTACHMENTPOINT_STENCIL: 2 + }; + + /** + * @param {number} attachment + * @return {sglrReferenceContext.AttachmentPoint} + * @throws {Error} + */ + sglrReferenceContext.mapGLAttachmentPoint = function(attachment) { + switch (attachment) { + case gl.COLOR_ATTACHMENT0: return sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_COLOR0; + case gl.DEPTH_ATTACHMENT: return sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_DEPTH; + case gl.STENCIL_ATTACHMENT: return sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_STENCIL; + default: throw new Error('Wrong attachment point:' + attachment); + } + }; + + /** + * @enum + */ + sglrReferenceContext.AttachmentType = { + ATTACHMENTTYPE_RENDERBUFFER: 0, + ATTACHMENTTYPE_TEXTURE: 1 + }; + + /** + * @enum + */ + sglrReferenceContext.TexTarget = { + TEXTARGET_2D: 0, + TEXTARGET_CUBE_MAP_POSITIVE_X: 1, + TEXTARGET_CUBE_MAP_POSITIVE_Y: 2, + TEXTARGET_CUBE_MAP_POSITIVE_Z: 3, + TEXTARGET_CUBE_MAP_NEGATIVE_X: 4, + TEXTARGET_CUBE_MAP_NEGATIVE_Y: 5, + TEXTARGET_CUBE_MAP_NEGATIVE_Z: 6, + TEXTARGET_2D_ARRAY: 7, + TEXTARGET_3D: 8, + TEXTARGET_CUBE_MAP_ARRAY: 9 + }; + + /** + * @param {?sglrReferenceContext.TexTarget} target + * @return {tcuTexture.CubeFace} + */ + sglrReferenceContext.texTargetToFace = function(target) { + switch (target) { + case sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_X: return tcuTexture.CubeFace.CUBEFACE_NEGATIVE_X; + case sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_X: return tcuTexture.CubeFace.CUBEFACE_POSITIVE_X; + case sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_Y: return tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Y; + case sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_Y: return tcuTexture.CubeFace.CUBEFACE_POSITIVE_Y; + case sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_Z: return tcuTexture.CubeFace.CUBEFACE_NEGATIVE_Z; + case sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_Z: return tcuTexture.CubeFace.CUBEFACE_POSITIVE_Z; + default: throw new Error('Invalid target ' + target); + } + }; + + /** + * @param {sglrReferenceContext.TexTarget} target + * @return {sglrReferenceContext.TexTarget} + * @throws {Error} + */ + sglrReferenceContext.mapGLFboTexTarget = function(target) { + switch (target) { + case gl.TEXTURE_2D: return sglrReferenceContext.TexTarget.TEXTARGET_2D; + case gl.TEXTURE_CUBE_MAP_POSITIVE_X: return sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_X; + case gl.TEXTURE_CUBE_MAP_POSITIVE_Y: return sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_Y; + case gl.TEXTURE_CUBE_MAP_POSITIVE_Z: return sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_Z; + case gl.TEXTURE_CUBE_MAP_NEGATIVE_X: return sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_X; + case gl.TEXTURE_CUBE_MAP_NEGATIVE_Y: return sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_Y; + case gl.TEXTURE_CUBE_MAP_NEGATIVE_Z: return sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_Z; + default: throw new Error('Wrong texture target:' + target); + } + }; + + /** + * @constructor + */ + sglrReferenceContext.Attachment = function() { + /** @type {?sglrReferenceContext.AttachmentType} */ this.type = null; + /** @type {sglrReferenceContext.TextureContainer|sglrReferenceContext.Renderbuffer} */ this.object = null; // TODO: fix reserved word + /** @type {?sglrReferenceContext.TexTarget} */ this.texTarget = null; + /** @type {number} */ this.level = 0; + /** @type {number} */ this.layer = 0; + }; + + /** + * @constructor + */ + sglrReferenceContext.Framebuffer = function() { + /** @type {Array<sglrReferenceContext.Attachment>} */ this.m_attachments = []; + for (var key in sglrReferenceContext.AttachmentPoint) + this.m_attachments[sglrReferenceContext.AttachmentPoint[key]] = new sglrReferenceContext.Attachment(); + }; + + /** + * @param {sglrReferenceContext.AttachmentPoint} point + * @return {sglrReferenceContext.Attachment} + */ + sglrReferenceContext.Framebuffer.prototype.getAttachment = function(point) { return this.m_attachments[point]; }; + + /** + * @param {sglrReferenceContext.AttachmentPoint} point + * @param {sglrReferenceContext.Attachment} attachment + */ + sglrReferenceContext.Framebuffer.prototype.setAttachment = function(point, attachment) { this.m_attachments[point] = attachment; }; + + // /** + // * @enum + // */ + // var Format = { + // FORMAT_DEPTH_COMPONENT16: 0, + // FORMAT_RGBA4: 1, + // FORMAT_RGB5_A1: 2, + // FORMAT_RGB565: 3, + // FORMAT_STENCIL_INDEX8: 4 + // }; + + /** + * @constructor + */ + sglrReferenceContext.Renderbuffer = function() { + /** @type {tcuTexture.TextureLevel} */ this.m_data; + }; + + /** + * @param {tcuTexture.TextureFormat} format + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.Renderbuffer.prototype.setStorage = function(format, width, height) { + this.m_data = new tcuTexture.TextureLevel(format, width, height); + }; + + /** + * @return {number} + */ + sglrReferenceContext.Renderbuffer.prototype.getWidth = function() { return this.m_data.getWidth(); }; + + /** + * @return {number} + */ + sglrReferenceContext.Renderbuffer.prototype.getHeight = function() { return this.m_data.getHeight(); }; + + /** + * @return {?tcuTexture.TextureFormat} + */ + sglrReferenceContext.Renderbuffer.prototype.getFormat = function() { return this.m_data.getFormat(); }; + + /** + * @return {tcuTexture.PixelBufferAccess} + */ + sglrReferenceContext.Renderbuffer.prototype.getAccess = function() { return this.m_data.getAccess(); }; + + /** + * @constructor + * @param {number} maxVertexAttribs + */ + sglrReferenceContext.VertexArray = function(maxVertexAttribs) { + /** @type {sglrReferenceContext.DataBuffer} */ this.m_elementArrayBufferBinding = null; + + /** @type {Array<sglrReferenceContext.VertexArray.VertexAttribArray>} */this.m_arrays = []; + for (var i = 0; i < maxVertexAttribs; i++) + this.m_arrays.push(new sglrReferenceContext.VertexArray.VertexAttribArray()); + }; + + /** @constructor */ + sglrReferenceContext.VertexArray.VertexAttribArray = function() { + this.enabled = false; + this.size = 4; + this.stride = 0; + this.type = gl.FLOAT; + + this.normalized = false; + this.integer = false; + this.divisor = 0; + this.offset = 0; + this.bufferBinding = null; + }; + + /** + * @constructor + */ + sglrReferenceContext.DataBuffer = function() { + /** @type {?ArrayBuffer} */ this.m_data = null; + }; + + /** + * @param {number} size + */ + sglrReferenceContext.DataBuffer.prototype.setStorage = function(size) {this.m_data = new ArrayBuffer(size); }; + + /** + * @return {number} + */ + sglrReferenceContext.DataBuffer.prototype.getSize = function() { + /** @type {number} */ var size = 0; + if (this.m_data) + size = this.m_data.byteLength; + return size; + }; + + /** + * @return {?ArrayBuffer} + */ + sglrReferenceContext.DataBuffer.prototype.getData = function() { return this.m_data; }; + + /** + * @param {ArrayBuffer|goog.NumberArray} data + */ + sglrReferenceContext.DataBuffer.prototype.setData = function(data) { + /** @type {ArrayBuffer} */ var buffer; + /** @type {number} */ var offset = 0; + /** @type {number} */ var byteLength = data.byteLength; + if (data instanceof ArrayBuffer) + buffer = data; + else { + buffer = data.buffer; + offset = data.byteOffset; + } + + if (!buffer) + throw new Error('Invalid buffer'); + + this.m_data = buffer.slice(offset, offset + byteLength); + }; + + /** + * @param {number} offset + * @param {goog.NumberArray} data + */ + sglrReferenceContext.DataBuffer.prototype.setSubData = function(offset, data) { + /** @type {ArrayBuffer} */ var buffer; + /** @type {number} */ var srcOffset = 0; + /** @type {number} */ var byteLength = data.byteLength; + if (data instanceof ArrayBuffer) + buffer = data; + else { + buffer = data.buffer; + srcOffset = data.byteOffset; + } + + if (!buffer) + throw new Error('Invalid buffer'); + + /** @type {goog.NumberArray} */ var src = new Uint8Array(buffer, srcOffset, byteLength); + /** @type {goog.NumberArray} */ var dst = new Uint8Array(this.m_data, offset, byteLength); + dst.set(src); + }; + + // /** + // * @constructor + // */ + // var ObjectManager = function() { + // this.m_objects = {}; + // }; + + // ObjectManager.prototype.insert = function(obj) { + // var name = obj.getName(); + // if (!name) + // throw new Error("Cannot insert unnamed object"); + // this.m_objects[name] = obj; + // }; + + // ObjectManager.prototype.find = function(name) { return this.m_objects[name]; }; + + // ObjectManager.prototype.acquireReference = function(obj) { + // if (this.find(obj.getName()) !== obj) + // throw new Error("Object is not in the object manager"); + // obj.incRefCount(); + // }; + + // ObjectManager.prototype.releaseReference = function(obj) { + // if (this.find(obj.getName()) !== obj) + // throw new Error("Object is not in the object manager"); + + // obj.decRefCount(); + + // if (obj.getRefCount() == 0) + // delete this.m_objects[obj.getName()]; + // }; + + // ObjectManager.prototype.getAll = function() { return this.m_objects; }; + + /** + * @constructor + */ + sglrReferenceContext.TextureUnit = function() { + /** @type {?sglrReferenceContext.TextureContainer} */ this.tex2DBinding = null; + /** @type {?sglrReferenceContext.TextureContainer} */ this.texCubeBinding = null; + /** @type {?sglrReferenceContext.TextureContainer} */ this.tex2DArrayBinding = null; + /** @type {?sglrReferenceContext.TextureContainer} */ this.tex3DBinding = null; + /** @type {?sglrReferenceContext.TextureContainer} */ this.texCubeArrayBinding = null; + }; + + /** + * @constructor + */ + sglrReferenceContext.StencilState = function() { + /** @type {number} */ this.func = gl.ALWAYS; + /** @type {number} */ this.ref = 0; + /** @type {number} */ this.opMask = ~0; + /** @type {number} */ this.opStencilFail = gl.KEEP; + /** @type {number} */ this.opDepthFail = gl.KEEP; + /** @type {number} */ this.opDepthPass = gl.KEEP; + /** @type {number} */ this.writeMask = ~0; + }; + + /** + * @param {tcuPixelFormat.PixelFormat} pixelFmt + * @return {tcuTexture.TextureFormat} + * @throws {Error} + */ + sglrReferenceContext.toTextureFormat = function(pixelFmt) { + if (pixelFmt.equals(8, 8, 8, 8)) + return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8); + else if (pixelFmt.equals(8, 8, 8, 0)) + return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT8); + else if (pixelFmt.equals(4, 4, 4, 4)) + return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_SHORT_4444); + else if (pixelFmt.equals(5, 5, 5, 1)) + return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_SHORT_5551); + else if (pixelFmt.equals(5, 6, 5, 0)) + return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_SHORT_565); + + throw new Error('Could not map pixel format:' + pixelFmt); + }; + + /** + * @param {number} depthBits + * @return {tcuTexture.TextureFormat} + * @throws {Error} + */ + sglrReferenceContext.getDepthFormat = function(depthBits) { + switch (depthBits) { + case 8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNORM_INT8); + case 16: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNORM_INT16); + case 24: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.UNSIGNED_INT_24_8); + case 32: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.D, tcuTexture.ChannelType.FLOAT); + default: + throw new Error("Can't map depth buffer format, bits: " + depthBits); + } + }; + + /** + * @param {number} stencilBits + * @return {tcuTexture.TextureFormat} + * @throws {Error} + */ + sglrReferenceContext.getStencilFormat = function(stencilBits) { + switch (stencilBits) { + case 8: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.S, tcuTexture.ChannelType.UNSIGNED_INT8); + case 16: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.S, tcuTexture.ChannelType.UNSIGNED_INT16); + case 24: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.S, tcuTexture.ChannelType.UNSIGNED_INT_24_8); + case 32: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.S, tcuTexture.ChannelType.UNSIGNED_INT32); + default: + throw new Error("Can't map stencil buffer format, bits: " + stencilBits); + } + }; + + /** + * @constructor + * @param {tcuPixelFormat.PixelFormat} colorBits + * @param {number} depthBits + * @param {number} stencilBits + * @param {number} width + * @param {number} height + * @param {number=} samples_ + */ + sglrReferenceContext.ReferenceContextBuffers = function(colorBits, depthBits, stencilBits, width, height, samples_) { + if (samples_ === undefined) + samples_ = 1; + + /** @type {number} */ var samples = samples_; + /** @type {tcuTexture.TextureLevel} */ this.m_colorbuffer = new tcuTexture.TextureLevel(sglrReferenceContext.toTextureFormat(colorBits), samples, width, height); + + if (depthBits > 0) + /** @type {tcuTexture.TextureLevel} */ this.m_depthbuffer = new tcuTexture.TextureLevel(sglrReferenceContext.getDepthFormat(depthBits), samples, width, height); + + if (stencilBits > 0) + /** @type {tcuTexture.TextureLevel} */ this.m_stencilbuffer = new tcuTexture.TextureLevel(sglrReferenceContext.getStencilFormat(stencilBits), samples, width, height); + }; + + /** + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContextBuffers.prototype.getColorbuffer = function() { return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromMultisampleAccess(this.m_colorbuffer.getAccess()); }; + + /** + * @return {?rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContextBuffers.prototype.getDepthbuffer = function() { return this.m_depthbuffer !== undefined ? rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromMultisampleAccess(this.m_depthbuffer.getAccess()) : null; }; + + /** + * @return {?rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContextBuffers.prototype.getStencilbuffer = function() { return this.m_stencilbuffer !== undefined ? rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromMultisampleAccess(this.m_stencilbuffer.getAccess()) : null; }; + + /** + * @param {sglrReferenceContext.ReferenceContextLimits} limits + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} colorbuffer + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} depthbuffer + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} stencilbuffer + * @constructor + */ + sglrReferenceContext.ReferenceContext = function(limits, colorbuffer, depthbuffer, stencilbuffer) { + /** @type {sglrReferenceContext.ReferenceContextLimits} */ this.m_limits = limits; + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ this.m_defaultColorbuffer = colorbuffer; + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ this.m_defaultDepthbuffer = depthbuffer; + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ this.m_defaultStencilbuffer = stencilbuffer; + /** @type {Array<number>} */ this.m_viewport = [0, 0, colorbuffer.raw().getHeight(), colorbuffer.raw().getDepth()]; + /** @type {Array<sglrReferenceContext.TextureUnit>} */ this.m_textureUnits = []; + for (var i = 0; i < this.m_limits.maxTextureImageUnits; i++) + this.m_textureUnits.push(new sglrReferenceContext.TextureUnit()); + /** @type {number} */ this.m_activeTexture = 0; + /** @type {number} */ this.m_lastError = gl.NO_ERROR; + // this.m_textures = new ObjectManager(); + /** @type {number} */ this.m_pixelUnpackRowLength = 0; + /** @type {number} */ this.m_pixelUnpackSkipRows = 0; + /** @type {number} */ this.m_pixelUnpackSkipPixels = 0; + /** @type {number} */ this.m_pixelUnpackImageHeight = 0; + /** @type {number} */ this.m_pixelUnpackSkipImages = 0; + /** @type {number} */ this.m_pixelUnpackAlignment = 4; + /** @type {number} */ this.m_pixelPackAlignment = 4; + /** @type {Array<number>} */ this.m_clearColor = [0, 0, 0, 0]; + /** @type {number} */ this.m_clearDepth = 1; + /** @type {number} */ this.m_clearStencil = 0; + /** @type {Array<number>} */ this.m_scissorBox = this.m_viewport; + /** @type {boolean} */ this.m_blendEnabled = false; + /** @type {boolean} */ this.m_scissorEnabled = false; + /** @type {boolean} */ this.m_depthTestEnabled = false; + /** @type {boolean} */ this.m_stencilTestEnabled = false; + /** @type {boolean} */ this.m_polygonOffsetFillEnabled = false; + /** @type {boolean} */ this.m_primitiveRestartFixedIndex = true; //always on + /** @type {boolean} */ this.m_primitiveRestartSettableIndex = true; //always on + /** @type {Array<sglrReferenceContext.StencilState>} */ this.m_stencil = []; + for (var type in rrDefs.FaceType) + this.m_stencil[rrDefs.FaceType[type]] = new sglrReferenceContext.StencilState(); + /** @type {number} */ this.m_depthFunc = gl.LESS; + /** @type {number} */ this.m_depthRangeNear = 0; + /** @type {number} */ this.m_depthRangeFar = 1; + /** @type {number} */ this.m_polygonOffsetFactor = 0; + /** @type {number} */ this.m_polygonOffsetUnits = 0; + /** @type {number} */ this.m_blendModeRGB = gl.FUNC_ADD; + /** @type {number} */ this.m_blendModeAlpha = gl.FUNC_ADD; + /** @type {number} */ this.m_blendFactorSrcRGB = gl.ONE; + /** @type {number} */ this.m_blendFactorDstRGB = gl.ZERO; + /** @type {number} */ this.m_blendFactorSrcAlpha = gl.ONE; + /** @type {number} */ this.m_blendFactorDstAlpha = gl.ZERO; + /** @type {Array<number>} */ this.m_blendColor = [0, 0, 0, 0]; + /** @type {boolean} */ this.m_sRGBUpdateEnabled = true; + /** @type {Array<boolean>} */ this.m_colorMask = [true, true, true, true]; + /** @type {boolean} */ this.m_depthMask = true; + /** @type {sglrReferenceContext.VertexArray} */ this.m_defaultVAO = new sglrReferenceContext.VertexArray(this.m_limits.maxVertexAttribs); + /** @type {sglrReferenceContext.VertexArray} */ this.m_vertexArrayBinding = this.m_defaultVAO; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_arrayBufferBinding = null; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_copyReadBufferBinding = null; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_copyWriteBufferBinding = null; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_drawIndirectBufferBinding = null; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_pixelPackBufferBinding = null; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_pixelUnpackBufferBinding = null; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_transformFeedbackBufferBinding = null; + /** @type {sglrReferenceContext.DataBuffer} */ this.m_uniformBufferBinding = null; + /** @type {sglrReferenceContext.Framebuffer} */ this.m_readFramebufferBinding = null; + /** @type {sglrReferenceContext.Framebuffer} */ this.m_drawFramebufferBinding = null; + /** @type {sglrReferenceContext.Renderbuffer} */ this.m_renderbufferBinding = null; + /** @type {sglrShaderProgram.ShaderProgram} */ this.m_currentProgram = null; + /** @type {Array<rrGenericVector.GenericVec4>} */ this.m_currentAttribs = []; + for (var i = 0; i < this.m_limits.maxVertexAttribs; i++) + this.m_currentAttribs.push(new rrGenericVector.GenericVec4()); + /** @type {number} */ this.m_lineWidth = 1; + + /** @type {sglrReferenceContext.TextureContainer} */ this.m_emptyTex2D = new sglrReferenceContext.TextureContainer(); + this.m_emptyTex2D.init(gl.TEXTURE_2D); + this.m_emptyTex2D.texture.getSampler().wrapS = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex2D.texture.getSampler().wrapT = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex2D.texture.getSampler().minFilter = tcuTexture.FilterMode.NEAREST; + this.m_emptyTex2D.texture.getSampler().magFilter = tcuTexture.FilterMode.NEAREST; + this.m_emptyTex2D.texture.allocLevel(0, new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), 1, 1); + this.m_emptyTex2D.texture.getLevel(0).setPixel([0, 0, 0, 1], 0, 0); + this.m_emptyTex2D.texture.updateView(); + + /** @type {sglrReferenceContext.TextureContainer} */ this.m_emptyTexCube = new sglrReferenceContext.TextureContainer(); + this.m_emptyTexCube.init(gl.TEXTURE_CUBE_MAP); + this.m_emptyTexCube.texture.getSampler().wrapS = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTexCube.texture.getSampler().wrapT = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTexCube.texture.getSampler().minFilter = tcuTexture.FilterMode.NEAREST; + this.m_emptyTexCube.texture.getSampler().magFilter = tcuTexture.FilterMode.NEAREST; + + for (var face in tcuTexture.CubeFace) { + this.m_emptyTexCube.texture.allocLevel(0, tcuTexture.CubeFace[face], + new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), 1, 1); + this.m_emptyTexCube.texture.getFace(0, tcuTexture.CubeFace[face]).setPixel([0, 0, 0, 1], 0, 0); + } + this.m_emptyTexCube.texture.updateView(); + + /** @type {sglrReferenceContext.TextureContainer} */ this.m_emptyTex2DArray = new sglrReferenceContext.TextureContainer(); + this.m_emptyTex2DArray.init(gl.TEXTURE_2D_ARRAY); + this.m_emptyTex2DArray.texture.getSampler().wrapS = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex2DArray.texture.getSampler().wrapT = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex2DArray.texture.getSampler().wrapR = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex2DArray.texture.getSampler().minFilter = tcuTexture.FilterMode.NEAREST; + this.m_emptyTex2DArray.texture.getSampler().magFilter = tcuTexture.FilterMode.NEAREST; + this.m_emptyTex2DArray.texture.allocLevel(0, new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), 1, 1); + this.m_emptyTex2DArray.texture.getLevel(0).setPixel([0, 0, 0, 1], 0, 0); + this.m_emptyTex2DArray.texture.updateView(); + + /** @type {sglrReferenceContext.TextureContainer} */ this.m_emptyTex3D = new sglrReferenceContext.TextureContainer(); + this.m_emptyTex3D.init(gl.TEXTURE_3D); + this.m_emptyTex3D.texture.getSampler().wrapS = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex3D.texture.getSampler().wrapT = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex3D.texture.getSampler().wrapR = tcuTexture.WrapMode.CLAMP_TO_EDGE; + this.m_emptyTex3D.texture.getSampler().minFilter = tcuTexture.FilterMode.NEAREST; + this.m_emptyTex3D.texture.getSampler().magFilter = tcuTexture.FilterMode.NEAREST; + this.m_emptyTex3D.texture.allocLevel(0, new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), 1, 1); + this.m_emptyTex3D.texture.getLevel(0).setPixel([0, 0, 0, 1], 0, 0); + this.m_emptyTex3D.texture.updateView(); + + /** @type {sglrReferenceContext.TextureType} */ this.m_type; + + /** @type {boolean} */ this.m_immutable; + + /** @type {tcuTexture.Sampler} */ this.m_sampler; + /** @type {number} */ this.m_baseLevel; + /** @type {number} */ this.m_maxLevel; + }; + + /** + * @return {number} + */ + sglrReferenceContext.ReferenceContext.prototype.getWidth = function() { return this.m_defaultColorbuffer.raw().getHeight(); }; + + /** + * @return {number} + */ + sglrReferenceContext.ReferenceContext.prototype.getHeight = function() { return this.m_defaultColorbuffer.raw().getDepth(); }; + + /** + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.ReferenceContext.prototype.viewport = function(x, y, width, height) { this.m_viewport = [x, y, width, height]; }; + + /** + * @param {number} texture + */ + sglrReferenceContext.ReferenceContext.prototype.activeTexture = function(texture) { + if (deMath.deInBounds32(texture, gl.TEXTURE0, gl.TEXTURE0 + this.m_textureUnits.length)) + this.m_activeTexture = texture - gl.TEXTURE0; + else + this.setError(gl.INVALID_ENUM); + }; + + /** + * @param {number} error + */ + sglrReferenceContext.ReferenceContext.prototype.setError = function(error) { + if (this.m_lastError == gl.NO_ERROR) + this.m_lastError = error; + }; + + /** + * @return {number} error + */ + sglrReferenceContext.ReferenceContext.prototype.getError = function() { + /** @type {number} */ var err = this.m_lastError; + this.m_lastError = gl.NO_ERROR; + return err; + }; + + /** + * @param {boolean} condition + * @param {number} error + */ + sglrReferenceContext.ReferenceContext.prototype.conditionalSetError = function(condition, error) { + if (condition) + this.setError(error); + return condition; + }; + + /** + * @param {number} target + * @param {sglrReferenceContext.TextureContainer} texture + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.bindTexture = function(target, texture) { + /** @type {number} */ var unitNdx = this.m_activeTexture; + + if (this.conditionalSetError((target != gl.TEXTURE_2D && + target != gl.TEXTURE_CUBE_MAP && + target != gl.TEXTURE_2D_ARRAY && + target != gl.TEXTURE_3D), // && + // target != gl.TEXTURE_CUBE_MAP_ARRAY), + gl.INVALID_ENUM)) + return; + + if (!texture) { + // Clear binding. + switch (target) { + case gl.TEXTURE_2D: this.setTex2DBinding(unitNdx, null); break; + case gl.TEXTURE_CUBE_MAP: this.setTexCubeBinding(unitNdx, null); break; + case gl.TEXTURE_2D_ARRAY: this.setTex2DArrayBinding(unitNdx, null); break; + case gl.TEXTURE_3D: this.setTex3DBinding(unitNdx, null); break; + default: + throw new Error('Unrecognized target: ' + target); + } + } else { + if (texture.textureType == null) { + texture.init(target); + } else { + // Validate type. + /** @type {sglrReferenceContext.TextureType} */ var expectedType; + switch (target) { + case gl.TEXTURE_2D: expectedType = sglrReferenceContext.TextureType.TYPE_2D; break; + case gl.TEXTURE_CUBE_MAP: expectedType = sglrReferenceContext.TextureType.TYPE_CUBE_MAP; break; + case gl.TEXTURE_2D_ARRAY: expectedType = sglrReferenceContext.TextureType.TYPE_2D_ARRAY; break; + case gl.TEXTURE_3D: expectedType = sglrReferenceContext.TextureType.TYPE_3D; break; + default: throw new Error('Unrecognized target: ' + target); + } + if (this.conditionalSetError((texture.textureType != expectedType), gl.INVALID_OPERATION)) + return; + } + switch (target) { + case gl.TEXTURE_2D: this.setTex2DBinding(unitNdx, texture); break; + case gl.TEXTURE_CUBE_MAP: this.setTexCubeBinding(unitNdx, texture); break; + case gl.TEXTURE_2D_ARRAY: this.setTex2DArrayBinding(unitNdx, texture); break; + case gl.TEXTURE_3D: this.setTex3DBinding(unitNdx, texture); break; + default: + throw new Error('Unrecognized target: ' + target); + } + } + }; + + /** + * @param {number} unitNdx + * @param {?sglrReferenceContext.TextureContainer} texture + */ + sglrReferenceContext.ReferenceContext.prototype.setTexCubeBinding = function(unitNdx, texture) { + if (this.m_textureUnits[unitNdx].texCubeBinding) { + this.m_textureUnits[unitNdx].texCubeBinding = null; + } + + if (texture) { + this.m_textureUnits[unitNdx].texCubeBinding = texture; + } + }; + + /** + * @param {number} unitNdx + * @param {?sglrReferenceContext.TextureContainer} texture + */ + sglrReferenceContext.ReferenceContext.prototype.setTex2DBinding = function(unitNdx, texture) { + if (this.m_textureUnits[unitNdx].tex2DBinding) { + // this.m_textures.releaseReference(this.m_textureUnits[unitNdx].tex2DBinding); + this.m_textureUnits[unitNdx].tex2DBinding = null; + } + + if (texture) { + // this.m_textures.acquireReference(texture); + this.m_textureUnits[unitNdx].tex2DBinding = texture; + } + }; + + /** + * @param {number} unitNdx + * @param {?sglrReferenceContext.TextureContainer} texture + */ + sglrReferenceContext.ReferenceContext.prototype.setTex2DArrayBinding = function(unitNdx, texture) { + if (this.m_textureUnits[unitNdx].tex2DArrayBinding) { + // this.m_textures.releaseReference(this.m_textureUnits[unitNdx].tex2DArrayBinding); + this.m_textureUnits[unitNdx].tex2DArrayBinding = null; + } + + if (texture) { + // this.m_textures.acquireReference(texture); + this.m_textureUnits[unitNdx].tex2DArrayBinding = texture; + } + }; + + /** + * @param {number} unitNdx + * @param {?sglrReferenceContext.TextureContainer} texture + */ + sglrReferenceContext.ReferenceContext.prototype.setTex3DBinding = function(unitNdx, texture) { + if (this.m_textureUnits[unitNdx].tex3DBinding) { + // this.m_textures.releaseReference(this.m_textureUnits[unitNdx].tex3DBinding); + this.m_textureUnits[unitNdx].tex3DBinding = null; + } + + if (texture) { + // this.m_textures.acquireReference(texture); + this.m_textureUnits[unitNdx].tex3DBinding = texture; + } + }; + + /** + * @return {sglrReferenceContext.TextureContainer} + */ + sglrReferenceContext.ReferenceContext.prototype.createTexture = function() { return new sglrReferenceContext.TextureContainer(); }; + + /** + * @param {sglrReferenceContext.Texture} texture + */ + sglrReferenceContext.ReferenceContext.prototype.deleteTexture = function(texture) { /*empty*/ }; + + /** + * @param {number} target + * @param {framework.opengl.simplereference.sglrReferenceContext.Framebuffer} fbo + */ + sglrReferenceContext.ReferenceContext.prototype.bindFramebuffer = function(target, fbo) { + if (this.conditionalSetError((target != gl.FRAMEBUFFER && + target != gl.DRAW_FRAMEBUFFER && + target != gl.READ_FRAMEBUFFER), gl.INVALID_ENUM)) + return; + for (var ndx = 0; ndx < 2; ndx++) { + /** @type {number} */ var bindingTarget = ndx ? gl.DRAW_FRAMEBUFFER : gl.READ_FRAMEBUFFER; + + if (target != gl.FRAMEBUFFER && target != bindingTarget) + continue; // Doesn't match this target. + + if (ndx) + this.m_drawFramebufferBinding = fbo; + else + this.m_readFramebufferBinding = fbo; + } + }; + + /** + * @return {sglrReferenceContext.Framebuffer} + */ + sglrReferenceContext.ReferenceContext.prototype.createFramebuffer = function() { return new sglrReferenceContext.Framebuffer(); }; + + /** + * @param {sglrReferenceContext.Framebuffer} fbo + */ + sglrReferenceContext.ReferenceContext.prototype.deleteFramebuffer = function(fbo) { /*empty*/ }; + + /** + * @param {number} target + * @param {sglrReferenceContext.Renderbuffer} rbo + */ + sglrReferenceContext.ReferenceContext.prototype.bindRenderbuffer = function(target, rbo) { + if (this.conditionalSetError(target != gl.RENDERBUFFER, gl.INVALID_ENUM)) + return; + + this.m_renderbufferBinding = rbo; + }; + + /** + * @return {sglrReferenceContext.Renderbuffer} + */ + sglrReferenceContext.ReferenceContext.prototype.createRenderbuffer = function() { return new sglrReferenceContext.Renderbuffer(); }; + + /** + * @param {sglrReferenceContext.Renderbuffer} rbo + */ + sglrReferenceContext.ReferenceContext.prototype.deleteRenderbuffer = function(rbo) { /*empty*/ }; + + /** + * @param {number} pname + * @param {number} param + */ + sglrReferenceContext.ReferenceContext.prototype.pixelStorei = function(pname, param) { + switch (pname) { + case gl.UNPACK_ALIGNMENT: + if (this.conditionalSetError((param != 1 && param != 2 && param != 4 && param != 8), gl.INVALID_VALUE)) return; + this.m_pixelUnpackAlignment = param; + break; + + case gl.PACK_ALIGNMENT: + if (this.conditionalSetError((param != 1 && param != 2 && param != 4 && param != 8), gl.INVALID_VALUE)) return; + this.m_pixelPackAlignment = param; + break; + + case gl.UNPACK_ROW_LENGTH: + if (this.conditionalSetError(param < 0, gl.INVALID_VALUE)) return; + this.m_pixelUnpackRowLength = param; + break; + + case gl.UNPACK_SKIP_ROWS: + if (this.conditionalSetError(param < 0, gl.INVALID_VALUE)) return; + this.m_pixelUnpackSkipRows = param; + break; + + case gl.UNPACK_SKIP_PIXELS: + if (this.conditionalSetError(param < 0, gl.INVALID_VALUE)) return; + this.m_pixelUnpackSkipPixels = param; + break; + + case gl.UNPACK_IMAGE_HEIGHT: + if (this.conditionalSetError(param < 0, gl.INVALID_VALUE)) return; + this.m_pixelUnpackImageHeight = param; + break; + + case gl.UNPACK_SKIP_IMAGES: + if (this.conditionalSetError(param < 0, gl.INVALID_VALUE)) return; + this.m_pixelUnpackSkipImages = param; + break; + + default: + this.setError(gl.INVALID_ENUM); + } + }; + + /** + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + */ + sglrReferenceContext.ReferenceContext.prototype.clearColor = function(red, green, blue, alpha) { + this.m_clearColor = [deMath.clamp(red, 0, 1), + deMath.clamp(green, 0, 1), + deMath.clamp(blue, 0, 1), + deMath.clamp(alpha, 0, 1)]; + }; + + /** + * @param {number} depth + */ + sglrReferenceContext.ReferenceContext.prototype.clearDepthf = function(depth) { + this.m_clearDepth = deMath.clamp(depth, 0, 1); + }; + + /** + * @param {number} stencil + */ + sglrReferenceContext.ReferenceContext.prototype.clearStencil = function(stencil) { + this.m_clearStencil = stencil; + }; + + /** + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.ReferenceContext.prototype.scissor = function(x, y, width, height) { + if (this.conditionalSetError(width < 0 || height < 0, gl.INVALID_VALUE)) + return; + this.m_scissorBox = [x, y, width, height]; + }; + + /** + * @param {number} cap + */ + sglrReferenceContext.ReferenceContext.prototype.enable = function(cap) { + switch (cap) { + case gl.BLEND: this.m_blendEnabled = true; break; + case gl.SCISSOR_TEST: this.m_scissorEnabled = true; break; + case gl.DEPTH_TEST: this.m_depthTestEnabled = true; break; + case gl.STENCIL_TEST: this.m_stencilTestEnabled = true; break; + case gl.POLYGON_OFFSET_FILL: this.m_polygonOffsetFillEnabled = true; break; + + case gl.DITHER: + // Not implemented - just ignored. + break; + + default: + this.setError(gl.INVALID_ENUM); + break; + } + }; + + /** + * @param {number} cap + */ + sglrReferenceContext.ReferenceContext.prototype.disable = function(cap) { + switch (cap) { + case gl.BLEND: this.m_blendEnabled = false; break; + case gl.SCISSOR_TEST: this.m_scissorEnabled = false; break; + case gl.DEPTH_TEST: this.m_depthTestEnabled = false; break; + case gl.STENCIL_TEST: this.m_stencilTestEnabled = false; break; + case gl.POLYGON_OFFSET_FILL: this.m_polygonOffsetFillEnabled = false; break; + + case gl.DITHER: + // Not implemented - just ignored. + break; + + default: + this.setError(gl.INVALID_ENUM); + break; + } + }; + + /** + * @param {number} func + * @param {number} ref + * @param {number} mask + */ + sglrReferenceContext.ReferenceContext.prototype.stencilFunc = function(func, ref, mask) { + this.stencilFuncSeparate(gl.FRONT_AND_BACK, func, ref, mask); + }; + + /** + * @param {number} face + * @param {number} func + * @param {number} ref + * @param {number} mask + */ + sglrReferenceContext.ReferenceContext.prototype.stencilFuncSeparate = function(face, func, ref, mask) { + /** @type {boolean} */ var setFront = face == gl.FRONT || face == gl.FRONT_AND_BACK; + /** @type {boolean} */ var setBack = face == gl.BACK || face == gl.FRONT_AND_BACK; + + if (this.conditionalSetError(!sglrReferenceContext.isValidCompareFunc(func), gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(!setFront && !setBack, gl.INVALID_ENUM)) + return; + + for (var key in rrDefs.FaceType) { + /** @type {number} */ var type = rrDefs.FaceType[key]; + if ((type == rrDefs.FaceType.FACETYPE_FRONT && setFront) || + (type == rrDefs.FaceType.FACETYPE_BACK && setBack)) { + this.m_stencil[type].func = func; + this.m_stencil[type].ref = ref; + this.m_stencil[type].opMask = mask; + } + } + }; + + /** + * @param {number} func + * @return {boolean} + */ + sglrReferenceContext.isValidCompareFunc = function(func) { + switch (func) { + case gl.NEVER: + case gl.LESS: + case gl.LEQUAL: + case gl.GREATER: + case gl.GEQUAL: + case gl.EQUAL: + case gl.NOTEQUAL: + case gl.ALWAYS: + return true; + + default: + return false; + } + }; + + /** + * @param {number} op + * @return {boolean} + */ + sglrReferenceContext.isValidStencilOp = function(op) { + switch (op) { + case gl.KEEP: + case gl.ZERO: + case gl.REPLACE: + case gl.INCR: + case gl.INCR_WRAP: + case gl.DECR: + case gl.DECR_WRAP: + case gl.INVERT: + return true; + + default: + return false; + } + }; + + /** + * @param {number} sfail + * @param {number} dpfail + * @param {number} dppass + */ + sglrReferenceContext.ReferenceContext.prototype.stencilOp = function(sfail, dpfail, dppass) { + this.stencilOpSeparate(gl.FRONT_AND_BACK, sfail, dpfail, dppass); + }; + + /** + * @param {number} face + * @param {number} sfail + * @param {number} dpfail + * @param {number} dppass + */ + sglrReferenceContext.ReferenceContext.prototype.stencilOpSeparate = function(face, sfail, dpfail, dppass) { + /** @type {boolean} */ var setFront = face == gl.FRONT || face == gl.FRONT_AND_BACK; + /** @type {boolean} */ var setBack = face == gl.BACK || face == gl.FRONT_AND_BACK; + + if (this.conditionalSetError((!sglrReferenceContext.isValidStencilOp(sfail) || + !sglrReferenceContext.isValidStencilOp(dpfail) || + !sglrReferenceContext.isValidStencilOp(dppass)), + gl.INVALID_ENUM)) + return; + + if (this.conditionalSetError(!setFront && !setBack, gl.INVALID_ENUM)) + return; + + for (var key in rrDefs.FaceType) { + /** @type {number} */ var type = rrDefs.FaceType[key]; + if ((type == rrDefs.FaceType.FACETYPE_FRONT && setFront) || + (type == rrDefs.FaceType.FACETYPE_BACK && setBack)) { + this.m_stencil[type].opStencilFail = sfail; + this.m_stencil[type].opDepthFail = dpfail; + this.m_stencil[type].opDepthPass = dppass; + } + } + }; + + /** + * @param {number} func + */ + sglrReferenceContext.ReferenceContext.prototype.depthFunc = function(func) { + if (this.conditionalSetError(!sglrReferenceContext.isValidCompareFunc(func), gl.INVALID_ENUM)) + return; + this.m_depthFunc = func; + }; + + /** + * @param {number} n + * @param {number} f + */ + sglrReferenceContext.ReferenceContext.prototype.depthRange = function(n, f) { + this.m_depthRangeNear = deMath.clamp(n, 0, 1); + this.m_depthRangeFar = deMath.clamp(f, 0, 1); + }; + + /** + * @param {number} factor + * @param {number} units + */ + sglrReferenceContext.ReferenceContext.prototype.polygonOffset = function(factor, units) { + this.m_polygonOffsetFactor = factor; + this.m_polygonOffsetUnits = units; + }; + + /** + * @param {number} mode + * @return {boolean} + */ + sglrReferenceContext.isValidBlendEquation = function(mode) { + return mode == gl.FUNC_ADD || + mode == gl.FUNC_SUBTRACT || + mode == gl.FUNC_REVERSE_SUBTRACT || + mode == gl.MIN || + mode == gl.MAX; + }; + + /** + * @param {number} factor + * @return {boolean} + */ + sglrReferenceContext.isValidBlendFactor = function(factor) { + switch (factor) { + case gl.ZERO: + case gl.ONE: + case gl.SRC_COLOR: + case gl.ONE_MINUS_SRC_COLOR: + case gl.DST_COLOR: + case gl.ONE_MINUS_DST_COLOR: + case gl.SRC_ALPHA: + case gl.ONE_MINUS_SRC_ALPHA: + case gl.DST_ALPHA: + case gl.ONE_MINUS_DST_ALPHA: + case gl.CONSTANT_COLOR: + case gl.ONE_MINUS_CONSTANT_COLOR: + case gl.CONSTANT_ALPHA: + case gl.ONE_MINUS_CONSTANT_ALPHA: + case gl.SRC_ALPHA_SATURATE: + return true; + + default: + return false; + } + }; + + /** + * @param {number} mode + */ + sglrReferenceContext.ReferenceContext.prototype.blendEquation = function(mode) { + if (this.conditionalSetError(!sglrReferenceContext.isValidBlendEquation(mode), gl.INVALID_ENUM)) + return; + this.m_blendModeRGB = mode; + this.m_blendModeAlpha = mode; + }; + + /** + * @param {number} modeRGB + * @param {number} modeAlpha + */ + sglrReferenceContext.ReferenceContext.prototype.blendEquationSeparate = function(modeRGB, modeAlpha) { + if (this.conditionalSetError(!sglrReferenceContext.isValidBlendEquation(modeRGB) || + !sglrReferenceContext.isValidBlendEquation(modeAlpha), + gl.INVALID_ENUM)) + return; + + this.m_blendModeRGB = modeRGB; + this.m_blendModeAlpha = modeAlpha; + }; + + /** + * @param {number} src + * @param {number} dst + */ + sglrReferenceContext.ReferenceContext.prototype.blendFunc = function(src, dst) { + if (this.conditionalSetError(!sglrReferenceContext.isValidBlendFactor(src) || + !sglrReferenceContext.isValidBlendFactor(dst), + gl.INVALID_ENUM)) + return; + + this.m_blendFactorSrcRGB = src; + this.m_blendFactorSrcAlpha = src; + this.m_blendFactorDstRGB = dst; + this.m_blendFactorDstAlpha = dst; + }; + + /** + * @param {number} srcRGB + * @param {number} dstRGB + * @param {number} srcAlpha + * @param {number} dstAlpha + */ + sglrReferenceContext.ReferenceContext.prototype.blendFuncSeparate = function(srcRGB, dstRGB, srcAlpha, dstAlpha) { + if (this.conditionalSetError(!sglrReferenceContext.isValidBlendFactor(srcRGB) || + !sglrReferenceContext.isValidBlendFactor(dstRGB) || + !sglrReferenceContext.isValidBlendFactor(srcAlpha) || + !sglrReferenceContext.isValidBlendFactor(dstAlpha), + gl.INVALID_ENUM)) + return; + + this.m_blendFactorSrcRGB = srcRGB; + this.m_blendFactorSrcAlpha = srcAlpha; + this.m_blendFactorDstRGB = dstRGB; + this.m_blendFactorDstAlpha = dstAlpha; + }; + + /** + * @param {number} red + * @param {number} green + * @param {number} blue + * @param {number} alpha + */ + sglrReferenceContext.ReferenceContext.prototype.blendColor = function(red, green, blue, alpha) { + this.m_blendColor = [deMath.clamp(red, 0, 1), + deMath.clamp(green, 0, 1), + deMath.clamp(blue, 0, 1), + deMath.clamp(alpha, 0, 1)]; + }; + + /** + * @param {boolean} r + * @param {boolean} g + * @param {boolean} b + * @param {boolean} a + */ + sglrReferenceContext.ReferenceContext.prototype.colorMask = function(r, g, b, a) { + this.m_colorMask = [r, g, b, a]; + }; + + /** + * @param {boolean} mask + */ + sglrReferenceContext.ReferenceContext.prototype.depthMask = function(mask) { + this.m_depthMask = mask; + }; + + /** + * @param {number} mask + */ + sglrReferenceContext.ReferenceContext.prototype.stencilMask = function(mask) { + this.stencilMaskSeparate(gl.FRONT_AND_BACK, mask); + }; + + /** + * @param {number} face + * @param {number} mask + */ + sglrReferenceContext.ReferenceContext.prototype.stencilMaskSeparate = function(face, mask) { + /** @type {boolean} */ var setFront = face == gl.FRONT || face == gl.FRONT_AND_BACK; + /** @type {boolean} */ var setBack = face == gl.BACK || face == gl.FRONT_AND_BACK; + + if (this.conditionalSetError(!setFront && !setBack, gl.INVALID_ENUM)) + return; + + if (setFront) this.m_stencil[rrDefs.FaceType.FACETYPE_FRONT].writeMask = mask; + if (setBack) this.m_stencil[rrDefs.FaceType.FACETYPE_BACK].writeMask = mask; + }; + + /** + * @param {sglrReferenceContext.VertexArray} array + */ + sglrReferenceContext.ReferenceContext.prototype.bindVertexArray = function(array) { + if (array) + this.m_vertexArrayBinding = array; + else + this.m_vertexArrayBinding = this.m_defaultVAO; + }; + + /** + * @return {sglrReferenceContext.VertexArray} + */ + sglrReferenceContext.ReferenceContext.prototype.createVertexArray = function() { return new sglrReferenceContext.VertexArray(this.m_limits.maxVertexAttribs); }; + + /** + * @param {number} array + */ + sglrReferenceContext.ReferenceContext.prototype.deleteVertexArray = function(array) {}; + + /** + * @param {number} index + * @param {number} rawSize + * @param {number} type + * @param {boolean} normalized + * @param {number} stride + * @param {number} offset + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttribPointer = function(index, rawSize, type, normalized, stride, offset) { + /** @type {boolean} */ var allowBGRA = false; + /** @type {number} */ var effectiveSize = rawSize; + + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(effectiveSize <= 0 || effectiveSize > 4, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(type != gl.BYTE && type != gl.UNSIGNED_BYTE && + type != gl.SHORT && type != gl.UNSIGNED_SHORT && + type != gl.INT && type != gl.UNSIGNED_INT && + type != gl.FLOAT && type != gl.HALF_FLOAT && + type != gl.INT_2_10_10_10_REV && type != gl.UNSIGNED_INT_2_10_10_10_REV, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(normalized != true && normalized != false, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(stride < 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError((type == gl.INT_2_10_10_10_REV || type == gl.UNSIGNED_INT_2_10_10_10_REV) && effectiveSize != 4, gl.INVALID_OPERATION)) + return; + if (this.conditionalSetError(this.m_vertexArrayBinding != null && this.m_arrayBufferBinding == null && offset != 0, gl.INVALID_OPERATION)) + return; + + /** @type {?(sglrReferenceContext.VertexArray.VertexAttribArray)} */ var array_ = this.m_vertexArrayBinding.m_arrays[index]; // TODO: fix type + + array_.size = rawSize; + array_.stride = stride; + array_.type = type; + array_.normalized = normalized; + array_.integer = false; + array_.offset = offset; + + array_.bufferBinding = this.m_arrayBufferBinding; + }; + + /** + * @param {number} index + * @param {number} size + * @param {number} type + * @param {number} stride + * @param {number} offset + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttribIPointer = function(index, size, type, stride, offset) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(size <= 0 || size > 4, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(type != gl.BYTE && type != gl.UNSIGNED_BYTE && + type != gl.SHORT && type != gl.UNSIGNED_SHORT && + type != gl.INT && type != gl.UNSIGNED_INT, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(stride < 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(this.m_vertexArrayBinding != null && this.m_arrayBufferBinding == null && offset != 0, gl.INVALID_OPERATION)) + return; + + /** @type {?(sglrReferenceContext.VertexArray.VertexAttribArray)} */ var array_ = this.m_vertexArrayBinding.m_arrays[index]; // TODO: fix type + + array_.size = size; + array_.stride = stride; + array_.type = type; + array_.normalized = false; + array_.integer = true; + array_.offset = offset; + + array_.bufferBinding = this.m_arrayBufferBinding; + }; + + /** + * @param {number} index + */ + sglrReferenceContext.ReferenceContext.prototype.enableVertexAttribArray = function(index) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_vertexArrayBinding.m_arrays[index].enabled = true; + }; + + /** + * @param {number} index + */ + sglrReferenceContext.ReferenceContext.prototype.disableVertexAttribArray = function(index) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_vertexArrayBinding.m_arrays[index].enabled = false; + }; + + /** + * @param {number} index + * @param {number} divisor + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttribDivisor = function(index, divisor) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_vertexArrayBinding.m_arrays[index].divisor = divisor; + }; + + /** + * @param {number} index + * @param {number} x + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttrib1f = function(index, x) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_currentAttribs[index] = new rrGenericVector.GenericVec4(x, 0, 0, 1); + }; + + /** + * @param {number} index + * @param {number} x + * @param {number} y + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttrib2f = function(index, x, y) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_currentAttribs[index] = new rrGenericVector.GenericVec4(x, y, 0, 1); + }; + + /** + * @param {number} index + * @param {number} x + * @param {number} y + * @param {number} z + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttrib3f = function(index, x, y, z) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_currentAttribs[index] = new rrGenericVector.GenericVec4(x, y, z, 1); + }; + + /** + * @param {number} index + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttrib4f = function(index, x, y, z, w) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_currentAttribs[index] = new rrGenericVector.GenericVec4(x, y, z, w); + }; + + /** + * @param {number} index + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttribI4i = function(index, x, y, z, w) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_currentAttribs[index] = new rrGenericVector.GenericVec4(x, y, z, w); + }; + + /** + * @param {number} index + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + */ + sglrReferenceContext.ReferenceContext.prototype.vertexAttribI4ui = function(index, x, y, z, w) { + if (this.conditionalSetError(index >= this.m_limits.maxVertexAttribs, gl.INVALID_VALUE)) + return; + + this.m_currentAttribs[index] = new rrGenericVector.GenericVec4(x, y, z, w); + }; + + /** + * @param {sglrShaderProgram.ShaderProgram} program + * @param {string} name + * @return {number} + */ + sglrReferenceContext.ReferenceContext.prototype.getAttribLocation = function(program, name) { + if (this.conditionalSetError(!(program), gl.INVALID_OPERATION)) + return -1; + + for (var i = 0; i < program.m_attributeNames.length; i++) + if (program.m_attributeNames[i] === name) + return i; + + return -1; + }; + + /** + * @param {number} pname + */ + sglrReferenceContext.ReferenceContext.prototype.getParameter = function(pname) { + switch (pname) { + case (gl.VIEWPORT): return new Int32Array(this.m_viewport); + case (gl.SCISSOR_BOX): return new Int32Array(this.m_scissorBox); + default: + throw new Error('Unimplemented'); + } + }; + + /** + * @param {number} location + * @param {gluShaderUtil.DataType} type + * @param {Array<number>} value + */ + sglrReferenceContext.ReferenceContext.prototype.uniformValue = function(location, type, value) { + if (this.conditionalSetError(!this.m_currentProgram, gl.INVALID_OPERATION)) + return; + + if (location === null) + return; + + /** @type {sglrShaderProgram.Uniform} */ var uniform = this.m_currentProgram.m_uniforms[location]; + + if (this.conditionalSetError(!uniform, gl.INVALID_OPERATION)) + return; + + if (gluShaderUtil.isDataTypeSampler(uniform.type)) { + if (this.conditionalSetError(type != gluShaderUtil.DataType.INT, gl.INVALID_OPERATION)) + return; + } else if (this.conditionalSetError(uniform.type != type, gl.INVALID_OPERATION)) + return; + /* TODO: Do we need to copy objects? */ + uniform.value = value; + }; + + /** + * @param {number} location + * @param {number} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform1f = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT, [x]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform1fv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT, x); + }; + + /** + * @param {number} location + * @param {number} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform1i = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.INT, [x]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform1iv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.INT, x); + }; + + /** + * @param {number} location + * @param {number} x + * @param {number} y + */ + sglrReferenceContext.ReferenceContext.prototype.uniform2f = function(location, x, y) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_VEC2, [x, y]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform2fv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_VEC2, x); + }; + + /** + * @param {number} location + * @param {number} x + * @param {number} y + */ + sglrReferenceContext.ReferenceContext.prototype.uniform2i = function(location, x, y) { + return this.uniformValue(location, gluShaderUtil.DataType.INT_VEC2, [x, y]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform2iv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.INT_VEC2, x); + }; + + /** + * @param {number} location + * @param {number} x + * @param {number} y + * @param {number} z + */ + sglrReferenceContext.ReferenceContext.prototype.uniform3f = function(location, x, y, z) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_VEC3, [x, y, z]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform3fv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_VEC3, x); + }; + + /** + * @param {number} location + * @param {number} x + * @param {number} y + * @param {number} z + */ + sglrReferenceContext.ReferenceContext.prototype.uniform3i = function(location, x, y, z) { + return this.uniformValue(location, gluShaderUtil.DataType.INT_VEC3, [x, y, z]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform3iv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.INT_VEC3, x); + }; + + /** + * @param {number} location + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + */ + sglrReferenceContext.ReferenceContext.prototype.uniform4f = function(location, x, y, z, w) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_VEC4, [x, y, z, w]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform4fv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_VEC4, x); + }; + + /** + * @param {number} location + * @param {number} x + * @param {number} y + * @param {number} z + * @param {number} w + */ + sglrReferenceContext.ReferenceContext.prototype.uniform4i = function(location, x, y, z, w) { + return this.uniformValue(location, gluShaderUtil.DataType.INT_VEC4, [x, y, z, w]); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniform4iv = function(location, x) { + return this.uniformValue(location, gluShaderUtil.DataType.INT_VEC4, x); + }; + + /** + * @return {Array<string>} + */ + sglrReferenceContext.ReferenceContext.prototype.getSupportedExtensions = function() { + var extensions = gl.getSupportedExtensions(); //TODO: Let's just return gl's supported extensions for now + return extensions; + }; + + /** + * @param {string} name + * @return {*} + */ + sglrReferenceContext.ReferenceContext.prototype.getExtension = function(name) { + return gl.getExtension(name); //TODO: Let's just return gl's supported extensions for now + }; + + /** transpose matrix 'x' of 'size' columns and rows + * @param {number} size + * @param {Array<number>} x + * @return {Array<number>} + */ + sglrReferenceContext.trans = function(size, x) { + /** @type {Array<number>} */ var result = []; + for (var row = 0; row < size; ++row) + for (var col = 0; col < size; ++col) + result[row * size + col] = x[col * size + row]; + + return result; + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniformMatrix2fv = function(location, transpose, x) { + /* change from column-major to internal row-major if transpose if FALSE */ + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_MAT2, !transpose ? sglrReferenceContext.trans(2, x) : x); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniformMatrix3fv = function(location, transpose, x) { + /* change from column-major to internal row-major if transpose if FALSE */ + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_MAT3, !transpose ? sglrReferenceContext.trans(3, x) : x); + }; + + /** + * @param {number} location + * @param {Array<number>} x + */ + sglrReferenceContext.ReferenceContext.prototype.uniformMatrix4fv = function(location, transpose, x) { + /* change from column-major to internal row-major if transpose if FALSE */ + return this.uniformValue(location, gluShaderUtil.DataType.FLOAT_MAT4, !transpose ? sglrReferenceContext.trans(4, x) : x); + }; + + /** + * @param {sglrShaderProgram.ShaderProgram} program + * @param {string} name + * @return {number} + */ + sglrReferenceContext.ReferenceContext.prototype.getUniformLocation = function(program, name) { + if (this.conditionalSetError(!program, gl.INVALID_OPERATION)) + return -1; + + for (var i = 0; i < program.m_uniforms.length; i++) + if (program.m_uniforms[i].name === name) + return i; + + return -1; + }; + + /** + * @param {number} w + */ + sglrReferenceContext.ReferenceContext.prototype.lineWidth = function(w) { + if (this.conditionalSetError(w < 0, gl.INVALID_VALUE)) + return; + this.m_lineWidth = w; + }; + + /** + * @param {number} target + * @return {boolean} + */ + sglrReferenceContext.isValidBufferTarget = function(target) { + switch (target) { + case gl.ARRAY_BUFFER: + case gl.COPY_READ_BUFFER: + case gl.COPY_WRITE_BUFFER: + case gl.ELEMENT_ARRAY_BUFFER: + case gl.PIXEL_PACK_BUFFER: + case gl.PIXEL_UNPACK_BUFFER: + case gl.TRANSFORM_FEEDBACK_BUFFER: + case gl.UNIFORM_BUFFER: + return true; + + default: + return false; + } + }; + + /** + * @param {number} target + * @param {sglrReferenceContext.DataBuffer} buffer + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.setBufferBinding = function(target, buffer) { + switch (target) { + case gl.ARRAY_BUFFER: this.m_arrayBufferBinding = buffer; break; + case gl.COPY_READ_BUFFER: this.m_copyReadBufferBinding = buffer; break; + case gl.COPY_WRITE_BUFFER: this.m_copyWriteBufferBinding = buffer; break; + case gl.ELEMENT_ARRAY_BUFFER: this.m_vertexArrayBinding.m_elementArrayBufferBinding = buffer; break; + case gl.PIXEL_PACK_BUFFER: this.m_pixelPackBufferBinding = buffer; break; + case gl.PIXEL_UNPACK_BUFFER: this.m_pixelUnpackBufferBinding = buffer; break; + case gl.TRANSFORM_FEEDBACK_BUFFER: this.m_transformFeedbackBufferBinding = buffer; break; + case gl.UNIFORM_BUFFER: this.m_uniformBufferBinding = buffer; break; + default: + throw new Error('Unrecognized target: ' + target); + } + }; + + /** + * @param {number} target + * @return {sglrReferenceContext.DataBuffer} + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.getBufferBinding = function(target) { + switch (target) { + case gl.ARRAY_BUFFER: return this.m_arrayBufferBinding; + case gl.COPY_READ_BUFFER: return this.m_copyReadBufferBinding; + case gl.COPY_WRITE_BUFFER: return this.m_copyWriteBufferBinding; + case gl.ELEMENT_ARRAY_BUFFER: return this.m_vertexArrayBinding.m_elementArrayBufferBinding; + case gl.PIXEL_PACK_BUFFER: return this.m_pixelPackBufferBinding; + case gl.PIXEL_UNPACK_BUFFER: return this.m_pixelUnpackBufferBinding; + case gl.TRANSFORM_FEEDBACK_BUFFER: return this.m_transformFeedbackBufferBinding; + case gl.UNIFORM_BUFFER: return this.m_uniformBufferBinding; + default: + throw new Error('Unrecognized target: ' + target); + } + }; + + /** + * @param {number} target + * @param {sglrReferenceContext.DataBuffer} buffer + */ + sglrReferenceContext.ReferenceContext.prototype.bindBuffer = function(target, buffer) { + if (this.conditionalSetError(!sglrReferenceContext.isValidBufferTarget(target), gl.INVALID_ENUM)) + return; + + this.setBufferBinding(target, buffer); + }; + + /** + * @return {sglrReferenceContext.DataBuffer} + */ + sglrReferenceContext.ReferenceContext.prototype.createBuffer = function() { return new sglrReferenceContext.DataBuffer(); }; + + /** + * @param {number} buffer + */ + sglrReferenceContext.ReferenceContext.prototype.deleteBuffer = function(buffer) {}; + + /** + * @param {number} target + * @param {number|goog.NumberArray} input + * @param {number} usage + */ + sglrReferenceContext.ReferenceContext.prototype.bufferData = function(target, input, usage) { + if (this.conditionalSetError(!sglrReferenceContext.isValidBufferTarget(target), gl.INVALID_ENUM)) + return; + /** @type {sglrReferenceContext.DataBuffer} */ var buffer = this.getBufferBinding(target); + if (this.conditionalSetError(!buffer, gl.INVALID_OPERATION)) + return; + + if (typeof input == 'number') { + if (this.conditionalSetError(input < 0, gl.INVALID_VALUE)) + return; + buffer.setStorage(input); + } else { + buffer.setData(input); + } + }; + + /** + * @param {number} target + * @param {number} offset + * @param {goog.NumberArray} data + */ + sglrReferenceContext.ReferenceContext.prototype.bufferSubData = function(target, offset, data) { + if (this.conditionalSetError(!sglrReferenceContext.isValidBufferTarget(target), gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(offset < 0, gl.INVALID_VALUE)) + return; + /** @type {sglrReferenceContext.DataBuffer} */ var buffer = this.getBufferBinding(target); + if (this.conditionalSetError(!buffer, gl.INVALID_OPERATION)) + return; + + if (this.conditionalSetError(offset + data.byteLength > buffer.getSize(), gl.INVALID_VALUE)) + return; + buffer.setSubData(offset, data); + }; + + /** + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @param {number} format + * @param {number} type + * @param {goog.NumberArray} pixels + */ + sglrReferenceContext.ReferenceContext.prototype.readPixels = function(x, y, width, height, format, type, pixels) { + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var src = this.getReadColorbuffer(); + + // Map transfer format. + /** @type {tcuTexture.TextureFormat} */ var transferFmt = gluTextureUtil.mapGLTransferFormat(format, type); + + // Clamp input values + /** @type {number} */ var copyX = deMath.clamp(x, 0, src.raw().getHeight()); + /** @type {number} */ var copyY = deMath.clamp(y, 0, src.raw().getDepth()); + /** @type {number} */ var copyWidth = deMath.clamp(width, 0, src.raw().getHeight() - x); + /** @type {number} */ var copyHeight = deMath.clamp(height, 0, src.raw().getDepth() - y); + + /** @type {?ArrayBuffer} */ var data; + /** @type {number} */ var offset; + if (this.m_pixelPackBufferBinding) { + if (this.conditionalSetError(typeof pixels !== 'number', gl.INVALID_VALUE)) + return; + data = this.m_pixelPackBufferBinding.getData(); + offset = pixels.byteOffset; + } else { + if (pixels instanceof ArrayBuffer) { + data = pixels; + offset = 0; + } else { + data = pixels.buffer; + offset = pixels.byteOffset; + } + } + + /** @type {tcuTexture.PixelBufferAccess} */ + var dst = new tcuTexture.PixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + depth: 1, + rowPitch: deMath.deAlign32(width * transferFmt.getPixelSize(), this.m_pixelPackAlignment), + slicePitch: 0, + data: data, + offset: offset}); + + src = src.getSubregion([copyX, copyY, copyWidth, copyHeight]); + src.resolveMultisampleColorBuffer(tcuTextureUtil.getSubregion(dst, 0, 0, 0, copyWidth, copyHeight, 1)); + }; + + /** + * @return {number} + */ + sglrReferenceContext.ReferenceContext.prototype.getType = function() { + return this.m_type; + }; + + /** + * @return {tcuTexture.PixelBufferAccess} + */ + sglrReferenceContext.nullAccess = function() { + return new tcuTexture.PixelBufferAccess({ + width: 0, + height: 0}); + }; + + /** + * @param {sglrReferenceContext.Framebuffer} framebuffer + * @param {sglrReferenceContext.AttachmentPoint} point + * @return {tcuTexture.PixelBufferAccess} + */ + sglrReferenceContext.ReferenceContext.prototype.getFboAttachment = function(framebuffer, point) { + /** @type {sglrReferenceContext.Attachment} */ var attachment = framebuffer.getAttachment(point); + + switch (attachment.type) { + case sglrReferenceContext.AttachmentType.ATTACHMENTTYPE_TEXTURE: { + var container = /** @type {sglrReferenceContext.TextureContainer} */ (attachment.object); + /** @type {?sglrReferenceContext.TextureType} */ var type = container.getType(); + var texture = container.texture; + + if (type == sglrReferenceContext.TextureType.TYPE_2D) + return texture.getLevel(attachment.level); + else if (type == sglrReferenceContext.TextureType.TYPE_CUBE_MAP) + return texture.getFace(attachment.level, sglrReferenceContext.texTargetToFace(attachment.texTarget)); + else if (type == sglrReferenceContext.TextureType.TYPE_2D_ARRAY || + type == sglrReferenceContext.TextureType.TYPE_3D || + type == sglrReferenceContext.TextureType.TYPE_CUBE_MAP_ARRAY) { + /** @type {tcuTexture.PixelBufferAccess} */ var level = texture.getLevel(attachment.level); + + return new tcuTexture.PixelBufferAccess({ + format: level.getFormat(), + width: level.getWidth(), + height: level.getHeight(), + depth: 1, + rowPitch: level.getRowPitch(), + slicePitch: 0, + data: level.getBuffer(), + offset: level.getSlicePitch() * attachment.layer}); + } else + return sglrReferenceContext.nullAccess(); + } + + case sglrReferenceContext.AttachmentType.ATTACHMENTTYPE_RENDERBUFFER: { + var rbo = /** @type {sglrReferenceContext.Renderbuffer} */ (attachment.object); + return rbo.getAccess(); + } + + default: + return sglrReferenceContext.nullAccess(); + } + }; + + /** + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContext.prototype.getReadColorbuffer = function() { + if (this.m_readFramebufferBinding) + return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromSinglesampleAccess(this.getFboAttachment(this.m_readFramebufferBinding, sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_COLOR0)); + else + return this.m_defaultColorbuffer; + }; + + // sglrReferenceContext.ReferenceContext.prototype.drawArrays = function(mode, first, count) { + // this.drawArraysInstanced(mode, first, count, 1); + // }; + + /** + * @param {number} target + * @return {number} + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.checkFramebufferStatus = function(target) { + if (this.conditionalSetError(target != gl.FRAMEBUFFER && + target != gl.DRAW_FRAMEBUFFER && + target != gl.READ_FRAMEBUFFER, gl.INVALID_ENUM)) + return 0; + + // Select binding point. + /** @type {sglrReferenceContext.Framebuffer} */ var framebufferBinding = (target == gl.FRAMEBUFFER || target == gl.DRAW_FRAMEBUFFER) ? this.m_drawFramebufferBinding : this.m_readFramebufferBinding; + + // Default framebuffer is always complete. + if (!framebufferBinding) + return gl.FRAMEBUFFER_COMPLETE; + + /** @type {number} */ var width = -1; + /** @type {number} */ var height = -1; + /** @type {boolean} */ var hasAttachment = false; + /** @type {boolean} */ var attachmentComplete = true; + /** @type {boolean} */ var dimensionsOk = true; + + for (var key in sglrReferenceContext.AttachmentPoint) { + /** @type {sglrReferenceContext.AttachmentPoint} */ var point = sglrReferenceContext.AttachmentPoint[key]; + /** @type {sglrReferenceContext.Attachment} */ var attachment = framebufferBinding.getAttachment(point); + /** @type {number} */ var attachmentWidth = 0; + /** @type {number} */ var attachmentHeight = 0; + /** @type {tcuTexture.TextureFormat} */ var attachmentFormat; + + if (attachment.type == sglrReferenceContext.AttachmentType.ATTACHMENTTYPE_TEXTURE) { + var container = /** @type {sglrReferenceContext.TextureContainer} */ (attachment.object); + /** @type {tcuTexture.ConstPixelBufferAccess} */ var level; + + if (attachment.texTarget == sglrReferenceContext.TexTarget.TEXTARGET_2D) { + DE_ASSERT(container.textureType == sglrReferenceContext.TextureType.TYPE_2D); + /** @type {sglrReferenceContext.Texture2D} */ var tex2D = /** @type {sglrReferenceContext.Texture2D} */ (container.texture); + + if (tex2D.hasLevel(attachment.level)) + level = tex2D.getLevel(attachment.level); + // TODO: implement CUBE_MAP, 2D_ARRAY, 3D, CUBE_MAP_ARRAY + } else if (deMath.deInRange32(attachment.texTarget, sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_X, + sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_Z)) { + DE_ASSERT(container.textureType == sglrReferenceContext.TextureType.TYPE_CUBE_MAP); + + var texCube = /** @type {sglrReferenceContext.TextureCube} */ (container.texture); + var face = sglrReferenceContext.texTargetToFace(attachment.texTarget); + + if (texCube.hasFace(attachment.level, face)) + level = texCube.getFace(attachment.level, face); + } else if (attachment.texTarget == sglrReferenceContext.TexTarget.TEXTARGET_2D_ARRAY) { + DE_ASSERT(container.textureType == sglrReferenceContext.TextureType.TYPE_2D_ARRAY); + var tex2DArr = /** @type {sglrReferenceContext.Texture2DArray} */ (container.texture); + + if (tex2DArr.hasLevel(attachment.level)) + level = tex2DArr.getLevel(attachment.level); // \note Slice doesn't matter here. + } else if (attachment.texTarget == sglrReferenceContext.TexTarget.TEXTARGET_3D) { + DE_ASSERT(container.textureType == sglrReferenceContext.TextureType.TYPE_3D); + var tex3D = /** @type {sglrReferenceContext.Texture3D} */ (container.texture); + + if (tex3D.hasLevel(attachment.level)) + level = tex3D.getLevel(attachment.level); // \note Slice doesn't matter here. + // } else if (attachment.texTarget == sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_ARRAY) { + // DE_ASSERT(container.textureType == sglrReferenceContext.TextureType.TYPE_CUBE_MAP_ARRAY); + // var texCubeArr = container.texture; + // + // if (texCubeArr.hasLevel(attachment.level)) + // level = texCubeArr.getLevel(attachment.level); // \note Slice doesn't matter here. + } else + throw new Error('sglrReferenceContext.Framebuffer attached to a texture but no valid target specified.'); + + attachmentWidth = level.getWidth(); + attachmentHeight = level.getHeight(); + attachmentFormat = level.getFormat(); + } else if (attachment.type == sglrReferenceContext.AttachmentType.ATTACHMENTTYPE_RENDERBUFFER) { + var renderbuffer = attachment.object; + + attachmentWidth = renderbuffer.getWidth(); + attachmentHeight = renderbuffer.getHeight(); + attachmentFormat = renderbuffer.getFormat(); + } else + continue; // Skip rest of checks. + + if (!hasAttachment && attachmentWidth > 0 && attachmentHeight > 0) { + width = attachmentWidth; + height = attachmentHeight; + hasAttachment = true; + } else if (attachmentWidth != width || attachmentHeight != height) + dimensionsOk = false; + + // Validate attachment point compatibility. + switch (attachmentFormat.order) { + case tcuTexture.ChannelOrder.R: + case tcuTexture.ChannelOrder.RG: + case tcuTexture.ChannelOrder.RGB: + case tcuTexture.ChannelOrder.RGBA: + case tcuTexture.ChannelOrder.sRGB: + case tcuTexture.ChannelOrder.sRGBA: + if (point != sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_COLOR0) + attachmentComplete = false; + break; + + case tcuTexture.ChannelOrder.D: + if (point != sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_DEPTH) + attachmentComplete = false; + break; + + case tcuTexture.ChannelOrder.S: + if (point != sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_STENCIL) + attachmentComplete = false; + break; + + case tcuTexture.ChannelOrder.DS: + if (point != sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_DEPTH && + point != sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_STENCIL) + attachmentComplete = false; + break; + + default: + throw new Error('Unsupported attachment channel order:' + attachmentFormat.order); + } + } + + if (!attachmentComplete) + return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + else if (!hasAttachment) + return gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + else if (!dimensionsOk) + return gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + else + return gl.FRAMEBUFFER_COMPLETE; + }; + + /** + * @param {number} mode + * @return {boolean} + */ + sglrReferenceContext.ReferenceContext.prototype.predrawErrorChecks = function(mode) { + if (this.conditionalSetError(mode != gl.POINTS && + mode != gl.LINE_STRIP && mode != gl.LINE_LOOP && mode != gl.LINES && + mode != gl.TRIANGLE_STRIP && mode != gl.TRIANGLE_FAN && mode != gl.TRIANGLES, + gl.INVALID_ENUM)) + return false; + + // \todo [jarkko] Uncomment following code when the buffer mapping support is added + //for (size_t ndx = 0; ndx < vao.m_arrays.length; ++ndx) + // if (vao.m_arrays[ndx].enabled && vao.m_arrays[ndx].bufferBinding && vao.m_arrays[ndx].bufferBinding->isMapped) + // RC_ERROR_RET(gl.INVALID_OPERATION, RC_RET_VOID); + + if (this.conditionalSetError(this.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE, gl.INVALID_FRAMEBUFFER_OPERATION)) + return false; + + return true; + }; + + /** + * Draws quads from vertex arrays + * @param {number} mode GL primitive type to draw with. + * @param {number} first First vertex to begin drawing with + * @param {number} count How many vertices to draw (not counting vertices before first) + * @param {number} instanceCount + */ + sglrReferenceContext.ReferenceContext.prototype.drawArraysInstanced = function(mode, first, count, instanceCount) { + if (this.conditionalSetError(first < 0 || count < 0 || instanceCount < 0, gl.INVALID_VALUE)) + return; + + if (!this.predrawErrorChecks(mode)) + return; + + // All is ok + this.drawQuads(mode, first, count, instanceCount); + }; + + /** + * @param {number} mode GL primitive type to draw with. + * @param {number} start + * @param {number} end + * @param {number} count How many vertices to draw (not counting vertices before first) + * @param {number} type Data type + * @param {number} offset + */ + sglrReferenceContext.ReferenceContext.prototype.drawRangeElements = function(mode, start, end, count, type, offset) { + if (this.conditionalSetError(end < start, gl.INVALID_VALUE)) + return; + + this.drawElements(mode, count, type, offset); + }; + + /** + * @param {number} mode GL primitive type to draw with. + * @param {number} count How many vertices to draw (not counting vertices before first) + * @param {number} type Data type + * @param {number} offset + */ + sglrReferenceContext.ReferenceContext.prototype.drawElements = function(mode, count, type, offset) { + this.drawElementsInstanced(mode, count, type, offset, 1); + }; + + /** + * @param {number} mode GL primitive type to draw with. + * @param {number} count How many vertices to draw (not counting vertices before first) + * @param {number} type Data type + * @param {number} offset + * @param {number} instanceCount + */ + sglrReferenceContext.ReferenceContext.prototype.drawElementsInstanced = function(mode, count, type, offset, instanceCount) { + this.drawElementsInstancedBaseVertex(mode, count, type, offset, instanceCount, 0); + }; + + /** + * @param {number} mode GL primitive type to draw with. + * @param {number} count How many vertices to draw (not counting vertices before first) + * @param {number} type Data type + * @param {number} offset + * @param {number} instanceCount + * @param {number} baseVertex + */ + sglrReferenceContext.ReferenceContext.prototype.drawElementsInstancedBaseVertex = function(mode, count, type, offset, instanceCount, baseVertex) { + var vao = this.m_vertexArrayBinding; + + if (this.conditionalSetError(type != gl.UNSIGNED_BYTE && + type != gl.UNSIGNED_SHORT && + type != gl.UNSIGNED_INT, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(count < 0 || instanceCount < 0, gl.INVALID_VALUE)) + return; + + if (!this.predrawErrorChecks(mode)) + return; + + if (this.conditionalSetError(count > 0 && !vao.m_elementArrayBufferBinding, gl.INVALID_OPERATION)) + return; + // All is ok + var data = vao.m_elementArrayBufferBinding.getData(); + var indices = new rrRenderer.DrawIndices(data, sglrReferenceUtils.mapGLIndexType(type), offset, baseVertex); + + this.drawQuads(mode, indices, count, instanceCount); + }; + + /** + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} access + * @return {Array<number>} + */ + sglrReferenceContext.getBufferRect = function(access) { return [0, 0, access.raw().getHeight(), access.raw().getDepth()]; }; + + /** + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContext.prototype.getDrawColorbuffer = function() { + if (this.m_drawFramebufferBinding) + return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromSinglesampleAccess(this.getFboAttachment(this.m_drawFramebufferBinding, sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_COLOR0)); + return this.m_defaultColorbuffer; + }; + + /** + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContext.prototype.getDrawDepthbuffer = function() { + if (this.m_drawFramebufferBinding) + return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromSinglesampleAccess(this.getFboAttachment(this.m_drawFramebufferBinding, sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_DEPTH)); + return this.m_defaultDepthbuffer; + }; + + /** + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContext.prototype.getDrawStencilbuffer = function() { + if (this.m_drawFramebufferBinding) + return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromSinglesampleAccess(this.getFboAttachment(this.m_drawFramebufferBinding, sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_STENCIL)); + return this.m_defaultStencilbuffer; + }; + + /** + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContext.prototype.getReadDepthbuffer = function() { + if (this.m_readFramebufferBinding) + return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromSinglesampleAccess(this.getFboAttachment(this.m_readFramebufferBinding, sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_DEPTH)); + return this.m_defaultDepthbuffer; + }; + + /** + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ + sglrReferenceContext.ReferenceContext.prototype.getReadStencilbuffer = function() { + if (this.m_readFramebufferBinding) + return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromSinglesampleAccess(this.getFboAttachment(this.m_readFramebufferBinding, sglrReferenceContext.AttachmentPoint.ATTACHMENTPOINT_STENCIL)); + return this.m_defaultStencilbuffer; + }; + + /** + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} access + * @param {number} s + * @param {number} x + * @param {number} y + * @param {number} depth + */ + sglrReferenceContext.writeDepthOnly = function(access, s, x, y, depth) { access.raw().setPixDepth(depth, s, x, y); }; + + /** + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} access + * @param {number} s + * @param {number} x + * @param {number} y + * @param {number} stencil + * @param {number} writeMask + */ + sglrReferenceContext.writeStencilOnly = function(access, s, x, y, stencil, writeMask) { + /** @type {number} */ var oldVal = access.raw().getPixelInt(s, x, y)[3]; + access.raw().setPixStencil((oldVal & ~writeMask) | (stencil & writeMask), s, x, y); + }; + + /** + * @param {number} bits + * @param {number} s + * @return {number} + */ + sglrReferenceContext.maskStencil = function(bits, s) { return s & ((1 << bits) - 1); }; + + /** + * @param {number} buffers + */ + sglrReferenceContext.ReferenceContext.prototype.clear = function(buffers) { + if (this.conditionalSetError((buffers & ~(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)) != 0, gl.INVALID_VALUE)) + return; + + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var colorBuf0 = this.getDrawColorbuffer(); + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var depthBuf = this.getDrawDepthbuffer(); + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var stencilBuf = this.getDrawStencilbuffer(); + /** @type {boolean} */ var hasColor0 = /** @type {!boolean} */ (colorBuf0 && !colorBuf0.isEmpty()); + /** @type {boolean} */ var hasDepth = /** @type {!boolean} */ (depthBuf && !depthBuf.isEmpty()); + /** @type {boolean} */ var hasStencil = /** @type {!boolean} */ (stencilBuf && !stencilBuf.isEmpty()); + /** @type {Array<number>} */ var baseArea = this.m_scissorEnabled ? this.m_scissorBox : [0, 0, 0x7fffffff, 0x7fffffff]; + + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var access; + /** @type {boolean} */ var isSharedDepthStencil; + + if (hasColor0 && (buffers & gl.COLOR_BUFFER_BIT) != 0) { + /** @type {Array<number>} */ var colorArea = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(colorBuf0)); + access = colorBuf0.getSubregion(colorArea); + /** @type {boolean} */ var isSRGB = colorBuf0.raw().getFormat().isSRGB(); + /** @type {Array<number>} */ var c = (isSRGB && this.m_sRGBUpdateEnabled) ? tcuTextureUtil.linearToSRGB(this.m_clearColor) : this.m_clearColor; + /** @type {boolean} */ var maskUsed = !this.m_colorMask[0] || !this.m_colorMask[1] || !this.m_colorMask[2] || !this.m_colorMask[3]; + /** @type {boolean} */ var maskZero = !this.m_colorMask[0] && !this.m_colorMask[1] && !this.m_colorMask[2] && !this.m_colorMask[3]; + + if (!maskUsed) + access.clear(c); + else if (!maskZero) { + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + access.raw().setPixel(tcuTextureUtil.select(c, access.raw().getPixel(s, x, y), this.m_colorMask), s, x, y); + } + // else all channels masked out + } + + if (hasDepth && (buffers & gl.DEPTH_BUFFER_BIT) != 0 && this.m_depthMask) { + /** @type {Array<number>} */ var depthArea = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(depthBuf)); + access = depthBuf.getSubregion(depthArea); + isSharedDepthStencil = depthBuf.raw().getFormat().order != tcuTexture.ChannelOrder.D; + + if (isSharedDepthStencil) { + // Slow path where stencil is masked out in write. + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + sglrReferenceContext.writeDepthOnly(access, s, x, y, this.m_clearDepth); + } else + access.clear([this.m_clearDepth, 0, 0, 0]); + } + + if (hasStencil && (buffers & gl.STENCIL_BUFFER_BIT) != 0) { + /** @type {Array<number>} */ var stencilArea = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(stencilBuf)); + access = stencilBuf.getSubregion(stencilArea); + /** @type {number} */ var stencilBits = stencilBuf.raw().getFormat().getNumStencilBits(); + /** @type {number} */ var stencil = sglrReferenceContext.maskStencil(stencilBits, this.m_clearStencil); + isSharedDepthStencil = stencilBuf.raw().getFormat().order != tcuTexture.ChannelOrder.S; + + if (isSharedDepthStencil || ((this.m_stencil[rrDefs.FaceType.FACETYPE_FRONT].writeMask & ((1 << stencilBits) - 1)) != ((1 << stencilBits) - 1))) { + // Slow path where depth or stencil is masked out in write. + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + sglrReferenceContext.writeStencilOnly(access, s, x, y, stencil, this.m_stencil[rrDefs.FaceType.FACETYPE_FRONT].writeMask); + } else + access.clear([0, 0, 0, stencil]); + } + }; + + /** + * @param {number} buffer + * @param {number} drawbuffer + * @param {Array<number>} value + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.clearBufferiv = function(buffer, drawbuffer, value) { + if (this.conditionalSetError(buffer != gl.COLOR && buffer != gl.STENCIL, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(drawbuffer != 0, gl.INVALID_VALUE)) + return; + + /** @type {Array<number>} */ var baseArea = this.m_scissorEnabled ? this.m_scissorBox : [0, 0, 0x7fffffff, 0x7fffffff]; + + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var access; + + if (buffer == gl.COLOR) { + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var colorBuf = this.getDrawColorbuffer(); + /** @type {boolean} */ var maskUsed = !this.m_colorMask[0] || !this.m_colorMask[1] || !this.m_colorMask[2] || !this.m_colorMask[3]; + /** @type {boolean} */ var maskZero = !this.m_colorMask[0] && !this.m_colorMask[1] && !this.m_colorMask[2] && !this.m_colorMask[3]; + + if (!colorBuf.isEmpty() && !maskZero) { + /** @type {Array<number>} */ var colorArea = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(colorBuf)); + access = colorBuf.getSubregion(colorArea); + + if (!maskUsed) + access.clear(value); + else { + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + access.raw().setPixel(tcuTextureUtil.select(value, access.raw().getPixel(s, x, y), this.m_colorMask), s, x, y); + } + } + } else { + if (buffer !== gl.STENCIL) + throw new Error('Unexpected buffer type: ' + buffer); + + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var stencilBuf = this.getDrawStencilbuffer(); + + if (!stencilBuf.isEmpty() && this.m_stencil[rrDefs.FaceType.FACETYPE_FRONT].writeMask != 0) { + /** @type {Array<number>} */ var area = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(stencilBuf)); + access = stencilBuf.getSubregion(area); + /** @type {number} */ var stencil = value[0]; + + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + sglrReferenceContext.writeStencilOnly(access, s, x, y, stencil, this.m_stencil[rrDefs.FaceType.FACETYPE_FRONT].writeMask); + } + } + }; + + /** + * @param {number} buffer + * @param {number} drawbuffer + * @param {Array<number>} value + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.clearBufferfv = function(buffer, drawbuffer, value) { + if (this.conditionalSetError(buffer != gl.COLOR && buffer != gl.DEPTH, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(drawbuffer != 0, gl.INVALID_VALUE)) + return; + + /** @type {Array<number>} */ var baseArea = this.m_scissorEnabled ? this.m_scissorBox : [0, 0, 0x7fffffff, 0x7fffffff]; + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var access; + if (buffer == gl.COLOR) { + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var colorBuf = this.getDrawColorbuffer(); + /** @type {boolean} */ var maskUsed = !this.m_colorMask[0] || !this.m_colorMask[1] || !this.m_colorMask[2] || !this.m_colorMask[3]; + /** @type {boolean} */ var maskZero = !this.m_colorMask[0] && !this.m_colorMask[1] && !this.m_colorMask[2] && !this.m_colorMask[3]; + + if (!colorBuf.isEmpty() && !maskZero) { + /** @type {Array<number>} */ var colorArea = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(colorBuf)); + access = colorBuf.getSubregion(colorArea); + var color = value; + + if (this.m_sRGBUpdateEnabled && access.raw().getFormat().isSRGB()) + color = tcuTextureUtil.linearToSRGB(color); + + if (!maskUsed) + access.clear(color); + else { + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + access.raw().setPixel(tcuTextureUtil.select(color, access.raw().getPixel(s, x, y), this.m_colorMask), s, x, y); + } + } + } else { + if (buffer !== gl.DEPTH) + throw new Error('Unexpected buffer type: ' + buffer); + + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var depthBuf = this.getDrawDepthbuffer(); + + if (!depthBuf.isEmpty() && this.m_depthMask) { + /** @type {Array<number>} */ var area = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(depthBuf)); + access = depthBuf.getSubregion(area); + /** @type {number} */ var depth = value[0]; + + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + sglrReferenceContext.writeDepthOnly(access, s, x, y, depth); + } + } + }; + + /** + * @param {number} buffer + * @param {number} drawbuffer + * @param {Array<number>} value + */ + sglrReferenceContext.ReferenceContext.prototype.clearBufferuiv = function(buffer, drawbuffer, value) { + if (this.conditionalSetError(buffer != gl.COLOR, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(drawbuffer != 0, gl.INVALID_VALUE)) + return; + + /** @type {Array<number>} */ var baseArea = this.m_scissorEnabled ? this.m_scissorBox : [0, 0, 0x7fffffff, 0x7fffffff]; + + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var colorBuf = this.getDrawColorbuffer(); + /** @type {boolean} */ var maskUsed = !this.m_colorMask[0] || !this.m_colorMask[1] || !this.m_colorMask[2] || !this.m_colorMask[3]; + /** @type {boolean} */ var maskZero = !this.m_colorMask[0] && !this.m_colorMask[1] && !this.m_colorMask[2] && !this.m_colorMask[3]; + + if (!colorBuf.isEmpty() && !maskZero) { + /** @type {Array<number>} */ var colorArea = deMath.intersect(baseArea, sglrReferenceContext.getBufferRect(colorBuf)); + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var access = colorBuf.getSubregion(colorArea); + + if (!maskUsed) + access.clear(value); + else { + for (var y = 0; y < access.raw().getDepth(); y++) + for (var x = 0; x < access.raw().getHeight(); x++) + for (var s = 0; s < access.getNumSamples(); s++) + access.raw().setPixel(tcuTextureUtil.select(value, access.raw().getPixel(s, x, y), this.m_colorMask), s, x, y); + } + } + }; + + /** + * @param {number} buffer + * @param {number} drawbuffer + * @param {number} depth + * @param {number} stencil + */ + sglrReferenceContext.ReferenceContext.prototype.clearBufferfi = function(buffer, drawbuffer, depth, stencil) { + if (this.conditionalSetError(buffer != gl.DEPTH_STENCIL, gl.INVALID_ENUM)) + return; + this.clearBufferfv(gl.DEPTH, drawbuffer, [depth]); + this.clearBufferiv(gl.STENCIL, drawbuffer, [stencil]); + }; + + /** + * @param {number} target + * @param {number} attachment + * @param {sglrReferenceContext.TexTarget} textarget + * @param {sglrReferenceContext.TextureContainer} texture + * @param {number} level + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.framebufferTexture2D = function(target, attachment, textarget, texture, level) { + if (attachment == gl.DEPTH_STENCIL_ATTACHMENT) { + // Attach to both depth and stencil. + this.framebufferTexture2D(target, gl.DEPTH_ATTACHMENT, textarget, texture, level); + this.framebufferTexture2D(target, gl.STENCIL_ATTACHMENT, textarget, texture, level); + } else { + /** @type {sglrReferenceContext.AttachmentPoint} */ var point = sglrReferenceContext.mapGLAttachmentPoint(attachment); + /** @type {sglrReferenceContext.TexTarget} */ var fboTexTarget = sglrReferenceContext.mapGLFboTexTarget(textarget); + + if (this.conditionalSetError(target != gl.FRAMEBUFFER && + target != gl.DRAW_FRAMEBUFFER && + target != gl.READ_FRAMEBUFFER, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(point == undefined, gl.INVALID_ENUM)) + return; + + // Select binding point. + /** @type {sglrReferenceContext.Framebuffer} */ var framebufferBinding = (target == gl.FRAMEBUFFER || target == gl.DRAW_FRAMEBUFFER) ? this.m_drawFramebufferBinding : this.m_readFramebufferBinding; + if (this.conditionalSetError(!framebufferBinding, gl.INVALID_OPERATION)) + return; + + if (texture) { + if (this.conditionalSetError(level != 0, gl.INVALID_VALUE)) + return; + + if (texture.getType() == sglrReferenceContext.TextureType.TYPE_2D) { + if (this.conditionalSetError(fboTexTarget != sglrReferenceContext.TexTarget.TEXTARGET_2D, gl.INVALID_OPERATION)) + return; + } else { + if (!texture.getType() == sglrReferenceContext.TextureType.TYPE_CUBE_MAP) + throw new Error('Unsupported texture type'); + if (this.conditionalSetError(!deMath.deInRange32(fboTexTarget, sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_POSITIVE_X, sglrReferenceContext.TexTarget.TEXTARGET_CUBE_MAP_NEGATIVE_Z), gl.INVALID_OPERATION)) + return; + } + } + + /** @type {sglrReferenceContext.Attachment} */ var fboAttachment = new sglrReferenceContext.Attachment(); + + if (texture) { + fboAttachment.type = sglrReferenceContext.AttachmentType.ATTACHMENTTYPE_TEXTURE; + fboAttachment.object = texture; + fboAttachment.texTarget = fboTexTarget; + fboAttachment.level = level; + } + framebufferBinding.setAttachment(point, fboAttachment); + } + }; + + /** + * @param {number} target + * @param {number} attachment + * @param {sglrReferenceContext.TextureContainer} texture + * @param {number} level + * @param {number} layer + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.framebufferTextureLayer = function(target, attachment, texture, level, layer) { + if (attachment == gl.DEPTH_STENCIL_ATTACHMENT) { + // Attach to both depth and stencil. + this.framebufferTextureLayer(target, gl.DEPTH_ATTACHMENT, texture, level, layer); + this.framebufferTextureLayer(target, gl.STENCIL_ATTACHMENT, texture, level, layer); + } else { + /** @type {sglrReferenceContext.AttachmentPoint} */ var point = sglrReferenceContext.mapGLAttachmentPoint(attachment); + + if (this.conditionalSetError(target != gl.FRAMEBUFFER && + target != gl.DRAW_FRAMEBUFFER && + target != gl.READ_FRAMEBUFFER, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(point === undefined, gl.INVALID_ENUM)) + return; + + // Select binding point. + /** @type {sglrReferenceContext.Framebuffer} */ var framebufferBinding = (target == gl.FRAMEBUFFER || target == gl.DRAW_FRAMEBUFFER) ? this.m_drawFramebufferBinding : this.m_readFramebufferBinding; + if (this.conditionalSetError(!framebufferBinding, gl.INVALID_OPERATION)) + return; + + if (texture) { + if (this.conditionalSetError(level != 0, gl.INVALID_VALUE)) + return; + + if (this.conditionalSetError(texture.getType() != sglrReferenceContext.TextureType.TYPE_2D_ARRAY && + texture.getType() != sglrReferenceContext.TextureType.TYPE_3D && + texture.getType() != sglrReferenceContext.TextureType.TYPE_CUBE_MAP_ARRAY, gl.INVALID_OPERATION)) + return; + + if (texture.getType() == sglrReferenceContext.TextureType.TYPE_2D_ARRAY || texture.getType() == sglrReferenceContext.TextureType.TYPE_CUBE_MAP_ARRAY) { + if (this.conditionalSetError((layer < 0) || (layer >= gl.MAX_ARRAY_TEXTURE_LAYERS), gl.INVALID_VALUE)) + return; + if (this.conditionalSetError((level < 0) || (level > Math.floor(Math.log2(gl.MAX_TEXTURE_SIZE))), gl.INVALID_VALUE)) + return; + } else if (texture.getType() == sglrReferenceContext.TextureType.TYPE_3D) { + if (this.conditionalSetError((layer < 0) || (layer >= gl.MAX_3D_TEXTURE_SIZE), gl.INVALID_VALUE)) + return; + if (this.conditionalSetError((level < 0) || (level > Math.floor(Math.log2(gl.MAX_3D_TEXTURE_SIZE))), gl.INVALID_VALUE)) + return; + } + } + + /** @type {sglrReferenceContext.Attachment} */ var fboAttachment = new sglrReferenceContext.Attachment(); + + if (texture) { + fboAttachment.type = sglrReferenceContext.AttachmentType.ATTACHMENTTYPE_TEXTURE; + fboAttachment.object = texture; + fboAttachment.texTarget = sglrReferenceContext.texLayeredTypeToTarget(texture.getType()); + fboAttachment.level = level; + fboAttachment.layer = layer; + } + framebufferBinding.setAttachment(point, fboAttachment); + + } + }; + + /** + * @param {number} target + * @param {number} attachment + * @param {number} renderbuffertarget + * @param {sglrReferenceContext.Renderbuffer} renderbuffer + */ + sglrReferenceContext.ReferenceContext.prototype.framebufferRenderbuffer = function(target, attachment, renderbuffertarget, renderbuffer) { + if (attachment == gl.DEPTH_STENCIL_ATTACHMENT) { + // Attach both to depth and stencil. + this.framebufferRenderbuffer(target, gl.DEPTH_ATTACHMENT, renderbuffertarget, renderbuffer); + this.framebufferRenderbuffer(target, gl.STENCIL_ATTACHMENT, renderbuffertarget, renderbuffer); + } else { + /** @type {sglrReferenceContext.AttachmentPoint} */ var point = sglrReferenceContext.mapGLAttachmentPoint(attachment); + + if (this.conditionalSetError(target != gl.FRAMEBUFFER && + target != gl.DRAW_FRAMEBUFFER && + target != gl.READ_FRAMEBUFFER, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(point == undefined, gl.INVALID_ENUM)) + return; + + // Select binding point. + /** @type {sglrReferenceContext.Framebuffer} */ var framebufferBinding = (target == gl.FRAMEBUFFER || target == gl.DRAW_FRAMEBUFFER) ? this.m_drawFramebufferBinding : this.m_readFramebufferBinding; + if (this.conditionalSetError(!framebufferBinding, gl.INVALID_OPERATION)) + return; + + if (renderbuffer) { + if (this.conditionalSetError(renderbuffertarget != gl.RENDERBUFFER, gl.INVALID_ENUM)) + return; + } + + /** @type {sglrReferenceContext.Attachment} */ var fboAttachment = new sglrReferenceContext.Attachment(); + + if (renderbuffer) { + fboAttachment.type = sglrReferenceContext.AttachmentType.ATTACHMENTTYPE_RENDERBUFFER; + fboAttachment.object = renderbuffer; + } + framebufferBinding.setAttachment(point, fboAttachment); + } + }; + + /** + * @param {number} target + * @param {number} internalformat + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.ReferenceContext.prototype.renderbufferStorage = function(target, internalformat, width, height) { + /** @type {tcuTexture.TextureFormat} */ var format = gluTextureUtil.mapGLInternalFormat(internalformat); + + if (this.conditionalSetError(target != gl.RENDERBUFFER, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError(!this.m_renderbufferBinding, gl.INVALID_OPERATION)) + return; + if (this.conditionalSetError(!deMath.deInRange32(width, 0, this.m_limits.maxRenderbufferSize) || + !deMath.deInRange32(height, 0, this.m_limits.maxRenderbufferSize), + gl.INVALID_OPERATION)) + return; + if (this.conditionalSetError(!format, gl.INVALID_ENUM)) + return; + + this.m_renderbufferBinding.setStorage(format, width, height); + }; + + /** + * @param {number} target + * @param {number} samples + * @param {number} internalformat + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.ReferenceContext.prototype.renderbufferStorageMultisample = function(target, samples, internalformat, width, height) { + this.renderbufferStorage(target, internalformat, width, height); + }; + + /** + * @param {rrRenderer.PrimitiveType} derivedType + * @return {rrRenderer.PrimitiveType} + * @throws {Error} + */ + sglrReferenceContext.getPrimitiveBaseType = function(derivedType) { + switch (derivedType) { + case rrRenderer.PrimitiveType.TRIANGLES: + case rrRenderer.PrimitiveType.TRIANGLE_STRIP: + case rrRenderer.PrimitiveType.TRIANGLE_FAN: + return rrRenderer.PrimitiveType.TRIANGLES; + + case rrRenderer.PrimitiveType.LINES: + case rrRenderer.PrimitiveType.LINE_STRIP: + case rrRenderer.PrimitiveType.LINE_LOOP: + return rrRenderer.PrimitiveType.LINES; + + case rrRenderer.PrimitiveType.POINTS: + return rrRenderer.PrimitiveType.POINTS; + + default: + throw new Error('Unrecognized primitive type:' + derivedType); + } + }; + + /** + * createProgram + * @param {sglrShaderProgram.ShaderProgram} program + * @return {sglrShaderProgram.ShaderProgram} + */ + sglrReferenceContext.ReferenceContext.prototype.createProgram = function(program) { + return program; + }; + + /** + * deleteProgram + * @param {sglrShaderProgram.ShaderProgram} program + */ + sglrReferenceContext.ReferenceContext.prototype.deleteProgram = function(program) {}; + + /** + * @param {sglrShaderProgram.ShaderProgram} program + */ + sglrReferenceContext.ReferenceContext.prototype.useProgram = function(program) { + this.m_currentProgram = program; + }; + + /** + * Draws quads from vertex arrays + * @param {number} primitive GL primitive type to draw with. + * @param {number} first First vertex to begin drawing with + * @param {number} count How many vertices to draw (not counting vertices before first) + */ + sglrReferenceContext.ReferenceContext.prototype.drawArrays = function(primitive, first, count) { + this.drawQuads(primitive, first, count, 1); + }; + + /** + * Draws quads from vertex arrays + * @param {number} primitive GL primitive type to draw with. + * @param {(number|rrRenderer.DrawIndices)} first First vertex to begin drawing with + * @param {number} count Number of vertices + * @param {number=} instances Number of instances + */ + sglrReferenceContext.ReferenceContext.prototype.drawQuads = function(primitive, first, count, instances) { + // undefined results + if (!this.m_currentProgram) + return; + + if (typeof instances === 'undefined') + instances = 1; + + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var colorBuf0 = this.getDrawColorbuffer(); + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var depthBuf = this.getDrawDepthbuffer(); + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var stencilBuf = this.getDrawStencilbuffer(); + /** @type {boolean} */ var hasStencil = /** @type {!boolean} */ (stencilBuf && !stencilBuf.isEmpty()); + /** @type {number} */ var stencilBits = (hasStencil) ? stencilBuf.raw().getFormat().getNumStencilBits() : 0; + + /** @type {rrRenderer.RenderTarget} */ var renderTarget = new rrRenderer.RenderTarget(colorBuf0, + depthBuf, + stencilBuf); + /** @type {sglrShaderProgram.ShaderProgram} */ var program = this.m_currentProgram; + + /*new rrRenderer.Program( + * this.m_currentProgram.getVertexShader(), + * this.m_currentProgram.getFragmentShader());*/ + + /** @type {rrRenderState.ViewportState} */ var viewportState = new rrRenderState.ViewportState(colorBuf0); + /** @type {rrRenderState.RenderState} */ var state = new rrRenderState.RenderState(viewportState); + + /** @type {Array<rrVertexAttrib.VertexAttrib>} */ var vertexAttribs = []; + + // Gen state + /** @type {rrRenderer.PrimitiveType} */ var baseType = rrRenderer.PrimitiveType.TRIANGLES; + /** @type {boolean} */ var polygonOffsetEnabled = + (baseType == rrRenderer.PrimitiveType.TRIANGLES) ? + (this.m_polygonOffsetFillEnabled) : + (false); + + //state.cullMode = m_cullMode + + state.fragOps.scissorTestEnabled = this.m_scissorEnabled; + state.fragOps.scissorRectangle = new rrRenderState.WindowRectangle(this.m_scissorBox); + + state.fragOps.numStencilBits = stencilBits; + state.fragOps.stencilTestEnabled = this.m_stencilTestEnabled; + + for (var key in rrDefs.FaceType) { + /** @type {number} */ var faceType = rrDefs.FaceType[key]; + state.fragOps.stencilStates[faceType].compMask = this.m_stencil[faceType].opMask; + state.fragOps.stencilStates[faceType].writeMask = this.m_stencil[faceType].writeMask; + state.fragOps.stencilStates[faceType].ref = this.m_stencil[faceType].ref; + state.fragOps.stencilStates[faceType].func = sglrReferenceUtils.mapGLTestFunc(this.m_stencil[faceType].func); + state.fragOps.stencilStates[faceType].sFail = sglrReferenceUtils.mapGLStencilOp(this.m_stencil[faceType].opStencilFail); + state.fragOps.stencilStates[faceType].dpFail = sglrReferenceUtils.mapGLStencilOp(this.m_stencil[faceType].opDepthFail); + state.fragOps.stencilStates[faceType].dpPass = sglrReferenceUtils.mapGLStencilOp(this.m_stencil[faceType].opDepthPass); + } + + state.fragOps.depthTestEnabled = this.m_depthTestEnabled; + state.fragOps.depthFunc = sglrReferenceUtils.mapGLTestFunc(this.m_depthFunc); + state.fragOps.depthMask = this.m_depthMask; + + state.fragOps.blendMode = this.m_blendEnabled ? rrRenderState.BlendMode.STANDARD : rrRenderState.BlendMode.NONE; + state.fragOps.blendRGBState.equation = sglrReferenceUtils.mapGLBlendEquation(this.m_blendModeRGB); + state.fragOps.blendRGBState.srcFunc = sglrReferenceUtils.mapGLBlendFunc(this.m_blendFactorSrcRGB); + state.fragOps.blendRGBState.dstFunc = sglrReferenceUtils.mapGLBlendFunc(this.m_blendFactorDstRGB); + state.fragOps.blendAState.equation = sglrReferenceUtils.mapGLBlendEquation(this.m_blendModeAlpha); + state.fragOps.blendAState.srcFunc = sglrReferenceUtils.mapGLBlendFunc(this.m_blendFactorSrcAlpha); + state.fragOps.blendAState.dstFunc = sglrReferenceUtils.mapGLBlendFunc(this.m_blendFactorDstAlpha); + state.fragOps.blendColor = this.m_blendColor; + + state.fragOps.colorMask = this.m_colorMask; + + state.viewport.rect = new rrRenderState.WindowRectangle(this.m_viewport); + state.viewport.zn = this.m_depthRangeNear; + state.viewport.zf = this.m_depthRangeFar; + + //state.point.pointSize = this.m_pointSize; + state.line.lineWidth = this.m_lineWidth; + + state.fragOps.polygonOffsetEnabled = polygonOffsetEnabled; + state.fragOps.polygonOffsetFactor = this.m_polygonOffsetFactor; + state.fragOps.polygonOffsetUnits = this.m_polygonOffsetUnits; + + state.provokingVertexConvention = (this.m_provokingFirstVertexConvention) ? (rrDefs.ProvokingVertex.PROVOKINGVERTEX_FIRST) : (rrDefs.ProvokingVertex.PROVOKINGVERTEX_LAST); + + // gen attributes + /** @type {sglrReferenceContext.VertexArray} */ var vao = this.m_vertexArrayBinding; + for (var ndx = 0; ndx < vao.m_arrays.length; ++ndx) { + vertexAttribs[ndx] = new rrVertexAttrib.VertexAttrib(); + if (!vao.m_arrays[ndx].enabled) { + vertexAttribs[ndx].type = rrVertexAttrib.VertexAttribType.DONT_CARE; // reading with wrong type is allowed, but results are undefined + vertexAttribs[ndx].generic = this.m_currentAttribs[ndx]; + } else { + vertexAttribs[ndx].type = (vao.m_arrays[ndx].integer) ? + (sglrReferenceUtils.mapGLPureIntegerVertexAttributeType(vao.m_arrays[ndx].type)) : + (sglrReferenceUtils.mapGLFloatVertexAttributeType(vao.m_arrays[ndx].type, vao.m_arrays[ndx].normalized, vao.m_arrays[ndx].size)); + vertexAttribs[ndx].size = sglrReferenceUtils.mapGLSize(vao.m_arrays[ndx].size); + vertexAttribs[ndx].stride = vao.m_arrays[ndx].stride; + vertexAttribs[ndx].instanceDivisor = vao.m_arrays[ndx].divisor; + vertexAttribs[ndx].pointer = vao.m_arrays[ndx].bufferBinding.getData(); + vertexAttribs[ndx].offset = vao.m_arrays[ndx].offset; + vertexAttribs[ndx].componentCount = vao.m_arrays[ndx].size; + } + } + + // Set shader samplers + for (var uniformNdx = 0; uniformNdx < this.m_currentProgram.m_uniforms.length; ++uniformNdx) { + /** @type {number} */ var texNdx = this.m_currentProgram.m_uniforms[uniformNdx].value[0]; + + switch (this.m_currentProgram.m_uniforms[uniformNdx].type) { + case gluShaderUtil.DataType.SAMPLER_2D: + case gluShaderUtil.DataType.UINT_SAMPLER_2D: + case gluShaderUtil.DataType.INT_SAMPLER_2D: { + /** @type {sglrReferenceContext.Texture2D} */ var tex; + + if (texNdx >= 0 && texNdx < this.m_textureUnits.length) + tex = /** @type {sglrReferenceContext.Texture2D} */ (this.m_textureUnits[texNdx].tex2DBinding.texture); + + if (tex && tex.isComplete()) { + tex.updateView(); + this.m_currentProgram.m_uniforms[uniformNdx].sampler = tex; + } else + this.m_currentProgram.m_uniforms[uniformNdx].sampler = this.m_emptyTex2D.texture; + + break; + } + case gluShaderUtil.DataType.SAMPLER_CUBE: + case gluShaderUtil.DataType.UINT_SAMPLER_CUBE: + case gluShaderUtil.DataType.INT_SAMPLER_CUBE: { + /** @type {sglrReferenceContext.TextureCube} */ var texCube; + + if (texNdx >= 0 && texNdx < this.m_textureUnits.length) + texCube = /** @type {sglrReferenceContext.TextureCube} */ (this.m_textureUnits[texNdx].texCubeBinding.texture); + + if (texCube && texCube.isComplete()) { + texCube.updateView(); + this.m_currentProgram.m_uniforms[uniformNdx].sampler = texCube; + } else + this.m_currentProgram.m_uniforms[uniformNdx].sampler = this.m_emptyTexCube.texture; + + break; + } + case gluShaderUtil.DataType.SAMPLER_2D_ARRAY: + case gluShaderUtil.DataType.UINT_SAMPLER_2D_ARRAY: + case gluShaderUtil.DataType.INT_SAMPLER_2D_ARRAY: { + /** @type {sglrReferenceContext.Texture2DArray} */ var tex2DArray; + + if (texNdx >= 0 && texNdx < this.m_textureUnits.length) + tex2DArray = /** @type {sglrReferenceContext.Texture2DArray} */ (this.m_textureUnits[texNdx].tex2DArrayBinding.texture); + + if (tex2DArray && tex2DArray.isComplete()) { + tex2DArray.updateView(); + this.m_currentProgram.m_uniforms[uniformNdx].sampler = tex2DArray; + } else + this.m_currentProgram.m_uniforms[uniformNdx].sampler = this.m_emptyTex2DArray.texture; + + break; + } + case gluShaderUtil.DataType.SAMPLER_3D: + case gluShaderUtil.DataType.UINT_SAMPLER_3D: + case gluShaderUtil.DataType.INT_SAMPLER_3D: { + /** @type {sglrReferenceContext.Texture3D} */ var tex3D; + + if (texNdx >= 0 && texNdx < this.m_textureUnits.length) + tex3D = /** @type {sglrReferenceContext.Texture3D} */ (this.m_textureUnits[texNdx].tex3DBinding.texture); + + if (tex3D && tex3D.isComplete()) { + tex3D.updateView(); + this.m_currentProgram.m_uniforms[uniformNdx].sampler = tex3D; + } else + this.m_currentProgram.m_uniforms[uniformNdx].sampler = this.m_emptyTex3D.texture; + + break; + } + /* TODO: Port + case gluShaderUtil.DataType.SAMPLER_CUBE_ARRAY: + case gluShaderUtil.DataType.UINT_SAMPLER_CUBE_ARRAY: + case gluShaderUtil.DataType.INT_SAMPLER_CUBE_ARRAY:{ + rc::TextureCubeArray* tex = DE_NULL; + + if (texNdx >= 0 && (size_t)texNdx < m_textureUnits.length) + tex = (this.m_textureUnits[texNdx].texCubeArrayBinding) ? (this.m_textureUnits[texNdx].texCubeArrayBinding) : (&this.m_textureUnits[texNdx].defaultCubeArrayTex); + + if (tex && tex.isComplete()) { + tex.updateView(); + this.m_currentProgram.m_uniforms[uniformNdx].sampler.texCubeArray = tex; + } else + this.m_currentProgram.m_uniforms[uniformNdx].sampler.texCubeArray = &this.m_emptyTexCubeArray; + + break; + } + */ + default: + // nothing + break; + } + } + + var primitiveType = sglrReferenceUtils.mapGLPrimitiveType(primitive); + + var renderFunction = rrRenderer.drawTriangles; + if (primitiveType == rrRenderer.PrimitiveType.LINES || + primitiveType == rrRenderer.PrimitiveType.LINE_STRIP || + primitiveType == rrRenderer.PrimitiveType.LINE_LOOP) + renderFunction = rrRenderer.drawLines; + else if (primitiveType == rrRenderer.PrimitiveType.POINTS) + renderFunction = rrRenderer.drawPoints; + + for (var instanceID = 0; instanceID < instances; instanceID++) + renderFunction(state, renderTarget, program, vertexAttribs, primitiveType, first, count, instanceID); + }; + + /** + * @param {Array<number>} rect + * @return {boolean} + */ + sglrReferenceContext.isEmpty = function(rect) { return rect[2] == 0 || rect[3] == 0; }; + + /** + * @param {number} mask + * @param {Array<number>} srcRect + * @param {Array<number>} dstRect + * @param {boolean} flipX + * @param {boolean} flipY + * @throws {Error} + */ + sglrReferenceContext.ReferenceContext.prototype.blitResolveMultisampleFramebuffer = function(mask, srcRect, dstRect, flipX, flipY) { + throw new Error('Unimplemented'); + }; + + /** + * @param {number} srcX0 + * @param {number} srcY0 + * @param {number} srcX1 + * @param {number} srcY1 + * @param {number} dstX0 + * @param {number} dstY0 + * @param {number} dstX1 + * @param {number} dstY1 + * @param {number} mask + * @param {number} filter + */ + sglrReferenceContext.ReferenceContext.prototype.blitFramebuffer = function(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) { + // p0 in inclusive, p1 exclusive. + // Negative width/height means swap. + /** @type {boolean} */ var swapSrcX = srcX1 < srcX0; + /** @type {boolean} */ var swapSrcY = srcY1 < srcY0; + /** @type {boolean} */ var swapDstX = dstX1 < dstX0; + /** @type {boolean} */ var swapDstY = dstY1 < dstY0; + /** @type {number} */ var srcW = Math.abs(srcX1 - srcX0); + /** @type {number} */ var srcH = Math.abs(srcY1 - srcY0); + /** @type {number} */ var dstW = Math.abs(dstX1 - dstX0); + /** @type {number} */ var dstH = Math.abs(dstY1 - dstY0); + /** @type {boolean} */ var scale = srcW != dstW || srcH != dstH; + /** @type {number} */ var srcOriginX = swapSrcX ? srcX1 : srcX0; + /** @type {number} */ var srcOriginY = swapSrcY ? srcY1 : srcY0; + /** @type {number} */ var dstOriginX = swapDstX ? dstX1 : dstX0; + /** @type {number} */ var dstOriginY = swapDstY ? dstY1 : dstY0; + /** @type {Array<number>} */ var srcRect = [srcOriginX, srcOriginY, srcW, srcH]; + /** @type {Array<number>} */ var dstRect = [dstOriginX, dstOriginY, dstW, dstH]; + + if (this.conditionalSetError(filter != gl.NEAREST && filter != gl.LINEAR, gl.INVALID_ENUM)) + return; + if (this.conditionalSetError((mask & (gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)) != 0 && filter != gl.NEAREST, gl.INVALID_OPERATION)) + return; + + // Validate that both targets are complete. + if (this.conditionalSetError(this.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || + this.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE, gl.INVALID_OPERATION)) + return; + + // Check samples count is valid + if (this.conditionalSetError(this.getDrawColorbuffer().getNumSamples() != 1, gl.INVALID_OPERATION)) + return; + + // Check size restrictions of multisampled case + if (this.getReadColorbuffer().getNumSamples() != 1) { + // Src and Dst rect dimensions must be the same + if (this.conditionalSetError(srcW != dstW || srcH != dstH, gl.INVALID_OPERATION)) + return; + + // sglrReferenceContext.Framebuffer formats must match + if (mask & gl.COLOR_BUFFER_BIT) + if (this.conditionalSetError(this.getReadColorbuffer().raw().getFormat() != this.getDrawColorbuffer().raw().getFormat(), gl.INVALID_OPERATION)) + return; + if (mask & gl.DEPTH_BUFFER_BIT) + if (this.conditionalSetError(this.getReadDepthbuffer().raw().getFormat() != this.getDrawDepthbuffer().raw().getFormat(), gl.INVALID_OPERATION)) + return; + if (mask & gl.STENCIL_BUFFER_BIT) + if (this.conditionalSetError(this.getReadStencilbuffer().raw().getFormat() != this.getDrawStencilbuffer().raw().getFormat(), gl.INVALID_OPERATION)) + return; + } + + // Compute actual source rect. + srcRect = (mask & gl.COLOR_BUFFER_BIT) ? deMath.intersect(srcRect, sglrReferenceContext.getBufferRect(this.getReadColorbuffer())) : srcRect; + srcRect = (mask & gl.DEPTH_BUFFER_BIT) ? deMath.intersect(srcRect, sglrReferenceContext.getBufferRect(this.getReadDepthbuffer())) : srcRect; + srcRect = (mask & gl.STENCIL_BUFFER_BIT) ? deMath.intersect(srcRect, sglrReferenceContext.getBufferRect(this.getReadStencilbuffer())) : srcRect; + + // Compute destination rect. + dstRect = (mask & gl.COLOR_BUFFER_BIT) ? deMath.intersect(dstRect, sglrReferenceContext.getBufferRect(this.getDrawColorbuffer())) : dstRect; + dstRect = (mask & gl.DEPTH_BUFFER_BIT) ? deMath.intersect(dstRect, sglrReferenceContext.getBufferRect(this.getDrawDepthbuffer())) : dstRect; + dstRect = (mask & gl.STENCIL_BUFFER_BIT) ? deMath.intersect(dstRect, sglrReferenceContext.getBufferRect(this.getDrawStencilbuffer())) : dstRect; + dstRect = this.m_scissorEnabled ? deMath.intersect(dstRect, this.m_scissorBox) : dstRect; + + if (sglrReferenceContext.isEmpty(srcRect) || sglrReferenceContext.isEmpty(dstRect)) + return; // Don't attempt copy. + + // Multisampled read buffer is a special case + if (this.getReadColorbuffer().getNumSamples() != 1) { + /** @type {boolean} */ var swapX = swapSrcX ^ swapDstX ? true : false; + /** @type {boolean} */ var swapY = swapSrcY ^ swapDstY ? true : false; + var error = this.blitResolveMultisampleFramebuffer(mask, srcRect, dstRect, swapX, swapY); + + if (error != gl.NO_ERROR) + this.setError(error); + + return; + } + + // \note Multisample pixel buffers can now be accessed like non-multisampled because multisample read buffer case is already handled. => sample count must be 1 + + // Coordinate transformation: + // Dst offset space -> dst rectangle space -> src rectangle space -> src offset space. + /** @type {tcuMatrix.Matrix} */ var matrix = tcuMatrixUtil.translationMatrix([srcX0 - srcRect[0], srcY0 - srcRect[1]]); + matrix = tcuMatrix.multiply(matrix, tcuMatrix.matrixFromVector(3, 3, [(srcX1 - srcX0) / (dstX1 - dstX0), (srcY1 - srcY0) / (dstY1 - dstY0), 1])); + matrix = tcuMatrix.multiply(matrix, tcuMatrixUtil.translationMatrix([dstRect[0] - dstX0, dstRect[1] - dstY0])); + + /** + * @param {number} x + * @param {number} y + * @return {number} + */ + var transform = function(x, y) { return matrix.get(x, y); }; + + /** @type {number} */ var dX; + /** @type {number} */ var dY; + /** @type {number} */ var sX; + /** @type {number} */ var sY; + /** @type {tcuTexture.PixelBufferAccess|rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var src; + /** @type {tcuTexture.PixelBufferAccess|rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var dst; + + if (mask & gl.COLOR_BUFFER_BIT) { + src = tcuTextureUtil.getSubregion(this.getReadColorbuffer().toSinglesampleAccess(), srcRect[0], srcRect[1], 0, srcRect[2], srcRect[3], 1); + dst = tcuTextureUtil.getSubregion(this.getDrawColorbuffer().toSinglesampleAccess(), dstRect[0], dstRect[1], 0, dstRect[2], dstRect[3], 1); + /** @type {tcuTexture.TextureChannelClass} */ var dstClass = tcuTexture.getTextureChannelClass(dst.getFormat().type); + /** @type {boolean} */ var dstIsFloat = dstClass == tcuTexture.TextureChannelClass.FLOATING_POINT || + dstClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT || + dstClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT; + /** @type {tcuTexture.FilterMode} */ var sFilter = (scale && filter == gl.LINEAR) ? tcuTexture.FilterMode.LINEAR : tcuTexture.FilterMode.NEAREST; + /** @type {tcuTexture.Sampler} */ var sampler = new tcuTexture.Sampler(tcuTexture.WrapMode.CLAMP_TO_EDGE, tcuTexture.WrapMode.CLAMP_TO_EDGE, tcuTexture.WrapMode.CLAMP_TO_EDGE, + sFilter, sFilter, 0.0 /* lod threshold */, false /* non-normalized coords */); + /** @type {boolean} */ var srcIsSRGB = src.getFormat().order == tcuTexture.ChannelOrder.sRGB || src.getFormat().order == tcuTexture.ChannelOrder.sRGBA; + /** @type {boolean} */ var dstIsSRGB = dst.getFormat().order == tcuTexture.ChannelOrder.sRGB || dst.getFormat().order == tcuTexture.ChannelOrder.sRGBA; + /** @type {boolean} */ var convertSRGB = this.m_sRGBUpdateEnabled; + + // \note We don't check for unsupported conversions, unlike spec requires. + + for (var yo = 0; yo < dstRect[3]; yo++) { + for (var xo = 0; xo < dstRect[2]; xo++) { + dX = xo + 0.5; + dY = yo + 0.5; + + // \note Only affine part is used. + sX = transform(0, 0) * dX + transform(0, 1) * dY + transform(0, 2); + sY = transform(1, 0) * dX + transform(1, 1) * dY + transform(1, 2); + + // do not copy pixels outside the modified source region (modified by buffer intersection) + if (sX < 0.0 || sX >= srcRect[2] || + sY < 0.0 || sY >= srcRect[3]) + continue; + + if (dstIsFloat || srcIsSRGB || filter == tcuTexture.FilterMode.LINEAR) { + /** @type {Array<number>} */ var p = src.sample2D(sampler, sampler.minFilter, sX, sY, 0); + dst.setPixel((dstIsSRGB && convertSRGB) ? tcuTextureUtil.linearToSRGB(p) : p, xo, yo); + } else + dst.setPixelInt(src.getPixelInt(Math.floor(sX), Math.floor(sY)), xo, yo); + } + } + } + + if ((mask & gl.DEPTH_BUFFER_BIT) && this.m_depthMask) { + src = this.getReadDepthbuffer().getSubregion(srcRect); + dst = this.getDrawDepthbuffer().getSubregion(dstRect); + + for (var yo = 0; yo < dstRect[3]; yo++) { + for (var xo = 0; xo < dstRect[2]; xo++) { + var sampleNdx = 0; // multisample read buffer case is already handled + + dX = xo + 0.5; + dY = yo + 0.5; + sX = transform(0, 0) * dX + transform(0, 1) * dY + transform(0, 2); + sY = transform(1, 0) * dX + transform(1, 1) * dY + transform(1, 2); + + sglrReferenceContext.writeDepthOnly(dst, sampleNdx, xo, yo, src.raw().getPixel(sampleNdx, Math.floor(sX), Math.floor(sY))[0]); + } + } + } + + if (mask & gl.STENCIL_BUFFER_BIT) { + src = this.getReadStencilbuffer().getSubregion(srcRect); + dst = this.getDrawStencilbuffer().getSubregion(dstRect); + + for (var yo = 0; yo < dstRect[3]; yo++) { + for (var xo = 0; xo < dstRect[2]; xo++) { + var sampleNdx = 0; // multisample read buffer case is already handled + + dX = xo + 0.5; + dY = yo + 0.5; + sX = transform(0, 0) * dX + transform(0, 1) * dY + transform(0, 2); + sY = transform(1, 0) * dX + transform(1, 1) * dY + transform(1, 2); + + sglrReferenceContext.writeStencilOnly(dst, sampleNdx, xo, yo, src.raw().getPixelInt(sampleNdx, Math.floor(sX), Math.floor(sY))[3], this.m_stencil[rrDefs.FaceType.FACETYPE_FRONT].writeMask); + } + } + } + }; + + /** + * @param {number} internalFormat + * @return {tcuTexture.TextureFormat} + */ + sglrReferenceContext.mapInternalFormat = function(internalFormat) { + switch (internalFormat) { + case gl.ALPHA: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.A, tcuTexture.ChannelType.UNORM_INT8); + case gl.LUMINANCE: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.L, tcuTexture.ChannelType.UNORM_INT8); + case gl.LUMINANCE_ALPHA: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.LA, tcuTexture.ChannelType.UNORM_INT8); + case gl.RGB: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB, tcuTexture.ChannelType.UNORM_INT8); + case gl.RGBA: return new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8); + + default: + return gluTextureUtil.mapGLInternalFormat(internalFormat); + } + }; + + /** + * @param {tcuTexture.PixelBufferAccess} dst + * @param {tcuTexture.ConstPixelBufferAccess} src + */ + sglrReferenceContext.depthValueFloatClampCopy = function(dst, src) { + /** @type {number} */ var width = dst.getWidth(); + /** @type {number} */ var height = dst.getHeight(); + /** @type {number} */ var depth = dst.getDepth(); + + DE_ASSERT(src.getWidth() == width && src.getHeight() == height && src.getDepth() == depth); + + // clamping copy + for (var z = 0; z < depth; z++) + for (var y = 0; y < height; y++) + for (var x = 0; x < width; x++) { + /** @type {Array<number>} */ var data = src.getPixel(x, y, z); + dst.setPixel([deMath.clamp(data[0], 0.0, 1.0), data[1], data[2], data[3]], x, y, z); + } + }; + + /** + * @param {number} target + * @param {number} level + * @param {number} internalFormat + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.ReferenceContext.prototype.texImage2DDelegate = function (target, level, internalFormat, width, height) { + var format; + var dataType; + + switch (internalFormat) + { + case gl.ALPHA: + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + case gl.RGB: + case gl.RGBA: + format = internalFormat; + dataType = GL.UNSIGNED_BYTE; + break; + default: + { + var transferFmt = gluTextureUtil.getTransferFormat(gluTextureUtil.mapGLInternalFormat(internalFormat)); + format = transferFmt.format; + dataType = transferFmt.dataType; + break; + } + } + this.texImage2D(target, level, internalFormat, width, height, 0, format, dataType, null); + }; + + /** + * @param {number} target + * @param {number} level + * @param {number} internalFormat + * @param {number} width + * @param {number} height + * @param {number} border + * @param {number} format + * @param {number} type + * @param {number} pixels + */ + sglrReferenceContext.ReferenceContext.prototype.texImage2D = function(target, level, internalFormat, width, height, border, format, type, pixels) { + this.texImage3D(target, level, internalFormat, width, height, 1, border, format, type, pixels); + }; + + sglrReferenceContext.ReferenceContext.prototype.texImage3D = function(target, level, internalFormat, width, height, depth, border, format, type, pixels) { + /** @type {sglrReferenceContext.TextureUnit} */var unit = this.m_textureUnits[this.m_activeTexture]; + /** @type {ArrayBuffer} */ var data = null; + /** @type {number} */ var offset = 0; + /** @type {tcuTexture.PixelBufferAccess} */ var dst; + /** @type {tcuTexture.ConstPixelBufferAccess} */ var src; + if (this.m_pixelUnpackBufferBinding) { + if (this.conditionalSetError(typeof pixels !== 'number', gl.INVALID_VALUE)) + return; + data = this.m_pixelUnpackBufferBinding.getData(); + offset = pixels; + } else if (pixels) { + if (pixels instanceof ArrayBuffer) { + data = pixels; + offset = 0; + } else { + data = pixels.buffer; + offset = pixels.byteOffset; + } + } + /** @type {boolean} */ var isDstFloatDepthFormat = (internalFormat == gl.DEPTH_COMPONENT32F || internalFormat == gl.DEPTH32F_STENCIL8); // depth components are limited to [0,1] range + + if (this.conditionalSetError(border != 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(width < 0 || height < 0 || depth < 0 || level < 0, gl.INVALID_VALUE)) + return; + + // Map storage format. + /** @type {tcuTexture.TextureFormat} */ var storageFmt = sglrReferenceContext.mapInternalFormat(internalFormat); + if (this.conditionalSetError(!storageFmt, gl.INVALID_ENUM)) + return; + + // Map transfer format. + /** @type {tcuTexture.TextureFormat} */ var transferFmt = gluTextureUtil.mapGLTransferFormat(format, type); + if (this.conditionalSetError(!transferFmt, gl.INVALID_ENUM)) + return; + + if (target == gl.TEXTURE_2D) { + // Validate size and level. + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || height > this.m_limits.maxTexture2DSize || depth != 1, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.log2(this.m_limits.maxTexture2DSize), gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture2D} */ + var texture = /** @type {sglrReferenceContext.Texture2D} */ (unit.tex2DBinding.texture); + + if (texture.isImmutable()) { + if (this.conditionalSetError(!texture.hasLevel(level), gl.INVALID_OPERATION)) + return; + + //NOTE: replaces this: var dst = tcuTexture.PixelBufferAccess.newFromTextureLevel(texture.getLevel(level)); + dst = texture.getLevel(level); + + if (this.conditionalSetError(!storageFmt.isEqual(dst.getFormat()) || + width != dst.getWidth() || + height != dst.getHeight(), gl.INVALID_OPERATION)) + return; + } else + texture.allocLevel(level, storageFmt, width, height); + + if (data) { + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var skip = this.m_pixelUnpackSkipRows * rowPitch + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + rowPitch: rowPitch, + data: data, + offset: offset + skip}); + + //NOTE: replaces this: var dst = tcuTexture.PixelBufferAccess.newFromTextureLevel(texture.getLevel(level)); + dst = texture.getLevel(level); + + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(dst, src); + else + tcuTextureUtil.copy(dst, src); + } else { + // No data supplied, clear to black. + + //NOTE: replaces this: var dst = tcuTexture.PixelBufferAccess.newFromTextureLevel(texture.getLevel(level)); + dst = texture.getLevel(level); + dst.clear([0.0, 0.0, 0.0, 1.0]); + } + } else if (target == gl.TEXTURE_CUBE_MAP_NEGATIVE_X || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_X || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Y || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Y || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Z || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Z) { + // Validate size and level. + if (this.conditionalSetError(width != height || width > this.m_limits.maxTextureCubeSize || depth != 1, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.floor(Math.log2(this.m_limits.maxTextureCubeSize)), gl.INVALID_VALUE)) + return; + + var textureCube = /** @type {sglrReferenceContext.TextureCube} */ (unit.texCubeBinding.texture); + + var face = sglrReferenceContext.mapGLCubeFace(target); + + if (textureCube.isImmutable()) { + if (this.conditionalSetError(!textureCube.hasFace(level, face), gl.INVALID_OPERATION)) + return; + + dst = textureCube.getFace(level, face); + + if (this.conditionalSetError(!storageFmt.isEqual(dst.getFormat()) || + width != dst.getWidth() || + height != dst.getHeight(), gl.INVALID_OPERATION)) + return; + } else + textureCube.allocLevel(level, face, storageFmt, width, height); + + if (data) { + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var skip = this.m_pixelUnpackSkipRows * rowPitch + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + rowPitch: rowPitch, + data: data, + offset: offset + skip}); + + dst = textureCube.getFace(level, face); + + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(dst, src); + else + tcuTextureUtil.copy(dst, src); + } else { + // No data supplied, clear to black. + dst = textureCube.getFace(level, face); + dst.clear([0.0, 0.0, 0.0, 1.0]); + } + } else if (target == gl.TEXTURE_2D_ARRAY) { + // Validate size and level. + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || + height > this.m_limits.maxTexture2DSize || + depth > this.m_limits.maxTexture2DArrayLayers, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.floor(Math.log2(this.m_limits.maxTexture2DSize)), gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture2DArray} */ + var texture2DArray = /** @type {sglrReferenceContext.Texture2DArray} */ (unit.tex2DArrayBinding.texture); + + if (texture2DArray.isImmutable()) { + if (this.conditionalSetError(!texture2DArray.hasLevel(level), gl.INVALID_OPERATION)) + return; + + dst = texture2DArray.getLevel(level); + if (this.conditionalSetError(!storageFmt.isEqual(dst.getFormat()) || + width != dst.getWidth() || + height != dst.getHeight() || + depth != dst.getDepth(), gl.INVALID_OPERATION)) + return; + } else + texture2DArray.allocLevel(level, storageFmt, width, height, depth); + + if (data) { + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var imageHeight = this.m_pixelUnpackImageHeight > 0 ? this.m_pixelUnpackImageHeight : height; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var slicePitch = imageHeight * rowPitch; + var skip = this.m_pixelUnpackSkipImages * slicePitch + this.m_pixelUnpackSkipRows * rowPitch + + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + depth: depth, + rowPitch: rowPitch, + slicePitch: slicePitch, + data: data, + offset: offset + skip}); + + dst = texture2DArray.getLevel(level); + + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(dst, src); + else + tcuTextureUtil.copy(dst, src); + } else { + // No data supplied, clear to black. + dst = texture2DArray.getLevel(level); + dst.clear([0.0, 0.0, 0.0, 1.0]); + } + } else if (target == gl.TEXTURE_3D) { + // Validate size and level. + if (this.conditionalSetError(width > this.m_limits.maxTexture3DSize || + height > this.m_limits.maxTexture3DSize || + depth > this.m_limits.maxTexture3DSize, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.floor(Math.log2(this.m_limits.maxTexture3DSize)), gl.INVALID_VALUE)) + return; + + var texture3D = /** @type {sglrReferenceContext.Texture3D} */ (unit.tex3DBinding.texture); + + if (texture3D.isImmutable()) { + if (this.conditionalSetError(!texture3D.hasLevel(level), gl.INVALID_OPERATION)) + return; + + dst = texture3D.getLevel(level); + if (this.conditionalSetError(!storageFmt.isEqual(dst.getFormat()) || + width != dst.getWidth() || + height != dst.getHeight() || + depth != dst.getDepth(), gl.INVALID_OPERATION)) + return; + } else + texture3D.allocLevel(level, storageFmt, width, height, depth); + + if (data) { + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var imageHeight = this.m_pixelUnpackImageHeight > 0 ? this.m_pixelUnpackImageHeight : height; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var slicePitch = imageHeight * rowPitch; + var skip = this.m_pixelUnpackSkipImages * slicePitch + this.m_pixelUnpackSkipRows * rowPitch + + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + depth: depth, + rowPitch: rowPitch, + slicePitch: slicePitch, + data: data, + offset: offset + skip}); + + dst = texture3D.getLevel(level); + + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(dst, src); + else + tcuTextureUtil.copy(dst, src); + + } else { + // No data supplied, clear to black. + dst = texture3D.getLevel(level); + dst.clear([0.0, 0.0, 0.0, 1.0]); + } + } + // else if (target == gl.TEXTURE_CUBE_MAP_ARRAY) + // { + // // Validate size and level. + // RC_IF_ERROR(width != height || + // width > m_limits.maxTexture2DSize || + // depth % 6 != 0 || + // depth > m_limits.maxTexture2DArrayLayers, gl.INVALID_VALUE, RC_RET_VOID); + // RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture2DSize), gl.INVALID_VALUE, RC_RET_VOID); + + // TextureCubeArray* texture = unit.texCubeArrayBinding ? unit.texCubeArrayBinding : &unit.defaultCubeArrayTex; + + // if (texture->isImmutable()) + // { + // RC_IF_ERROR(!texture->hasLevel(level), gl.INVALID_OPERATION, RC_RET_VOID); + + // ConstPixelBufferAccess dst(texture->getLevel(level)); + // RC_IF_ERROR(storageFmt != dst.getFormat() || + // width != dst.getWidth() || + // height != dst.getHeight() || + // depth != dst.getDepth(), gl.INVALID_OPERATION, RC_RET_VOID); + // } + // else + // texture->allocLevel(level, storageFmt, width, height, depth); + + // if (unpackPtr) + // { + // ConstPixelBufferAccess src = getUnpack3DAccess(transferFmt, width, height, depth, unpackPtr); + // PixelBufferAccess dst (texture->getLevel(level)); + + // if (isDstFloatDepthFormat) + // sglrReferenceContext.depthValueFloatClampCopy(dst, src); + // else + // tcu::copy(dst, src); + // } + // else + // { + // // No data supplied, clear to black. + // PixelBufferAccess dst = texture->getLevel(level); + // tcu::clear(dst, Vec4(0.0f, 0.0f, 0.0f, 1.0f)); + // } + // } /**/ + else + this.setError(gl.INVALID_ENUM); + }; + + sglrReferenceContext.ReferenceContext.prototype.texSubImage2D = function(target, level, xoffset, yoffset, width, height, format, type, pixels) { + this.texSubImage3D(target, level, xoffset, yoffset, 0, width, height, 1, format, type, pixels); + }; + + sglrReferenceContext.ReferenceContext.prototype.texSubImage3D = function(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) { + /** @type {sglrReferenceContext.TextureUnit} */var unit = this.m_textureUnits[this.m_activeTexture]; + /** @type {ArrayBuffer} */ var data = null; + /** @type {number} */ var offset = 0; + /** @type {tcuTexture.PixelBufferAccess} */ var dst; + /** @type {tcuTexture.PixelBufferAccess} */ var sub; + /** @type {tcuTexture.ConstPixelBufferAccess} */ var src; + /** @type {boolean} */ var isDstFloatDepthFormat; + if (this.m_pixelUnpackBufferBinding) { + if (this.conditionalSetError(typeof pixels !== 'number', gl.INVALID_VALUE)) + return; + data = this.m_pixelUnpackBufferBinding.getData(); + offset = pixels; + } else if (pixels) { + if (pixels instanceof ArrayBuffer) { + data = pixels; + offset = 0; + } else { + data = pixels.buffer; + offset = pixels.byteOffset; + } + } + + if (this.conditionalSetError(xoffset < 0 || yoffset < 0 || zoffset < 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(width < 0 || height < 0 || depth < 0 || level < 0, gl.INVALID_VALUE)) + return; + + // Map transfer format. + /** @type {tcuTexture.TextureFormat} */ var transferFmt = gluTextureUtil.mapGLTransferFormat(format, type); + if (this.conditionalSetError(!transferFmt, gl.INVALID_ENUM)) + return; + + if (target == gl.TEXTURE_2D) { + // Validate size and level. + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || height > this.m_limits.maxTexture2DSize || depth != 1, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.log2(this.m_limits.maxTexture2DSize), gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture2D} */ + var texture = /** @type {sglrReferenceContext.Texture2D} */ (unit.tex2DBinding.texture); + + if (this.conditionalSetError(!texture.hasLevel(level), gl.INVALID_OPERATION)) + return; + + //NOTE: replaces this: var dst = tcuTexture.PixelBufferAccess.newFromTextureLevel(texture.getLevel(level)); + dst = texture.getLevel(level); + + if (this.conditionalSetError(xoffset + width > dst.getWidth() || + yoffset + height > dst.getHeight() || + zoffset + depth > dst.getDepth(), + gl.INVALID_VALUE)) + return; + + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var skip = this.m_pixelUnpackSkipRows * rowPitch + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + rowPitch: rowPitch, + data: data, + offset: offset + skip}); + + sub = tcuTextureUtil.getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth); + isDstFloatDepthFormat = (dst.getFormat().order == tcuTexture.ChannelOrder.D || dst.getFormat().order == tcuTexture.ChannelOrder.DS); // depth components are limited to [0,1] range + + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(sub, src); + else + tcuTextureUtil.copy(sub, src); + } else if (target == gl.TEXTURE_CUBE_MAP_NEGATIVE_X || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_X || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Y || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Y || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Z || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Z) { + var textureCube = /** @type {sglrReferenceContext.TextureCube} */ (unit.texCubeBinding.texture); + + var face = sglrReferenceContext.mapGLCubeFace(target); + + if (this.conditionalSetError(!textureCube.hasFace(level, face), gl.INVALID_OPERATION)) + return; + + dst = textureCube.getFace(level, face); + + if (this.conditionalSetError(xoffset + width > dst.getWidth() || + yoffset + height > dst.getHeight() || + zoffset + depth > dst.getDepth(), + gl.INVALID_VALUE)) + return; + + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var skip = this.m_pixelUnpackSkipRows * rowPitch + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + rowPitch: rowPitch, + slicePitach: slicePitch, + data: data, + offset: offset + skip}); + + sub = tcuTextureUtil.getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth); + isDstFloatDepthFormat = (dst.getFormat().order == tcuTexture.ChannelOrder.D || dst.getFormat().order == tcuTexture.ChannelOrder.DS); // depth components are limited to [0,1] range + + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(sub, src); + else + tcuTextureUtil.copy(sub, src); + } else if (target == gl.TEXTURE_2D_ARRAY) { + // Validate size and level. + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || + height > this.m_limits.maxTexture2DSize || + depth > this.m_limits.maxTexture2DArrayLayers, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.floor(Math.log2(this.m_limits.maxTexture2DSize)), gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture2DArray} */ + var texture2DArray = /** @type {sglrReferenceContext.Texture2DArray} */ (unit.tex2DArrayBinding.texture); + + if (this.conditionalSetError(!texture2DArray.hasLevel(level), gl.INVALID_OPERATION)) + return; + + dst = texture2DArray.getLevel(level); + if (this.conditionalSetError(xoffset + width > dst.getWidth() || + yoffset + height > dst.getHeight() || + zoffset + depth > dst.getDepth(), + gl.INVALID_VALUE)) + return; + + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var imageHeight = this.m_pixelUnpackImageHeight > 0 ? this.m_pixelUnpackImageHeight : height; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var slicePitch = imageHeight * rowPitch; + var skip = this.m_pixelUnpackSkipImages * slicePitch + this.m_pixelUnpackSkipRows * rowPitch + + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + depth: depth, + rowPitch: rowPitch, + slicePitch: slicePitch, + data: data, + offset: offset + skip}); + + sub = tcuTextureUtil.getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth); + isDstFloatDepthFormat = (dst.getFormat().order == tcuTexture.ChannelOrder.D || dst.getFormat().order == tcuTexture.ChannelOrder.DS); // depth components are limited to [0,1] range + + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(sub, src); + else + tcuTextureUtil.copy(sub, src); + } else if (target == gl.TEXTURE_3D) { + // Validate size and level. + if (this.conditionalSetError(width > this.m_limits.maxTexture3DSize || + height > this.m_limits.maxTexture3DSize || + depth > this.m_limits.maxTexture3DSize, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.floor(Math.log2(this.m_limits.maxTexture3DSize)), gl.INVALID_VALUE)) + return; + + var texture3D = /** @type {sglrReferenceContext.Texture3D} */ (unit.tex3DBinding.texture); + + if (this.conditionalSetError(!texture3D.hasLevel(level), gl.INVALID_OPERATION)) + return; + + dst = texture3D.getLevel(level); + if (this.conditionalSetError(xoffset + width > dst.getWidth() || + yoffset + height > dst.getHeight() || + zoffset + depth > dst.getDepth(), + gl.INVALID_VALUE)) + return; + + var rowLen = this.m_pixelUnpackRowLength > 0 ? this.m_pixelUnpackRowLength : width; + var imageHeight = this.m_pixelUnpackImageHeight > 0 ? this.m_pixelUnpackImageHeight : height; + var rowPitch = deMath.deAlign32(rowLen * transferFmt.getPixelSize(), this.m_pixelUnpackAlignment); + var slicePitch = imageHeight * rowPitch; + var skip = this.m_pixelUnpackSkipImages * slicePitch + this.m_pixelUnpackSkipRows * rowPitch + + this.m_pixelUnpackSkipPixels * transferFmt.getPixelSize(); + src = new tcuTexture.ConstPixelBufferAccess({ + format: transferFmt, + width: width, + height: height, + depth: depth, + rowPitch: rowPitch, + slicePitch: slicePitch, + data: data, + offset: offset + skip}); + + sub = tcuTextureUtil.getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth); + + isDstFloatDepthFormat = (dst.getFormat().order == tcuTexture.ChannelOrder.D || dst.getFormat().order == tcuTexture.ChannelOrder.DS); // depth components are limited to [0,1] range + if (isDstFloatDepthFormat) + sglrReferenceContext.depthValueFloatClampCopy(sub, src); + else + tcuTextureUtil.copy(sub, src); + } else + this.setError(gl.INVALID_ENUM); + }; + + /** + * @param {number} target + * @param {number} level + * @param {number} internalFormat + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @param {number} border + */ + sglrReferenceContext.ReferenceContext.prototype.copyTexImage2D = function(target, level, internalFormat, x, y, width, height, border) { + /** @type {sglrReferenceContext.TextureUnit} */var unit = this.m_textureUnits[this.m_activeTexture]; + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var src = this.getReadColorbuffer(); + + if (this.conditionalSetError(border != 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(width < 0 || height < 0 || level < 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(src.isEmpty(), gl.INVALID_OPERATION)) + return; + + // Map storage format. + /** @type {tcuTexture.TextureFormat} */ var storageFmt = sglrReferenceContext.mapInternalFormat(internalFormat); + if (this.conditionalSetError(!storageFmt, gl.INVALID_ENUM)) + return; + + if (target == gl.TEXTURE_2D) { + // Validate size and level. + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || height > this.m_limits.maxTexture2DSize, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.floor(Math.log2(this.m_limits.maxTexture2DSize)), gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture2D} */ + var texture = /** @type {sglrReferenceContext.Texture2D} */ (unit.tex2DBinding.texture); + + if (texture.isImmutable()) { + if (this.conditionalSetError(!texture.hasLevel(level), gl.INVALID_OPERATION)) + return; + + /** @type {tcuTexture.PixelBufferAccess} */ var dst = texture.getLevel(level); + if (this.conditionalSetError(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight(), gl.INVALID_OPERATION)) + return; + } else { + texture.allocLevel(level, storageFmt, width, height); + } + + // Copy from current framebuffer. + /** @type {tcuTexture.PixelBufferAccess} */ var dst = texture.getLevel(level); + for (var yo = 0; yo < height; yo++) { + for (var xo = 0; xo < width; xo++) { + if (!deMath.deInBounds32(x+xo, 0, src.raw().getHeight()) || !deMath.deInBounds32(y+yo, 0, src.raw().getDepth())) + continue; // Undefined pixel. + + dst.setPixel(src.resolveMultisamplePixel(x+xo, y+yo), xo, yo); + } + } + } else if (target == gl.TEXTURE_CUBE_MAP_NEGATIVE_X || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_X || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Y || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Y || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Z || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Z) { + // Validate size and level. + if (this.conditionalSetError(width != height || width > this.m_limits.maxTextureCubeSize, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(level > Math.floor(Math.log2(this.m_limits.maxTextureCubeSize)), gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.TextureCube} */ + var texture = /** @type {sglrReferenceContext.TextureCube} */ (unit.texCubeBinding.texture); + var face = sglrReferenceContext.mapGLCubeFace(target); + + if (texture.isImmutable()) { + if (this.conditionalSetError(!texture.hasFace(level, face), gl.INVALID_OPERATION)) + return; + + /** @type {tcuTexture.PixelBufferAccess} */ var dst = texture.getFace(level, face); + if (this.conditionalSetError(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight(), gl.INVALID_OPERATION)) + return; + } else { + texture.allocLevel(level, face, storageFmt, width, height); + } + + // Copy from current framebuffer. + /** @type {tcuTexture.PixelBufferAccess} */ var dst = texture.getFace(level, face); + for (var yo = 0; yo < height; yo++) { + for (var xo = 0; xo < width; xo++) { + if (!deMath.deInBounds32(x+xo, 0, src.raw().getHeight()) || !deMath.deInBounds32(y+yo, 0, src.raw().getDepth())) + continue; // Undefined pixel. + + dst.setPixel(src.resolveMultisamplePixel(x+xo, y+yo), xo, yo); + } + } + } else { + this.setError(gl.INVALID_ENUM); + } + } + + /** + * @param {number} target + * @param {number} level + * @param {number} xoffset + * @param {number} yoffset + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + sglrReferenceContext.ReferenceContext.prototype.copyTexSubImage2D = function(target, level, xoffset, yoffset, x, y, width, height) { + /** @type {sglrReferenceContext.TextureUnit} */var unit = this.m_textureUnits[this.m_activeTexture]; + /** @type {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} */ var src = this.getReadColorbuffer(); + + if (this.conditionalSetError(xoffset < 0 || yoffset < 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(width < 0 || height < 0 || level < 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(src.isEmpty(), gl.INVALID_OPERATION)) + return; + + if (target == gl.TEXTURE_2D) { + /** @type {sglrReferenceContext.Texture2D} */ + var texture = /** @type {sglrReferenceContext.Texture2D} */ (unit.tex2DBinding.texture); + + if (this.conditionalSetError(!texture.hasLevel(level), gl.INVALID_VALUE)) + return; + + /** @type {tcuTexture.PixelBufferAccess} */ var dst = texture.getLevel(level); + + if (this.conditionalSetError(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight(), gl.INVALID_VALUE)) + return; + + for (var yo = 0; yo < height; yo++) { + for (var xo = 0; xo < width; xo++) { + if (!deMath.deInBounds32(x+xo, 0, src.raw().getHeight()) || !deMath.deInBounds32(y+yo, 0, src.raw().getDepth())) + continue; + + dst.setPixel(src.resolveMultisamplePixel(x+xo, y+yo), xo+xoffset, yo+yoffset); + } + } + } else if (target == gl.TEXTURE_CUBE_MAP_NEGATIVE_X || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_X || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Y || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Y || + target == gl.TEXTURE_CUBE_MAP_NEGATIVE_Z || + target == gl.TEXTURE_CUBE_MAP_POSITIVE_Z) { + /** @type {sglrReferenceContext.TextureCube} */ + var texture = /** @type {sglrReferenceContext.TextureCube} */ (unit.texCubeBinding.texture); + var face = sglrReferenceContext.mapGLCubeFace(target); + + if (this.conditionalSetError(!texture.hasFace(level, face), gl.INVALID_VALUE)) + return; + + /** @type {tcuTexture.PixelBufferAccess} */ var dst = texture.getFace(level, face); + + if (this.conditionalSetError(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight(), gl.INVALID_VALUE)) + return; + + for (var yo = 0; yo < height; yo++) { + for (var xo = 0; xo < width; xo++) { + if (!deMath.deInBounds32(x+xo, 0, src.raw().getHeight()) || !deMath.deInBounds32(y+yo, 0, src.raw().getDepth())) + continue; + + dst.setPixel(src.resolveMultisamplePixel(x+xo, y+yo), xo+xoffset, yo+yoffset); + } + } + } else { + this.setError(gl.INVALID_ENUM); + } + } + + sglrReferenceContext.ReferenceContext.prototype.texStorage3D = function(target, levels, internalFormat, width, height, depth) { + /** @type {sglrReferenceContext.TextureUnit} */var unit = this.m_textureUnits[this.m_activeTexture]; + + if (this.conditionalSetError(width <= 0 || height <= 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(levels < 1 || levels > Math.floor(Math.log2(Math.max(width, height))) + 1, gl.INVALID_VALUE)) + return; + + // Map storage format. + /** @type {tcuTexture.TextureFormat} */ var storageFmt = sglrReferenceContext.mapInternalFormat(internalFormat); + if (this.conditionalSetError(!storageFmt, gl.INVALID_ENUM)) + return; + + if (target == gl.TEXTURE_2D_ARRAY) { + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || + height > this.m_limits.maxTexture2DSize || + depth >= this.m_limits.maxTexture2DArrayLayers, gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture2DArray} */ + var textureArray = /** @type {sglrReferenceContext.Texture2DArray} */ (unit.tex2DArrayBinding.texture); + if (this.conditionalSetError(textureArray.isImmutable(), gl.INVALID_OPERATION)) + return; + + textureArray.clearLevels(); + textureArray.setImmutable(); + + for (var level = 0; level < levels; level++) { + var levelW = Math.max(1, width >> level); + var levelH = Math.max(1, height >> level); + + textureArray.allocLevel(level, storageFmt, levelW, levelH, depth); + } + } else if (target == gl.TEXTURE_3D) { + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || + height > this.m_limits.maxTexture2DSize || + depth >= this.m_limits.maxTexture3DSize, gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture3D} */ + var texture3D = /** @type {sglrReferenceContext.Texture3D} */ (unit.tex3DBinding.texture); + if (this.conditionalSetError(texture3D.isImmutable(), gl.INVALID_OPERATION)) + return; + + texture3D.clearLevels(); + texture3D.setImmutable(); + + for (var level = 0; level < levels; level++) { + var levelW = Math.max(1, width >> level); + var levelH = Math.max(1, height >> level); + var levelD = Math.max(1, depth >> level); + + texture3D.allocLevel(level, storageFmt, levelW, levelH, levelD); + } + } else + this.setError(gl.INVALID_ENUM); + }; + + sglrReferenceContext.ReferenceContext.prototype.texStorage2D = function(target, levels, internalFormat, width, height) { + /** @type {sglrReferenceContext.TextureUnit} */var unit = this.m_textureUnits[this.m_activeTexture]; + + if (this.conditionalSetError(width <= 0 || height <= 0, gl.INVALID_VALUE)) + return; + if (this.conditionalSetError(levels < 1 || levels > Math.floor(Math.log2(Math.max(width, height))) + 1, gl.INVALID_VALUE)) + return; + + // Map storage format. + /** @type {tcuTexture.TextureFormat} */ var storageFmt = sglrReferenceContext.mapInternalFormat(internalFormat); + if (this.conditionalSetError(!storageFmt, gl.INVALID_ENUM)) + return; + + if (target == gl.TEXTURE_2D) { + if (this.conditionalSetError(width > this.m_limits.maxTexture2DSize || height > this.m_limits.maxTexture2DSize, gl.INVALID_VALUE)) + return; + + /** @type {sglrReferenceContext.Texture2D} */ + var texture = /** @type {sglrReferenceContext.Texture2D} */ (unit.tex2DBinding.texture); + if (this.conditionalSetError(texture.isImmutable(), gl.INVALID_OPERATION)) + return; + + texture.clearLevels(); + texture.setImmutable(); + + for (var level = 0; level < levels; level++) { + var levelW = Math.max(1, width >> level); + var levelH = Math.max(1, height >> level); + + texture.allocLevel(level, storageFmt, levelW, levelH); + } + } else if (target == gl.TEXTURE_CUBE_MAP) { + if (this.conditionalSetError(width != height || width > this.m_limits.maxTextureCubeSize, gl.INVALID_VALUE)) + return; + var textureCube = /** @type {sglrReferenceContext.TextureCube} */ (unit.texCubeBinding.texture); + if (this.conditionalSetError(textureCube.isImmutable(), gl.INVALID_OPERATION)) + return; + + textureCube.clearLevels(); + textureCube.setImmutable(); + + for (var level = 0; level < levels; level++) { + var levelW = Math.max(1, width >> level); + var levelH = Math.max(1, height >> level); + + for (var face in tcuTexture.CubeFace) + textureCube.allocLevel(level, tcuTexture.CubeFace[face], storageFmt, levelW, levelH); + } + } else + this.setError(gl.INVALID_ENUM); + }; + + /** + * @param {number} value + * @return {?tcuTexture.WrapMode} + */ + sglrReferenceContext.mapGLWrapMode = function(value) { + switch (value) { + case gl.CLAMP_TO_EDGE: return tcuTexture.WrapMode.CLAMP_TO_EDGE; + case gl.REPEAT: return tcuTexture.WrapMode.REPEAT_GL; + case gl.MIRRORED_REPEAT: return tcuTexture.WrapMode.MIRRORED_REPEAT_GL; + } + return null; + }; + + /** + * @param {number} value + * @return {?tcuTexture.FilterMode} + */ + sglrReferenceContext.mapGLFilterMode = function(value) { + switch (value) { + case gl.NEAREST: return tcuTexture.FilterMode.NEAREST; + case gl.LINEAR: return tcuTexture.FilterMode.LINEAR; + case gl.NEAREST_MIPMAP_NEAREST: return tcuTexture.FilterMode.NEAREST_MIPMAP_NEAREST; + case gl.NEAREST_MIPMAP_LINEAR: return tcuTexture.FilterMode.NEAREST_MIPMAP_LINEAR; + case gl.LINEAR_MIPMAP_NEAREST: return tcuTexture.FilterMode.LINEAR_MIPMAP_NEAREST; + case gl.LINEAR_MIPMAP_LINEAR: return tcuTexture.FilterMode.LINEAR_MIPMAP_LINEAR; + } + return null; + }; + + /** + * @param {number} target + * @param {number} pname + * @param {number} value + */ + sglrReferenceContext.ReferenceContext.prototype.texParameteri = function(target, pname, value) { + /** @type {sglrReferenceContext.TextureUnit} */ var unit = this.m_textureUnits[this.m_activeTexture]; + /** @type {sglrReferenceContext.TextureContainer} */ var container = null; + + switch (target) { + case gl.TEXTURE_2D: container = unit.tex2DBinding; break; + case gl.TEXTURE_CUBE_MAP: container = unit.texCubeBinding; break; + case gl.TEXTURE_2D_ARRAY: container = unit.tex2DArrayBinding; break; + case gl.TEXTURE_3D: container = unit.tex3DBinding; break; + + default: this.setError(gl.INVALID_ENUM); + } + + if (!container) + return; + + /** @type {sglrReferenceContext.Texture} */ + var texture = container.texture; + + switch (pname) { + case gl.TEXTURE_WRAP_S: { + /** @type {?tcuTexture.WrapMode} */ var wrapS = sglrReferenceContext.mapGLWrapMode(value); + if (this.conditionalSetError(null == wrapS, gl.INVALID_VALUE)) + return; + texture.getSampler().wrapS = /** @type {tcuTexture.WrapMode} */ (wrapS); + break; + } + + case gl.TEXTURE_WRAP_T: { + /** @type {?tcuTexture.WrapMode} */ var wrapT = sglrReferenceContext.mapGLWrapMode(value); + if (this.conditionalSetError(null == wrapT, gl.INVALID_VALUE)) + return; + texture.getSampler().wrapT = /** @type {tcuTexture.WrapMode} */ (wrapT); + break; + } + + case gl.TEXTURE_WRAP_R: { + /** @type {?tcuTexture.WrapMode} */ var wrapR = sglrReferenceContext.mapGLWrapMode(value); + if (this.conditionalSetError(null == wrapR, gl.INVALID_VALUE)) + return; + texture.getSampler().wrapR = /** @type {tcuTexture.WrapMode} */ (wrapR); + break; + } + + case gl.TEXTURE_MIN_FILTER: { + /** @type {?tcuTexture.FilterMode} */ var minMode = sglrReferenceContext.mapGLFilterMode(value); + if (this.conditionalSetError(null == minMode, gl.INVALID_VALUE)) + return; + texture.getSampler().minFilter = /** @type {tcuTexture.FilterMode} */ (minMode); + break; + } + + case gl.TEXTURE_MAG_FILTER: { + /** @type {?tcuTexture.FilterMode} */ var magMode = sglrReferenceContext.mapGLFilterMode(value); + if (this.conditionalSetError(null == magMode, gl.INVALID_VALUE)) + return; + texture.getSampler().magFilter = /** @type {tcuTexture.FilterMode} */ (magMode); + break; + } + + case gl.TEXTURE_MAX_LEVEL: { + if (this.conditionalSetError(value < 0, gl.INVALID_VALUE)) + return; + texture.setMaxLevel(value); + break; + } + + default: + this.setError(gl.INVALID_ENUM); + return; + } + }; + + sglrReferenceContext.ReferenceContext.prototype.invalidateFramebuffer = function(target, attachments) {}; + sglrReferenceContext.ReferenceContext.prototype.invalidateSubFramebuffer = function(target, attachments, x, y, width, height) {}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceContextTest.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceContextTest.js new file mode 100644 index 000000000..cc8abf596 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceContextTest.js @@ -0,0 +1,834 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.simplereference.sglrReferenceContextTest'); +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.opengl.gluDrawUtil'); +goog.require('framework.opengl.simplereference.sglrReferenceContext'); +goog.require('framework.opengl.simplereference.sglrShaderProgram'); +goog.require('framework.referencerenderer.rrFragmentOperations'); +goog.require('framework.referencerenderer.rrGenericVector'); +goog.require('framework.referencerenderer.rrShadingContext'); +goog.require('framework.referencerenderer.rrVertexAttrib'); +goog.require('framework.referencerenderer.rrVertexPacket'); + +goog.scope(function() { + var sglrReferenceContextTest = framework.opengl.simplereference.sglrReferenceContextTest; + var sglrReferenceContext = framework.opengl.simplereference.sglrReferenceContext; + var tcuTestCase = framework.common.tcuTestCase; + var tcuPixelFormat = framework.common.tcuPixelFormat; + var gluDrawUtil = framework.opengl.gluDrawUtil; + var tcuSurface = framework.common.tcuSurface; + var tcuLogImage = framework.common.tcuLogImage; + var sglrShaderProgram = framework.opengl.simplereference.sglrShaderProgram; + var rrGenericVector = framework.referencerenderer.rrGenericVector; + var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; + var rrShadingContext = framework.referencerenderer.rrShadingContext; + var rrVertexPacket = framework.referencerenderer.rrVertexPacket; + var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations; + var tcuRGBA = framework.common.tcuRGBA; + + /** + * @constructor + * @extends {tcuTestCase.DeqpTest} + * @param {string} name + * @param {string} description + */ + sglrReferenceContextTest.ClearContext = function(name, description) { + tcuTestCase.DeqpTest.call(this, name, description); + }; + + sglrReferenceContextTest.ClearContext.prototype = Object.create(tcuTestCase.DeqpTest.prototype); + sglrReferenceContextTest.ClearContext.prototype.constructor = sglrReferenceContextTest.ClearContext; + + sglrReferenceContextTest.ClearContext.prototype.init = function() {}; + + sglrReferenceContextTest.ClearContext.prototype.iterate = function() { + + var width = 200; + var height = 188; + var samples = 1; + var limits = new sglrReferenceContext.ReferenceContextLimits(gl); + var format = new tcuPixelFormat.PixelFormat(8, 8, 8, 8); + var buffers = new sglrReferenceContext.ReferenceContextBuffers(format, 24, 8, width, height, samples); + var ctx = new sglrReferenceContext.ReferenceContext(limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); + ctx.clearColor(1, 0, 0, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + var pixels = new tcuSurface.Surface(width, height); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + var numFailedPixels = 0; + var redPixel = new gluDrawUtil.Pixel([255, 0, 0, 255]); + for (var x = 0; x < width; x++) + for (var y = 0; y < height; y++) { + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(x, y)); + if (!pixel.equals(redPixel)) + numFailedPixels += 1; + } + + var access = pixels.getAccess(); + + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + ctx.scissor(width / 4, height / 4, width / 2, height / 2); + ctx.enable(gl.SCISSOR_TEST); + ctx.clearColor(0, 1, 1, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + numFailedPixels = 0; + var greenBluePixel = new gluDrawUtil.Pixel([0, 255, 255, 255]); + for (var x = 0; x < width; x++) + for (var y = 0; y < height; y++) { + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(x, y)); + if ((x >= width / 4 && x < width - width / 4) && (y >= height / 4 && y < height - height / 4)) { + if (!pixel.equals(greenBluePixel)) + numFailedPixels += 1; + } else + if (!pixel.equals(redPixel)) + numFailedPixels += 1; + } + + access = pixels.getAccess(); + + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + return tcuTestCase.IterateResult.STOP; + }; + + /** + * @constructor + * @extends {tcuTestCase.DeqpTest} + * @param {string} name + * @param {string} description + */ + sglrReferenceContextTest.Framebuffer = function(name, description) { + tcuTestCase.DeqpTest.call(this, name, description); + }; + + sglrReferenceContextTest.Framebuffer.prototype = Object.create(tcuTestCase.DeqpTest.prototype); + sglrReferenceContextTest.Framebuffer.prototype.constructor = sglrReferenceContextTest.Framebuffer; + + sglrReferenceContextTest.Framebuffer.prototype.init = function() {}; + + sglrReferenceContextTest.Framebuffer.prototype.iterate = function() { + var limits = new sglrReferenceContext.ReferenceContextLimits(gl); + var format = new tcuPixelFormat.PixelFormat(8, 8, 8, 8); + var width = 200; + var height = 188; + var samples = 1; + var buffers = new sglrReferenceContext.ReferenceContextBuffers(format, 24, 8, width, height, samples); + var ctx = new sglrReferenceContext.ReferenceContext(limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); + ctx.clearColor(0, 0, 1, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + var fbo = ctx.createFramebuffer(); + var rbo = ctx.createRenderbuffer(); + ctx.bindFramebuffer(gl.FRAMEBUFFER, fbo); + ctx.bindRenderbuffer(gl.RENDERBUFFER, rbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, width, height); + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo); + bufferedLogToConsole('Framebuffer status: ' + (ctx.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE)); + ctx.clearColor(1, 0, 0, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + var pixels = new tcuSurface.Surface(width, height); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + var numFailedPixels = 0; + var redPixel = new gluDrawUtil.Pixel([255, 0, 0, 255]); + for (var x = 0; x < width; x++) + for (var y = 0; y < height; y++) { + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(x, y)); + if (!pixel.equals(redPixel)) + numFailedPixels += 1; + } + var access = pixels.getAccess(); + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + ctx.scissor(width / 4, height / 4, width / 2, height / 2); + ctx.enable(gl.SCISSOR_TEST); + ctx.clearColor(0, 1, 1, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + numFailedPixels = 0; + var greenBluePixel = new gluDrawUtil.Pixel([0, 255, 255, 255]); + for (var x = 0; x < width; x++) + for (var y = 0; y < height; y++) { + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(x, y)); + if ((x >= width / 4 && x < width - width / 4) && (y >= height / 4 && y < height - height / 4)) { + if (!pixel.equals(greenBluePixel)) + numFailedPixels += 1; + } else + if (!pixel.equals(redPixel)) + numFailedPixels += 1; + } + + access = pixels.getAccess(); + + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, null); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + var bluePixel = new gluDrawUtil.Pixel([0, 0, 255, 255]); + for (var x = 0; x < width; x++) + for (var y = 0; y < height; y++) { + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(x, y)); + if (!pixel.equals(bluePixel)) + numFailedPixels += 1; + } + access = pixels.getAccess(); + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + return tcuTestCase.IterateResult.STOP; + }; + + /** + * @constructor + * @extends {tcuTestCase.DeqpTest} + * @param {string} name + * @param {string} description + */ + sglrReferenceContextTest.Shader = function(name, description) { + tcuTestCase.DeqpTest.call(this, name, description); + }; + + sglrReferenceContextTest.Shader.prototype = Object.create(tcuTestCase.DeqpTest.prototype); + sglrReferenceContextTest.Shader.prototype.constructor = sglrReferenceContextTest.Shader; + + sglrReferenceContextTest.Shader.prototype.init = function() {}; + + sglrReferenceContextTest.Shader.prototype.iterate = function() { + var limits = new sglrReferenceContext.ReferenceContextLimits(gl); + var format = new tcuPixelFormat.PixelFormat(8, 8, 8, 8); + var width = 200; + var height = 188; + var samples = 1; + var buffers = new sglrReferenceContext.ReferenceContextBuffers(format, 24, 8, width, height, samples); + var ctx = new sglrReferenceContext.ReferenceContext(limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); + ctx.clearColor(0, 0, 1, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + var vertices = [ + -0.5, 0.5, + 0.5, 0.5, + -0.5, -0.5, + 0.5, 0.5, + 0.5, -0.5, + -0.5, -0.5 + ]; + + var vertices32 = new Float32Array(vertices); + + var squareVerticesBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, vertices32, gl.STATIC_DRAW); + + var colors = [ + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1 + ]; + + var colors32 = new Float32Array(colors); + + var squareColorsBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, colors32, gl.STATIC_DRAW); + + /** @type {sglrShaderProgram.ShaderProgramDeclaration} */ var progDecl = new sglrShaderProgram.ShaderProgramDeclaration(); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexPosition', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexColor', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexSource(new sglrShaderProgram.VertexSource('')); + + progDecl.pushFragmentOutput(new sglrShaderProgram.FragmentOutput(rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushFragmentSource(new sglrShaderProgram.FragmentSource('')); + + /** @type {sglrReferenceContextTest.ContextShaderProgram} */ var program = new sglrReferenceContextTest.ContextShaderProgram(progDecl); + + //Create program + ctx.createProgram(program); + + //Use program + ctx.useProgram(program); + + var vertexPositionAttribute = ctx.getAttribLocation(program, 'aVertexPosition'); + var vertexColorAttribute = ctx.getAttribLocation(program, 'aVertexColor'); + ctx.enableVertexAttribArray(vertexPositionAttribute); + ctx.enableVertexAttribArray(vertexColorAttribute); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.vertexAttribPointer(vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0); + + ctx.drawQuads(gl.TRIANGLES, 0, 6); + + var pixels = new tcuSurface.Surface(width, height); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + var numFailedPixels = 0; + + var redPixel = new gluDrawUtil.Pixel([255, 0, 0, 255]); + var bluePixel = new gluDrawUtil.Pixel([0, 0, 255, 255]); + + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(0, 0)); + if (!pixel.equals(bluePixel)) + numFailedPixels += 1; + + pixel = new gluDrawUtil.Pixel(pixels.getPixel(100, 94)); + if (!pixel.equals(redPixel)) + numFailedPixels += 1; + + var access = pixels.getAccess(); + + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + return tcuTestCase.IterateResult.STOP; + }; + + /** + * @constructor + * @extends {tcuTestCase.DeqpTest} + * @param {string} name + * @param {string} description + */ + sglrReferenceContextTest.TriangleStrip = function(name, description) { + tcuTestCase.DeqpTest.call(this, name, description); + }; + + sglrReferenceContextTest.TriangleStrip.prototype = Object.create(tcuTestCase.DeqpTest.prototype); + sglrReferenceContextTest.TriangleStrip.prototype.constructor = sglrReferenceContextTest.TriangleStrip; + + sglrReferenceContextTest.TriangleStrip.prototype.init = function() {}; + + sglrReferenceContextTest.TriangleStrip.prototype.iterate = function() { + var limits = new sglrReferenceContext.ReferenceContextLimits(gl); + var format = new tcuPixelFormat.PixelFormat(8, 8, 8, 8); + var width = 200; + var height = 188; + var samples = 1; + var buffers = new sglrReferenceContext.ReferenceContextBuffers(format, 24, 8, width, height, samples); + var ctx = new sglrReferenceContext.ReferenceContext(limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); + ctx.clearColor(0, 0, 1, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + var vertices = [ + -0.5, 0.5, + 0.5, 0.5, + -0.5, 0, + 0.5, 0, + -0.5, -0.5, + 0.5, -0.5 + ]; + + var vertices32 = new Float32Array(vertices); + + var squareVerticesBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, vertices32, gl.STATIC_DRAW); + + var colors = [ + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1 + ]; + + var colors32 = new Float32Array(colors); + + var squareColorsBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, colors32, gl.STATIC_DRAW); + + /** @type {sglrShaderProgram.ShaderProgramDeclaration} */ var progDecl = new sglrShaderProgram.ShaderProgramDeclaration(); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexPosition', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexColor', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexSource(new sglrShaderProgram.VertexSource('')); + + progDecl.pushFragmentOutput(new sglrShaderProgram.FragmentOutput(rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushFragmentSource(new sglrShaderProgram.FragmentSource('')); + + /** @type {sglrReferenceContextTest.ContextShaderProgram} */ var program = new sglrReferenceContextTest.ContextShaderProgram(progDecl); + + //Create program + ctx.createProgram(program); + + //Use program + ctx.useProgram(program); + + var vertexPositionAttribute = ctx.getAttribLocation(program, 'aVertexPosition'); + var vertexColorAttribute = ctx.getAttribLocation(program, 'aVertexColor'); + ctx.enableVertexAttribArray(vertexPositionAttribute); + ctx.enableVertexAttribArray(vertexColorAttribute); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.vertexAttribPointer(vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0); + + ctx.drawQuads(gl.TRIANGLE_STRIP, 0, 6); + + var pixels = new tcuSurface.Surface(width, height); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + var numFailedPixels = 0; + + var redPixel = new gluDrawUtil.Pixel([255, 0, 0, 255]); + var bluePixel = new gluDrawUtil.Pixel([0, 0, 255, 255]); + + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(0, 0)); + if (!pixel.equals(bluePixel)) + numFailedPixels += 1; + + pixel = new gluDrawUtil.Pixel(pixels.getPixel(100, 94)); + if (!pixel.equals(redPixel)) + numFailedPixels += 1; + + var access = pixels.getAccess(); + + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + return tcuTestCase.IterateResult.STOP; + }; + + /** + * @constructor + * @extends {tcuTestCase.DeqpTest} + * @param {string} name + * @param {string} description + */ + sglrReferenceContextTest.TriangleFan = function(name, description) { + tcuTestCase.DeqpTest.call(this, name, description); + }; + + sglrReferenceContextTest.TriangleFan.prototype = Object.create(tcuTestCase.DeqpTest.prototype); + sglrReferenceContextTest.TriangleFan.prototype.constructor = sglrReferenceContextTest.TriangleFan; + + sglrReferenceContextTest.TriangleFan.prototype.init = function() {}; + + sglrReferenceContextTest.TriangleFan.prototype.iterate = function() { + var limits = new sglrReferenceContext.ReferenceContextLimits(gl); + var format = new tcuPixelFormat.PixelFormat(8, 8, 8, 8); + var width = 200; + var height = 188; + var samples = 1; + var buffers = new sglrReferenceContext.ReferenceContextBuffers(format, 24, 8, width, height, samples); + var ctx = new sglrReferenceContext.ReferenceContext(limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); + ctx.clearColor(0, 0, 1, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + var vertices = [ + -0.5, 0, + -0.5, 0.5, + 0.5, 0.5, + 0.5, 0, + 0.5, -0.5, + -0.5, -0.5 + ]; + + var vertices32 = new Float32Array(vertices); + + var squareVerticesBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, vertices32, gl.STATIC_DRAW); + + var colors = [ + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1, + 1, 0, 0, 1 + ]; + + var colors32 = new Float32Array(colors); + + var squareColorsBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, colors32, gl.STATIC_DRAW); + + /** @type {sglrShaderProgram.ShaderProgramDeclaration} */ var progDecl = new sglrShaderProgram.ShaderProgramDeclaration(); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexPosition', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexColor', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexSource(new sglrShaderProgram.VertexSource('')); + + progDecl.pushFragmentOutput(new sglrShaderProgram.FragmentOutput(rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushFragmentSource(new sglrShaderProgram.FragmentSource('')); + + /** @type {sglrReferenceContextTest.ContextShaderProgram} */ var program = new sglrReferenceContextTest.ContextShaderProgram(progDecl); + + //Create program + ctx.createProgram(program); + + //Use program + ctx.useProgram(program); + + var vertexPositionAttribute = ctx.getAttribLocation(program, 'aVertexPosition'); + var vertexColorAttribute = ctx.getAttribLocation(program, 'aVertexColor'); + ctx.enableVertexAttribArray(vertexPositionAttribute); + ctx.enableVertexAttribArray(vertexColorAttribute); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.vertexAttribPointer(vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0); + + ctx.drawQuads(gl.TRIANGLE_FAN, 0, 6); + + var pixels = new tcuSurface.Surface(width, height); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + var numFailedPixels = 0; + + var redPixel = new gluDrawUtil.Pixel([255, 0, 0, 255]); + var bluePixel = new gluDrawUtil.Pixel([0, 0, 255, 255]); + + var pixel = new gluDrawUtil.Pixel(pixels.getPixel(0, 0)); + if (!pixel.equals(bluePixel)) + numFailedPixels += 1; + + pixel = new gluDrawUtil.Pixel(pixels.getPixel(100, 94)); + if (!pixel.equals(redPixel)) + numFailedPixels += 1; + + var access = pixels.getAccess(); + + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + return tcuTestCase.IterateResult.STOP; + }; + + /** + * @constructor + * @extends {tcuTestCase.DeqpTest} + * @param {string} name + * @param {string} description + */ + sglrReferenceContextTest.DrawElements = function(name, description) { + tcuTestCase.DeqpTest.call(this, name, description); + }; + + sglrReferenceContextTest.DrawElements.prototype = Object.create(tcuTestCase.DeqpTest.prototype); + sglrReferenceContextTest.DrawElements.prototype.constructor = sglrReferenceContextTest.DrawElements; + + sglrReferenceContextTest.DrawElements.prototype.init = function() {}; + + sglrReferenceContextTest.DrawElements.prototype.iterate = function() { + var limits = new sglrReferenceContext.ReferenceContextLimits(gl); + var format = new tcuPixelFormat.PixelFormat(8, 8, 8, 8); + var width = 200; + var height = 188; + var samples = 1; + var buffers = new sglrReferenceContext.ReferenceContextBuffers(format, 24, 8, width, height, samples); + var ctx = new sglrReferenceContext.ReferenceContext(limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); + ctx.clearColor(0, 0, 1, 1); + ctx.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + var vertices = [ + -0.5, 0.5, + 0, 0.5, + 0.4, 0.5, + + -0.5, 0.1, + 0, 0.1, + 0.4, 0.1, + + -0.5, -0.7, + 0, -0.7, + 0.4, -0.7 + ]; + + var vertices32 = new Float32Array(vertices); + + var squareVerticesBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, vertices32, gl.STATIC_DRAW); + + var indices = [ + 0, 1, 3, 1, 3, 4, + 1, 2, 4, 2, 4, 5, + 3, 4, 6, 4, 6, 7, + 4, 5, 7, 5, 7, 8 + ]; + var indicesBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer); + ctx.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); + + var colors = [ + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + 1, 1, 1, 1, + 1, 1, 0, 1, + 0, 1, 1, 1, + 1, 0, 1, 1, + 0.5, 0.5, 0.5, 1, + 0, 0, 0, 0 + ]; + + var colors32 = new Float32Array(colors); + + var squareColorsBuffer = ctx.createBuffer(); + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.bufferData(gl.ARRAY_BUFFER, colors32, gl.STATIC_DRAW); + + /** @type {sglrShaderProgram.ShaderProgramDeclaration} */ var progDecl = new sglrShaderProgram.ShaderProgramDeclaration(); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexPosition', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexAttribute(new sglrShaderProgram.VertexAttribute('aVertexColor', rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushVertexSource(new sglrShaderProgram.VertexSource('')); + + progDecl.pushFragmentOutput(new sglrShaderProgram.FragmentOutput(rrGenericVector.GenericVecType.FLOAT)); + + progDecl.pushFragmentSource(new sglrShaderProgram.FragmentSource('')); + + /** @type {sglrReferenceContextTest.ContextShaderProgram} */ var program = new sglrReferenceContextTest.ContextShaderProgram(progDecl); + + //Create program + ctx.createProgram(program); + + //Use program + ctx.useProgram(program); + + var vertexPositionAttribute = ctx.getAttribLocation(program, 'aVertexPosition'); + var vertexColorAttribute = ctx.getAttribLocation(program, 'aVertexColor'); + ctx.enableVertexAttribArray(vertexPositionAttribute); + ctx.enableVertexAttribArray(vertexColorAttribute); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer); + ctx.vertexAttribPointer(vertexPositionAttribute, 2, gl.FLOAT, false, 0, 0); + + ctx.bindBuffer(gl.ARRAY_BUFFER, squareColorsBuffer); + ctx.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0); + + ctx.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); + + var pixels = new tcuSurface.Surface(width, height); + ctx.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels.getAccess().getDataPtr()); + + var numFailedPixels = 0; + + var access = pixels.getAccess(); + + var pixelsTotest = [ + // location, color + [2, 1], [0, 0, 255, 255], + // The red vertex is between 140 and 141 so account for some blending with the white vertex + [50, 140], [255, 5, 5, 255], + [50, 28], [255, 0, 255, 255], + [139, 28], [0, 0, 0, 255], + [50, 102], [255, 255, 255, 255], + [139, 102], [0, 255, 255, 255] + ]; + + var threshold = new tcuRGBA.RGBA([5, 5, 5, 5]); + + for (var i = 0; i < pixelsTotest.length; i += 2) { + var location = pixelsTotest[i]; + var reference = new tcuRGBA.RGBA(pixelsTotest[i + 1]); + var color = access.getPixelInt(location[0], location[1]); + var pixel = new tcuRGBA.RGBA(color); + if (!tcuRGBA.compareThreshold(pixel, reference, threshold)) + numFailedPixels++; + } + + tcuLogImage.logImage('Result', '', access); + + if (numFailedPixels > 0) + testFailedOptions('Image comparison failed, got ' + numFailedPixels + ' non-equal pixels.', false); + else + testPassedOptions('Image comparison succeed', true); + + return tcuTestCase.IterateResult.STOP; + }; + + /** + * @constructor + * @extends {sglrShaderProgram.ShaderProgram} + * @param {sglrShaderProgram.ShaderProgramDeclaration} progDecl + */ + sglrReferenceContextTest.ContextShaderProgram = function(progDecl) { + sglrShaderProgram.ShaderProgram.call(this, progDecl); + }; + + sglrReferenceContextTest.ContextShaderProgram.prototype = Object.create(sglrShaderProgram.ShaderProgram.prototype); + sglrReferenceContextTest.ContextShaderProgram.prototype.constructor = sglrReferenceContextTest.ContextShaderProgram; + + /** + * @param {Array<rrVertexAttrib.VertexAttrib>} inputs + * @param {Array<rrVertexPacket.VertexPacket>} packets + * @param {number} numPackets + */ + sglrReferenceContextTest.ContextShaderProgram.prototype.shadeVertices = function(inputs, packets, numPackets) { + for (var packetNdx = 0; packetNdx < numPackets; ++packetNdx) { + /** @type {number} */ var varyingLocColor = 0; + + /** @type {rrVertexPacket.VertexPacket} */ var packet = packets[packetNdx]; + + // Calc output color + /** @type {Array<number>} */ var coord = [1.0, 1.0]; + /** @type {Array<number>} */ var color = [1.0, 1.0, 1.0]; + + for (var attribNdx = 0; attribNdx < this.getVertexShader().getInputs().length; attribNdx++) { + /** @type {number} */ var numComponents = inputs[attribNdx].componentCount; + + var attribValue = rrVertexAttrib.readVertexAttrib(inputs[attribNdx], packet.instanceNdx, packet.vertexNdx, this.getVertexShader().getInputs()[attribNdx].type); + + if (attribNdx == 0) { + coord[0] = attribValue[0]; + coord[1] = attribValue[1]; + } else { + color[0] = attribValue[0] * attribValue[3]; + color[1] = attribValue[1] * attribValue[3]; + color[2] = attribValue[2] * attribValue[3]; + } + } + + // Transform position + packet.position = [coord[0], coord[1], 1.0, 1.0]; + + // Pass color to FS + packet.outputs[varyingLocColor] = [color[0], color[1], color[2], 1.0]; + } + }; + + /** + * @param {Array<rrFragmentOperations.Fragment>} packets + * @param {rrShadingContext.FragmentShadingContext} context + */ + sglrReferenceContextTest.ContextShaderProgram.prototype.shadeFragments = function(packets, context) { + var varyingLocColor = 0; + + // Normal shading + for (var packetNdx = 0; packetNdx < packets.length; ++packetNdx) + packets[packetNdx].value = rrShadingContext.readTriangleVarying(packets[packetNdx], context, varyingLocColor); + }; + + sglrReferenceContextTest.init = function() { + var state = tcuTestCase.runner; + /** @type {tcuTestCase.DeqpTest} */ var testGroup = state.testCases; + + /** @type {tcuTestCase.DeqpTest} */ var referenceContextGroup = tcuTestCase.newTest('reference_context', 'Test reference context'); + + referenceContextGroup.addChild(new sglrReferenceContextTest.ClearContext('clear_context', 'Clear Context Test')); + referenceContextGroup.addChild(new sglrReferenceContextTest.Framebuffer('Framebuffer', 'Framebuffer Test')); + referenceContextGroup.addChild(new sglrReferenceContextTest.Shader('Shaders', 'Drawing using TRIANGLES')); + referenceContextGroup.addChild(new sglrReferenceContextTest.TriangleStrip('TriangleStrip', 'Drawing using TRIANGLE_STRIP')); + referenceContextGroup.addChild(new sglrReferenceContextTest.TriangleFan('TriangleFan', 'Drawing using TRIANGLE_FAN')); + referenceContextGroup.addChild(new sglrReferenceContextTest.DrawElements('DrawElements', 'Drawing using DrawElements and TRIANGLES')); + + testGroup.addChild(referenceContextGroup); + + }; + + sglrReferenceContextTest.run = function(context) { + gl = context; + //Set up Test Root parameters + var testName = 'single_reference_context'; + var testDescription = 'Single Reference Context Tests'; + var state = tcuTestCase.runner; + + state.testName = testName; + state.testCases = tcuTestCase.newTest(testName, testDescription, null); + + //Set up name and description of this test series. + setCurrentTestName(testName); + description(testDescription); + + try { + //Create test cases + sglrReferenceContextTest.init(); + //Run test cases + tcuTestCase.runTestCases(); + } + catch (err) { + bufferedLogToConsole(err); + tcuTestCase.runner.terminate(); + } + + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceUtils.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceUtils.js new file mode 100644 index 000000000..3b93dd8f9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrReferenceUtils.js @@ -0,0 +1,317 @@ +/*------------------------------------------------------------------------- + * 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. + * + *//*! + * \file + * \brief Reference context utils + *//*--------------------------------------------------------------------*/ + +'use strict'; +goog.provide('framework.opengl.simplereference.sglrReferenceUtils'); +goog.require('framework.common.tcuFloat'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.referencerenderer.rrDefs'); +goog.require('framework.referencerenderer.rrGenericVector'); +goog.require('framework.referencerenderer.rrRenderState'); +goog.require('framework.referencerenderer.rrRenderer'); +goog.require('framework.referencerenderer.rrShaders'); +goog.require('framework.referencerenderer.rrVertexAttrib'); + +goog.scope(function() { + + var sglrReferenceUtils = framework.opengl.simplereference.sglrReferenceUtils; + var deMath = framework.delibs.debase.deMath; + var tcuFloat = framework.common.tcuFloat; + var rrGenericVector = framework.referencerenderer.rrGenericVector; + var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; + var rrRenderer = framework.referencerenderer.rrRenderer; + var rrDefs = framework.referencerenderer.rrDefs; + var rrShaders = framework.referencerenderer.rrShaders; + var rrRenderState = framework.referencerenderer.rrRenderState; + + /** + * @param {number} type (32-bit, unsigend) + * @return {rrVertexAttrib.VertexAttribType} + * @throws {Error} + */ + sglrReferenceUtils.mapGLPureIntegerVertexAttributeType = function(type) { + switch (type) { + case gl.UNSIGNED_BYTE: return rrVertexAttrib.VertexAttribType.PURE_UINT8; + case gl.UNSIGNED_SHORT: return rrVertexAttrib.VertexAttribType.PURE_UINT16; + case gl.UNSIGNED_INT: return rrVertexAttrib.VertexAttribType.PURE_UINT32; + case gl.BYTE: return rrVertexAttrib.VertexAttribType.PURE_INT8; + case gl.SHORT: return rrVertexAttrib.VertexAttribType.PURE_INT16; + case gl.INT: return rrVertexAttrib.VertexAttribType.PURE_INT32; + default: + throw new Error('Value to do mapping not compatible'); + } + }; + + /** + * @param {number} type (32-bit, unsigend) + * @param {boolean} normalizedInteger + * @param {number} size + * @return {rrVertexAttrib.VertexAttribType} converted value from type to VertexAttribType + * @throws {Error} + */ + sglrReferenceUtils.mapGLFloatVertexAttributeType = function(type, normalizedInteger, size) { + + /** @type {boolean} */ var useClampingNormalization = true; + + switch (type) { + case gl.FLOAT: + return rrVertexAttrib.VertexAttribType.FLOAT; + case gl.HALF_FLOAT: + return rrVertexAttrib.VertexAttribType.HALF; + /* Not supported in WebGL 1/2 case gl.FIXED: + return rrVertexAttrib.VertexAttribType.FIXED; + case gl.DOUBLE: + return rrVertexAttrib.VertexAttribType.DOUBLE; */ + case gl.UNSIGNED_BYTE: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_UINT8; + else + return rrVertexAttrib.VertexAttribType.NONPURE_UNORM8; + + case gl.UNSIGNED_SHORT: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_UINT16; + else + return rrVertexAttrib.VertexAttribType.NONPURE_UNORM16; + + case gl.UNSIGNED_INT: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_UINT32; + else + return rrVertexAttrib.VertexAttribType.NONPURE_UNORM32; + + case gl.UNSIGNED_INT_2_10_10_10_REV: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_UINT_2_10_10_10_REV; + else + return rrVertexAttrib.VertexAttribType.NONPURE_UNORM_2_10_10_10_REV; + + case gl.BYTE: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_INT8; + else if (useClampingNormalization) + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM8_CLAMP; + else + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM8_SCALE; + + case gl.SHORT: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_INT16; + else if (useClampingNormalization) + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM16_CLAMP; + else + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM16_SCALE; + + case gl.INT: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_INT32; + else if (useClampingNormalization) + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM32_CLAMP; + else + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM32_SCALE; + + case gl.INT_2_10_10_10_REV: + if (!normalizedInteger) + return rrVertexAttrib.VertexAttribType.NONPURE_INT_2_10_10_10_REV; + else if (useClampingNormalization) + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_CLAMP; + else + return rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_SCALE; + + default: + throw new Error('Value to do mapping not compatible'); + + } + + }; + + /** + * @param {number} size + * @return {number} + * @throws {Error} + */ + sglrReferenceUtils.mapGLSize = function(size) { + switch (size) { + case 1: return 1; + case 2: return 2; + case 3: return 3; + case 4: return 4; + /* NOT in GL + case gl.BGRA: return 4; + */ + + default: + throw new Error('Value to do mapping not compatible'); + } + }; + + /** + * @param {number} type (32-bit, unsigned) + * @return {rrRenderer.PrimitiveType} + * @throws {Error} + */ + sglrReferenceUtils.mapGLPrimitiveType = function(type) { + switch (type) { + case gl.TRIANGLES: return rrRenderer.PrimitiveType.TRIANGLES; + case gl.TRIANGLE_STRIP: return rrRenderer.PrimitiveType.TRIANGLE_STRIP; + case gl.TRIANGLE_FAN: return rrRenderer.PrimitiveType.TRIANGLE_FAN; + case gl.LINES: return rrRenderer.PrimitiveType.LINES; + case gl.LINE_STRIP: return rrRenderer.PrimitiveType.LINE_STRIP; + case gl.LINE_LOOP: return rrRenderer.PrimitiveType.LINE_LOOP; + case gl.POINTS: return rrRenderer.PrimitiveType.POINTS; + + default: + throw new Error('Value to do mapping not compatible'); + } + }; + + /** + * @param {number} type (32-bit, unsigned) + * @return {rrDefs.IndexType} + * @throws {Error} + */ + sglrReferenceUtils.mapGLIndexType = function(type) { + switch (type) { + case gl.UNSIGNED_BYTE: return rrDefs.IndexType.INDEXTYPE_UINT8; + case gl.UNSIGNED_SHORT: return rrDefs.IndexType.INDEXTYPE_UINT16; + case gl.UNSIGNED_INT: return rrDefs.IndexType.INDEXTYPE_UINT32; + default: + throw new Error('Value to do mapping not compatible'); + } + }; + + /** + * @param {number} func (deUint32) + * @return {rrRenderState.TestFunc} + * @throws {Error} + */ + sglrReferenceUtils.mapGLTestFunc = function(func) { + switch (func) { + case gl.ALWAYS: return rrRenderState.TestFunc.ALWAYS; + case gl.EQUAL: return rrRenderState.TestFunc.EQUAL; + case gl.GEQUAL: return rrRenderState.TestFunc.GEQUAL; + case gl.GREATER: return rrRenderState.TestFunc.GREATER; + case gl.LEQUAL: return rrRenderState.TestFunc.LEQUAL; + case gl.LESS: return rrRenderState.TestFunc.LESS; + case gl.NEVER: return rrRenderState.TestFunc.NEVER; + case gl.NOTEQUAL: return rrRenderState.TestFunc.NOTEQUAL; + default: + throw new Error('Value to do mapping not compatible'); + } + }; + + /** + * @param {number} op (deUint32) + * @return {rrRenderState.StencilOp} + * @throws {Error} + */ + sglrReferenceUtils.mapGLStencilOp = function(op) { + switch (op) { + case gl.KEEP: return rrRenderState.StencilOp.KEEP; + case gl.ZERO: return rrRenderState.StencilOp.ZERO; + case gl.REPLACE: return rrRenderState.StencilOp.REPLACE; + case gl.INCR: return rrRenderState.StencilOp.INCR; + case gl.DECR: return rrRenderState.StencilOp.DECR; + case gl.INCR_WRAP: return rrRenderState.StencilOp.INCR_WRAP; + case gl.DECR_WRAP: return rrRenderState.StencilOp.DECR_WRAP; + case gl.INVERT: return rrRenderState.StencilOp.INVERT; + default: + throw new Error('Value to do mapping not compatible'); + } + }; + + /** + * @param {number} equation (deUint32) + * @return {rrRenderState.BlendEquation} + * @throws {Error} + */ + sglrReferenceUtils.mapGLBlendEquation = function(equation) { + switch (equation) { + case gl.FUNC_ADD: return rrRenderState.BlendEquation.ADD; + case gl.FUNC_SUBTRACT: return rrRenderState.BlendEquation.SUBTRACT; + case gl.FUNC_REVERSE_SUBTRACT: return rrRenderState.BlendEquation.REVERSE_SUBTRACT; + case gl.MIN: return rrRenderState.BlendEquation.MIN; + case gl.MAX: return rrRenderState.BlendEquation.MAX; + default: + throw new Error('Value to do mapping not compatible'); + } + }; + + /** + * @param {number} equation (deUint32) + * @return {rrRenderState.BlendEquationAdvanced} + * @throws {Error} + */ + /*sglrReferenceUtils.mapGLBlendEquationAdvanced = function(equation) { + switch (equation) { + case gl.MULTIPLY_KHR: return rrRenderState.BlendEquationAdvanced.MULTIPLY; + case gl.SCREEN_KHR: return rrRenderState.BlendEquationAdvanced.SCREEN; + case gl.OVERLAY_KHR: return rrRenderState.BlendEquationAdvanced.OVERLAY; + case gl.DARKEN_KHR: return rrRenderState.BlendEquationAdvanced.DARKEN; + case gl.LIGHTEN_KHR: return rrRenderState.BlendEquationAdvanced.LIGHTEN; + case gl.COLORDODGE_KHR: return rrRenderState.BlendEquationAdvanced.COLORDODGE; + case gl.COLORBURN_KHR: return rrRenderState.BlendEquationAdvanced.COLORBURN; + case gl.HARDLIGHT_KHR: return rrRenderState.BlendEquationAdvanced.HARDLIGHT; + case gl.SOFTLIGHT_KHR: return rrRenderState.BlendEquationAdvanced.SOFTLIGHT; + case gl.DIFFERENCE_KHR: return rrRenderState.BlendEquationAdvanced.DIFFERENCE; + case gl.EXCLUSION_KHR: return rrRenderState.BlendEquationAdvanced.EXCLUSION; + case gl.HSL_HUE_KHR: return rrRenderState.BlendEquationAdvanced.HSL_HUE; + case gl.HSL_SATURATION_KHR: return rrRenderState.BlendEquationAdvanced.HSL_SATURATION; + case gl.HSL_COLOR_KHR: return rrRenderState.BlendEquationAdvanced.HSL_COLOR; + case gl.HSL_LUMINOSITY_KHR: return rrRenderState.BlendEquationAdvanced.HSL_LUMINOSITY; + default: + throw new Error("Value to do mapping not compatible"); + } + };*/ + + /** + * @param {number} func (deUint32) + * @return {rrRenderState.BlendFunc} + * @throws {Error} + */ + sglrReferenceUtils.mapGLBlendFunc = function(func) { + switch (func) { + case gl.ZERO: return rrRenderState.BlendFunc.ZERO; + case gl.ONE: return rrRenderState.BlendFunc.ONE; + case gl.SRC_COLOR: return rrRenderState.BlendFunc.SRC_COLOR; + case gl.ONE_MINUS_SRC_COLOR: return rrRenderState.BlendFunc.ONE_MINUS_SRC_COLOR; + case gl.DST_COLOR: return rrRenderState.BlendFunc.DST_COLOR; + case gl.ONE_MINUS_DST_COLOR: return rrRenderState.BlendFunc.ONE_MINUS_DST_COLOR; + case gl.SRC_ALPHA: return rrRenderState.BlendFunc.SRC_ALPHA; + case gl.ONE_MINUS_SRC_ALPHA: return rrRenderState.BlendFunc.ONE_MINUS_SRC_ALPHA; + case gl.DST_ALPHA: return rrRenderState.BlendFunc.DST_ALPHA; + case gl.ONE_MINUS_DST_ALPHA: return rrRenderState.BlendFunc.ONE_MINUS_DST_ALPHA; + case gl.CONSTANT_COLOR: return rrRenderState.BlendFunc.CONSTANT_COLOR; + case gl.ONE_MINUS_CONSTANT_COLOR: return rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_COLOR; + case gl.CONSTANT_ALPHA: return rrRenderState.BlendFunc.CONSTANT_ALPHA; + case gl.ONE_MINUS_CONSTANT_ALPHA: return rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_ALPHA; + case gl.SRC_ALPHA_SATURATE: return rrRenderState.BlendFunc.SRC_ALPHA_SATURATE; + // case gl.SRC1_COLOR: return rrRenderState.BlendFunc.SRC1_COLOR; + // case gl.ONE_MINUS_SRC1_COLOR: return rrRenderState.BlendFunc.ONE_MINUS_SRC1_COLOR; + // case gl.SRC1_ALPHA: return rrRenderState.BlendFunc.SRC1_ALPHA; + // case gl.ONE_MINUS_SRC1_ALPHA: return rrRenderState.BlendFunc.ONE_MINUS_SRC1_ALPHA; + default: + throw new Error('Value to do mapping not compatible'); + } + }; +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrShaderProgram.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrShaderProgram.js new file mode 100644 index 000000000..f5201a531 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/opengl/simplereference/sglrShaderProgram.js @@ -0,0 +1,336 @@ +/*------------------------------------------------------------------------- + * 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('framework.opengl.simplereference.sglrShaderProgram'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.opengl.gluShaderUtil'); +goog.require('framework.opengl.gluTextureUtil'); +goog.require('framework.referencerenderer.rrDefs'); +goog.require('framework.referencerenderer.rrFragmentOperations'); +goog.require('framework.referencerenderer.rrGenericVector'); +goog.require('framework.referencerenderer.rrShaders'); +goog.require('framework.referencerenderer.rrShadingContext'); +goog.require('framework.referencerenderer.rrVertexAttrib'); +goog.require('framework.referencerenderer.rrVertexPacket'); + +goog.scope(function() { + + var sglrShaderProgram = framework.opengl.simplereference.sglrShaderProgram; + var rrShaders = framework.referencerenderer.rrShaders; + var rrGenericVector = framework.referencerenderer.rrGenericVector; + var tcuTexture = framework.common.tcuTexture; + var deMath = framework.delibs.debase.deMath; + var gluTextureUtil = framework.opengl.gluTextureUtil; + var gluShaderUtil = framework.opengl.gluShaderUtil; + var tcuTextureUtil = framework.common.tcuTextureUtil; + var rrDefs = framework.referencerenderer.rrDefs; + var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations; + var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; + var rrVertexPacket = framework.referencerenderer.rrVertexPacket; + var rrShadingContext = framework.referencerenderer.rrShadingContext; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * sglrShaderProgram.VaryingFlags + * @constructor + * @struct + */ + sglrShaderProgram.VaryingFlags = function() { + this.NONE = true; //TODO: is NONE necessary? + this.FLATSHADE = false; + }; + + /** + * sglrShaderProgram.VertexAttribute + * @constructor + * @param {string} name_ + * @param {rrGenericVector.GenericVecType} type_ + */ + sglrShaderProgram.VertexAttribute = function(name_, type_) { + this.name = name_; + this.type = type_; + }; + + /** + * sglrShaderProgram.VertexToFragmentVarying + * @constructor + * @param {rrGenericVector.GenericVecType} type_ + * @param {sglrShaderProgram.VaryingFlags=} flags + */ + sglrShaderProgram.VertexToFragmentVarying = function(type_, flags) { + this.type = type_; + this.flatshade = flags === undefined ? new sglrShaderProgram.VaryingFlags().FLATSHADE : flags.FLATSHADE; + }; + + /** + * sglrShaderProgram.FragmentOutput + * @constructor + * @param {rrGenericVector.GenericVecType} type_ + */ + sglrShaderProgram.FragmentOutput = function(type_) { + /** @type {rrGenericVector.GenericVecType} */ this.type = type_; + }; + + /** + * sglrShaderProgram.Uniform + * @constructor + * @param {string} name_ + * @param {gluShaderUtil.DataType} type_ + */ + sglrShaderProgram.Uniform = function(name_, type_) { + /** @type {string} */ this.name = name_; + /** @type {gluShaderUtil.DataType} */ this.type = type_; + /** @type {Array<number>} */ this.value; + /** @type {?rrDefs.Sampler} */ this.sampler = null; + }; + + /** + * sglrShaderProgram.VertexSource + * @constructor + * @param {string} str + */ + sglrShaderProgram.VertexSource = function(str) { + /** @type {string} */ this.source = str; + }; + + /** + * sglrShaderProgram.FragmentSource + * @constructor + * @param {string} str + */ + sglrShaderProgram.FragmentSource = function(str) { + /** @type {string} */ this.source = str; + }; + + /** + * sglrShaderProgram.ShaderProgramDeclaration + * @constructor + */ + sglrShaderProgram.ShaderProgramDeclaration = function() { + /** @type {Array<sglrShaderProgram.VertexAttribute>} */ this.m_vertexAttributes = []; + /** @type {Array<sglrShaderProgram.VertexToFragmentVarying>} */ this.m_vertexToFragmentVaryings = []; + /** @type {Array<sglrShaderProgram.FragmentOutput>} */ this.m_fragmentOutputs = []; + /** @type {Array<sglrShaderProgram.Uniform>} */ this.m_uniforms = []; + /** @type {string} */ this.m_vertexSource; + /** @type {string} */ this.m_fragmentSource; + + /** @type {boolean} */ this.m_vertexShaderSet = false; + /** @type {boolean} */ this.m_fragmentShaderSet = false; + }; + + /** + * Add a vertex attribute to the shader program declaration. + * @param {sglrShaderProgram.VertexAttribute} v + * @return {sglrShaderProgram.ShaderProgramDeclaration} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.pushVertexAttribute = function(v) { + this.m_vertexAttributes.push(v); + return this; + }; + + /** + * Add a vertex to fragment varying to the shader program declaration. + * @param {sglrShaderProgram.VertexToFragmentVarying} v + * @return {sglrShaderProgram.ShaderProgramDeclaration} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.pushVertexToFragmentVarying = function(v) { + this.m_vertexToFragmentVaryings.push(v); + return this; + }; + + /** + * Add a fragment output to the shader program declaration. + * @param {sglrShaderProgram.FragmentOutput} v + * @return {sglrShaderProgram.ShaderProgramDeclaration} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.pushFragmentOutput = function(v) { + this.m_fragmentOutputs.push(v); + return this; + }; + + /** + * Add a uniform to the shader program declaration. + * @param {sglrShaderProgram.Uniform} v + * @return {sglrShaderProgram.ShaderProgramDeclaration} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.pushUniform = function(v) { + this.m_uniforms.push(v); + return this; + }; + + /** + * @param {sglrShaderProgram.VertexSource} c + * @return {sglrShaderProgram.ShaderProgramDeclaration} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.pushVertexSource = function(c) { + DE_ASSERT(!this.m_vertexShaderSet); + this.m_vertexSource = c.source; + this.m_vertexShaderSet = true; + return this; + }; + + /** + * @param {sglrShaderProgram.FragmentSource} c + * @return {sglrShaderProgram.ShaderProgramDeclaration} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.pushFragmentSource = function(c) { + DE_ASSERT(!this.m_fragmentSource); + /** @type {sglrShaderProgram.FragmentSource} */ this.m_fragmentSource = c.source; + /** @type {boolean} */ this.m_fragmentShaderSet = true; + return this; + }; + + /** + * @return {boolean} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.valid = function() { + if (!this.m_vertexShaderSet || !this.m_fragmentShaderSet) + return false; + + if (this.m_fragmentOutputs.length == 0) + return false; + + return true; + }; + + /** + * @return {number} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.getVertexInputCount = function() { + return this.m_vertexAttributes.length; + }; + + /** + * @return {number} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.getVertexOutputCount = function() { + return this.m_vertexToFragmentVaryings.length; + }; + + /** + * @return {number} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.getFragmentInputCount = function() { + return this.m_vertexToFragmentVaryings.length; + }; + + /** + * @return {number} + */ + sglrShaderProgram.ShaderProgramDeclaration.prototype.getFragmentOutputCount = function() { + return this.m_fragmentOutputs.length; + }; + + /** + * @constructor + * @param {sglrShaderProgram.ShaderProgramDeclaration} decl + */ + sglrShaderProgram.ShaderProgram = function(decl) { + /** @type {rrShaders.VertexShader} */ this.vertexShader = new rrShaders.VertexShader(decl.getVertexInputCount(), decl.getVertexOutputCount()); + /** @type {rrShaders.FragmentShader} */ this.fragmentShader = new rrShaders.FragmentShader(decl.getFragmentInputCount(), decl.getFragmentOutputCount()); + + /** @type {Array<string>} */ this.m_attributeNames = []; + /** @type {Array<sglrShaderProgram.Uniform>} */ this.m_uniforms = []; + /** @type {string} */ this.m_vertSrc = decl.m_vertexSource; + /** @type {string} */ this.m_fragSrc = decl.m_fragmentSource; + + DE_ASSERT(decl.valid()); + + // Set up shader IO + + for (var ndx = 0; ndx < decl.m_vertexAttributes.length; ++ndx) { + this.vertexShader.m_inputs[ndx].type = decl.m_vertexAttributes[ndx].type; + this.m_attributeNames[ndx] = decl.m_vertexAttributes[ndx].name; + } + + for (var ndx = 0; ndx < decl.m_vertexToFragmentVaryings.length; ++ndx) { + this.vertexShader.m_outputs[ndx].type = decl.m_vertexToFragmentVaryings[ndx].type; + this.vertexShader.m_outputs[ndx].flatshade = decl.m_vertexToFragmentVaryings[ndx].flatshade; + + this.fragmentShader.m_inputs[ndx] = this.vertexShader.m_outputs[ndx]; + } + + for (var ndx = 0; ndx < decl.m_fragmentOutputs.length; ++ndx) + this.fragmentShader.m_outputs[ndx].type = decl.m_fragmentOutputs[ndx].type; + + // Set up uniforms + + for (var ndx = 0; ndx < decl.m_uniforms.length; ++ndx) + this.m_uniforms[ndx] = new sglrShaderProgram.Uniform(decl.m_uniforms[ndx].name, decl.m_uniforms[ndx].type); + }; + + /** + * @return {rrShaders.VertexShader} + */ + sglrShaderProgram.ShaderProgram.prototype.getVertexShader = function() { + return this.vertexShader; + }; + + /** + * @return {rrShaders.FragmentShader} + */ + sglrShaderProgram.ShaderProgram.prototype.getFragmentShader = function() { + return this.fragmentShader; + }; + + /** + * @param {string} name + * @return {sglrShaderProgram.Uniform} + * @throws {Error} + */ + sglrShaderProgram.ShaderProgram.prototype.getUniformByName = function(name) { + DE_ASSERT(name); + + for (var ndx = 0; ndx < this.m_uniforms.length; ++ndx) + if (this.m_uniforms[ndx].name == name) + return this.m_uniforms[ndx]; + + throw new Error('Invalid uniform name, uniform not found.'); + }; + + /** + * shadeFragments - abstract function, to be implemented by children classes + * @param {Array<rrFragmentOperations.Fragment>} packets + * @param {rrShadingContext.FragmentShadingContext} context + * @throws {Error} + */ + sglrShaderProgram.ShaderProgram.prototype.shadeFragments = function(packets, context) { + throw new Error('This function needs to be overwritten in a child class.'); + }; + + /** + * shadeVertices - abstract function, to be implemented by children classes + * @param {Array<rrVertexAttrib.VertexAttrib>} inputs + * @param {Array<rrVertexPacket.VertexPacket>} packets + * @param {number} numPackets + * @throws {Error} + */ + sglrShaderProgram.ShaderProgram.prototype.shadeVertices = function(inputs, packets, numPackets) { + throw new Error('This function needs to be overwritten in a child class.'); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrDefs.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrDefs.js new file mode 100644 index 000000000..c0400465e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrDefs.js @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrDefs'); + +goog.scope(function() { + +var rrDefs = framework.referencerenderer.rrDefs; + +/** + * @enum + */ +rrDefs.FaceType = { + FACETYPE_FRONT: 0, + FACETYPE_BACK: 1 +}; + +/** + * @enum + */ +rrDefs.IndexType = { + INDEXTYPE_UINT8: 0, + INDEXTYPE_UINT16: 1, + INDEXTYPE_UINT32: 2 +}; + +/** + * @enum + */ +rrDefs.ProvokingVertex = { + PROVOKINGVERTEX_FIRST: 1, + PROVOKINGVERTEX_LAST: 2 // \note valid value, "last vertex", not last of enum +}; + +/** + * @interface + */ +rrDefs.Sampler = function() {}; + +/** + * @param {Array<number>} pos + * @param {number=} lod + * @return {Array<number>} + */ +rrDefs.Sampler.prototype.sample = function(pos, lod) {}; + +/** + * @param {Array<Array<number>>} packetTexcoords 4 coordinates + * @param {number} lodBias + * @return {Array<Array<number>>} 4 vec4 samples + */ +rrDefs.Sampler.prototype.sample4 = function(packetTexcoords, lodBias) {}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrFragmentOperations.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrFragmentOperations.js new file mode 100644 index 000000000..a9c2a1f46 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrFragmentOperations.js @@ -0,0 +1,583 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrFragmentOperations'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.referencerenderer.rrRenderState'); + +goog.scope(function() { + +var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations; +var deMath = framework.delibs.debase.deMath; +var rrRenderState = framework.referencerenderer.rrRenderState; +var tcuTexture = framework.common.tcuTexture; +var tcuTextureUtil = framework.common.tcuTextureUtil; + +/** Return oldValue with the bits indicated by mask replaced by corresponding bits of newValue. + * @param {number} oldValue + * @param {number} newValue + * @param {number} mask + * @return {number} + */ +rrFragmentOperations.maskedBitReplace = function(oldValue, newValue, mask) { + return (oldValue & ~mask) | (newValue & mask); +}; + +/** + * @param {Array<number>} point + * @param {?} rect + * @return {boolean} + */ +rrFragmentOperations.isInsideRect = function(point, rect) { + return deMath.deInBounds32(point[0], rect.left, rect.left + rect.width) && + deMath.deInBounds32(point[1], rect.bottom, rect.bottom + rect.height); +}; + +/** + * @constructor + * @param {Array<number>} coefficents + * @param {Array<number>} coords + * @param {number} depth + */ +rrFragmentOperations.Fragment = function(coefficents, coords, depth) { + /** @type {Array<number>} */ this.barycentric = coefficents; + /** @type {Array<number>} */ this.pixelCoord = coords; + /** @type {boolean} */ this.isAlive = true; + /** @type {boolean} */ this.stencilPassed = true; + /** @type {boolean} */ this.depthPassed = true; + /** @type {Array<number>} */ this.sampleDepths = [depth]; + /** @type {Array<number>} */ this.clampedBlendSrcColor = []; + /** @type {Array<number>} */ this.clampedBlendSrc1Color = []; + /** @type {Array<number>} */ this.clampedBlendDstColor = []; + /** @type {Array<number>} */ this.blendSrcFactorRGB = []; + /** @type {number} */ this.blendSrcFactorA = NaN; + /** @type {Array<number>} */ this.blendDstFactorRGB = []; + /** @type {number} */ this.blendDstFactorA = NaN; + /** @type {Array<number>} */ this.blendedRGB = []; + /** @type {number} */ this.blendedA = NaN; + /** @type {Array<number>} */ this.signedValue = []; //!< integer targets + /** @type {Array<number>} */ this.unsignedValue = []; //!< unsigned integer targets + /** @type {Array<number>} */ this.value = []; /*TODO: what type should it be? */ + /** @type {Array<number>} */ this.value1 = []; /*TODO: what type should it be? */ +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {rrRenderState.WindowRectangle} scissorRect + */ +rrFragmentOperations.executeScissorTest = function(inputFragments, scissorRect) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + if (!rrFragmentOperations.isInsideRect(frag.pixelCoord, scissorRect)) + frag.isAlive = false; + } + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {rrRenderState.StencilState} stencilState + * @param {number} numStencilBits + * @param {tcuTexture.PixelBufferAccess} stencilBuffer + */ +rrFragmentOperations.executeStencilCompare = function(inputFragments, stencilState, numStencilBits, stencilBuffer) { + var clampedStencilRef = deMath.clamp(stencilState.ref, 0, (1 << numStencilBits) - 1); + + /** + * @param {function(number=,number=):boolean} expression + */ + var sample_register_stencil_compare = function(expression) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var fragSampleNdx = 0; + var stencilBufferValue = stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + var maskedRef = stencilState.compMask & clampedStencilRef; + var maskedBuf = stencilState.compMask & stencilBufferValue; + frag.stencilPassed = expression(maskedRef, maskedBuf); + } + } + }; + + switch (stencilState.func) { + case rrRenderState.TestFunc.NEVER: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return false;}); break; + case rrRenderState.TestFunc.ALWAYS: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return true;}); break; + case rrRenderState.TestFunc.LESS: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef < maskedBuf;}); break; + case rrRenderState.TestFunc.LEQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef <= maskedBuf;}); break; + case rrRenderState.TestFunc.GREATER: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef > maskedBuf;}); break; + case rrRenderState.TestFunc.GEQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef >= maskedBuf;}); break; + case rrRenderState.TestFunc.EQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef == maskedBuf;}); break; + case rrRenderState.TestFunc.NOTEQUAL: sample_register_stencil_compare(function(maskedRef, maskedBuf) { return maskedRef != maskedBuf;}); break; + default: + throw new Error('Unrecognized stencil test function:' + stencilState.func); + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {rrRenderState.StencilState} stencilState + * @param {number} numStencilBits + * @param {tcuTexture.PixelBufferAccess} stencilBuffer + */ +rrFragmentOperations.executeStencilSFail = function(inputFragments, stencilState, numStencilBits, stencilBuffer) { + var clampedStencilRef = deMath.clamp(stencilState.ref, 0, (1 << numStencilBits) - 1); + /** + * @param {function(number,number):number} expression + */ + var sample_register_sfail = function(expression) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive && !frag.stencilPassed) { + var fragSampleNdx = 0; + var stencilBufferValue = stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + stencilBuffer.setPixStencil(rrFragmentOperations.maskedBitReplace(stencilBufferValue, expression(stencilBufferValue, numStencilBits), stencilState.writeMask), fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + frag.isAlive = false; + } + } + }; + + switch (stencilState.sFail) { + case rrRenderState.StencilOp.KEEP: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return stencilBufferValue;}); break; + case rrRenderState.StencilOp.ZERO: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return 0;}); break; + case rrRenderState.StencilOp.REPLACE: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return clampedStencilRef;}); break; + case rrRenderState.StencilOp.INCR: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue + 1, 0, (1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.DECR: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue - 1, 0, (1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.INCR_WRAP: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return (stencilBufferValue + 1) & ((1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.DECR_WRAP: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return (stencilBufferValue - 1) & ((1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.INVERT: + sample_register_sfail(function(stencilBufferValue, numStencilBits) { return (~stencilBufferValue) & ((1 << numStencilBits) - 1);}); break; + default: + throw new Error('Unrecognized stencil op:' + stencilState.sFail); + } + +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {rrRenderState.TestFunc} depthFunc + * @param {tcuTexture.PixelBufferAccess} depthBuffer + */ +rrFragmentOperations.executeDepthCompare = function(inputFragments, depthFunc, depthBuffer) { + /** + * @param {function(number=,number=):boolean} expression + */ + var convertToDepthBuffer = false; + + var access; + if (depthBuffer.getFormat().type != tcuTexture.ChannelType.FLOAT && + depthBuffer.getFormat().type != tcuTexture.ChannelType.FLOAT_UNSIGNED_INT_24_8_REV) { + access = new tcuTexture.PixelBufferAccess({ + format: depthBuffer.getFormat(), + width: 1, + height: 1, + depth: 1, + data: new ArrayBuffer(8) + }); + convertToDepthBuffer = true; + } + + var sample_register_depth_compare = function(expression) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var fragSampleNdx = 0; + var depthBufferValue = depthBuffer.getPixDepth(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + var sampleDepthFloat = frag.sampleDepths[fragSampleNdx]; + + var sampleDepth; + if (convertToDepthBuffer) { + /* convert input float to target buffer format for comparison */ + access.setPixDepth(sampleDepthFloat, 0, 0, 0); + sampleDepth = access.getPixDepth(0, 0, 0); + } else { + sampleDepth = deMath.clamp(sampleDepthFloat, 0.0, 1.0); + } + + frag.depthPassed = expression(sampleDepth, depthBufferValue); + } + } + }; + + switch (depthFunc) { + case rrRenderState.TestFunc.NEVER: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return false;}); break; + case rrRenderState.TestFunc.ALWAYS: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return true;}); break; + case rrRenderState.TestFunc.LESS: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth < depthBufferValue;}); break; + case rrRenderState.TestFunc.LEQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth <= depthBufferValue;}); break; + case rrRenderState.TestFunc.GREATER: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth > depthBufferValue;}); break; + case rrRenderState.TestFunc.GEQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth >= depthBufferValue;}); break; + case rrRenderState.TestFunc.EQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth == depthBufferValue;}); break; + case rrRenderState.TestFunc.NOTEQUAL: sample_register_depth_compare(function(sampleDepth, depthBufferValue) { return sampleDepth != depthBufferValue;}); break; + default: + throw new Error('Unrecognized depth function:' + depthFunc); + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {tcuTexture.PixelBufferAccess} depthBuffer + */ +rrFragmentOperations.executeDepthWrite = function(inputFragments, depthBuffer) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive && frag.depthPassed) { + var fragSampleNdx = 0; + var clampedDepth = deMath.clamp(frag.sampleDepths[fragSampleNdx], 0.0, 1.0); + depthBuffer.setPixDepth(clampedDepth, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + } + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {rrRenderState.StencilState} stencilState + * @param {number} numStencilBits + * @param {tcuTexture.PixelBufferAccess} stencilBuffer + */ +rrFragmentOperations.executeStencilDpFailAndPass = function(inputFragments, stencilState, numStencilBits, stencilBuffer) { + var clampedStencilRef = deMath.clamp(stencilState.ref, 0, (1 << numStencilBits) - 1); + + /** + * @param {function(boolean):boolean} condition + * @param {function(number,number):number} expression + */ + var sample_register_dpfail_or_dppass = function(condition, expression) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive && condition(frag.depthPassed)) { + var fragSampleNdx = 0; + var stencilBufferValue = stencilBuffer.getPixStencil(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + stencilBuffer.setPixStencil(rrFragmentOperations.maskedBitReplace(stencilBufferValue, expression(stencilBufferValue, numStencilBits), stencilState.writeMask), fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + } + } + }; + + var switch_dpfail_or_dppass = function(op_name, condition) { + switch (stencilState[op_name]) { + case rrRenderState.StencilOp.KEEP: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return stencilBufferValue;}); break; + case rrRenderState.StencilOp.ZERO: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return 0;}); break; + case rrRenderState.StencilOp.REPLACE: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return clampedStencilRef;}); break; + case rrRenderState.StencilOp.INCR: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue + 1, 0, (1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.DECR: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return deMath.clamp(stencilBufferValue - 1, 0, (1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.INCR_WRAP: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return (stencilBufferValue + 1) & ((1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.DECR_WRAP: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return (stencilBufferValue - 1) & ((1 << numStencilBits) - 1);}); break; + case rrRenderState.StencilOp.INVERT: sample_register_dpfail_or_dppass(condition, function(stencilBufferValue, numStencilBits) { return (~stencilBufferValue) & ((1 << numStencilBits) - 1);}); break; + default: + throw new Error('Unrecognized stencil operation:' + op_name); + } + }; + + var passed = function(depthPassed) { return depthPassed;}; + var failed = function(depthPassed) { return !depthPassed;}; + + switch_dpfail_or_dppass('dpFail', failed); + switch_dpfail_or_dppass('dpPass', passed); +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {Array<number>} blendColor + * @param {rrRenderState.BlendState} blendRGBState + */ +rrFragmentOperations.executeBlendFactorComputeRGB = function(inputFragments, blendColor, blendRGBState) { + /** + * @param {string} factor_name + * @param {function(Array<number>, Array<number>, Array<number>):Array<number>} expression + */ + var sample_register_blend_factor = function(factor_name, expression) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var src = frag.clampedBlendSrcColor; + var src1 = frag.clampedBlendSrc1Color; + var dst = frag.clampedBlendDstColor; + frag[factor_name] = deMath.clampVector(expression(src, src1, dst), 0, 1); + } + } + }; + + var switch_src_or_dst_factor_rgb = function(func_name, factor_name) { + switch (blendRGBState[func_name]) { + case rrRenderState.BlendFunc.ZERO: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [0, 0, 0];}); break; + case rrRenderState.BlendFunc.ONE: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1, 1, 1];}); break; + case rrRenderState.BlendFunc.SRC_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(src, [0, 1, 2]);}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(src, [0, 1, 2]));}); break; + case rrRenderState.BlendFunc.DST_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(dst, [0, 1, 2]);}); break; + case rrRenderState.BlendFunc.ONE_MINUS_DST_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(dst, [0, 1, 2]));}); break; + case rrRenderState.BlendFunc.SRC_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [src[3], src[3], src[3]];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - src[3], 1.0 - src[3], 1.0 - src[3]];}); break; + case rrRenderState.BlendFunc.DST_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [dst[3], dst[3], dst[3]];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_DST_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - dst[3], 1.0 - dst[3], 1.0 - dst[3]];}); break; + case rrRenderState.BlendFunc.CONSTANT_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(blendColor, [0, 1, 2]);}); break; + case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(blendColor, [0, 1, 2]));}); break; + case rrRenderState.BlendFunc.CONSTANT_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [blendColor[3], blendColor[3], blendColor[3]];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - blendColor[3], 1.0 - blendColor[3], 1.0 - blendColor[3]];}); break; + case rrRenderState.BlendFunc.SRC_ALPHA_SATURATE: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [Math.min(src[3], 1.0 - dst[3]), Math.min(src[3], 1.0 - dst[3]), Math.min(src[3], 1.0 - dst[3])];}); break; + case rrRenderState.BlendFunc.SRC1_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.swizzle(src1, [0, 1, 2]);}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC1_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return deMath.subtract([1, 1, 1], deMath.swizzle(src1, [0, 1, 2]));}); break; + case rrRenderState.BlendFunc.SRC1_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [src1[3], src1[3], src1[3]];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC1_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return [1.0 - src1[3], 1.0 - src1[3], 1.0 - src1[3]];}); break; + default: + throw new Error('Unrecognized blend function:' + func_name); + } + }; + + switch_src_or_dst_factor_rgb('srcFunc', 'blendSrcFactorRGB'); + switch_src_or_dst_factor_rgb('dstFunc', 'blendDstFactorRGB'); + +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {Array<number>} blendColor + * @param {rrRenderState.BlendState} blendAState + */ +rrFragmentOperations.executeBlendFactorComputeA = function(inputFragments, blendColor, blendAState) { + /** + * @param {string} factor_name + * @param {function(Array<number>, Array<number>, Array<number>):number} expression + */ + var sample_register_blend_factor = function(factor_name, expression) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var src = frag.clampedBlendSrcColor; + var src1 = frag.clampedBlendSrc1Color; + var dst = frag.clampedBlendDstColor; + frag[factor_name] = deMath.clamp(expression(src, src1, dst), 0, 1); + } + } + }; + + var swictch_src_or_dst_factor_a = function(func_name, factor_name) { + switch (blendAState[func_name]) { + case rrRenderState.BlendFunc.ZERO: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 0.0;}); break; + case rrRenderState.BlendFunc.ONE: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0;}); break; + case rrRenderState.BlendFunc.SRC_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return src[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src[3];}); break; + case rrRenderState.BlendFunc.DST_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return dst[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_DST_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - dst[3];}); break; + case rrRenderState.BlendFunc.SRC_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return src[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src[3];}); break; + case rrRenderState.BlendFunc.DST_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return dst[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_DST_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - dst[3];}); break; + case rrRenderState.BlendFunc.CONSTANT_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return blendColor[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - blendColor[3];}); break; + case rrRenderState.BlendFunc.CONSTANT_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return blendColor[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_CONSTANT_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - blendColor[3];}); break; + case rrRenderState.BlendFunc.SRC_ALPHA_SATURATE: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0;}); break; + case rrRenderState.BlendFunc.SRC1_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return src1[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC1_COLOR: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src1[3];}); break; + case rrRenderState.BlendFunc.SRC1_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return src1[3];}); break; + case rrRenderState.BlendFunc.ONE_MINUS_SRC1_ALPHA: + sample_register_blend_factor(factor_name, function(src, src1, dst) { return 1.0 - src1[3];}); break; + default: + throw new Error('Unrecognized blend function:' + func_name); + } + }; + + swictch_src_or_dst_factor_a('srcFunc', 'blendSrcFactorA'); + swictch_src_or_dst_factor_a('dstFunc', 'blendDstFactorA'); +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments Fragments to write + * @param {rrRenderState.BlendState} blendRGBState + * @param {rrRenderState.BlendState} blendAState + */ +rrFragmentOperations.executeBlend = function(inputFragments, blendRGBState, blendAState) { + var sample_register_blended_color = function(color_name, expression) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var src = frag.clampedBlendSrcColor; + var dst = frag.clampedBlendDstColor; + frag[color_name] = expression(src, dst, frag); + } + } + }; + + switch (blendRGBState.equation) { + case rrRenderState.BlendEquation.ADD: + sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.add(deMath.multiply(deMath.swizzle(src, [0, 1, 2]), frag.blendSrcFactorRGB), deMath.multiply(deMath.swizzle(dst, [0, 1, 2]), frag.blendDstFactorRGB));}); break; + case rrRenderState.BlendEquation.SUBTRACT: + sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.subtract(deMath.multiply(deMath.swizzle(src, [0, 1, 2]), frag.blendSrcFactorRGB), deMath.multiply(deMath.swizzle(dst, [0, 1, 2]), frag.blendDstFactorRGB));}); break; + case rrRenderState.BlendEquation.REVERSE_SUBTRACT: + sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.subtract(deMath.multiply(deMath.swizzle(dst, [0, 1, 2]), frag.blendDstFactorRGB), deMath.multiply(deMath.swizzle(src, [0, 1, 2]), frag.blendSrcFactorRGB));}); break; + case rrRenderState.BlendEquation.MIN: + sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.min(deMath.swizzle(src, [0, 1, 2]), deMath.swizzle(dst, [0, 1, 2]));}); break; + case rrRenderState.BlendEquation.MAX: + sample_register_blended_color('blendedRGB', function(src, dst, frag) { return deMath.max(deMath.swizzle(src, [0, 1, 2]), deMath.swizzle(dst, [0, 1, 2]));}); break; + default: + throw new Error('Unrecognized blend equation:' + blendRGBState.equation); + } + + switch (blendAState.equation) { + case rrRenderState.BlendEquation.ADD: + sample_register_blended_color('blendedA', function(src, dst, frag) { return src[3] * frag.blendSrcFactorA + dst[3] * frag.blendDstFactorA;}); break; + case rrRenderState.BlendEquation.SUBTRACT: + sample_register_blended_color('blendedA', function(src, dst, frag) { return src[3] * frag.blendSrcFactorA - dst[3] * frag.blendDstFactorA;}); break; + case rrRenderState.BlendEquation.REVERSE_SUBTRACT: + sample_register_blended_color('blendedA', function(src, dst, frag) { return dst[3] * frag.blendDstFactorA - src[3] * frag.blendSrcFactorA;}); break; + case rrRenderState.BlendEquation.MIN: + sample_register_blended_color('blendedA', function(src, dst, frag) { return Math.min(src[3], dst[3]);}); break; + case rrRenderState.BlendEquation.MAX: + sample_register_blended_color('blendedA', function(src, dst, frag) { return Math.max(src[3], dst[3]);}); break; + default: + throw new Error('Unrecognized blend equation:' + blendAState.equation); + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments + * @param {boolean} isSRGB + * @param {tcuTexture.PixelBufferAccess} colorBuffer + */ +rrFragmentOperations.executeColorWrite = function(inputFragments, isSRGB, colorBuffer) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var combinedColor = frag.blendedRGB.slice(); + combinedColor[3] = frag.blendedA; + if (isSRGB) + combinedColor = tcuTextureUtil.linearToSRGB(combinedColor); + + colorBuffer.setPixel(combinedColor, 0, frag.pixelCoord[0], frag.pixelCoord[1]); + } + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments + * @param {Array<boolean>} colorMaskFactor + * @param {Array<boolean>} colorMaskNegationFactor + * @param {boolean} isSRGB + * @param {tcuTexture.PixelBufferAccess} colorBuffer + */ +rrFragmentOperations.executeMaskedColorWrite = function(inputFragments, colorMaskFactor, colorMaskNegationFactor, isSRGB, colorBuffer) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var fragSampleNdx = 0; + var originalColor = colorBuffer.getPixel(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + var newColor = frag.blendedRGB.slice(); + newColor[3] = frag.blendedA; + + if (isSRGB) + newColor = tcuTextureUtil.linearToSRGB(newColor); + + newColor = deMath.add(deMath.multiply(colorMaskFactor, newColor), deMath.multiply(colorMaskNegationFactor, originalColor)); + + colorBuffer.setPixel(newColor, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + } + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments + * @param {Array<boolean>} colorMask + * @param {tcuTexture.PixelBufferAccess} colorBuffer + */ +rrFragmentOperations.executeSignedValueWrite = function(inputFragments, colorMask, colorBuffer) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var fragSampleNdx = 0; + var originalValue = colorBuffer.getPixelInt(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + var newValue = tcuTextureUtil.select(frag.signedValue, originalValue, colorMask); + + colorBuffer.setPixelInt(newValue, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + } + } +}; + +/** + * @param {Array<rrFragmentOperations.Fragment>} inputFragments + * @param {Array<boolean>} colorMask + * @param {tcuTexture.PixelBufferAccess} colorBuffer + */ +rrFragmentOperations.executeUnsignedValueWrite = function(inputFragments, colorMask, colorBuffer) { + for (var i = 0; i < inputFragments.length; i++) { + var frag = inputFragments[i]; + if (frag.isAlive) { + var fragSampleNdx = 0; + var originalValue = colorBuffer.getPixelInt(fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + var newValue = tcuTextureUtil.select(frag.unsignedValue, originalValue, colorMask); + + colorBuffer.setPixelInt(newValue, fragSampleNdx, frag.pixelCoord[0], frag.pixelCoord[1]); + } + } +}; + +/** + * @constructor + */ +rrFragmentOperations.FragmentProcessor = function() { + /* TODO: implement */ +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrGenericVector.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrGenericVector.js new file mode 100644 index 000000000..62a92a422 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrGenericVector.js @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrGenericVector'); + +goog.scope(function() { + +var rrGenericVector = framework.referencerenderer.rrGenericVector; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * rrGenericVector.GenericVecType + * @enum + */ + rrGenericVector.GenericVecType = { + FLOAT: 0, + UINT32: 1, + INT32: 2 + }; + + /** + * @constructor + * @param {number=} a + * @param {number=} b + * @param {number=} c + * @param {number=} d + */ + rrGenericVector.GenericVec4 = function(a, b, c, d) { + this.data = [a || 0, b || 0, c || 0, d || 0]; + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrMultisamplePixelBufferAccess.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrMultisamplePixelBufferAccess.js new file mode 100644 index 000000000..6de1ca270 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrMultisamplePixelBufferAccess.js @@ -0,0 +1,190 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrMultisamplePixelBufferAccess'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var rrMultisamplePixelBufferAccess = framework.referencerenderer.rrMultisamplePixelBufferAccess; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var tcuTextureUtil = framework.common.tcuTextureUtil; + +var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); +}; + +/** + * \brief Read-write pixel data access to multisampled buffers. + * + * Multisampled data access follows the multisampled indexing convention. + * + * Prevents accidental usage of non-multisampled buffer as multisampled + * with PixelBufferAccess. + * @constructor + * @param {tcuTexture.PixelBufferAccess=} rawAccess + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess = function(rawAccess) { + this.m_access = rawAccess || new tcuTexture.PixelBufferAccess({ + width: 0, + height: 0}); +}; + +/** + * @return {tcuTexture.PixelBufferAccess} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.raw = function() { return this.m_access; }; + +/** + * @return {boolean} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.isEmpty = function() { return this.m_access.isEmpty(); }; + +/** + * @return {number} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.getNumSamples = function() { return this.raw().getWidth(); }; + +/** + * @return {tcuTexture.PixelBufferAccess} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.toSinglesampleAccess = function() { + DE_ASSERT(this.getNumSamples() == 1); + + return new tcuTexture.PixelBufferAccess({ + format: this.m_access.getFormat(), + width: this.m_access.getHeight(), + height: this.m_access.getDepth(), + depth: 1, + rowPitch: this.m_access.getSlicePitch(), + slicePitch: this.m_access.getSlicePitch() * this.m_access.getDepth(), + data: this.m_access.m_data, + offset: this.m_access.m_offset}); +}; + +/** + * @param {tcuTexture.PixelBufferAccess} original + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromSinglesampleAccess = function(original) { + return new rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess( + new tcuTexture.PixelBufferAccess({ + format: original.getFormat(), + width: 1, + height: original.getWidth(), + depth: original.getHeight(), + rowPitch: original.getFormat().getPixelSize(), + slicePitch: original.getRowPitch(), + data: original.m_data, + offset: original.m_offset})); +}; + +/** + * @param {tcuTexture.PixelBufferAccess} multisampledAccess + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromMultisampleAccess = function(multisampledAccess) { + return new rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess(multisampledAccess); +}; + +/** + * @param {Array<number>} region + * @return {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.getSubregion = function(region) { + var x = region[0]; + var y = region[1]; + var width = region[2]; + var height = region[3]; + + return rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.fromMultisampleAccess(tcuTextureUtil.getSubregion(this.raw(), 0, x, y, this.getNumSamples(), width, height)); +}; + +/** + * @return {Array<number>} [x, y, width, height] + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.getBufferSize = function() { + return [0, 0, this.raw().getHeight(), this.raw().getDepth()]; +}; + +/** + * @param {tcuTexture.PixelBufferAccess} dst + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.resolveMultisampleColorBuffer = function(dst) { + var src = this; + DE_ASSERT(dst.getWidth() == src.raw().getHeight()); + DE_ASSERT(dst.getHeight() == src.raw().getDepth()); + + var numSamples = src.getNumSamples(); + var sum = [0, 0, 0, 0]; + for (var y = 0; y < dst.getHeight(); y++) { + for (var x = 0; x < dst.getWidth(); x++) { + sum[0] = 0; + sum[1] = 0; + sum[2] = 0; + sum[3] = 0; + + for (var s = 0; s < src.raw().getWidth(); s++) { + var pixel = src.raw().getPixel(s, x, y); + sum[0] += pixel[0]; + sum[1] += pixel[1]; + sum[2] += pixel[2]; + sum[3] += pixel[3]; + } + + sum[0] /= numSamples; + sum[1] /= numSamples; + sum[2] /= numSamples; + sum[3] /= numSamples; + + dst.setPixel(sum, x, y); + } + } +}; + +/** + * @param {number} x + * @param {number} y + * @return {Array<number>} + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.resolveMultisamplePixel = function(x, y) { + var sum = [0, 0, 0, 0]; + for (var s = 0; s < this.getNumSamples(); s++) + sum = deMath.add(sum, this.raw().getPixel(s, x, y)); + + for (var i = 0; i < sum.length; i++) + sum[i] = sum[i] / this.getNumSamples(); + + return sum; +}; + +/** + * @param {Array<number>} color + */ +rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess.prototype.clear = function(color) { + this.raw().clear(color); +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrRenderState.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrRenderState.js new file mode 100644 index 000000000..cac4dc56a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrRenderState.js @@ -0,0 +1,323 @@ +/*------------------------------------------------------------------------- + * drawElements Quality Program Reference Renderer + * ----------------------------------------------- + * + * 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. + * + *//*! + * \file + * \brief Reference renderer render state. + *//*--------------------------------------------------------------------*/ +'use strict'; +goog.provide('framework.referencerenderer.rrRenderState'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.referencerenderer.rrDefs'); +goog.require('framework.referencerenderer.rrMultisamplePixelBufferAccess'); + +goog.scope(function() { + +var rrRenderState = framework.referencerenderer.rrRenderState; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var rrMultisamplePixelBufferAccess = framework.referencerenderer.rrMultisamplePixelBufferAccess; +var rrDefs = framework.referencerenderer.rrDefs; + +/** + * Enum for rrRenderState.HorizontalFill values. + * @enum {number} + */ +rrRenderState.HorizontalFill = { + LEFT: 0, + RIGHT: 1 +}; + +/** + * Enum for rrRenderState.VerticalFill values. + * @enum {number} + */ +rrRenderState.VerticalFill = { + TOP: 0, + BOTTOM: 1 +}; + +/** + * Enum for rrRenderState.Winding values. + * @enum {number} + */ +rrRenderState.Winding = { + CCW: 0, + CC: 1 +}; + +/** + * Enum for rrRenderState.CullMode values. + * @enum {number} + */ +rrRenderState.CullMode = { + NONE: 0, + BACK: 1, + FRONT: 2 +}; + +/**rrRenderState.Winding : rrRenderState.Winding, + + * @constructor + */ +rrRenderState.RasterizationState = function() { + /** @type {number} */ this.winding = rrRenderState.Winding.CCW; + /** @type {number} */ this.horizontalFill = rrRenderState.HorizontalFill.LEFT; + /** @type {number} */ this.verticalFill = rrRenderState.VerticalFill.BOTTOM; +}; + +/** + * Enum for rrRenderState.TestFunc values. + * @enum {number} + */ +rrRenderState.TestFunc = { + NEVER: 0, + ALWAYS: 1, + LESS: 2, + LEQUAL: 3, + GREATER: 4, + GEQUAL: 5, + EQUAL: 6, + NOTEQUAL: 7 +}; + +/** + * Enum for rrRenderState.StencilOp values. + * @enum {number} + */ +rrRenderState.StencilOp = { + KEEP: 0, + ZERO: 1, + REPLACE: 2, + INCR: 3, //!< Increment with saturation. + DECR: 4, //!< Decrement with saturation. + INCR_WRAP: 5, + DECR_WRAP: 6, + INVERT: 7 +}; + +/** + * Enum for rrRenderState.BlendMode values. + * @enum {number} + */ +rrRenderState.BlendMode = { + NONE: 0, //!< No blending. + STANDARD: 1 //!< Standard blending. +// Advanced blending is not supported +// ADVANCED : 2 //!< Advanced blending mode, as defined in gl.KHR_blend_equation_advanced. +}; + +/** + * Enum for rrRenderState.BlendEquation values. + * @enum {number} + */ +rrRenderState.BlendEquation = { + ADD: 0, + SUBTRACT: 1, + REVERSE_SUBTRACT: 2, + MIN: 3, + MAX: 4 +}; + +// /** +// * Enum for rrRenderState.BlendEquationAdvanced values. +// * @enum {number} +// */ +// rrRenderState.BlendEquationAdvanced = { +// MULTIPLY : 0, +// SCREEN : 1, +// OVERLAY : 2, +// DARKEN : 3, +// LIGHTEN : 4, +// COLORDODGE : 5, +// COLORBURN : 6, +// HARDLIGHT : 7, +// SOFTLIGHT : 8, +// DIFFERENCE : 9, +// EXCLUSION : 10, +// HSL_HUE : 11, +// HSL_SATURATION : 12, +// HSL_COLOR : 13, +// HSL_LUMINOSITY : 14 +// }; + +/** + * Enum for rrRenderState.BlendFunc values. + * @enum {number} + */ +rrRenderState.BlendFunc = { + ZERO: 0, + ONE: 1, + SRC_COLOR: 2, + ONE_MINUS_SRC_COLOR: 3, + DST_COLOR: 4, + ONE_MINUS_DST_COLOR: 5, + SRC_ALPHA: 6, + ONE_MINUS_SRC_ALPHA: 7, + DST_ALPHA: 8, + ONE_MINUS_DST_ALPHA: 9, + CONSTANT_COLOR: 10, + ONE_MINUS_CONSTANT_COLOR: 11, + CONSTANT_ALPHA: 12, + ONE_MINUS_CONSTANT_ALPHA: 13, + SRC_ALPHA_SATURATE: 14, + SRC1_COLOR: 15, + ONE_MINUS_SRC1_COLOR: 16, + SRC1_ALPHA: 17, + ONE_MINUS_SRC1_ALPHA: 18 +}; + +/** + * @constructor + */ +rrRenderState.StencilState = function() { + /** @type {number} */ this.func = rrRenderState.TestFunc.ALWAYS; + /** @type {number} */ this.ref = 0; + /** @type {number} */ this.compMask = ~0; + /** @type {number} */ this.sFail = rrRenderState.StencilOp.KEEP; + /** @type {number} */ this.dpFail = rrRenderState.StencilOp.KEEP; + /** @type {number} */ this.dpPass = rrRenderState.StencilOp.KEEP; + /** @type {number} */ this.writeMask = ~0; +}; + +/** + * @constructor + */ +rrRenderState.BlendState = function() { + /** @type {number} */ this.equation = rrRenderState.BlendEquation.ADD; + /** @type {number} */ this.srcFunc = rrRenderState.BlendFunc.ONE; + /** @type {number} */ this.dstFunc = rrRenderState.BlendFunc.ZERO; +}; + +/** + * @param {(Array<number>|number)} left_ + * @param {number=} bottom_ + * @param {number=} width_ + * @param {number=} height_ + * @constructor + */ +rrRenderState.WindowRectangle = function(left_, bottom_, width_, height_) { + // Is first parameter an array? Use it + if (left_.length && left_.length == 4) { + this.left = left_[0]; + this.bottom = left_[1]; + this.width = left_[2]; + this.height = left_[3]; + } else { + this.left = left_; + this.bottom = bottom_; + this.width = width_; + this.height = height_; + } +}; + +/** + * @constructor + */ +rrRenderState.FragmentOperationState = function() { + /** @type {boolean} */ this.scissorTestEnabled = false; + /** @type {rrRenderState.WindowRectangle} */ this.scissorRectangle = new rrRenderState.WindowRectangle(0, 0, 1, 1); + + /** @type {boolean} */ this.stencilTestEnabled = false; + + /** @type {Array<rrRenderState.StencilState>} */ this.stencilStates = []; + for (var type in rrDefs.FaceType) + this.stencilStates[rrDefs.FaceType[type]] = new rrRenderState.StencilState(); + + /** @type {boolean} */ this.depthTestEnabled = false; + /** @type {rrRenderState.TestFunc} */ this.depthFunc = rrRenderState.TestFunc.LESS; + /** @type {boolean} */ this.depthMask = true; + + /** @type {rrRenderState.BlendMode} */ this.blendMode = rrRenderState.BlendMode.NONE; + /** @type {rrRenderState.BlendState} */ this.blendRGBState = new rrRenderState.BlendState(); + /** @type {rrRenderState.BlendState} */ this.blendAState = new rrRenderState.BlendState(); + /** @type {Array<number>} */ this.blendColor = [0.0, 0.0, 0.0, 0.0]; +// /** @type {rrRenderState.BlendEquationAdvanced} */ this.blendEquationAdvanced = null; + + /** @type {boolean} */ this.sRGBEnabled = true; + + /** @type {boolean} */ this.depthClampEnabled = false; + + /** @type {boolean} */ this.polygonOffsetEnabled = false; + /** @type {number} */ this.polygonOffsetFactor = 0.0; + /** @type {number} */ this.polygonOffsetUnits = 0.0; + + /** @type {Array<boolean>} */ this.colorMask = [true, true, true, true]; + + /** @type {number} */ this.numStencilBits = 8; +}; + +/** + * @constructor + */ +rrRenderState.PointState = function() { + /** @type {number} */ this.pointSize = 1.0; +}; + +/** + * @constructor + */ +rrRenderState.LineState = function() { + /** @type {number} */ this.lineWidth = 1.0; +}; + +/** + * Constructor checks if the parameter has a "raw" member to detect if the instance is + * of type rrRenderState.WindowRectangle or MultisamplePixelBufferAccess. + * @param {rrRenderState.WindowRectangle|rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} rect_ + * @constructor + */ +rrRenderState.ViewportState = function(rect_) { + /** @type {number} */ this.zn = 0.0; + /** @type {number} */ this.zf = 1.0; + /** @type {rrRenderState.WindowRectangle} */ this.rect; + + if (rect_.raw) { + this.rect = new rrRenderState.WindowRectangle(0, 0, rect_.raw().getHeight(), + rect_.raw().getDepth()); + } else { + this.rect = /** @type {rrRenderState.WindowRectangle} */ (rect_); + } +}; + +/** + * @constructor + */ +rrRenderState.RestartState = function() { + /** @type {boolean} */ this.enabled = false; + /** @type {number} */ this.restartIndex = 0xFFFFFFFF; +}; + +/** + * @constructor + * @param {rrRenderState.ViewportState} viewport_ + */ +rrRenderState.RenderState = function(viewport_) { + /** @type {rrRenderState.CullMode} */ this.cullMode = rrRenderState.CullMode.NONE; + /** @type {number} */ this.provokingVertexConvention; + /** @type {rrRenderState.ViewportState} */ this.viewport = viewport_; + + /** @type {rrRenderState.RasterizationState} */ this.rasterization = new rrRenderState.RasterizationState(); + /** @type {rrRenderState.FragmentOperationState} */ this.fragOps = new rrRenderState.FragmentOperationState(); + /** @type {rrRenderState.PointState} */ this.point = new rrRenderState.PointState(); + /** @type {rrRenderState.LineState} */ this.line = new rrRenderState.LineState(); + /** @type {rrRenderState.RestartState} */ this.restart = new rrRenderState.RestartState(); +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrRenderer.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrRenderer.js new file mode 100644 index 000000000..4d5752b2c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrRenderer.js @@ -0,0 +1,1274 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrRenderer'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.common.tcuTextureUtil'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.delibs.debase.deString'); +goog.require('framework.delibs.debase.deUtil'); +goog.require('framework.opengl.simplereference.sglrShaderProgram'); +goog.require('framework.referencerenderer.rrDefs'); +goog.require('framework.referencerenderer.rrFragmentOperations'); +goog.require('framework.referencerenderer.rrGenericVector'); +goog.require('framework.referencerenderer.rrMultisamplePixelBufferAccess'); +goog.require('framework.referencerenderer.rrRenderState'); +goog.require('framework.referencerenderer.rrShadingContext'); +goog.require('framework.referencerenderer.rrVertexAttrib'); +goog.require('framework.referencerenderer.rrVertexPacket'); + +goog.scope(function() { + +var rrRenderer = framework.referencerenderer.rrRenderer; +var rrVertexPacket = framework.referencerenderer.rrVertexPacket; +var rrDefs = framework.referencerenderer.rrDefs; +var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations; +var deMath = framework.delibs.debase.deMath; +var tcuTextureUtil = framework.common.tcuTextureUtil; +var tcuTexture = framework.common.tcuTexture; +var rrRenderState = framework.referencerenderer.rrRenderState; +var rrMultisamplePixelBufferAccess = framework.referencerenderer.rrMultisamplePixelBufferAccess; +var rrShadingContext = framework.referencerenderer.rrShadingContext; +var rrGenericVector = framework.referencerenderer.rrGenericVector; +var sglrShaderProgram = framework.opengl.simplereference.sglrShaderProgram; +var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; +var deString = framework.delibs.debase.deString; +var deUtil = framework.delibs.debase.deUtil; + +/** + * @enum + */ +rrRenderer.PrimitiveType = { + TRIANGLES: 0, //!< Separate rrRenderer.triangles + TRIANGLE_STRIP: 1, //!< rrRenderer.Triangle strip + TRIANGLE_FAN: 2, //!< rrRenderer.Triangle fan + + LINES: 3, //!< Separate lines + LINE_STRIP: 4, //!< Line strip + LINE_LOOP: 5, //!< Line loop + + POINTS: 6 //!< Points +}; + +// /** +// * @constructor +// * @param {boolean} depthEnabled Is depth buffer enabled +// */ +// rrRenderer.RasterizationInternalBuffers = function(depthEnabled) { +// /*std::vector<rrFragmentOperations.Fragment>*/ this.fragmentPackets = []; +// /*std::vector<GenericVec4>*/ this.shaderOutputs = []; +// /*std::vector<Fragment>*/ this.shadedFragments = []; +// /*float**/ this.fragmentDepthBuffer = depthEnabled ? [] : null; +// }; + +/** + * @constructor + * @param {number=} id + */ +rrRenderer.DrawContext = function(id) { + this.primitiveID = id || 0; + +}; + +/** + * Transform [x, y] to window (pixel) coordinates. + * z and w are unchanged + * @param {rrRenderState.RenderState} state + * @param {rrVertexPacket.VertexPacket} packet + * Wreturn {Array<number>} + */ +rrRenderer.transformGLToWindowCoords = function(state, packet) { + var transformed = [packet.position[0] / packet.position[3], + packet.position[1] / packet.position[3], + packet.position[2], + packet.position[3]]; + var viewport = state.viewport.rect; + var halfW = viewport.width / 2; + var halfH = viewport.height / 2; + var oX = viewport.left + halfW; + var oY = viewport.bottom + halfH; + + return [ + transformed[0] * halfW + oX, + transformed[1] * halfH + oY, + transformed[2], + transformed[3] + ]; +}; + +/** + * @constructor + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess} colorMultisampleBuffer + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess=} depthMultisampleBuffer + * @param {rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess=} stencilMultisampleBuffer + */ +rrRenderer.RenderTarget = function(colorMultisampleBuffer, depthMultisampleBuffer, stencilMultisampleBuffer) { + this.MAX_COLOR_BUFFERS = 4; + this.colorBuffers = []; + this.colorBuffers[0] = colorMultisampleBuffer; + this.depthBuffer = depthMultisampleBuffer || new rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess(); + this.stencilBuffer = stencilMultisampleBuffer || new rrMultisamplePixelBufferAccess.MultisamplePixelBufferAccess(); + this.numColorBuffers = 1; +}; + +// NOTE: Program object is useless. Let's just use the sglrShaderProgram +// /** +// * @constructor +// * @param {rrShaders.VertexShader} vertexShader_ +// * @param {rrShaders.FragmentShader} fragmentShader_ +// */ +// var Program = function(vertexShader_, fragmentShader_) { +// this.vertexShader = vertexShader_; +// this.fragmentShader = fragmentShader_; +// }; + +/** + * @constructor + * @param {ArrayBuffer} data + * @param {rrDefs.IndexType} type + * @param {number} offset + * @param {number=} baseVertex_ + */ +rrRenderer.DrawIndices = function(data, type, offset, baseVertex_) { + /** @type {ArrayBuffer} */ this.data = data; + /** @type {number} */ this.baseVertex = baseVertex_ || 0; + /** @type {rrDefs.IndexType} */ this.indexType = type; + /** @type {goog.NumberArray} */ this.access = null; + switch (type) { + case rrDefs.IndexType.INDEXTYPE_UINT8: this.access = new Uint8Array(data).subarray(offset); break; + case rrDefs.IndexType.INDEXTYPE_UINT16: this.access = new Uint16Array(data).subarray(offset / 2); break; + case rrDefs.IndexType.INDEXTYPE_UINT32: this.access = new Uint32Array(data).subarray(offset / 4); break; + default: throw new Error('Invalid type: ' + type); + } +}; + +/** + * @return {number} + */ +rrRenderer.DrawIndices.prototype.readIndexArray = function(index) { return this.access[index]; }; + +/** + * @constructor + * @param {rrRenderer.PrimitiveType} primitiveType + * @param {number} numElements + * @param {(number|rrRenderer.DrawIndices)} indices + */ +rrRenderer.PrimitiveList = function(primitiveType, numElements, indices) { + /** @type {rrRenderer.PrimitiveType} */ this.m_primitiveType = primitiveType; + /** @type {number} */ this.m_numElements = numElements; + if (typeof indices == 'number') { + // !< primitive list for drawArrays-like call + this.m_indices = null; + this.m_indexType = null; + this.m_baseVertex = indices; + } else { + // !< primitive list for drawElements-like call + this.m_indices = indices; + this.m_indexType = indices.indexType; + this.m_baseVertex = indices.baseVertex; + } + this.m_iterator = 0; +}; + +/** + * @param {number} elementNdx + * @return {number} + */ +rrRenderer.PrimitiveList.prototype.getIndex = function(elementNdx) { + if (this.m_indices) { + var index = this.m_baseVertex + this.m_indices.readIndexArray(elementNdx); + if (index < 0) + throw new Error('Index must not be negative'); + + return index; + } else + return this.m_baseVertex + elementNdx; +}; + +/** + * @param {number} elementNdx + * @param {number} restartIndex + * @return {boolean} + */ +rrRenderer.PrimitiveList.prototype.isRestartIndex = function(elementNdx, restartIndex) { + // implicit index or explicit index (without base vertex) equals restart + if (this.m_indices) + return this.m_indices.readIndexArray(elementNdx) == restartIndex; + else + return elementNdx == restartIndex; +}; + +/** + * @return {number} + */ +rrRenderer.PrimitiveList.prototype.getNumElements = function() {return this.m_numElements;}; + +/** + * @return {rrRenderer.PrimitiveType} + */ +rrRenderer.PrimitiveList.prototype.getPrimitiveType = function() {return this.m_primitiveType;}; + +/** + * @return {?rrDefs.IndexType} + */ +rrRenderer.PrimitiveList.prototype.getIndexType = function() {return this.m_indexType;}; + +/** + * Generate a primitive from indices + * @param {boolean=} reset Restart generating primitives. Default false + * @return {Array<number>} + */ +rrRenderer.PrimitiveList.prototype.getNextPrimitive = function(reset) { + if (reset) + this.m_iterator = 0; + var result = []; + var i = this.m_iterator; + switch (this.m_primitiveType) { + case rrRenderer.PrimitiveType.TRIANGLES: + if (this.m_iterator + 3 <= this.m_numElements) { + result = [i, i + 1, i + 2]; + this.m_iterator += 3; + } + break; + case rrRenderer.PrimitiveType.TRIANGLE_STRIP: + if (this.m_iterator + 3 <= this.m_numElements) { + result = [i, i + 1, i + 2]; + this.m_iterator += 1; + } + break; + case rrRenderer.PrimitiveType.TRIANGLE_FAN: + if (this.m_iterator + 3 <= this.m_numElements) { + result = [0, i + 1, i + 2]; + this.m_iterator += 1; + } + break; + case rrRenderer.PrimitiveType.LINES: + if (this.m_iterator + 2 <= this.m_numElements) { + result = [i, i + 1]; + this.m_iterator += 2; + } + break; + case rrRenderer.PrimitiveType.LINE_STRIP: + if (this.m_iterator + 2 <= this.m_numElements) { + result = [i, i + 1]; + this.m_iterator += 1; + } + break; + case rrRenderer.PrimitiveType.LINE_LOOP: + if (this.m_iterator == this.m_numElements) + break; + if (this.m_iterator + 2 <= this.m_numElements) + result = [i, i + 1]; + else + result = [i, 0]; + this.m_iterator += 1; + break; + case rrRenderer.PrimitiveType.POINTS: + if (this.m_iterator == this.m_numElements) + break; + else + result = [i]; + this.m_iterator += 1; + break; + default: + throw new Error('Unsupported primitive type: ' + deString.enumToString(rrRenderer.PrimitiveType, this.m_primitiveType)); + } + + return result; +}; + +/** + * @param {rrRenderState.RenderState} state + * @param {rrRenderer.RenderTarget} renderTarget + * @param {Array<rrFragmentOperations.Fragment>} fragments Fragments to write +*/ +rrRenderer.writeFragments = function(state, renderTarget, fragments) { + /* TODO: Add blending, depth, stencil ... */ + var colorbuffer = renderTarget.colorBuffers[0].raw(); + for (var i = 0; i < fragments.length; i++) { + var fragment = fragments[i]; + colorbuffer.setPixel(fragment.value, 0, fragment.pixelCoord[0], fragment.pixelCoord[1]); + } + +}; + +/** + * @param {rrRenderState.RenderState} renderState + * @param {rrRenderer.RenderTarget} renderTarget + * @param {Array<rrFragmentOperations.Fragment>} fragments Fragments to write +*/ +rrRenderer.writeFragments2 = function(renderState, renderTarget, fragments) { + /* +void FragmentProcessor::render (const rr::MultisamplePixelBufferAccess& msColorBuffer, + const rr::MultisamplePixelBufferAccess& msDepthBuffer, + const rr::MultisamplePixelBufferAccess& msStencilBuffer, + const Fragment* fragments, + int numFragments, + FaceType fragmentFacing, + const FragmentOperationState& state) +*/ + + /** @const */ var fragmentFacing = rrDefs.FaceType.FACETYPE_FRONT; + var colorBuffer = renderTarget.colorBuffers[0].raw(); + var depthBuffer = renderTarget.depthBuffer.raw(); + var stencilBuffer = renderTarget.stencilBuffer.raw(); + var state = renderState.fragOps; + + var hasDepth = depthBuffer.getWidth() > 0 && depthBuffer.getHeight() > 0 && depthBuffer.getDepth() > 0; + var hasStencil = stencilBuffer.getWidth() > 0 && stencilBuffer.getHeight() > 0 && stencilBuffer.getDepth() > 0; + var doDepthTest = hasDepth && state.depthTestEnabled; + var doStencilTest = hasStencil && state.stencilTestEnabled; + + var colorbufferClass = tcuTexture.getTextureChannelClass(colorBuffer.getFormat().type); + var fragmentDataType = rrGenericVector.GenericVecType.FLOAT; + switch (colorbufferClass) { + case tcuTexture.TextureChannelClass.SIGNED_INTEGER: + fragmentDataType = rrGenericVector.GenericVecType.INT32; + break; + case tcuTexture.TextureChannelClass.UNSIGNED_INTEGER: + fragmentDataType = rrGenericVector.GenericVecType.UINT32; + break; + } + + if (!((!hasDepth || colorBuffer.getWidth() == depthBuffer.getWidth()) && (!hasStencil || colorBuffer.getWidth() == stencilBuffer.getWidth()))) + throw new Error('Attachment must have the same width'); + if (!((!hasDepth || colorBuffer.getHeight() == depthBuffer.getHeight()) && (!hasStencil || colorBuffer.getHeight() == stencilBuffer.getHeight()))) + throw new Error('Attachment must have the same height'); + if (!((!hasDepth || colorBuffer.getDepth() == depthBuffer.getDepth()) && (!hasStencil || colorBuffer.getDepth() == stencilBuffer.getDepth()))) + throw new Error('Attachment must have the same depth'); + + var stencilState = state.stencilStates[fragmentFacing]; + var colorMaskFactor = [state.colorMask[0] ? 1 : 0, state.colorMask[1] ? 1 : 0, state.colorMask[2] ? 1 : 0, state.colorMask[3] ? 1 : 0]; + var colorMaskNegationFactor = [state.colorMask[0] ? false : true, state.colorMask[1] ? false : true, state.colorMask[2] ? false : true, state.colorMask[3] ? false : true]; + var sRGBTarget = state.sRGBEnabled && colorBuffer.getFormat().isSRGB(); + + // Scissor test. + + if (state.scissorTestEnabled) + rrFragmentOperations.executeScissorTest(fragments, state.scissorRectangle); + + // Stencil test. + + if (doStencilTest) { + rrFragmentOperations.executeStencilCompare(fragments, stencilState, state.numStencilBits, stencilBuffer); + rrFragmentOperations.executeStencilSFail(fragments, stencilState, state.numStencilBits, stencilBuffer); + } + + // Depth test. + // \note Current value of isAlive is needed for dpPass and dpFail, so it's only updated after them and not right after depth test. + + if (doDepthTest) { + rrFragmentOperations.executeDepthCompare(fragments, state.depthFunc, depthBuffer); + + if (state.depthMask) + rrFragmentOperations.executeDepthWrite(fragments, depthBuffer); + } + + // Do dpFail and dpPass stencil writes. + + if (doStencilTest) + rrFragmentOperations.executeStencilDpFailAndPass(fragments, stencilState, state.numStencilBits, stencilBuffer); + + // Kill the samples that failed depth test. + + if (doDepthTest) { + for (var i = 0; i < fragments.length; i++) + fragments[i].isAlive = fragments[i].isAlive && fragments[i].depthPassed; + } + + // Paint fragments to target + + switch (fragmentDataType) { + case rrGenericVector.GenericVecType.FLOAT: + // Blend calculation - only if using blend. + if (state.blendMode == rrRenderState.BlendMode.STANDARD) { + // Put dst color to register, doing srgb-to-linear conversion if needed. + for (var i = 0; i < fragments.length; i++) { + var frag = fragments[i]; + if (frag.isAlive) { + var dstColor = colorBuffer.getPixel(0, frag.pixelCoord[0], frag.pixelCoord[1]); + + /* TODO: Check frag.value and frag.value1 types */ + frag.clampedBlendSrcColor = deMath.clampVector(frag.value, 0, 1); + frag.clampedBlendSrc1Color = deMath.clampVector(frag.value1, 0, 1); + frag.clampedBlendDstColor = deMath.clampVector(sRGBTarget ? tcuTexture.sRGBToLinear(dstColor) : dstColor, 0, 1); + } + } + + // Calculate blend factors to register. + rrFragmentOperations.executeBlendFactorComputeRGB(fragments, state.blendColor, state.blendRGBState); + rrFragmentOperations.executeBlendFactorComputeA(fragments, state.blendColor, state.blendAState); + + // Compute blended color. + rrFragmentOperations.executeBlend(fragments, state.blendRGBState, state.blendAState); + } else { + // Not using blend - just put values to register as-is. + + for (var i = 0; i < fragments.length; i++) { + var frag = fragments[i]; + if (frag.isAlive) { + frag.blendedRGB = deMath.swizzle(frag.value, [0, 1, 2]); + frag.blendedA = frag.value[3]; + } + } + } + + // Finally, write the colors to the color buffer. + + if (state.colorMask[0] && state.colorMask[1] && state.colorMask[2] && state.colorMask[3]) { + /* TODO: Add quick path */ + // if (colorBuffer.getFormat().isEqual(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8))) + // executeRGBA8ColorWrite(fragments, colorBuffer); + // else + rrFragmentOperations.executeColorWrite(fragments, sRGBTarget, colorBuffer); + } else if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3]) + rrFragmentOperations.executeMaskedColorWrite(fragments, colorMaskFactor, colorMaskNegationFactor, sRGBTarget, colorBuffer); + break; + + case rrGenericVector.GenericVecType.INT32: + // Write fragments + for (var i = 0; i < fragments.length; i++) { + var frag = fragments[i]; + if (frag.isAlive) { + frag.signedValue = frag.value; + } + } + + if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3]) + rrFragmentOperations.executeSignedValueWrite(fragments, state.colorMask, colorBuffer); + break; + + case rrGenericVector.GenericVecType.UINT32: + // Write fragments + for (var i = 0; i < fragments.length; i++) { + var frag = fragments[i]; + if (frag.isAlive) { + frag.unsignedValue = frag.value; + } + } + + if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3]) + rrFragmentOperations.executeUnsignedValueWrite(fragments, state.colorMask, colorBuffer); + break; + + default: + throw new Error('Unrecognized fragment data type:' + fragmentDataType); + } +}; + +/** + * Determines the index of the corresponding vertex according to top/right conditions. + * @param {boolean} isTop + * @param {boolean} isRight + * @return {number} + */ +rrRenderer.getIndexOfCorner = function(isTop, isRight, vertexPackets) { + var x = null; + var y = null; + + var xcriteria = isRight ? Math.max : Math.min; + var ycriteria = isTop ? Math.max : Math.min; + + // Determine corner values + for (var i = 0; i < vertexPackets.length; i++) { + x = x != null ? xcriteria(vertexPackets[i].position[0], x) : vertexPackets[i].position[0]; + y = y != null ? ycriteria(vertexPackets[i].position[1], y) : vertexPackets[i].position[1]; + } + + // Search for matching vertex + for (var v = 0; v < vertexPackets.length; v++) + if (vertexPackets[v].position[0] == x && + vertexPackets[v].position[1] == y) + return v; + + throw new Error('Corner not found'); +}; + +/** + * Check that point is in the clipping volume + * @param {number} x + * @param {number} y + * @param {number} z + * @param {rrRenderState.WindowRectangle} rect + * @return {boolean} + */ +rrRenderer.clipTest = function(x, y, z, rect) { + x = Math.round(x); + y = Math.round(y); + if (!deMath.deInBounds32(x, rect.left, rect.left + rect.width)) + return false; + if (!deMath.deInBounds32(y, rect.bottom, rect.bottom + rect.height)) + return false; + if (z < 0 || z > 1) + return false; + return true; +}; + +// Rasterizer configuration +rrRenderer.RASTERIZER_SUBPIXEL_BITS = 8; +rrRenderer.RASTERIZER_MAX_SAMPLES_PER_FRAGMENT = 16; + +// Referenced from rrRasterizer.hpp + +/** + * Get coverage bit value + * @param {number} numSamples + * @param {number} x + * @param {number} y + * @param {number} sampleNdx + * @return {number} + */ +rrRenderer.getCoverageBit = function(numSamples, x, y, sampleNdx) { + var maxSamples = 16; + assertMsgOptions(maxSamples >= rrRenderer.RASTERIZER_MAX_SAMPLES_PER_FRAGMENT, 'maxSamples should not greater than ' + rrRenderer.RASTERIZER_MAX_SAMPLES_PER_FRAGMENT, false, true); + assertMsgOptions(deMath.deInRange32(numSamples, 1, maxSamples) && deMath.deInBounds32(x, 0, 2) && deMath.deInBounds32(y, 0, 2), 'numSamples, x or y not in bound', false, true); + return 1 << ((x * 2 + y) * numSamples + sampleNdx); +}; + +/** + * Get all sample bits for fragment + * @param {number} numSamples + * @param {number} x + * @param {number} y + * @return {number} + */ +rrRenderer.getCoverageFragmentSampleBits = function(numSamples, x, y) { + assertMsgOptions(deMath.deInBounds32(x, 0, 2) && deMath.deInBounds32(y, 0, 2), 'x or y is not in bound 0 to 2', false, true); + var fragMask = (1 << numSamples) - 1; + return fragMask << (x * 2 + y) * numSamples; +}; + +/** + * Set coverage bit in coverage mask + * @param {number} mask + * @param {number} numSamples + * @param {number} x + * @param {number} y + * @param {number} sampleNdx + * @param {number} val + * @return {number} + */ +rrRenderer.setCoverageValue = function(mask, numSamples, x, y, sampleNdx, val) { + var bit = rrRenderer.getCoverageBit(numSamples, x, y, sampleNdx); + return val ? (mask | bit) : (mask & ~bit); +}; + +/** + * Test if any sample for fragment is live + * @param {number} mask + * @param {number} numSamples + * @param {number} x + * @param {number} y + * @return {number} + */ +rrRenderer.getCoverageAnyFragmentSampleLive = function(mask, numSamples, x, y) { + return (mask & rrRenderer.getCoverageFragmentSampleBits(numSamples, x, y)) != 0; +}; + +// Referenced from rrRasterizer.cpp + +/** + * Pixel coord to sub pixel coord + * @param {number} v + * @return {number} + */ +rrRenderer.toSubpixelCoord = function(v) { + return Math.trunc(v * (1 << rrRenderer.RASTERIZER_SUBPIXEL_BITS) + (v < 0 ? -0.5 : 0.5)); +}; + +/** + * Floor sub pixel coord to pixel coord + * @param {number} coord + * @param {boolean} fillEdge + * @return {number} + */ +rrRenderer.floorSubpixelToPixelCoord = function(coord, fillEdge) { + if (coord >= 0) + return Math.trunc((coord - (fillEdge ? 1 : 0)) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); + else + return Math.trunc((coord - ((1 << rrRenderer.RASTERIZER_SUBPIXEL_BITS) - (fillEdge ? 0 : 1))) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); +}; + +/** + * Ceil sub pixel coord to pixel coord + * @param {number} coord + * @param {boolean} fillEdge + * @return {number} + */ +rrRenderer.ceilSubpixelToPixelCoord = function(coord, fillEdge) { + if (coord >= 0) + return Math.trunc((coord + (1 << rrRenderer.RASTERIZER_SUBPIXEL_BITS) - (fillEdge ? 0 : 1)) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); + else + return Math.trunc((coord + (fillEdge ? 1 : 0)) >> rrRenderer.RASTERIZER_SUBPIXEL_BITS); +}; + +/** + * \brief Edge function - referenced from struct EdgeFunction in rrRasterizer.hpp + * + * Edge function can be evaluated for point P (in a fixed-point coordinates + * with RASTERIZER_SUBPIXEL_BITS fractional part) by computing + * D = a * Px + b * Py + c + * + * D will be fixed-point value where lower (RASTERIZER_SUBPIXEL_BITS * 2) bits + * will be fractional part. + * + * Member function evaluateEdge, reverseEdge and isInsideCCW are referenced from rrRasterizer.cpp. + * + * @param {number} a + * @param {number} b + * @param {number} c + * @param {boolean} inclusive + */ +rrRenderer.edgeFunction = function(a, b, c, inclusive) { + this.a = a; + this.b = b; + this.c = c; + this.inclusive = inclusive; // True if edge is inclusive according to fill rules +}; + +/** + * Evaluate point (x,y) + * @param {number} x + * @param {number} y + * @return {number} + */ +rrRenderer.edgeFunction.prototype.evaluateEdge = function(x, y) { + return this.a * x + this.b * y + this.c; +}; + +/** + * Reverse edge (e.g. from CCW to CW) + */ +rrRenderer.edgeFunction.prototype.reverseEdge = function () { + this.a = -this.a; + this.b = -this.b; + this.c = -this.c; + this.inclusive = !this.inclusive; +}; + +/** + * Determine if a point with value edgeVal is inside the CCW region of the edge + * @param {number} edgeVal + * @return {boolean} + */ +rrRenderer.edgeFunction.prototype.isInsideCCW = function(edgeVal) { + return this.inclusive ? edgeVal >= 0 : edgeVal > 0; +}; + +/** + * Init an edge function in counter-clockwise (CCW) orientation + * @param {number} horizontalFill + * @param {number} verticalFill + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @return {rrRenderer.edgeFunction} + */ +rrRenderer.initEdgeCCW = function(horizontalFill, verticalFill, x0, y0, x1, y1) { + var xd = x1 - x0; + var yd = y1 - y0; + var inclusive = false; + + if (yd == 0) + inclusive = verticalFill == rrRenderState.VerticalFill.BOTTOM ? xd >= 0 : xd <= 0; + else + inclusive = horizontalFill == rrRenderState.HorizontalFill.LEFT ? yd <= 0 : yd >=0; + + return new rrRenderer.edgeFunction(y0 - y1, x1 - x0, x0 * y1 - y0 * x1, inclusive); +}; + +/** + * \brief Triangle rasterizer - referenced from class TriangleRasterizer in rrRasterizer.hpp + * + * Triangle rasterizer implements following features: + * - Rasterization using fixed-point coordinates + * - 1-sample rasterization (the value of numSamples always equals 1 in sglrReferenceContext) + * - Depth interpolation + * - Perspective-correct barycentric computation for interpolation + * - Visible face determination + * - Clipping - native dEQP does clipping before rasterization; see function drawBasicPrimitives + * in rrRenderer.cpp for more details + * + * It does not (and will not) implement following: + * - Triangle setup + * - Degenerate elimination + * - Coordinate transformation (inputs are in screen-space) + * - Culling - logic can be implemented outside by querying visible face + * - Scissoring - (this can be done by controlling viewport rectangle) + * - Any per-fragment operations + * + * @param {rrRenderState.RenderState} state + */ +rrRenderer.triangleRasterizer = function(state) { + this.m_viewport = state.viewport; + this.m_winding = state.rasterization.winding; + this.m_horizontalFill = state.rasterization.horizontalFill; + this.m_verticalFill = state.rasterization.verticalFill; +}; + +/** + * Initialize triangle rasterization + * @param {vec} v0 Screen-space coordinates (x, y, z) and 1/w for vertex 0 + * @param {vec} v1 Screen-space coordinates (x, y, z) and 1/w for vertex 1 + * @param {vec} v2 Screen-space coordinates (x, y, z) and 1/w for vertex 2 + */ +rrRenderer.triangleRasterizer.prototype.init = function(v0, v1, v2) { + this.m_v0 = v0; + this.m_v1 = v1; + this.m_v2 = v2; + + // Positions in fixed-point coordinates + var x0 = rrRenderer.toSubpixelCoord(v0[0]); + var y0 = rrRenderer.toSubpixelCoord(v0[1]); + var x1 = rrRenderer.toSubpixelCoord(v1[0]); + var y1 = rrRenderer.toSubpixelCoord(v1[1]); + var x2 = rrRenderer.toSubpixelCoord(v2[0]); + var y2 = rrRenderer.toSubpixelCoord(v2[1]); + + // Initialize edge functions + if (this.m_winding == rrRenderState.Winding.CCW) { + this.m_edge01 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x0, y0, x1, y1); + this.m_edge12 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x1, y1, x2, y2); + this.m_edge20 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x2, y2, x0, y0); + } else { + // Reverse edges + this.m_edge01 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x1, y1, x0, y0); + this.m_edge12 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x2, y2, x1, y1); + this.m_edge20 = rrRenderer.initEdgeCCW(this.m_horizontalFill, this.m_verticalFill, x0, y0, x2, y2); + } + + // Determine face + var s = this.m_edge01.evaluateEdge(x2, y2); + var positiveArea = (this.m_winding == rrRenderState.Winding.CCW ) ? s > 0 : s < 0; + this.m_face = positiveArea ? rrDefs.FaceType.FACETYPE_FRONT : rrDefs.FaceType.FACETYPE_BACK; + if (!positiveArea) { + // Reverse edges so that we can use CCW area tests & interpolation + this.m_edge01.reverseEdge(); + this.m_edge12.reverseEdge(); + this.m_edge20.reverseEdge(); + } + + // Bounding box + var minX = Math.min(x0, x1, x2); + var maxX = Math.max(x0, x1, x2); + var minY = Math.min(y0, y1, y2); + var maxY = Math.max(y0, y1, y2); + + this.m_bboxMin = []; + this.m_bboxMax = []; + this.m_bboxMin[0] = rrRenderer.floorSubpixelToPixelCoord(minX, this.m_horizontalFill == rrRenderState.HorizontalFill.LEFT); + this.m_bboxMin[1] = rrRenderer.floorSubpixelToPixelCoord(minY, this.m_verticalFill == rrRenderState.VerticalFill.BOTTOM); + this.m_bboxMax[0] = rrRenderer.ceilSubpixelToPixelCoord(maxX, this.m_horizontalFill == rrRenderState.HorizontalFill.RIGHT); + this.m_bboxMax[1] = rrRenderer.ceilSubpixelToPixelCoord(maxY, this.m_verticalFill == rrRenderState.VerticalFill.TOP); + + // Clamp to viewport + var wX0 = this.m_viewport.rect.left; + var wY0 = this.m_viewport.rect.bottom; + var wX1 = wX0 + this.m_viewport.rect.width - 1; + var wY1 = wY0 + this.m_viewport.rect.height - 1; + + this.m_bboxMin[0] = deMath.clamp(this.m_bboxMin[0], wX0, wX1); + this.m_bboxMin[1] = deMath.clamp(this.m_bboxMin[1], wY0, wY1); + this.m_bboxMax[0] = deMath.clamp(this.m_bboxMax[0], wX0, wX1); + this.m_bboxMax[1] = deMath.clamp(this.m_bboxMax[1], wY0, wY1); + + this.m_curPos = [this.m_bboxMin[0], this.m_bboxMin[1]]; +}; + +rrRenderer.triangleRasterizer.prototype.rasterize = function() { + var fragmentPackets = []; + var halfPixel = 1 << (rrRenderer.RASTERIZER_SUBPIXEL_BITS - 1); + + // For depth interpolation; given barycentrics A, B, C = (1 - A -B) + // We can reformulate the usual z = z0 * A + z1 * B + z2 * C into more + // stable equation z = A * (z0 - z2) + B * (z1 - z2) + z2 + var za = this.m_v0[2] - this.m_v2[2]; + var zb = this.m_v1[2] - this.m_v2[2]; + var zc = this.m_v2[2]; + + var zn = this.m_viewport.zn; + var zf = this.m_viewport.zf; + var depthScale = (zf - zn) / 2; + var depthBias = (zf + zn) / 2; + + while (this.m_curPos[1] <= this.m_bboxMax[1]) { + var x0 = this.m_curPos[0]; + var y0 = this.m_curPos[1]; + + // Subpixel coords of (x0, y0), (x0 + 1, y0), (x0, y0 + 1), (x0 + 1, y0 + 1) + var sx0 = rrRenderer.toSubpixelCoord(x0) + halfPixel; + var sx1 = rrRenderer.toSubpixelCoord(x0 + 1) + halfPixel; + var sy0 = rrRenderer.toSubpixelCoord(y0) + halfPixel; + var sy1 = rrRenderer.toSubpixelCoord(y0 + 1) + halfPixel; + + var sx = [sx0, sx1, sx0, sx1]; + var sy = [sy0, sy0, sy1, sy1]; + + // Viewport test + var outX1 = x0 + 1 == this.m_viewport.rect.left + this.m_viewport.rect.width; + var outY1 = y0 + 1 == this.m_viewport.rect.bottom + this.m_viewport.rect.height; + + // Coverage + var coverage = 0; + + // Evaluate edge values + var e01 = []; + var e12 = []; + var e20 = []; + for (var i = 0; i < 4; i++) { + e01.push(this.m_edge01.evaluateEdge(sx[i], sy[i])); + e12.push(this.m_edge12.evaluateEdge(sx[i], sy[i])); + e20.push(this.m_edge20.evaluateEdge(sx[i], sy[i])); + } + + // Compute coverage mask + coverage = rrRenderer.setCoverageValue(coverage, 1, 0, 0, 0, this.m_edge01.isInsideCCW(e01[0]) && this.m_edge12.isInsideCCW(e12[0]) && this.m_edge20.isInsideCCW(e20[0])); + coverage = rrRenderer.setCoverageValue(coverage, 1, 1, 0, 0, !outX1 && this.m_edge01.isInsideCCW(e01[1]) && this.m_edge12.isInsideCCW(e12[1]) && this.m_edge20.isInsideCCW(e20[1])); + coverage = rrRenderer.setCoverageValue(coverage, 1, 0, 1, 0, !outY1 && this.m_edge01.isInsideCCW(e01[2]) && this.m_edge12.isInsideCCW(e12[2]) && this.m_edge20.isInsideCCW(e20[2])); + coverage = rrRenderer.setCoverageValue(coverage, 1, 1, 1, 0, !outX1 && !outY1 && this.m_edge01.isInsideCCW(e01[3]) && this.m_edge12.isInsideCCW(e12[3]) && this.m_edge20.isInsideCCW(e20[3])); + + // Advance to next location + this.m_curPos[0] += 2; + if (this.m_curPos[0] > this.m_bboxMax[0]) { + this.m_curPos[0] = this.m_bboxMin[0]; + this.m_curPos[1] += 2; + } + + if (coverage == 0) + continue; // Discard + + // Compute depth and barycentric coordinates + var edgeSum = deMath.add(deMath.add(e01, e12), e20); + var z0 = deMath.divide(e12, edgeSum); + var z1 = deMath.divide(e20, edgeSum); + + var b0 = deMath.multiply(e12, [this.m_v0[3], this.m_v0[3], this.m_v0[3], this.m_v0[3]]); + var b1 = deMath.multiply(e20, [this.m_v1[3], this.m_v1[3], this.m_v1[3], this.m_v1[3]]); + var b2 = deMath.multiply(e01, [this.m_v2[3], this.m_v2[3], this.m_v2[3], this.m_v2[3]]); + var bSum = deMath.add(deMath.add(b0, b1), b2); + var barycentric0 = deMath.divide(b0, bSum); + var barycentric1 = deMath.divide(b1, bSum); + var barycentric2 = deMath.subtract(deMath.subtract([1, 1, 1, 1], barycentric0), barycentric1); + + // In native dEQP, after rasterization, the pixel (x0, y0) actually represents four pixels: + // (x0, y0), (x0 + 1, y0), (x0, y0 + 1) and (x0 + 1, y0 + 1). + // The barycentrics and depths of these four pixels are to be computed after rasterization: + // barycentrics are computed in function shadeFragments in es3fFboTestUtil.cpp; + // depths are computed in function writeFragmentPackets in rrRenderer.cpp. + + // In js, pixels are processed one after another, so their depths and barycentrics should be computed immediately. + + // Determine if (x0, y0), (x0 + 1, y0), (x0, y0 + 1), (x0 + 1, y0 + 1) can be rendered + for (var fragNdx = 0; fragNdx < 4; fragNdx++) { + var xo = fragNdx % 2; + var yo = Math.trunc(fragNdx / 2); + var x = x0 + xo; + var y = y0 + yo; + + // The value of numSamples always equals 1 in sglrReferenceContext. + if(rrRenderer.getCoverageAnyFragmentSampleLive(coverage, 1, xo, yo)) { + // Barycentric coordinates - referenced from function readTriangleVarying in rrShadingContext.hpp + var b = [barycentric0[fragNdx], barycentric1[fragNdx], barycentric2[fragNdx]]; + + // Depth - referenced from writeFragmentPackets in rrRenderer.cpp + var depth = z0[fragNdx] * za + z1[fragNdx] * zb + zc; + depth = depth * depthScale + depthBias; + + // Clip test + // Native dEQP does clipping test before rasterization. + if (!rrRenderer.clipTest(x, y, depth, this.m_viewport.rect)) + continue; + + fragmentPackets.push(new rrFragmentOperations.Fragment(b, [x, y], depth)); + } + } + } + return fragmentPackets; +}; + +/** + * @param {rrRenderState.RenderState} state + * @param {rrRenderer.RenderTarget} renderTarget + * @param {sglrShaderProgram.ShaderProgram} program + * @param {Array<rrVertexAttrib.VertexAttrib>} vertexAttribs + * @param {rrRenderer.PrimitiveType} primitive + * @param {(number|rrRenderer.DrawIndices)} first Index of first quad vertex + * @param {number} count Number of indices + * @param {number} instanceID + */ +rrRenderer.drawTriangles = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) { + + /** + * @param {Array<rrVertexPacket.VertexPacket>} vertices + * @param {Array<number>} indices + * @return {Array<rrVertexPacket.VertexPacket>} + */ + var selectVertices = function(vertices, indices) { + var result = []; + for (var i = 0; i < indices.length; i++) + result.push(vertices[indices[i]]); + return result; + }; + + // Referenced from native dEQP Renderer::drawInstanced() in rrRenderer.cpp + + var primitives = new rrRenderer.PrimitiveList(primitive, count, first); + // Do not draw if nothing to draw + if (primitives.getNumElements() == 0) + return; + + // Prepare transformation + var numVaryings = program.vertexShader.getOutputs().length; + var vpalloc = new rrVertexPacket.VertexPacketAllocator(numVaryings); + var vertexPackets = vpalloc.allocArray(primitives.getNumElements()); + var drawContext = new rrRenderer.DrawContext(); + drawContext.primitiveID = 0; + + var numberOfVertices = primitives.getNumElements(); + var numVertexPackets = 0; + for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) { + + // input + vertexPackets[numVertexPackets].instanceNdx = instanceID; + vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx); + + // output + vertexPackets[numVertexPackets].pointSize = state.point.pointSize; // default value from the current state + vertexPackets[numVertexPackets].position = [0, 0, 0, 0]; // no undefined values + + ++numVertexPackets; + + } + program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets); + + // Referenced from native dEQP Renderer::rasterizePrimitive() for triangle rasterization in rrRenderer.cpp + + // In native dEQP, only maxFragmentPackets packets are processed per rasterize-shade-write loop; + // in js all packets are processed in one loop. + + var rasterizer = new rrRenderer.triangleRasterizer(state); + + for (var prim = primitives.getNextPrimitive(true); prim.length > 0; prim = primitives.getNextPrimitive()) { + var vertices = selectVertices(vertexPackets, prim); + + var v0 = rrRenderer.transformGLToWindowCoords(state, vertices[0]); + var v1 = rrRenderer.transformGLToWindowCoords(state, vertices[1]); + var v2 = rrRenderer.transformGLToWindowCoords(state, vertices[2]); + + rasterizer.init(v0, v1, v2); + + // Culling + if ((state.cullMode == rrRenderState.CullMode.FRONT && rasterizer.m_face == rrDefs.FaceType.FACETYPE_FRONT) || + (state.cullMode == rrRenderState.CullMode.BACK && rasterizer.m_face == rrDefs.FaceType.FACETYPE_BACK)) + return; + + /* TODO: Add Polygon Offset and Depth Clamp */ + + // Compute a conservative integer bounding box for the triangle + var minX = Math.floor(Math.min(v0[0], v1[0], v2[0])); + var maxX = Math.ceil(Math.max(v0[0], v1[0], v2[0])); + var minY = Math.floor(Math.min(v0[1], v1[1], v2[1])); + var maxY = Math.ceil(Math.max(v0[1], v1[1], v2[1])); + + // Shading context + var shadingContext = new rrShadingContext.FragmentShadingContext( + vertices[0].outputs, + vertices[1].outputs, + vertices[2].outputs + ); + shadingContext.setSize(maxX - minX, maxY - minY); + + // Rasterize + var fragmentPackets = rasterizer.rasterize(); + + // Shade + program.shadeFragments(fragmentPackets, shadingContext); + + // Handle fragment shader outputs + rrRenderer.writeFragments2(state, renderTarget, fragmentPackets); + } +}; + +/** + * @param {rrRenderState.RenderState} state + * @param {rrRenderer.RenderTarget} renderTarget + * @param {sglrShaderProgram.ShaderProgram} program + * @param {Array<rrVertexAttrib.VertexAttrib>} vertexAttribs + * @param {rrRenderer.PrimitiveType} primitive + * @param {(number|rrRenderer.DrawIndices)} first Index of first quad vertex + * @param {number} count Number of indices + * @param {number} instanceID + */ +rrRenderer.drawLines = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) { + + /** + * @param {Array<rrVertexPacket.VertexPacket>} vertices + * @param {Array<number>} indices + * @return {Array<rrVertexPacket.VertexPacket>} + */ + var selectVertices = function(vertices, indices) { + var result = []; + for (var i = 0; i < indices.length; i++) + result.push(vertices[indices[i]]); + return result; + }; + + var lengthSquared = function(a) { + var sqSum = 0; + for (var i = 0; i < a.length; i++) + sqSum += a[i] * a[i]; + return sqSum; + }; + + var dot = function(a, b) { + var res = 0; + for (var i = 0; i < a.length; i++) + res += a[i] * b[i]; + return res; + }; + + var rasterizeLine = function(v0, v1) { + var d = [ + Math.abs(v1[0] - v0[0]), + Math.abs(v1[1] - v0[1])]; + var xstep = v0[0] < v1[0] ? 1 : -1; + var ystep = v0[1] < v1[1] ? 1 : -1; + var x = v0[0]; + var y = v0[1]; + var offset = d[0] - d[1]; + var lenV = [v1[0] - v0[0], v1[1] - v0[1]]; + var lenSq = lengthSquared(lenV); + + var packets = []; + + while (true) { + var t = dot([x - v0[0], y - v0[1]], lenV) / lenSq; + var depth = (1 - t) * v0[2] + t * v1[2]; + var b = [0, 0, 0]; + b[0] = 1 - t; + b[1] = t; + + if (x == v1[0] && y == v1[1]) + break; + + depth = depth * depthScale + depthBias; + packets.push(new rrFragmentOperations.Fragment(b, [x, y], depth)); + + var offset2 = 2 * offset; + if (offset2 > -1 * d[1]) { + x += xstep; + offset -= d[1]; + } + + if (offset2 < d[0]) { + y += ystep; + offset += d[0]; + } + } + return packets; + }; + + var primitives = new rrRenderer.PrimitiveList(primitive, count, first); + // Do not draw if nothing to draw + if (primitives.getNumElements() == 0) + return; + + // Prepare transformation + var numVaryings = program.vertexShader.getOutputs().length; + var vpalloc = new rrVertexPacket.VertexPacketAllocator(numVaryings); + var vertexPackets = vpalloc.allocArray(primitives.getNumElements()); + var drawContext = new rrRenderer.DrawContext(); + drawContext.primitiveID = 0; + + var numberOfVertices = primitives.getNumElements(); + var numVertexPackets = 0; + for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) { + + // input + vertexPackets[numVertexPackets].instanceNdx = instanceID; + vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx); + + // output + vertexPackets[numVertexPackets].pointSize = state.point.pointSize; // default value from the current state + vertexPackets[numVertexPackets].position = [0, 0, 0, 0]; // no undefined values + + ++numVertexPackets; + + } + program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets); + + var zn = state.viewport.zn; + var zf = state.viewport.zf; + var depthScale = (zf - zn) / 2; + var depthBias = (zf + zn) / 2; + + // For each quad, we get a group of six vertex packets + for (var prim = primitives.getNextPrimitive(true); prim.length > 0; prim = primitives.getNextPrimitive()) { + var linePackets = selectVertices(vertexPackets, prim); + + var v0 = rrRenderer.transformGLToWindowCoords(state, linePackets[0]); + var v1 = rrRenderer.transformGLToWindowCoords(state, linePackets[1]); + v0[2] = linePackets[0].position[2]; + v1[2] = linePackets[1].position[2]; + + v0[0] = Math.floor(v0[0]); + v0[1] = Math.floor(v0[1]); + v1[0] = Math.floor(v1[0]); + v1[1] = Math.floor(v1[1]); + + var lineWidth = state.line.lineWidth; + + var shadingContext = new rrShadingContext.FragmentShadingContext( + linePackets[0].outputs, + linePackets[1].outputs, + null + ); + var isXmajor = Math.abs(v1[0] - v0[0]) >= Math.abs(v1[1] - v0[1]); + var packets = []; + if (isXmajor) + packets = rasterizeLine([v0[0], v0[1] - (lineWidth - 1) / 2, v0[2]], + [v1[0], v1[1] - (lineWidth - 1) / 2, v1[2]]); + else + packets = rasterizeLine([v0[0] - (lineWidth - 1) / 2, v0[1], v0[2]], + [v1[0] - (lineWidth - 1) / 2, v1[1], v1[2]]); + var numPackets = packets.length; + if (lineWidth > 1) + for (var i = 0; i < numPackets; i++) { + var p = packets[i]; + for (var j = 1; j < lineWidth; j++) { + var p2 = deUtil.clone(p); + if (isXmajor) + p2.pixelCoord[1] += j; + else + p2.pixelCoord[0] += j; + packets.push(p2); + } + } + + var clipped = []; + for (var i = 0; i < packets.length; i++) { + var p = packets[i]; + if (rrRenderer.clipTest(p.pixelCoord[0], p.pixelCoord[1], p.sampleDepths[0], state.viewport.rect)) + clipped.push(p); + } + program.shadeFragments(clipped, shadingContext); + + rrRenderer.writeFragments2(state, renderTarget, clipped); + } +}; + +/** + * @param {rrRenderState.RenderState} state + * @param {rrRenderer.RenderTarget} renderTarget + * @param {sglrShaderProgram.ShaderProgram} program + * @param {Array<rrVertexAttrib.VertexAttrib>} vertexAttribs + * @param {rrRenderer.PrimitiveType} primitive + * @param {(number|rrRenderer.DrawIndices)} first Index of first quad vertex + * @param {number} count Number of indices + * @param {number} instanceID + */ +rrRenderer.drawPoints = function(state, renderTarget, program, vertexAttribs, primitive, first, count, instanceID) { + /** + * @param {Array<rrVertexPacket.VertexPacket>} vertices + * @param {Array<number>} indices + * @return {Array<rrVertexPacket.VertexPacket>} + */ + var selectVertices = function(vertices, indices) { + var result = []; + for (var i = 0; i < indices.length; i++) + result.push(vertices[indices[i]]); + return result; + }; + + var primitives = new rrRenderer.PrimitiveList(primitive, count, first); + // Do not draw if nothing to draw + if (primitives.getNumElements() == 0) + return; + + // Prepare transformation + var numVaryings = program.vertexShader.getOutputs().length; + var vpalloc = new rrVertexPacket.VertexPacketAllocator(numVaryings); + var vertexPackets = vpalloc.allocArray(primitives.getNumElements()); + var drawContext = new rrRenderer.DrawContext(); + drawContext.primitiveID = 0; + + var numberOfVertices = primitives.getNumElements(); + var numVertexPackets = 0; + for (var elementNdx = 0; elementNdx < numberOfVertices; ++elementNdx) { + + // input + vertexPackets[numVertexPackets].instanceNdx = instanceID; + vertexPackets[numVertexPackets].vertexNdx = primitives.getIndex(elementNdx); + + // output + vertexPackets[numVertexPackets].pointSize = state.point.pointSize; // default value from the current state + vertexPackets[numVertexPackets].position = [0, 0, 0, 0]; // no undefined values + + ++numVertexPackets; + + } + program.shadeVertices(vertexAttribs, vertexPackets, numVertexPackets); + + var zn = state.viewport.zn; + var zf = state.viewport.zf; + var depthScale = (zf - zn) / 2; + var depthBias = (zf + zn) / 2; + + // For each primitive, we draw a point. + for (var prim = primitives.getNextPrimitive(true); prim.length > 0; prim = primitives.getNextPrimitive()) { + var pointPackets = selectVertices(vertexPackets, prim); + + var v0 = rrRenderer.transformGLToWindowCoords(state, pointPackets[0]); + v0[2] = pointPackets[0].position[2]; + var pointSize = pointPackets[0].pointSize; + + var shadingContext = new rrShadingContext.FragmentShadingContext( + pointPackets[0].outputs, + null, + null + ); + var packets = []; + + var x = v0[0]; + var y = v0[1]; + var depth = v0[2]; + var b = [1, 0, 0]; + depth = depth * depthScale + depthBias; + + for (var i = Math.floor(x - pointSize / 2); i < x + pointSize / 2; i++) { + for (var j = Math.floor(y - pointSize / 2); j < y + pointSize / 2; j++) { + var centerX = i + 0.5; + var centerY = j + 0.5; + if (Math.abs(centerX - x) <= pointSize / 2 && + Math.abs(centerY - y) <= pointSize / 2 && + rrRenderer.clipTest(i, j, depth, state.viewport.rect)) + packets.push(new rrFragmentOperations.Fragment(b, [i, j], depth)); + } + } + + program.shadeFragments(packets, shadingContext); + + rrRenderer.writeFragments2(state, renderTarget, packets); + } +}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrShaders.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrShaders.js new file mode 100644 index 000000000..143d9b728 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrShaders.js @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrShaders'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.referencerenderer.rrGenericVector'); +goog.require('framework.referencerenderer.rrShadingContext'); +goog.require('framework.referencerenderer.rrVertexAttrib'); +goog.require('framework.referencerenderer.rrVertexPacket'); + +goog.scope(function() { + +var rrShaders = framework.referencerenderer.rrShaders; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; +var rrGenericVector = framework.referencerenderer.rrGenericVector; +var rrShadingContext = framework.referencerenderer.rrShadingContext; +var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; +var rrVertexPacket = framework.referencerenderer.rrVertexPacket; + + /** + * Vertex shader input information + * @constructor + */ + rrShaders.VertexInputInfo = function() { + /** @type {rrGenericVector.GenericVecType} */ this.type; + }; + + /** + * Shader varying information + * @constructor + */ + rrShaders.VertexVaryingInfo = function() { + /** @type {rrGenericVector.GenericVecType} */ this.type; + /** @type {boolean} */ var flatshade = false; + }; + + /** + * Fragment shader output information + * @constructor + */ + rrShaders.FragmentOutputInfo = function() { + //Sensible defaults + /** @type {rrGenericVector.GenericVecType} */ this.type; + }; + + /** + * Vertex shader interface + * + * Vertex shaders execute shading for set of vertex packets. See VertexPacket + * documentation for more details on shading API. + * @constructor + * @param {number} numInputs + * @param {number} numOutputs + */ + rrShaders.VertexShader = function(numInputs, numOutputs) { + /** @type {Array<rrShaders.VertexInputInfo>} */ this.m_inputs = []; + for (var ndx = 0; ndx < numInputs; ndx++) this.m_inputs[ndx] = new rrShaders.VertexInputInfo(); + /** @type {Array<rrShaders.VertexVaryingInfo>} */ this.m_outputs = []; + for (var ndx = 0; ndx < numOutputs; ndx++) this.m_outputs[ndx] = new rrShaders.VertexVaryingInfo(); + }; + + /** + * getInputs + * @return {Array<rrShaders.VertexInputInfo>} + */ + rrShaders.VertexShader.prototype.getInputs = function() {return this.m_inputs;}; + + /** + * getOutputs + * @return {Array<rrShaders.VertexVaryingInfo>} + */ + rrShaders.VertexShader.prototype.getOutputs = function() {return this.m_outputs;}; + + /** + * Fragment shader interface + * + * Fragment shader executes shading for list of fragment packets. See + * FragmentPacket documentation for more details on shading API. + * @constructor + * @param {number} numInputs + * @param {number} numOutputs + */ + rrShaders.FragmentShader = function(numInputs, numOutputs) { + /** @type {Array<rrShaders.VertexVaryingInfo>} */ this.m_inputs = []; + for (var ndx = 0; ndx < numInputs; ndx++) this.m_inputs[ndx] = new rrShaders.VertexVaryingInfo(); + /** @type {Array<rrShaders.FragmentOutputInfo>} */ this.m_outputs = []; + for (var ndx = 0; ndx < numOutputs; ndx++) this.m_outputs[ndx] = new rrShaders.FragmentOutputInfo(); + /** @type {*} */ this.m_container; // owner object + }; + + /** + * getInputs + * @return {Array<rrShaders.VertexVaryingInfo>} + */ + rrShaders.FragmentShader.prototype.getInputs = function() {return this.m_inputs;}; + + /** + * getOutputs + * @return {Array<rrShaders.FragmentOutputInfo>} + */ + rrShaders.FragmentShader.prototype.getOutputs = function() {return this.m_outputs;}; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrShadingContext.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrShadingContext.js new file mode 100644 index 000000000..38b9d201f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrShadingContext.js @@ -0,0 +1,113 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrShadingContext'); +goog.require('framework.referencerenderer.rrFragmentOperations'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.referencerenderer.rrDefs'); +goog.require('framework.referencerenderer.rrFragmentOperations'); +goog.require('framework.referencerenderer.rrGenericVector'); + +goog.scope(function() { + + var rrShadingContext = framework.referencerenderer.rrShadingContext; + var deMath = framework.delibs.debase.deMath; + var rrDefs = framework.referencerenderer.rrDefs; + var rrFragmentOperations = framework.referencerenderer.rrFragmentOperations; + var rrGenericVector = framework.referencerenderer.rrGenericVector; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * Fragment shading context + * + * Contains per-primitive information used in fragment shading + * @constructor + * @param {Array<Array<number>>} varying0 (GenericVec4*) + * @param {Array<Array<number>>} varying1 (GenericVec4*) + * @param {Array<Array<number>>} varying2 (GenericVec4*) + */ + rrShadingContext.FragmentShadingContext = function(varying0, varying1, varying2) { + /** @type {Array<Array<Array<number>>>} */ this.varyings = [varying0, varying1, varying2]; //!< Vertex shader outputs. Pointer will be NULL if there is no such vertex. + this.m_width = 0xFFFFFFFF; + this.m_height = 0xFFFFFFFF; + }; + + /** + * @param {number} width + * @param {number} height + */ + rrShadingContext.FragmentShadingContext.prototype.setSize = function(width, height) { + this.m_width = width; + this.m_height = height; + }; + + rrShadingContext.FragmentShadingContext.prototype.getWidth = function() { + return this.m_width; + }; + + rrShadingContext.FragmentShadingContext.prototype.getHeight = function() { + return this.m_height; + }; + + // Read Varying + + /** + * @param {rrFragmentOperations.Fragment} packet + * @param {rrShadingContext.FragmentShadingContext} context + * @param {number} varyingLoc + * @return {Array<number>} (Vector<T, 4>) + */ + rrShadingContext.readTriangleVarying = function(packet, context, varyingLoc) { + var result = deMath.scale( + context.varyings[0][varyingLoc], + packet.barycentric[0] + ); + + if (context.varyings[1]) + result = deMath.add(result, deMath.scale( + context.varyings[1][varyingLoc], + packet.barycentric[1] + )); + + if (context.varyings[2]) + result = deMath.add(result, deMath.scale( + context.varyings[2][varyingLoc], + packet.barycentric[2] + )); + + return result; + }; + + /** + * @param {rrFragmentOperations.Fragment} packet + * @param {rrShadingContext.FragmentShadingContext} context + * @param {number} varyingLoc + * @return {Array<number>} (Vector<T, 4>) + */ + rrShadingContext.readVarying = function(packet, context, varyingLoc) { + return rrShadingContext.readTriangleVarying(packet, context, varyingLoc); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrUtil.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrUtil.js new file mode 100644 index 000000000..03a58168f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrUtil.js @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrUtil'); +goog.require('framework.opengl.simplereference.sglrGLContext'); +goog.require('framework.opengl.simplereference.sglrReferenceContext'); + +goog.scope(function() { + + var rrUtil = framework.referencerenderer.rrUtil; + var sglrGLContext = framework.opengl.simplereference.sglrGLContext; + var sglrReferenceContext = framework.opengl.simplereference.sglrReferenceContext; + + /** + * @param {sglrGLContext.GLContext | WebGL2RenderingContext | sglrReferenceContext.ReferenceContext} ctx + * @param {number|Object} program + * @param {Array<number>} p0 + * @param {Array<number>} p1 + */ + rrUtil.drawQuad = function(ctx, program, p0, p1) { + // Vertex data. + var hz = (p0[2] + p1[2]) * 0.5; + /** @type {Array<number>} */ var position = [ + p0[0], p0[1], p0[2], 1.0, + p0[0], p1[1], hz, 1.0, + p1[0], p0[1], hz, 1.0, + p1[0], p1[1], p1[2], 1.0 + ]; + /** @type {Array<number>} */ var coord = [ + 0.0, 0.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0 + ]; + /** @type {Array<number>} */ var indices = [0, 1, 2, 2, 1, 3]; + + var posLoc = ctx.getAttribLocation(program, 'a_position'); + if (posLoc == -1) + throw new Error('a_position attribute is not defined.'); + + var coordLoc = ctx.getAttribLocation(program, 'a_coord'); + var vaoID; + var bufIDs = []; + + vaoID = ctx.createVertexArray(); + ctx.bindVertexArray(vaoID); + + bufIDs[0] = ctx.createBuffer(); + bufIDs[1] = ctx.createBuffer(); + + ctx.useProgram(program); + + if (posLoc >= 0) { + ctx.bindBuffer(gl.ARRAY_BUFFER, bufIDs[0]); + ctx.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW); + + ctx.enableVertexAttribArray(posLoc); + ctx.vertexAttribPointer(posLoc, 4, gl.FLOAT, false, 0, 0); + + ctx.bindBuffer(gl.ARRAY_BUFFER, null); + } + + if (coordLoc >= 0) { + ctx.bindBuffer(gl.ARRAY_BUFFER, bufIDs[1]); + ctx.bufferData(gl.ARRAY_BUFFER, new Float32Array(coord), gl.STATIC_DRAW); + + ctx.enableVertexAttribArray(coordLoc); + ctx.vertexAttribPointer(coordLoc, 2, gl.FLOAT, false, 0, 0); + + ctx.bindBuffer(gl.ARRAY_BUFFER, null); + } + + { + var ndxID = ctx.createBuffer(); + + ctx.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ndxID); + ctx.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); + + ctx.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + + ctx.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + ctx.deleteBuffer(ndxID); + } + + ctx.bindVertexArray(null); + ctx.deleteBuffer(bufIDs[0]); + ctx.deleteBuffer(bufIDs[1]); + ctx.deleteVertexArray(vaoID); + + if (posLoc >= 0) + ctx.disableVertexAttribArray(posLoc); + + if (coordLoc >= 0) + ctx.disableVertexAttribArray(coordLoc); + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrVertexAttrib.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrVertexAttrib.js new file mode 100644 index 000000000..f6095e2cc --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrVertexAttrib.js @@ -0,0 +1,641 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrVertexAttrib'); +goog.require('framework.common.tcuFloat'); +goog.require('framework.delibs.debase.deMath'); +goog.require('framework.referencerenderer.rrGenericVector'); + +goog.scope(function() { + +var rrVertexAttrib = framework.referencerenderer.rrVertexAttrib; +var deMath = framework.delibs.debase.deMath; +var tcuFloat = framework.common.tcuFloat; +var rrGenericVector = framework.referencerenderer.rrGenericVector; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** + * rrVertexAttrib.NormalOrder + * @enum + */ + rrVertexAttrib.NormalOrder = { + T0: 0, + T1: 1, + T2: 2, + T3: 3 + }; + + /** + * rrVertexAttrib.BGRAOrder + * @enum + */ + rrVertexAttrib.BGRAOrder = { + T0: 2, + T1: 1, + T2: 0, + T3: 3 + }; + + /** + * rrVertexAttrib.VertexAttribType enum + * @enum + */ + rrVertexAttrib.VertexAttribType = { + // Can only be rrVertexAttrib.read as floats + FLOAT: 0, + HALF: 1, + FIXED: 2, + DOUBLE: 3, + + // Can only be rrVertexAttrib.read as floats, will be normalized + NONPURE_UNORM8: 4, + NONPURE_UNORM16: 5, + NONPURE_UNORM32: 6, + NONPURE_UNORM_2_10_10_10_REV: 7, //!< Packed format, only size = 4 is allowed + + // Clamped formats, GLES3-style conversion: max{c / (2^(b-1) - 1), -1 } + NONPURE_SNORM8_CLAMP: 8, + NONPURE_SNORM16_CLAMP: 9, + NONPURE_SNORM32_CLAMP: 10, + NONPURE_SNORM_2_10_10_10_REV_CLAMP: 11, //!< Packed format, only size = 4 is allowed + + // Scaled formats, GLES2-style conversion: (2c + 1) / (2^b - 1) + NONPURE_SNORM8_SCALE: 12, + NONPURE_SNORM16_SCALE: 13, + NONPURE_SNORM32_SCALE: 14, + NONPURE_SNORM_2_10_10_10_REV_SCALE: 15, //!< Packed format, only size = 4 is allowed + + // can only be rrVertexAttrib.read as float, will not be normalized + NONPURE_UINT8: 16, + NONPURE_UINT16: 17, + NONPURE_UINT32: 18, + + NONPURE_INT8: 19, + NONPURE_INT16: 20, + NONPURE_INT32: 21, + + NONPURE_UINT_2_10_10_10_REV: 22, //!< Packed format, only size = 4 is allowed + NONPURE_INT_2_10_10_10_REV: 23, //!< Packed format, only size = 4 is allowed + + // can only be rrVertexAttrib.read as integers + PURE_UINT8: 24, + PURE_UINT16: 25, + PURE_UINT32: 26, + + PURE_INT8: 27, + PURE_INT16: 28, + PURE_INT32: 29, + + // reordered formats of gl.ARB_vertex_array_bgra + NONPURE_UNORM8_BGRA: 30, + NONPURE_UNORM_2_10_10_10_REV_BGRA: 31, + NONPURE_SNORM_2_10_10_10_REV_CLAMP_BGRA: 32, + NONPURE_SNORM_2_10_10_10_REV_SCALE_BGRA: 33, + + // can be rrVertexAttrib.read as anything + DONT_CARE: 34 //!< Do not enforce type checking when reading GENERIC attribute. Used for current client side attributes. + }; + + /** + * rrVertexAttrib.VertexAttrib class + * @constructor + */ + rrVertexAttrib.VertexAttrib = function() { + /** @type {rrVertexAttrib.VertexAttribType} */ this.type = rrVertexAttrib.VertexAttribType.FLOAT; + /** @type {number} */ this.size = 0; + /** @type {number} */ this.stride = 0; + /** @type {number} */ this.instanceDivisor = 0; + /** @type {number} */ this.offset = 0; //Added this property to compensate functionality (not in original dEQP). + /** @type {ArrayBuffer} */ this.pointer = null; + /** @type {Array<number>|rrGenericVector.GenericVec4} */ this.generic; //!< Generic attribute, used if pointer is null. + }; + + /** + * @param {rrVertexAttrib.VertexAttribType} type + * @return {number} + */ + rrVertexAttrib.getComponentSize = function(type) { + switch (type) { + case rrVertexAttrib.VertexAttribType.FLOAT: return 4; + case rrVertexAttrib.VertexAttribType.HALF: return 2; + case rrVertexAttrib.VertexAttribType.FIXED: return 4; + case rrVertexAttrib.VertexAttribType.DOUBLE: return 8; //sizeof(double); + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM8: return 1; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM16: return 2; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM32: return 4; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM_2_10_10_10_REV: return 1; //sizeof(deUint32)/4; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM8_CLAMP: return 1; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM16_CLAMP: return 2; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM32_CLAMP: return 4; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_CLAMP: return 1; //sizeof(deUint32)/4; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM8_SCALE: return 1; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM16_SCALE: return 2; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM32_SCALE: return 4; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_SCALE: return 1; //sizeof(deUint32)/4; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT8: return 1; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT16: return 2; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT32: return 4; + case rrVertexAttrib.VertexAttribType.NONPURE_INT8: return 1; + case rrVertexAttrib.VertexAttribType.NONPURE_INT16: return 2; + case rrVertexAttrib.VertexAttribType.NONPURE_INT32: return 4; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT_2_10_10_10_REV: return 1; //sizeof(deUint32)/4; + case rrVertexAttrib.VertexAttribType.NONPURE_INT_2_10_10_10_REV: return 1; //sizeof(deUint32)/4; + case rrVertexAttrib.VertexAttribType.PURE_UINT8: return 1; + case rrVertexAttrib.VertexAttribType.PURE_UINT16: return 2; + case rrVertexAttrib.VertexAttribType.PURE_UINT32: return 4; + case rrVertexAttrib.VertexAttribType.PURE_INT8: return 1; + case rrVertexAttrib.VertexAttribType.PURE_INT16: return 2; + case rrVertexAttrib.VertexAttribType.PURE_INT32: return 4; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM8_BGRA: return 1; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM_2_10_10_10_REV_BGRA: return 1; //sizeof(deUint32)/4; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_CLAMP_BGRA: return 1; //sizeof(deUint32)/4; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_SCALE_BGRA: return 1; //sizeof(deUint32)/4; + default: + throw new Error('rrVertexAttrib.getComponentSize - Invalid type'); + } + }; + + /** + * rrVertexAttrib.isValidVertexAttrib function + * @param {rrVertexAttrib.VertexAttrib} vertexAttrib + * @return {boolean} + */ + rrVertexAttrib.isValidVertexAttrib = function(vertexAttrib) { + // Trivial range checks. + if (!deMath.deInBounds32(vertexAttrib.type, 0, Object.keys(rrVertexAttrib.VertexAttribType).length) || + !deMath.deInRange32(vertexAttrib.size, 0, 4) || + vertexAttrib.instanceDivisor < 0) + return false; + + // Generic attributes + if (!vertexAttrib.pointer && vertexAttrib.type != rrVertexAttrib.VertexAttribType.DONT_CARE) + return false; + + // Packed formats + if ((vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_INT_2_10_10_10_REV || + vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_UINT_2_10_10_10_REV || + vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_UNORM_2_10_10_10_REV || + vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_CLAMP || + vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_SCALE || + vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_UNORM_2_10_10_10_REV_BGRA || + vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_CLAMP_BGRA || + vertexAttrib.type == rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_SCALE_BGRA) && + vertexAttrib.size != 4) + return false; + + return true; + }; + + /** + * rrVertexAttrib.readVertexAttrib function + * @param {rrVertexAttrib.VertexAttrib} vertexAttrib + * @param {number} instanceNdx + * @param {number} vertexNdx + * @param {rrGenericVector.GenericVecType} genericType + * @return {goog.NumberArray} + */ + rrVertexAttrib.readVertexAttrib = function(vertexAttrib, instanceNdx, vertexNdx, genericType) { + DE_ASSERT(rrVertexAttrib.isValidVertexAttrib(vertexAttrib)); + /** @type {goog.NumberArray} */ var dst; + + var arrayType = null; + switch (genericType) { + case rrGenericVector.GenericVecType.INT32: + arrayType = Int32Array; + break; + case rrGenericVector.GenericVecType.UINT32: + arrayType = Uint32Array; + break; + case rrGenericVector.GenericVecType.FLOAT: + arrayType = Float32Array; + break; + } + + if (vertexAttrib.pointer) { + /** @type {number} */ var elementNdx = (vertexAttrib.instanceDivisor != 0) ? (instanceNdx / vertexAttrib.instanceDivisor) : vertexNdx; + /** @type {number} */ var compSize = rrVertexAttrib.getComponentSize(vertexAttrib.type); + /** @type {number} */ var stride = (vertexAttrib.stride != 0) ? (vertexAttrib.stride) : (vertexAttrib.size * compSize); + /** @type {number} */ var byteOffset = vertexAttrib.offset + (elementNdx * stride); + + dst = [0, 0, 0, 1]; // defaults + + if (arrayType != null) { + dst = new arrayType(dst); + } + + rrVertexAttrib.read(dst, vertexAttrib.type, vertexAttrib.size, new Uint8Array(vertexAttrib.pointer, byteOffset)); + } else { + dst = new arrayType(/** @type {Array<number>} */ vertexAttrib.generic.data); + } + + return dst; + }; + + /** + * rrVertexAttrib.readHalf + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + */ + rrVertexAttrib.readHalf = function(dst, size, ptr) { + var arraysize16 = 2; //2 bytes + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arraysize16)); //Small buffer copy (max. 8 bytes) + var aligned = new Uint16Array(ptrclone.buffer); + + //Reinterpret aligned's values into the dst vector. + dst[0] = tcuFloat.newFloat32From16(aligned[0]).getValue(); + if (size >= 2) dst[1] = tcuFloat.newFloat32From16(aligned[1]).getValue(); + if (size >= 3) dst[2] = tcuFloat.newFloat32From16(aligned[2]).getValue(); + if (size >= 4) dst[3] = tcuFloat.newFloat32From16(aligned[3]).getValue(); + }; + + /** + * rrVertexAttrib.readFixed + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + */ + /*rrVertexAttrib.readFixed = function(dst, size, ptr) { + var arraysize32 = 4; //4 bytes + + //Reinterpret ptr as a uint16 array, + //assuming original ptr is 8-bits per element + var aligned = new Int32Array(ptr.buffer).subarray( + ptr.byteOffset / arraysize32, + (ptr.byteOffset + ptr.byteLength) / arraysize32); + + //Reinterpret aligned's values into the dst vector. + dst[0] = aligned[0] / (1 << 16); + if (size >= 2) dst[1] = aligned[1] / (1 << 16); + if (size >= 3) dst[2] = aligned[2] / (1 << 16); + if (size >= 4) dst[3] = aligned[3] / (1 << 16); + };*/ + + /** + * TODO: Check 64 bit numbers are handled ok + * rrVertexAttrib.readDouble + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + */ + /*rrVertexAttrib.readDouble = function(dst, size, ptr) { + var arraysize64 = 8; //8 bytes + + //Reinterpret 'ptr' into 'aligned' as a float64 array, + //assuming original ptr is 8-bits per element. + var aligned = new Float64Array(ptr.buffer).subarray( + ptr.byteOffset / arraysize64, + (ptr.byteOffset + ptr.byteLength) / arraysize64); + + //Reinterpret aligned's values into the dst vector. + dst[0] = aligned[0]; + if (size >= 2) dst[1] = aligned[1]; + if (size >= 3) dst[2] = aligned[2]; + if (size >= 4) dst[3] = aligned[3]; + };*/ + + /** + * extendSign + * @param {number} integerLen + * @param {number} integer_ (deUint32) + * @return {number} (deInt32) + */ + rrVertexAttrib.extendSign = function(integerLen, integer_) { + return new Int32Array([ + deMath.binaryOp( + 0 - + deMath.shiftLeft( + deMath.binaryOp( + integer_, + deMath.shiftLeft( + 1, + (integerLen - 1) + ), + deMath.BinaryOp.AND + ), + 1 + ) , + +integer_, + deMath.BinaryOp.OR + ) + ])[0]; + }; + + /** + * rrVertexAttrib.readUint2101010Rev + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + */ + rrVertexAttrib.readUint2101010Rev = function(dst, size, ptr) { + var arraysize32 = 4; //4 bytes + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arraysize32)); //Small buffer copy (max. 16 bytes) + var aligned = new Uint32Array(ptrclone.buffer)[0]; + + dst[0] = deMath.binaryOp(deMath.shiftRight(aligned, 0), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND); + if (size >= 2) dst[1] = deMath.binaryOp(deMath.shiftRight(aligned, 10), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND); + if (size >= 3) dst[2] = deMath.binaryOp(deMath.shiftRight(aligned, 20), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND); + if (size >= 4) dst[3] = deMath.binaryOp(deMath.shiftRight(aligned, 30), deMath.shiftLeft(1, 2) - 1, deMath.BinaryOp.AND); + }; + + /** + * rrVertexAttrib.readInt2101010Rev + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + */ + rrVertexAttrib.readInt2101010Rev = function(dst, size, ptr) { + var arraysize32 = 4; //4 bytes + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arraysize32)); //Small buffer copy (max. 16 bytes) + var aligned = new Uint32Array(ptrclone.buffer)[0]; + + dst[0] = rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 0), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND)); + if (size >= 2) dst[1] = rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 10), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND)); + if (size >= 3) dst[2] = rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 20), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND)); + if (size >= 4) dst[3] = rrVertexAttrib.extendSign(2, deMath.binaryOp(deMath.shiftRight(aligned, 30), deMath.shiftLeft(1, 2) - 1, deMath.BinaryOp.AND)); + }; + + /** + * rrVertexAttrib.readUnorm2101010RevOrder + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + * @param {Object<rrVertexAttrib.NormalOrder|rrVertexAttrib.BGRAOrder>} order + */ + rrVertexAttrib.readUnorm2101010RevOrder = function(dst, size, ptr, order) { + var arraysize32 = 4; //4 bytes + + //Left shift within 32-bit range as 32-bit int. + var range10 = new Uint32Array([deMath.shiftLeft(1, 10) - 1])[0]; + var range2 = new Uint32Array([deMath.shiftLeft(1, 2) - 1])[0]; + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arraysize32)); //Small buffer copy (max. 16 bytes) + var aligned = new Uint32Array(ptrclone.buffer)[0]; + + dst[order.T0] = deMath.binaryOp(deMath.shiftRight(aligned, 0), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND) / range10; + if (size >= 2) dst[order.T1] = deMath.binaryOp(deMath.shiftRight(aligned, 10), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND) / range10; + if (size >= 3) dst[order.T2] = deMath.binaryOp(deMath.shiftRight(aligned, 20), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND) / range10; + if (size >= 4) dst[order.T3] = deMath.binaryOp(deMath.shiftRight(aligned, 30), deMath.shiftLeft(1, 2) - 1, deMath.BinaryOp.AND) / range2; + }; + + /** + * rrVertexAttrib.readSnorm2101010RevClampOrder + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + * @param {Object<rrVertexAttrib.NormalOrder|rrVertexAttrib.BGRAOrder>} order + */ + rrVertexAttrib.readSnorm2101010RevClampOrder = function(dst, size, ptr, order) { + var arraysize32 = 4; //4 bytes + + //Left shift within 32-bit range as 32-bit int. + var range10 = new Uint32Array([deMath.shiftLeft(1, 10 - 1) - 1])[0]; + var range2 = new Uint32Array([deMath.shiftLeft(1, 2 - 1) - 1])[0]; + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arraysize32)); //Small buffer copy (max. 16 bytes) + var aligned = new Uint32Array(ptrclone.buffer)[0]; + + dst[order.T0] = Math.max(-1.0, new Float32Array([rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 0), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND))])[0] / range10); + if (size >= 2) dst[order.T1] = Math.max(-1.0, new Float32Array([rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 10), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND))])[0] / range10); + if (size >= 3) dst[order.T2] = Math.max(-1.0, new Float32Array([rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 20), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND))])[0] / range10); + if (size >= 4) dst[order.T3] = Math.max(-1.0, new Float32Array([rrVertexAttrib.extendSign(2, deMath.binaryOp(deMath.shiftRight(aligned, 30), deMath.shiftLeft(1, 2) - 1, deMath.BinaryOp.AND))])[0] / range2); + }; + + /** + * rrVertexAttrib.readSnorm2101010RevScaleOrder + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + * @param {Object<rrVertexAttrib.NormalOrder|rrVertexAttrib.BGRAOrder>} order + */ + rrVertexAttrib.readSnorm2101010RevScaleOrder = function(dst, size, ptr, order) { + var arraysize32 = 4; //4 bytes + + //Left shift within 32-bit range as 32-bit int. + var range10 = new Uint32Array([deMath.shiftLeft(1, 10) - 1])[0]; + var range2 = new Uint32Array([deMath.shiftLeft(1, 2) - 1])[0]; + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arraysize32)); //Small buffer copy (max. 16 bytes) + var aligned = new Uint32Array(ptrclone.buffer)[0]; + + dst[order.T0] = new Float32Array([rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 0), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND)) * 2.0 + 1.0])[0] / range10; + if (size >= 2) dst[order.T1] = new Float32Array([rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 10), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND)) * 2.0 + 1.0])[0] / range10; + if (size >= 3) dst[order.T2] = new Float32Array([rrVertexAttrib.extendSign(10, deMath.binaryOp(deMath.shiftRight(aligned, 20), deMath.shiftLeft(1, 10) - 1, deMath.BinaryOp.AND)) * 2.0 + 1.0])[0] / range10; + if (size >= 4) dst[order.T3] = new Float32Array([rrVertexAttrib.extendSign(2, deMath.binaryOp(deMath.shiftRight(aligned, 30), deMath.shiftLeft(1, 2) - 1, deMath.BinaryOp.AND)) * 2.0 + 1.0])[0] / range2; + }; + + /** + * rrVertexAttrib.readUnormOrder + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + * @param {Object<rrVertexAttrib.NormalOrder|rrVertexAttrib.BGRAOrder>} order + * @param readAsTypeArray + */ + rrVertexAttrib.readUnormOrder = function(dst, size, ptr, order, readAsTypeArray) { + var arrayelementsize = readAsTypeArray.BYTES_PER_ELEMENT; + + //Left shift within 32-bit range as 32-bit float. + var range = new Float32Array([deMath.shiftLeft(1, arrayelementsize * 8) - 1])[0]; + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arrayelementsize)); //Small buffer copy (max. 16 bytes) + var aligned = new readAsTypeArray(ptrclone.buffer); + + //Reinterpret aligned's values into the dst vector. + dst[order.T0] = aligned[0] / range; + if (size >= 2) dst[order.T1] = aligned[1] / range; + if (size >= 3) dst[order.T2] = aligned[2] / range; + if (size >= 4) dst[order.T3] = aligned[3] / range; + }; + + /** + * rrVertexAttrib.readSnormClamp + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + * @param {function(new:ArrayBufferView,(Array<number>|ArrayBuffer|ArrayBufferView|null|number), number=, number=)} readAsTypeArray + */ + rrVertexAttrib.readSnormClamp = function(dst, size, ptr, readAsTypeArray) { + var arrayelementsize = readAsTypeArray.BYTES_PER_ELEMENT; + + //Left shift within 32-bit range as 32-bit float. + var range = new Float32Array([deMath.shiftLeft(1, arrayelementsize * 8 - 1) - 1])[0]; + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arrayelementsize)); //Small buffer copy (max. 16 bytes) + var aligned = new readAsTypeArray(ptrclone.buffer); + + //Reinterpret aligned's values into the dst vector. + dst[0] = Math.max(-1, aligned[0] / range); + if (size >= 2) dst[1] = Math.max(-1, aligned[1] / range); + if (size >= 3) dst[2] = Math.max(-1, aligned[2] / range); + if (size >= 4) dst[3] = Math.max(-1, aligned[3] / range); + }; + + /** + * rrVertexAttrib.readOrder + * @param {goog.NumberArray} dst + * @param {number} size + * @param {Uint8Array} ptr + * @param {Object<rrVertexAttrib.NormalOrder|rrVertexAttrib.BGRAOrder>} order NormalOrder or BGRAOrder + * @param readAsTypeArray Typed Array type + */ + rrVertexAttrib.readOrder = function(dst, size, ptr, order, readAsTypeArray) { + var arrayelementsize = readAsTypeArray.BYTES_PER_ELEMENT; + + var ptrclone = new Uint8Array(ptr.subarray(0, size * arrayelementsize)); //Small buffer copy (max. 16 bytes) + var aligned = new readAsTypeArray(ptrclone.buffer); + + //Reinterpret aligned's values into the dst vector. + //(automatic in JS typed arrays). + dst[order.T0] = aligned[0]; + if (size >= 2) dst[order.T1] = aligned[1]; + if (size >= 3) dst[order.T2] = aligned[2]; + if (size >= 4) dst[order.T3] = aligned[3]; + }; + + /** + * TODO: Implement readSNormScale. + * @param {goog.NumberArray} dst + * @param {rrVertexAttrib.VertexAttribType} type + * @param {number} size + * @param {Uint8Array} ptr + */ + rrVertexAttrib.read = function(dst, type, size, ptr) { + var order; + + switch (type) { + case rrVertexAttrib.VertexAttribType.FLOAT: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Float32Array); + break; + case rrVertexAttrib.VertexAttribType.HALF: + rrVertexAttrib.readHalf(dst, size, ptr); + break; + /*case rrVertexAttrib.VertexAttribType.FIXED: + rrVertexAttrib.readFixed(dst, size, ptr); + break; + case rrVertexAttrib.VertexAttribType.DOUBLE: + rrVertexAttrib.readDouble(dst, size, ptr); + break;*/ + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM8: + rrVertexAttrib.readUnormOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint8Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM16: + rrVertexAttrib.readUnormOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint16Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM32: + rrVertexAttrib.readUnormOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint32Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM_2_10_10_10_REV: + rrVertexAttrib.readUnorm2101010RevOrder(dst, size, ptr, rrVertexAttrib.NormalOrder); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM8_CLAMP: //Int8 + rrVertexAttrib.readSnormClamp(dst, size, ptr, Int8Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM16_CLAMP: //Int16 + rrVertexAttrib.readSnormClamp(dst, size, ptr, Int16Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM32_CLAMP: //Int32 + rrVertexAttrib.readSnormClamp(dst, size, ptr, Int32Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_CLAMP: + rrVertexAttrib.readSnorm2101010RevClampOrder(dst, size, ptr, rrVertexAttrib.NormalOrder); + break; + /*case rrVertexAttrib.VertexAttribType.NONPURE_SNORM8_SCALE: //Int8 + rrVertexAttrib.readSnormScale(dst, size, ptr, Int8Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM16_SCALE: //Int16 + rrVertexAttrib.readSnormScale(dst, size, ptr, Int16Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM32_SCALE: //Int32 + rrVertexAttrib.readSnormScale(dst, size, ptr, Int32Array); + break;*/ + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_SCALE: + rrVertexAttrib.readSnorm2101010RevScaleOrder(dst, size, ptr, rrVertexAttrib.NormalOrder); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT_2_10_10_10_REV: + rrVertexAttrib.readUint2101010Rev(dst, size, ptr); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_INT_2_10_10_10_REV: + rrVertexAttrib.readInt2101010Rev(dst, size, ptr); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM8_BGRA: + rrVertexAttrib.readUnormOrder(dst, size, ptr, rrVertexAttrib.BGRAOrder, Uint8Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UNORM_2_10_10_10_REV_BGRA: + rrVertexAttrib.readUnorm2101010RevOrder(dst, size, ptr, rrVertexAttrib.BGRAOrder); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_CLAMP_BGRA: + rrVertexAttrib.readSnorm2101010RevClampOrder(dst, size, ptr, rrVertexAttrib.BGRAOrder); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_SNORM_2_10_10_10_REV_SCALE_BGRA: + rrVertexAttrib.readSnorm2101010RevScaleOrder(dst, size, ptr, rrVertexAttrib.BGRAOrder); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT8: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint8Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT16: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint16Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_UINT32: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint32Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_INT8: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Int8Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_INT16: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Int16Array); + break; + case rrVertexAttrib.VertexAttribType.NONPURE_INT32: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Int32Array); + break; + case rrVertexAttrib.VertexAttribType.PURE_UINT8: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint8Array); + break; + case rrVertexAttrib.VertexAttribType.PURE_UINT16: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint16Array); + break; + case rrVertexAttrib.VertexAttribType.PURE_UINT32: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Uint32Array); + break; + case rrVertexAttrib.VertexAttribType.PURE_INT8: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Int8Array); + break; + case rrVertexAttrib.VertexAttribType.PURE_INT16: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Int16Array); + break; + case rrVertexAttrib.VertexAttribType.PURE_INT32: + rrVertexAttrib.readOrder(dst, size, ptr, rrVertexAttrib.NormalOrder, Int32Array); + break; + + default: + throw new Error('rrVertexAttrib.read - Invalid type'); + } + }; + +}); diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrVertexPacket.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrVertexPacket.js new file mode 100644 index 000000000..ec00e17d1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/referencerenderer/rrVertexPacket.js @@ -0,0 +1,101 @@ +/*------------------------------------------------------------------------- + * 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('framework.referencerenderer.rrVertexPacket'); +goog.require('framework.common.tcuTexture'); +goog.require('framework.delibs.debase.deMath'); + +goog.scope(function() { + +var rrVertexPacket = framework.referencerenderer.rrVertexPacket; +var tcuTexture = framework.common.tcuTexture; +var deMath = framework.delibs.debase.deMath; + + /** + * rrVertexPacket.VertexPacket class. (Should only be created by rrVertexPacket.VertexPacketAllocator) + * @constructor + */ + rrVertexPacket.VertexPacket = function() { + /** @type {number} */ this.instanceNdx; + /** @type {number} */ this.vertexNdx; + /** @type {goog.NumberArray} */ this.position; //!< Transformed position - must be written always. + /** @type {number} */ this.pointSize; //!< Point size, required when rendering points. + // /** @type {number} */ this.primitiveID; //!< Geometry shader output (Not used in webGL) + /** @type {Array<goog.NumberArray>} */ this.outputs = [[0, 0, 0, 0]]; + }; + + /** + * rrVertexPacket.VertexPacketAllocator class + * @constructor + * @param {number} numberOfVertexOutputs + */ + rrVertexPacket.VertexPacketAllocator = function(numberOfVertexOutputs) { + /** @type {number} */ this.m_numberOfVertexOutputs = numberOfVertexOutputs; + /** @type {Uint8Array} */ this.m_allocations; + /** @type {Array<rrVertexPacket.VertexPacket>} */ this.m_singleAllocPool = []; + }; + + /** + * @return {number} + */ + rrVertexPacket.VertexPacketAllocator.prototype.getNumVertexOutputs = function() { + return this.m_numberOfVertexOutputs; + }; + + /** + * allocArray + * @param {number} count + * @return {Array<rrVertexPacket.VertexPacket>} + */ + rrVertexPacket.VertexPacketAllocator.prototype.allocArray = function(count) { + if (!count) + return []; + + /** @type {number} */ var extraVaryings = (this.m_numberOfVertexOutputs == 0) ? (0) : (this.m_numberOfVertexOutputs - 1); + // /** @type {number} TODO: Check what this size is used for */ var packetSize = sizeof(rrVertexPacket.VertexPacket) + extraVaryings * sizeof(GenericVec4); + + /** @type {Array<rrVertexPacket.VertexPacket>} */ var retVal = []; + // /** @type {Uint8Array} TODO: same as above */ var ptr = new deInt8[packetSize * count]; // throws bad_alloc => ok + + //run ctors + for (var i = 0; i < count; ++i) + retVal.push(new rrVertexPacket.VertexPacket()); + + /** TODO: same as previous - this.m_allocations.push_back(ptr); */ + + return retVal; + }; + + /** + * @return {rrVertexPacket.VertexPacket} + */ + rrVertexPacket.VertexPacketAllocator.prototype.alloc = function() { + /** @type {number} */ var poolSize = 8; + + if (this.m_singleAllocPool.length == 0) + this.m_singleAllocPool = this.allocArray(poolSize); + + /** @type {rrVertexPacket.VertexPacket} */ var packet = this.m_singleAllocPool.pop(); + + return packet; + }; + +}); |