diff options
Diffstat (limited to '')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fFramebufferBlitTests.js | 1261 |
1 files changed, 1261 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fFramebufferBlitTests.js b/dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fFramebufferBlitTests.js new file mode 100644 index 0000000000..ffc6a0c68c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/deqp/functional/gles3/es3fFramebufferBlitTests.js @@ -0,0 +1,1261 @@ +/*------------------------------------------------------------------------- + * drawElements Quality Program OpenGL ES Utilities + * ------------------------------------------------ + * + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +'use strict'; +goog.provide('functional.gles3.es3fFramebufferBlitTests'); +goog.require('framework.common.tcuImageCompare'); +goog.require('framework.common.tcuRGBA'); +goog.require('framework.common.tcuSurface'); +goog.require('framework.common.tcuTestCase'); +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.sglrGLContext'); +goog.require('framework.opengl.simplereference.sglrReferenceContext'); +goog.require('framework.referencerenderer.rrUtil'); +goog.require('functional.gles3.es3fFboTestCase'); +goog.require('functional.gles3.es3fFboTestUtil'); + +goog.scope(function() { + + var es3fFramebufferBlitTests = functional.gles3.es3fFramebufferBlitTests; + var es3fFboTestCase = functional.gles3.es3fFboTestCase; + var es3fFboTestUtil = functional.gles3.es3fFboTestUtil; + var tcuTestCase = framework.common.tcuTestCase; + var tcuSurface = framework.common.tcuSurface; + var tcuRGBA = framework.common.tcuRGBA; + var tcuImageCompare = framework.common.tcuImageCompare; + var tcuTexture = framework.common.tcuTexture; + var tcuTextureUtil = framework.common.tcuTextureUtil; + var deMath = framework.delibs.debase.deMath; + var gluTextureUtil = framework.opengl.gluTextureUtil; + var gluShaderUtil = framework.opengl.gluShaderUtil; + var rrUtil = framework.referencerenderer.rrUtil; + var sglrReferenceContext = framework.opengl.simplereference.sglrReferenceContext; + var sglrGLContext = framework.opengl.simplereference.sglrGLContext; + + var DE_ASSERT = function(x) { + if (!x) + throw new Error('Assert failed'); + }; + + /** @type {WebGL2RenderingContext} */ var gl; + /** + * es3fFramebufferBlitTests.BlitRectCase class, inherits from FboTestCase + * @constructor + * @extends {es3fFboTestCase.FboTestCase} + * @param {string} name + * @param {string} desc + * @param {number} filter deUint32 + * @param {Array<number>} srcSize + * @param {Array<number>} srcRect + * @param {Array<number>} dstSize + * @param {Array<number>} dstRect + * @param {number=} cellSize + */ + es3fFramebufferBlitTests.BlitRectCase = function(name, desc, filter, srcSize, srcRect, dstSize, dstRect, cellSize) { + es3fFboTestCase.FboTestCase.call(this, name, desc); + /** @const {number} */ this.m_filter = filter; + /** @const {Array<number>} */ this.m_srcSize = srcSize; + /** @const {Array<number>} */ this.m_srcRect = srcRect; + /** @const {Array<number>} */ this.m_dstSize = dstSize; + /** @const {Array<number>} */ this.m_dstRect = dstRect; + /** @const {number} */ this.m_cellSize = cellSize === undefined ? 8 : cellSize; + /** @const {Array<number>} */ this.m_gridCellColorA = [0.2, 0.7, 0.1, 1.0]; + /** @const {Array<number>} */ this.m_gridCellColorB = [0.7, 0.1, 0.5, 0.8]; + }; + + es3fFramebufferBlitTests.BlitRectCase.prototype = Object.create(es3fFboTestCase.FboTestCase.prototype); + es3fFramebufferBlitTests.BlitRectCase.prototype.constructor = es3fFramebufferBlitTests.BlitRectCase; + + /** + * @param {tcuSurface.Surface} dst + */ + es3fFramebufferBlitTests.BlitRectCase.prototype.render = function(dst) { + var ctx = this.getCurrentContext(); + /** @type {number} */ var colorFormat = gl.RGBA8; + + /** @type {es3fFboTestUtil.GradientShader} */ + var gradShader = new es3fFboTestUtil.GradientShader( + gluShaderUtil.DataType.FLOAT_VEC4); + /** @type {es3fFboTestUtil.Texture2DShader} */ + var texShader = new es3fFboTestUtil.Texture2DShader( + [gluShaderUtil.DataType.SAMPLER_2D], + gluShaderUtil.DataType.FLOAT_VEC4); + + var gradShaderID = ctx.createProgram(gradShader); + var texShaderID = ctx.createProgram(texShader); + + var srcFbo; + var dstFbo; + var srcRbo; + var dstRbo; + + // Setup shaders + gradShader.setGradient(ctx, gradShaderID, [0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]); + texShader.setUniforms(ctx, texShaderID); + + // Create framebuffers. + + /** @type {Array<number>} */ var size; + + // source framebuffers + srcFbo = ctx.createFramebuffer(); + srcRbo = ctx.createRenderbuffer(); + size = this.m_srcSize; + + ctx.bindRenderbuffer(gl.RENDERBUFFER, srcRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, colorFormat, size[0], size[1]); + ctx.bindFramebuffer(gl.FRAMEBUFFER, srcFbo); + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, srcRbo); + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + // destination framebuffers + dstFbo = ctx.createFramebuffer(); + dstRbo = ctx.createRenderbuffer(); + size = this.m_dstSize; + + ctx.bindRenderbuffer(gl.RENDERBUFFER, dstRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, colorFormat, size[0], size[1]); + ctx.bindFramebuffer(gl.FRAMEBUFFER, dstFbo); + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, dstRbo); + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + // Fill destination with gradient. + ctx.bindFramebuffer(gl.FRAMEBUFFER, dstFbo); + ctx.viewport(0, 0, this.m_dstSize[0], this.m_dstSize[1]); + + rrUtil.drawQuad(ctx, gradShaderID, [-1, -1, 0], [1, 1, 0]); + + // Fill source with grid pattern. + /** @const {number} */ var format = gl.RGBA; + /** @const {number} */ var dataType = gl.UNSIGNED_BYTE; + /** @const {number} */ var texW = this.m_srcSize[0]; + /** @const {number} */ var texH = this.m_srcSize[1]; + var gridTex; + /** @type {tcuTexture.TextureLevel} */ var data = new tcuTexture.TextureLevel(gluTextureUtil.mapGLTransferFormat(format, dataType), texW, texH, 1); + + tcuTextureUtil.fillWithGrid(data.getAccess(), this.m_cellSize, this.m_gridCellColorA, this.m_gridCellColorB); + + gridTex = ctx.createTexture(); + ctx.bindTexture(gl.TEXTURE_2D, gridTex); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + ctx.texImage2D(gl.TEXTURE_2D, 0, format, texW, texH, 0, format, dataType, data.getAccess().getDataPtr()); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, srcFbo); + ctx.viewport(0, 0, this.m_srcSize[0], this.m_srcSize[1]); + + rrUtil.drawQuad(ctx, texShaderID, [-1, -1, 0], [1, 1, 0]); + + // Perform copy. + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, srcFbo); + ctx.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dstFbo); + ctx.blitFramebuffer(this.m_srcRect[0], this.m_srcRect[1], this.m_srcRect[2], this.m_srcRect[3], + this.m_dstRect[0], this.m_dstRect[1], this.m_dstRect[2], this.m_dstRect[3], + gl.COLOR_BUFFER_BIT, this.m_filter); + + // Read back results. + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, dstFbo); + + this.readPixelsUsingFormat(dst, 0, 0, this.m_dstSize[0], this.m_dstSize[1], + gluTextureUtil.mapGLInternalFormat(colorFormat), + [1.0, 1.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]); + }; + + /** + * @param {tcuSurface.Surface} reference + * @param {tcuSurface.Surface} result + * @return {boolean} + */ + es3fFramebufferBlitTests.BlitRectCase.prototype.compare = function(reference, result) { + // Use pixel-threshold compare for rect cases since 1px off will mean failure. + var threshold = [7, 7, 7, 7]; + return tcuImageCompare.pixelThresholdCompare('Result', 'Image comparison result', reference, result, threshold); + }; + + /** + * es3fFramebufferBlitTests.BlitNearestFilterConsistencyCase class + * @constructor + * @extends {es3fFramebufferBlitTests.BlitRectCase} + * @param {string} name + * @param {string} desc + * @param {Array<number>} srcSize + * @param {Array<number>} srcRect + * @param {Array<number>} dstSize + * @param {Array<number>} dstRect + */ + es3fFramebufferBlitTests.BlitNearestFilterConsistencyCase = function(name, desc, srcSize, srcRect, dstSize, dstRect) { + es3fFramebufferBlitTests.BlitRectCase.call(this, name, desc, gl.NEAREST, srcSize, srcRect, dstSize, dstRect, 1); + }; + + es3fFramebufferBlitTests.BlitNearestFilterConsistencyCase.prototype = Object.create(es3fFramebufferBlitTests.BlitRectCase.prototype); + es3fFramebufferBlitTests.BlitNearestFilterConsistencyCase.prototype.constructor = es3fFramebufferBlitTests.BlitNearestFilterConsistencyCase; + + /** + * @param {tcuSurface.Surface} reference + * @param {tcuSurface.Surface} result + * @return {boolean} + */ + es3fFramebufferBlitTests.BlitNearestFilterConsistencyCase.prototype.compare = function(reference, result) { + assertMsgOptions(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight(), + 'Reference and result images have different dimensions', false, true); + + // Image origin must be visible (for baseColor) + DE_ASSERT(Math.min(this.m_dstRect[0], this.m_dstRect[2]) >= 0); + DE_ASSERT(Math.min(this.m_dstRect[1], this.m_dstRect[3]) >= 0); + /** @const {tcuRGBA.RGBA} */ var cellColorA = tcuRGBA.newRGBAFromVec(this.m_gridCellColorA); + /** @const {tcuRGBA.RGBA} */ var cellColorB = tcuRGBA.newRGBAFromVec(this.m_gridCellColorB); + // TODO: implement + // const tcu::RGBA threshold = this.m_context.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(7,7,7,7); + /** @type {tcuRGBA.RGBA} */ var threshold = tcuRGBA.newRGBAComponents(7, 7, 7, 7); + /** @const {Array<number>} */ //IVec4.xyzw + var destinationArea = [ + deMath.clamp(Math.min(this.m_dstRect[0], this.m_dstRect[2]), 0, result.getWidth()), + deMath.clamp(Math.min(this.m_dstRect[1], this.m_dstRect[3]), 0, result.getHeight()), + deMath.clamp(Math.max(this.m_dstRect[0], this.m_dstRect[2]), 0, result.getWidth()), + deMath.clamp(Math.max(this.m_dstRect[1], this.m_dstRect[3]), 0, result.getHeight())]; + + /** @const {tcuRGBA.RGBA} */ var baseColor = new tcuRGBA.RGBA(result.getPixel(destinationArea[0], destinationArea[1])); + + /** @const {boolean} */ var signConfig = tcuRGBA.compareThreshold(baseColor, cellColorA, threshold); + + /** @type {boolean} */ var error = false; + /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(result.getWidth(), result.getHeight()); + /** @type {Array<boolean>} */ var horisontalSign = []; + /** @type {Array<boolean>} */ var verticalSign = []; + + errorMask.getAccess().clear([0.0, 1.0, 0.0, 1.0]); + + // Checking only area in our destination rect + + // m_testCtx.getLog() + // << tcu::TestLog::Message + // << 'Verifying consistency of NEAREST filtering. Verifying rect ' << m_dstRect << '.\n' + // << 'Rounding direction of the NEAREST filter at the horisontal texel edge (x = n + 0.5) should not depend on the y-coordinate.\n' + // << 'Rounding direction of the NEAREST filter at the vertical texel edge (y = n + 0.5) should not depend on the x-coordinate.\n' + // << 'Blitting a grid (with uniform sized cells) should result in a grid (with non-uniform sized cells).' + // << tcu::TestLog::EndMessage; + + // Verify that destination only contains valid colors + + /** @type {tcuRGBA.RGBA} */ var color; + + for (var dy = 0; dy < destinationArea[3] - destinationArea[1]; ++dy) { + for (var dx = 0; dx < destinationArea[2] - destinationArea[0]; ++dx) { + color = new tcuRGBA.RGBA(result.getPixel(destinationArea[0] + dx, destinationArea[1] + dy)); + + /** @const {boolean} */ + var isValidColor = + tcuRGBA.compareThreshold(color, cellColorA, threshold) || + tcuRGBA.compareThreshold(color, cellColorB, threshold); + + if (!isValidColor) { + errorMask.setPixel(destinationArea[0] + dx, destinationArea[1] + dy, tcuRGBA.RGBA.red.toVec()); + error = true; + } + } + } + + if (error) { + // m_testCtx.getLog() + // << tcu::TestLog::Message + // << 'Image verification failed, destination rect contains unexpected values. ' + // << 'Expected either ' << cellColorA << ' or ' << cellColorB << '.' + // << tcu::TestLog::EndMessage + // << tcu::TestLog::ImageSet('Result', 'Image verification result') + // << tcu::TestLog::Image('Result', 'Result', result) + // << tcu::TestLog::Image('ErrorMask', 'Error mask', errorMask) + // << tcu::TestLog::EndImageSet; + return false; + } + + // Detect result edges by reading the first row and first column of the blitted area. + // Blitting a grid should result in a grid-like image. ('sign changes' should be consistent) + + for (var dx = 0; dx < destinationArea[2] - destinationArea[0]; ++dx) { + color = new tcuRGBA.RGBA(result.getPixel(destinationArea[0] + dx, destinationArea[1])); + if (tcuRGBA.compareThreshold(color, cellColorA, threshold)) + horisontalSign[dx] = true; + else if (tcuRGBA.compareThreshold(color, cellColorB, threshold)) + horisontalSign[dx] = false; + else + DE_ASSERT(false); + } + for (var dy = 0; dy < destinationArea[3] - destinationArea[1]; ++dy) { + color = new tcuRGBA.RGBA(result.getPixel(destinationArea[0], destinationArea[1] + dy)); + + if (tcuRGBA.compareThreshold(color, cellColorA, threshold)) + verticalSign[dy] = true; + else if (tcuRGBA.compareThreshold(color, cellColorB, threshold)) + verticalSign[dy] = false; + else + DE_ASSERT(false); + } + + // Verify grid-like image + + for (var dy = 0; dy < destinationArea[3] - destinationArea[1]; ++dy) { + for (var dx = 0; dx < destinationArea[2] - destinationArea[0]; ++dx) { + color = new tcuRGBA.RGBA(result.getPixel(destinationArea[0] + dx, destinationArea[1] + dy)); + /** @const {boolean} */ var resultSign = tcuRGBA.compareThreshold(cellColorA, color, threshold); + /** @const {boolean} */ var correctSign = (horisontalSign[dx] == verticalSign[dy]) == signConfig; + + if (resultSign != correctSign) { + errorMask.setPixel(destinationArea[0] + dx, destinationArea[1] + dy, tcuRGBA.RGBA.red.toVec()); + error = true; + } + } + } + // Report result + + // if (error) + // { + // m_testCtx.getLog() + // << tcu::TestLog::Message + // << 'Image verification failed, nearest filter is not consistent.' + // << tcu::TestLog::EndMessage + // << tcu::TestLog::ImageSet('Result', 'Image verification result') + // << tcu::TestLog::Image('Result', 'Result', result) + // << tcu::TestLog::Image('ErrorMask', 'Error mask', errorMask) + // << tcu::TestLog::EndImageSet; + // } + // else + // { + // m_testCtx.getLog() + // << tcu::TestLog::Message + // << 'Image verification passed.' + // << tcu::TestLog::EndMessage + // << tcu::TestLog::ImageSet('Result', 'Image verification result') + // << tcu::TestLog::Image('Result', 'Result', result) + // << tcu::TestLog::EndImageSet; + // } + + return !error; + }; + + /** + * es3fFramebufferBlitTests.FramebufferBlitTests class, inherits from TestCase + * @constructor + * @extends {tcuTestCase.DeqpTest} + */ + es3fFramebufferBlitTests.FramebufferBlitTests = function() { + tcuTestCase.DeqpTest.call(this, 'blit', 'Framebuffer blit tests'); + }; + + es3fFramebufferBlitTests.FramebufferBlitTests.prototype = Object.create(tcuTestCase.DeqpTest.prototype); + es3fFramebufferBlitTests.FramebufferBlitTests.prototype.constructor = es3fFramebufferBlitTests.FramebufferBlitTests; + + es3fFramebufferBlitTests.FramebufferBlitTests.prototype.init = function() { + /** @const {Array<number>} */ var colorFormats = [ + // RGBA formats + gl.RGBA32I, + gl.RGBA32UI, + gl.RGBA16I, + gl.RGBA16UI, + gl.RGBA8, + gl.RGBA8I, + gl.RGBA8UI, + gl.SRGB8_ALPHA8, + gl.RGB10_A2, + gl.RGB10_A2UI, + gl.RGBA4, + gl.RGB5_A1, + + // RGB formats + gl.RGB8, + gl.RGB565, + + // RG formats + gl.RG32I, + gl.RG32UI, + gl.RG16I, + gl.RG16UI, + gl.RG8, + gl.RG8I, + gl.RG8UI, + + // R formats + gl.R32I, + gl.R32UI, + gl.R16I, + gl.R16UI, + gl.R8, + gl.R8I, + gl.R8UI, + + // gl.EXT_color_buffer_float + gl.RGBA32F, + gl.RGBA16F, + gl.R11F_G11F_B10F, + gl.RG32F, + gl.RG16F, + gl.R32F, + gl.R16F + ]; + + /** @const {Array<number>} */ var depthStencilFormats = [ + gl.DEPTH_COMPONENT32F, + gl.DEPTH_COMPONENT24, + gl.DEPTH_COMPONENT16, + gl.DEPTH32F_STENCIL8, + gl.DEPTH24_STENCIL8, + gl.STENCIL_INDEX8 + ]; + + // .rect + /** @constructor + * @param {string} name + * @param {Array<number>} srcRect + * @param {Array<number>} dstRect + */ + var CopyRect = function(name, srcRect, dstRect) { + /** @const {string} */ this.name = name; + /** @type {Array<number>} */ this.srcRect = srcRect; + /** @type {Array<number>} */ this.dstRect = dstRect; + }; + + /** @const {Array<CopyRect>} */ var copyRects = [ + new CopyRect('basic', [10, 20, 65, 100], [45, 5, 100, 85]), + new CopyRect('scale', [10, 20, 65, 100], [25, 30, 125, 94]), + new CopyRect('out_of_bounds', [-10, -15, 100, 63], [50, 30, 136, 144]) + ]; + + /** @const {Array<CopyRect>} */ var filterConsistencyRects = [ + + new CopyRect('mag', [20, 10, 74, 88], [10, 10, 91, 101]), + new CopyRect('min', [10, 20, 78, 100], [20, 20, 71, 80]), + new CopyRect('out_of_bounds_mag', [21, 10, 73, 82], [11, 43, 141, 151]), + new CopyRect('out_of_bounds_min', [11, 21, 77, 97], [80, 82, 135, 139]) + ]; + + /** @constructor + * @param {?string} name + * @param {Array<number>} srcSwizzle + * @param {Array<number>} dstSwizzle + */ + var Swizzle = function(name, srcSwizzle, dstSwizzle) { + /** @const {?string} */ this.name = name; + /** @type {Array<number>} */ this.srcSwizzle = srcSwizzle; + /** @type {Array<number>} */ this.dstSwizzle = dstSwizzle; + }; + + /** @const {Array<Swizzle>} */ var swizzles = [ + new Swizzle(null, [0, 1, 2, 3], [0, 1, 2, 3]), + new Swizzle('reverse_src_x', [2, 1, 0, 3], [0, 1, 2, 3]), + new Swizzle('reverse_src_y', [0, 3, 2, 1], [0, 1, 2, 3]), + new Swizzle('reverse_dst_x', [0, 1, 2, 3], [2, 1, 0, 3]), + new Swizzle('reverse_dst_y', [0, 1, 2, 3], [0, 3, 2, 1]), + new Swizzle('reverse_src_dst_x', [2, 1, 0, 3], [2, 1, 0, 3]), + new Swizzle('reverse_src_dst_y', [0, 3, 2, 1], [0, 3, 2, 1]) + ]; + + /** @const {Array<number>} */ var srcSize = [127, 119]; + /** @const {Array<number>} */ var dstSize = [132, 128]; + + // Blit rectangle tests. + for (var rectNdx = 0; rectNdx < copyRects.length; rectNdx++) { + /** @type {tcuTestCase.DeqpTest} */ var rectGroup = tcuTestCase.newTest('rect', 'Blit rectangle tests'); + this.addChild(rectGroup); + + for (var swzNdx = 0; swzNdx < swizzles.length; swzNdx++) { + /** @type {string} */ var name = copyRects[rectNdx].name + (swizzles[swzNdx].name ? ('_' + swizzles[swzNdx].name) : ''); + /** @type {Array<number>} */ var srcSwz = swizzles[swzNdx].srcSwizzle; + /** @type {Array<number>} */ var dstSwz = swizzles[swzNdx].dstSwizzle; + /** @type {Array<number>} */ var srcRect = deMath.swizzle(copyRects[rectNdx].srcRect, srcSwz); + /** @type {Array<number>} */ var dstRect = deMath.swizzle(copyRects[rectNdx].dstRect, dstSwz); + + rectGroup.addChild(new es3fFramebufferBlitTests.BlitRectCase((name + '_nearest'), '', gl.NEAREST, srcSize, srcRect, dstSize, dstRect)); + rectGroup.addChild(new es3fFramebufferBlitTests.BlitRectCase((name + '_linear'), '', gl.LINEAR, srcSize, srcRect, dstSize, dstRect)); + } + } + + // Nearest filter tests + for (var rectNdx = 0; rectNdx < filterConsistencyRects.length; rectNdx++) { + /** @type {tcuTestCase.DeqpTest} */ var rectGroup = tcuTestCase.newTest('rect', 'Blit rectangle tests'); + this.addChild(rectGroup); + for (var swzNdx = 0; swzNdx < swizzles.length; swzNdx++) { + var name = 'nearest_consistency_' + filterConsistencyRects[rectNdx].name + (swizzles[swzNdx].name ? ('_' + swizzles[swzNdx].name) : ''); + var srcSwz = swizzles[swzNdx].srcSwizzle; + var dstSwz = swizzles[swzNdx].dstSwizzle; + var srcRect = deMath.swizzle(filterConsistencyRects[rectNdx].srcRect, srcSwz); + var dstRect = deMath.swizzle(filterConsistencyRects[rectNdx].dstRect, dstSwz); + + rectGroup.addChild(new es3fFramebufferBlitTests.BlitNearestFilterConsistencyCase(name, 'Test consistency of the nearest filter', srcSize, srcRect, dstSize, dstRect)); + } + } + + // .conversion + for (var srcFmtNdx = 0; srcFmtNdx < colorFormats.length; srcFmtNdx++) { + /** @type {tcuTestCase.DeqpTest} */ var conversionGroup = tcuTestCase.newTest('conversion', 'Color conversion tests'); + this.addChild(conversionGroup); + for (var dstFmtNdx = 0; dstFmtNdx < colorFormats.length; dstFmtNdx++) { + /** @type {number} */ var srcFormat = colorFormats[srcFmtNdx]; + /** @type {tcuTexture.TextureFormat} */ var srcTexFmt = gluTextureUtil.mapGLInternalFormat(srcFormat); + /** @type {tcuTexture.TextureChannelClass} */ var srcType = tcuTexture.getTextureChannelClass(srcTexFmt.type); + /** @type {number} */ var dstFormat = colorFormats[dstFmtNdx]; + /** @type {tcuTexture.TextureFormat} */ var dstTexFmt = gluTextureUtil.mapGLInternalFormat(dstFormat); + /** @type {tcuTexture.TextureChannelClass} */ var dstType = tcuTexture.getTextureChannelClass(dstTexFmt.type); + + if (((srcType == tcuTexture.TextureChannelClass.FLOATING_POINT || srcType == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) != + (dstType == tcuTexture.TextureChannelClass.FLOATING_POINT || dstType == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT)) || + ((srcType == tcuTexture.TextureChannelClass.SIGNED_INTEGER) != (dstType == tcuTexture.TextureChannelClass.SIGNED_INTEGER)) || + ((srcType == tcuTexture.TextureChannelClass.UNSIGNED_INTEGER) != (dstType == tcuTexture.TextureChannelClass.UNSIGNED_INTEGER))) + continue; // Conversion not supported. + + var name = es3fFboTestUtil.getFormatName(srcFormat) + '_to_' + es3fFboTestUtil.getFormatName(dstFormat); + + conversionGroup.addChild(new es3fFramebufferBlitTests.BlitColorConversionCase(name, '', srcFormat, dstFormat, [127, 113])); + } + } + + // .depth_stencil + /** @type {tcuTestCase.DeqpTest} */ var depthStencilGroup = tcuTestCase.newTest('depth_stencil', 'Depth and stencil blits'); + this.addChild(depthStencilGroup); + + for (var fmtNdx = 0; fmtNdx < depthStencilFormats.length; fmtNdx++) { + /** @type {number} */ var format = depthStencilFormats[fmtNdx]; + /** @type {tcuTexture.TextureFormat} */ var texFmt = gluTextureUtil.mapGLInternalFormat(format); + /** @type {string} */ var fmtName = es3fFboTestUtil.getFormatName(format); + /** @type {boolean} */ var depth = texFmt.order == tcuTexture.ChannelOrder.D || texFmt.order == tcuTexture.ChannelOrder.DS; + /** @type {boolean} */ var stencil = texFmt.order == tcuTexture.ChannelOrder.S || texFmt.order == tcuTexture.ChannelOrder.DS; + /** @type {number} */ var buffers = (depth ? gl.DEPTH_BUFFER_BIT : 0) | (stencil ? gl.STENCIL_BUFFER_BIT : 0); + + depthStencilGroup.addChild(new es3fFramebufferBlitTests.BlitDepthStencilCase((fmtName + '_basic'), '', format, buffers, [128, 128], [0, 0, 128, 128], buffers, [128, 128], [0, 0, 128, 128], buffers)); + depthStencilGroup.addChild(new es3fFramebufferBlitTests.BlitDepthStencilCase((fmtName + '_scale'), '', format, buffers, [127, 119], [10, 30, 100, 70], buffers, [111, 130], [20, 5, 80, 130], buffers)); + + if (depth && stencil) { + depthStencilGroup.addChild(new es3fFramebufferBlitTests.BlitDepthStencilCase((fmtName + '_depth_only'), '', format, buffers, [128, 128], [0, 0, 128, 128], buffers, [128, 128], [0, 0, 128, 128], gl.DEPTH_BUFFER_BIT)); + depthStencilGroup.addChild(new es3fFramebufferBlitTests.BlitDepthStencilCase((fmtName + '_stencil_only'), '', format, buffers, [128, 128], [0, 0, 128, 128], buffers, [128, 128], [0, 0, 128, 128], gl.STENCIL_BUFFER_BIT)); + } + } + + // .default_framebuffer + /** + * @constructor + * @param {string} name + * @param {es3fFramebufferBlitTests.BlitArea} area + */ + var Area = function(name, area) { + /** @type {string} name */ this.name = name; + /** @type {es3fFramebufferBlitTests.BlitArea} area */ this.area = area; + }; + + /** @type {Array<Area>} */ var areas = [ + new Area('scale', es3fFramebufferBlitTests.BlitArea.AREA_SCALE), + new Area('out_of_bounds', es3fFramebufferBlitTests.BlitArea.AREA_OUT_OF_BOUNDS) + ]; + + var numDefaultFbSubGroups = 7; + /** @type {Array<tcuTestCase.DeqpTest>} */ var defaultFbGroup = []; + for (var ii = 0; ii < numDefaultFbSubGroups; ++ii) { + defaultFbGroup[ii] = tcuTestCase.newTest('default_framebuffer', 'Blits with default framebuffer'); + this.addChild(defaultFbGroup[ii]); + } + for (var fmtNdx = 0; fmtNdx < colorFormats.length; fmtNdx++) { + var format = colorFormats[fmtNdx]; + var texFmt = gluTextureUtil.mapGLInternalFormat(format); + var fmtClass = tcuTexture.getTextureChannelClass(texFmt.type); + var filter = gluTextureUtil.isGLInternalColorFormatFilterable(format) ? gl.LINEAR : gl.NEAREST; + var filterable = gluTextureUtil.isGLInternalColorFormatFilterable(format); + + if (fmtClass != tcuTexture.TextureChannelClass.FLOATING_POINT && + fmtClass != tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT && + fmtClass != tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT) + continue; // Conversion not supported. + + defaultFbGroup[fmtNdx % numDefaultFbSubGroups].addChild(new es3fFramebufferBlitTests.BlitDefaultFramebufferCase(es3fFboTestUtil.getFormatName(format), '', format, filter)); + + for (var areaNdx = 0; areaNdx < areas.length; areaNdx++) { + var name = areas[areaNdx].name; + var addLinear = filterable; + var addNearest = !addLinear || (areas[areaNdx].area != es3fFramebufferBlitTests.BlitArea.AREA_OUT_OF_BOUNDS); // No need to check out-of-bounds with different filtering + + if (addNearest) { + + defaultFbGroup[fmtNdx % numDefaultFbSubGroups].addChild(new es3fFramebufferBlitTests.DefaultFramebufferBlitCase((es3fFboTestUtil.getFormatName(format) + '_nearest_' + name + '_blit_from_default'), '', format, gl.NEAREST, es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET, areas[areaNdx].area)); + defaultFbGroup[fmtNdx % numDefaultFbSubGroups].addChild(new es3fFramebufferBlitTests.DefaultFramebufferBlitCase((es3fFboTestUtil.getFormatName(format) + '_nearest_' + name + '_blit_to_default'), '', format, gl.NEAREST, es3fFramebufferBlitTests.BlitDirection.BLIT_TO_DEFAULT_FROM_TARGET, areas[areaNdx].area)); + } + + if (addLinear) { + defaultFbGroup[fmtNdx % numDefaultFbSubGroups].addChild(new es3fFramebufferBlitTests.DefaultFramebufferBlitCase((es3fFboTestUtil.getFormatName(format) + '_linear_' + name + '_blit_from_default'), '', format, gl.LINEAR, es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET, areas[areaNdx].area)); + defaultFbGroup[fmtNdx % numDefaultFbSubGroups].addChild(new es3fFramebufferBlitTests.DefaultFramebufferBlitCase((es3fFboTestUtil.getFormatName(format) + '_linear_' + name + '_blit_to_default'), '', format, gl.LINEAR, es3fFramebufferBlitTests.BlitDirection.BLIT_TO_DEFAULT_FROM_TARGET, areas[areaNdx].area)); + } + } + } + }; + + /** + * @param {?tcuTexture.ChannelOrder} order + * @return {Array<boolean>} + */ + es3fFramebufferBlitTests.getChannelMask = function(order) { + switch (order) { + case tcuTexture.ChannelOrder.R: return [true, false, false, false]; + case tcuTexture.ChannelOrder.RG: return [true, true, false, false]; + case tcuTexture.ChannelOrder.RGB: return [true, true, true, false]; + case tcuTexture.ChannelOrder.RGBA: return [true, true, true, true]; + case tcuTexture.ChannelOrder.sRGB: return [true, true, true, false]; + case tcuTexture.ChannelOrder.sRGBA: return [true, true, true, true]; + default: + DE_ASSERT(false); + return [false, false, false, false]; + } + }; + + /** + * es3fFramebufferBlitTests.BlitColorConversionCase class, inherits from FboTestCase + * @constructor + * @extends {es3fFboTestCase.FboTestCase} + * @param {string} name + * @param {string} desc + * @param {number} srcFormat + * @param {number} dstFormat + * @param {Array<number>} size + */ + es3fFramebufferBlitTests.BlitColorConversionCase = function(name, desc, srcFormat, dstFormat, size) { + es3fFboTestCase.FboTestCase.call(this, name, desc); + /** @type {number} */ this.m_srcFormat = srcFormat; + /** @type {number} */ this.m_dstFormat = dstFormat; + /** @type {Array<number>} */ this.m_size = size; + }; + + es3fFramebufferBlitTests.BlitColorConversionCase.prototype = Object.create(es3fFboTestCase.FboTestCase.prototype); + es3fFramebufferBlitTests.BlitColorConversionCase.prototype.constructor = es3fFramebufferBlitTests.BlitColorConversionCase; + + es3fFramebufferBlitTests.BlitColorConversionCase.prototype.preCheck = function() { + this.checkFormatSupport(this.m_srcFormat); + this.checkFormatSupport(this.m_dstFormat); + return true; // No exception thrown + }; + + /** + * @param {tcuSurface.Surface} dst + */ + es3fFramebufferBlitTests.BlitColorConversionCase.prototype.render = function(dst) { + var ctx = this.getCurrentContext(); + /** @type {tcuTexture.TextureFormat} */ var srcFormat = gluTextureUtil.mapGLInternalFormat(this.m_srcFormat); + /** @type {tcuTexture.TextureFormat} */ var dstFormat = gluTextureUtil.mapGLInternalFormat(this.m_dstFormat); + + /** @type {gluShaderUtil.DataType} */ var srcOutputType = es3fFboTestUtil.getFragmentOutputType(srcFormat); + /** @type {gluShaderUtil.DataType} */ var dstOutputType = es3fFboTestUtil.getFragmentOutputType(dstFormat); + + // Compute ranges \note Doesn't handle case where src or dest is not subset of the another! + /** @type {tcuTextureUtil.TextureFormatInfo} */ var srcFmtRangeInfo = tcuTextureUtil.getTextureFormatInfo(srcFormat); + /** @type {tcuTextureUtil.TextureFormatInfo} */ var dstFmtRangeInfo = tcuTextureUtil.getTextureFormatInfo(dstFormat); + + /** @type {Array<boolean>} */ var copyMask = deMath.logicalAndBool(es3fFramebufferBlitTests.getChannelMask(srcFormat.order), es3fFramebufferBlitTests.getChannelMask(dstFormat.order)); + /** @type {Array<boolean>} */ var srcIsGreater = deMath.greaterThan(deMath.subtract(srcFmtRangeInfo.valueMax, srcFmtRangeInfo.valueMin), deMath.subtract(dstFmtRangeInfo.valueMax, dstFmtRangeInfo.valueMin)); + + /** @type {tcuTextureUtil.TextureFormatInfo} */ var srcRangeInfo = new tcuTextureUtil.TextureFormatInfo( + tcuTextureUtil.select(dstFmtRangeInfo.valueMin, srcFmtRangeInfo.valueMin, deMath.logicalAndBool(copyMask, srcIsGreater)), + tcuTextureUtil.select(dstFmtRangeInfo.valueMax, srcFmtRangeInfo.valueMax, deMath.logicalAndBool(copyMask, srcIsGreater)), + tcuTextureUtil.select(dstFmtRangeInfo.lookupScale, srcFmtRangeInfo.lookupScale, deMath.logicalAndBool(copyMask, srcIsGreater)), + tcuTextureUtil.select(dstFmtRangeInfo.lookupBias, srcFmtRangeInfo.lookupBias, deMath.logicalAndBool(copyMask, srcIsGreater))); + /** @type {tcuTextureUtil.TextureFormatInfo} */ var dstRangeInfo = new tcuTextureUtil.TextureFormatInfo( + tcuTextureUtil.select(dstFmtRangeInfo.valueMin, srcFmtRangeInfo.valueMin, deMath.logicalOrBool(deMath.logicalNotBool(copyMask), srcIsGreater)), + tcuTextureUtil.select(dstFmtRangeInfo.valueMax, srcFmtRangeInfo.valueMax, deMath.logicalOrBool(deMath.logicalNotBool(copyMask), srcIsGreater)), + tcuTextureUtil.select(dstFmtRangeInfo.lookupScale, srcFmtRangeInfo.lookupScale, deMath.logicalOrBool(deMath.logicalNotBool(copyMask), srcIsGreater)), + tcuTextureUtil.select(dstFmtRangeInfo.lookupBias, srcFmtRangeInfo.lookupBias, deMath.logicalOrBool(deMath.logicalNotBool(copyMask), srcIsGreater))); + + // Shaders. + /** @type {es3fFboTestUtil.GradientShader} */ + var gradientToSrcShader = new es3fFboTestUtil.GradientShader(srcOutputType); + /** @type {es3fFboTestUtil.GradientShader} */ + var gradientToDstShader = new es3fFboTestUtil.GradientShader(dstOutputType); + + var gradShaderSrcID = ctx.createProgram(gradientToSrcShader); + var gradShaderDstID = ctx.createProgram(gradientToDstShader); + + var srcFbo; + var dstFbo; + var srcRbo; + var dstRbo; + + // Create framebuffers. + // Source framebuffers + srcFbo = ctx.createFramebuffer(); + srcRbo = ctx.createRenderbuffer(); + + ctx.bindRenderbuffer(gl.RENDERBUFFER, srcRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, this.m_srcFormat, this.m_size[0], this.m_size[1]); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, srcFbo); + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, srcRbo); + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + // Destination framebuffers + dstFbo = ctx.createFramebuffer(); + dstRbo = ctx.createRenderbuffer(); + + ctx.bindRenderbuffer(gl.RENDERBUFFER, dstRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, this.m_dstFormat, this.m_size[0], this.m_size[1]); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, dstFbo); + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, dstRbo); + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + ctx.viewport(0, 0, this.m_size[0], this.m_size[1]); + + // Render gradients. + for (var ndx = 0; ndx < 2; ndx++) { + ctx.bindFramebuffer(gl.FRAMEBUFFER, ndx ? dstFbo : srcFbo); + if (ndx) { + gradientToDstShader.setGradient(ctx, gradShaderDstID, dstRangeInfo.valueMax, dstRangeInfo.valueMin); + rrUtil.drawQuad(ctx, gradShaderDstID, [-1, -1, 0], [1, 1, 0]); + } else { + gradientToSrcShader.setGradient(ctx, gradShaderSrcID, srcRangeInfo.valueMin, srcRangeInfo.valueMax); + rrUtil.drawQuad(ctx, gradShaderSrcID, [-1, -1, 0], [1, 1, 0]); + } + } + + // Execute copy. + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, srcFbo); + ctx.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dstFbo); + ctx.blitFramebuffer(0, 0, this.m_size[0], this.m_size[1], 0, 0, this.m_size[0], this.m_size[1], gl.COLOR_BUFFER_BIT, gl.NEAREST); + this.checkError(); + + // Read results. + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, dstFbo); + this.readPixelsUsingFormat(dst, 0, 0, this.m_size[0], this.m_size[1], dstFormat, dstRangeInfo.lookupScale, dstRangeInfo.lookupBias); + + }; + + /** + * @param {tcuSurface.Surface} reference + * @param {tcuSurface.Surface} result + */ + es3fFramebufferBlitTests.BlitColorConversionCase.prototype.compare = function(reference, result) { + /** @const {tcuTexture.TextureFormat} */ var srcFormat = gluTextureUtil.mapGLInternalFormat(this.m_srcFormat); + /** @const {tcuTexture.TextureFormat} */ var dstFormat = gluTextureUtil.mapGLInternalFormat(this.m_dstFormat); + /** @const {boolean} */ var srcIsSRGB = (srcFormat.order == tcuTexture.ChannelOrder.sRGBA); + /** @const {boolean} */ var dstIsSRGB = (dstFormat.order == tcuTexture.ChannelOrder.sRGBA); + /** @type {tcuRGBA.RGBA} */ var threshold = new tcuRGBA.RGBA(); + + if (dstIsSRGB) + threshold = es3fFboTestUtil.getToSRGBConversionThreshold(srcFormat, dstFormat); + else { + /** @type {tcuRGBA.RGBA} */ var srcMaxDiff = es3fFboTestUtil.getThresholdFromTextureFormat(srcFormat); + /** @type {tcuRGBA.RGBA} */ var dstMaxDiff = es3fFboTestUtil.getThresholdFromTextureFormat(dstFormat); + if (srcIsSRGB) + srcMaxDiff = tcuRGBA.multiply(srcMaxDiff, 2); + + threshold = tcuRGBA.max(srcMaxDiff, dstMaxDiff); + } + + // m_testCtx.getLog() << tcu::TestLog::Message << 'threshold = ' << threshold << tcu::TestLog::EndMessage; + return tcuImageCompare.pixelThresholdCompare('Result', 'Image comparison result', reference, result, threshold.toIVec()); + }; + + /** + * @constructor + * @extends {es3fFboTestCase.FboTestCase} + * @param {string} name + * @param {string} desc + * @param {number} format deUint32 + * @param {number} srcBuffers deUint32 + * @param {Array<number>} srcSize IVec2 + * @param {Array<number>} srcRect IVec4 + * @param {number} dstBuffers deUint32 + * @param {Array<number>} dstSize IVec2 + * @param {Array<number>} dstRect IVec4 + * @param {number} copyBuffers deUint32 + */ + es3fFramebufferBlitTests.BlitDepthStencilCase = function(name, desc, format, srcBuffers, srcSize, srcRect, dstBuffers, dstSize, dstRect, copyBuffers) { + es3fFboTestCase.FboTestCase.call(this, name, desc); + /** @type {number} */ this.m_format = format; + /** @type {number} */ this.m_srcBuffers = srcBuffers; + /** @type {Array<number>} */ this.m_srcSize = srcSize; + /** @type {Array<number>} */ this.m_srcRect = srcRect; + /** @type {number} */ this.m_dstBuffers = dstBuffers; + /** @type {Array<number>} */ this.m_dstSize = dstSize; + /** @type {Array<number>} */ this.m_dstRect = dstRect; + /** @type {number} */ this.m_copyBuffers = copyBuffers; + }; + + es3fFramebufferBlitTests.BlitDepthStencilCase.prototype = Object.create(es3fFboTestCase.FboTestCase.prototype); + es3fFramebufferBlitTests.BlitDepthStencilCase.prototype.constructor = es3fFramebufferBlitTests.BlitDepthStencilCase; + + /** + * @protected + */ + es3fFramebufferBlitTests.BlitDepthStencilCase.prototype.preCheck = function() { + this.checkFormatSupport(this.m_format); + return true; // No exception thrown + }; + + /** + * @protected + * @param {tcuSurface.Surface} dst + */ + es3fFramebufferBlitTests.BlitDepthStencilCase.prototype.render = function(dst) { + var ctx = this.getCurrentContext(); + /** @const {number} */ var colorFormat = gl.RGBA8; + var gradShader = new es3fFboTestUtil.GradientShader(gluShaderUtil.DataType.FLOAT_VEC4); + var texShader = new es3fFboTestUtil.Texture2DShader( + [gluShaderUtil.DataType.SAMPLER_2D] , + gluShaderUtil.DataType.FLOAT_VEC4); + var flatShader = new es3fFboTestUtil.FlatColorShader(gluShaderUtil.DataType.FLOAT_VEC4); + + var flatShaderID = ctx.createProgram(flatShader); + var texShaderID = ctx.createProgram(texShader); + var gradShaderID = ctx.createProgram(gradShader); + + var srcFbo; + var dstFbo; + var srcColorRbo; + var dstColorRbo; + var srcDepthStencilRbo; + var dstDepthStencilRbo; + + // setup shaders + gradShader.setGradient(ctx, gradShaderID, [0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]); + texShader.setUniforms(ctx, texShaderID); + + // Create framebuffers + // Source framebuffers + srcFbo = ctx.createFramebuffer(); + srcColorRbo = ctx.createRenderbuffer(); + srcDepthStencilRbo = ctx.createRenderbuffer(); + + ctx.bindRenderbuffer(gl.RENDERBUFFER, srcColorRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, colorFormat, this.m_srcSize[0], this.m_srcSize[1]); + + ctx.bindRenderbuffer(gl.RENDERBUFFER, srcDepthStencilRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, this.m_format, this.m_srcSize[0], this.m_srcSize[1]); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, srcFbo); + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, srcColorRbo); + + if (this.m_srcBuffers & gl.DEPTH_BUFFER_BIT) + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, srcDepthStencilRbo); + if (this.m_srcBuffers & gl.STENCIL_BUFFER_BIT) + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, srcDepthStencilRbo); + + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + // Clear depth to 1 and stencil to 0. + ctx.clearBufferfi(gl.DEPTH_STENCIL, 0, 1.0, 0); + + // Destination framebuffers + dstFbo = ctx.createFramebuffer(); + dstColorRbo = ctx.createRenderbuffer(); + dstDepthStencilRbo = ctx.createRenderbuffer(); + + ctx.bindRenderbuffer(gl.RENDERBUFFER, dstColorRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, colorFormat, this.m_dstSize[0], this.m_dstSize[1]); + + ctx.bindRenderbuffer(gl.RENDERBUFFER, dstDepthStencilRbo); + ctx.renderbufferStorage(gl.RENDERBUFFER, this.m_format, this.m_dstSize[0], this.m_dstSize[1]); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, dstFbo); + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, dstColorRbo); + + if (this.m_dstBuffers & gl.DEPTH_BUFFER_BIT) + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, dstDepthStencilRbo); + if (this.m_dstBuffers & gl.STENCIL_BUFFER_BIT) + ctx.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, dstDepthStencilRbo); + + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + // Clear depth to 1 and stencil to 0. + ctx.clearBufferfi(gl.DEPTH_STENCIL, 0, 1.0, 0); + + // Fill source with gradient, depth = [-1..1], stencil = 7 + ctx.bindFramebuffer(gl.FRAMEBUFFER, srcFbo); + ctx.viewport(0, 0, this.m_srcSize[0], this.m_srcSize[1]); + ctx.enable(gl.DEPTH_TEST); + ctx.enable(gl.STENCIL_TEST); + ctx.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + ctx.stencilFunc(gl.ALWAYS, 7, 0xff); + + rrUtil.drawQuad(ctx, gradShaderID, [-1, -1, -1], [1, 1, 1]); + + // Fill destination with grid pattern, depth = 0 and stencil = 1 + /** @const {number} */ var format = gl.RGBA; + /** @const {number} */ var dataType = gl.UNSIGNED_BYTE; + /** @const {number} */ var texW = this.m_srcSize[0]; + /** @const {number} */ var texH = this.m_srcSize[1]; + /** @type {WebGLTexture|sglrReferenceContext.TextureContainer} */ var gridTex = null; + /** @type {tcuTexture.TextureLevel} */ var data = new tcuTexture.TextureLevel(gluTextureUtil.mapGLTransferFormat(format, dataType), texW, texH, 1); + + tcuTextureUtil.fillWithGrid(data.getAccess(), 8, [0.2, 0.7, 0.1, 1.0], [0.7, 0.1, 0.5, 0.8]); + + gridTex = ctx.createTexture(); + ctx.bindTexture(gl.TEXTURE_2D, gridTex); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + ctx.texImage2D(gl.TEXTURE_2D, 0, format, texW, texH, 0, format, dataType, data.getAccess().getDataPtr()); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, dstFbo); + ctx.viewport(0, 0, this.m_dstSize[0], this.m_dstSize[1]); + ctx.stencilFunc(gl.ALWAYS, 1, 0xff); + + rrUtil.drawQuad(ctx, texShaderID, [-1, -1, 0], [1, 1, 0]); + + // Perform copy. + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, srcFbo); + ctx.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dstFbo); + ctx.blitFramebuffer(this.m_srcRect[0], this.m_srcRect[1], this.m_srcRect[2], this.m_srcRect[3], this.m_dstRect[0], this.m_dstRect[1], this.m_dstRect[2], this.m_dstRect[3], this.m_copyBuffers, gl.NEAREST); + + // Render blue color where depth < 0, decrement on depth failure. + ctx.bindFramebuffer(gl.FRAMEBUFFER, dstFbo); + ctx.viewport(0, 0, this.m_dstSize[0], this.m_dstSize[1]); + ctx.stencilOp(gl.KEEP, gl.DECR, gl.KEEP); + ctx.stencilFunc(gl.ALWAYS, 0, 0xff); + + flatShader.setColor(this.getCurrentContext(), flatShaderID, [0.0, 0.0, 1.0, 1.0]); + + rrUtil.drawQuad(ctx, flatShaderID, [-1, -1, 0], [1, 1, 0]); + + if (this.m_dstBuffers & gl.STENCIL_BUFFER_BIT) { + // Render green color where stencil == 6. + ctx.disable(gl.DEPTH_TEST); + ctx.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + ctx.stencilFunc(gl.EQUAL, 6, 0xff); + + flatShader.setColor(this.getCurrentContext(), flatShaderID, [0.0, 1.0, 0.0, 1.0]); + + rrUtil.drawQuad(ctx, flatShaderID, [-1, -1, 0], [1, 1, 0]); + + } + this.readPixelsUsingFormat(dst, 0, 0, this.m_dstSize[0], this.m_dstSize[1], gluTextureUtil.mapGLInternalFormat(colorFormat), [1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]); + + }; + + /** + * @constructor + * @extends {es3fFboTestCase.FboTestCase} + * @param {string} name + * @param {string} desc + * @param {number} format + * @param {number} filter + */ + es3fFramebufferBlitTests.BlitDefaultFramebufferCase = function(name, desc, format, filter) { + es3fFboTestCase.FboTestCase.call(this, name, desc); + /** @const {number} */ this.m_format = format; + /** @const {number} */ this.m_filter = filter; + }; + + es3fFramebufferBlitTests.BlitDefaultFramebufferCase.prototype = Object.create(es3fFboTestCase.FboTestCase.prototype); + es3fFramebufferBlitTests.BlitDefaultFramebufferCase.prototype.constructor = es3fFramebufferBlitTests.BlitDefaultFramebufferCase; + + /** + * @protected + */ + es3fFramebufferBlitTests.BlitDefaultFramebufferCase.prototype.preCheck = function() { + this.checkFormatSupport(this.m_format); + return true; // No exception thrown + }; + + /** + * @protected + * @param {tcuSurface.Surface} dst + */ + es3fFramebufferBlitTests.BlitDefaultFramebufferCase.prototype.render = function(dst) { + var ctx = this.getCurrentContext(); + /** @type {tcuTexture.TextureFormat} */ var colorFormat = gluTextureUtil.mapGLInternalFormat(this.m_format); + /** @type {gluTextureUtil.TransferFormat} */ var transferFmt = gluTextureUtil.getTransferFormat(colorFormat); + + /** @type {es3fFboTestUtil.GradientShader} */ var gradShader = new es3fFboTestUtil.GradientShader(gluShaderUtil.DataType.FLOAT_VEC4); + /** @type {es3fFboTestUtil.Texture2DShader} */ var texShader = new es3fFboTestUtil.Texture2DShader([gluTextureUtil.getSampler2DType(colorFormat)], gluShaderUtil.DataType.FLOAT_VEC4); + + var gradShaderID = ctx.createProgram(gradShader); + var texShaderID = ctx.createProgram(texShader); + var fbo; + var tex; + /** @const {number} */ var texW = 128; + /** @const {number} */ var texH = 128; + + // Setup shaders + gradShader.setGradient(ctx, gradShaderID, [0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]); + texShader.setUniforms(ctx, texShaderID); + + // FBO + fbo = ctx.createFramebuffer(); + tex = ctx.createTexture(); + + ctx.bindTexture(gl.TEXTURE_2D, tex); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.m_filter); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.m_filter); + ctx.texImage2D(gl.TEXTURE_2D, 0, this.m_format, texW, texH, 0, transferFmt.format, transferFmt.dataType, null); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, fbo); + ctx.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + // Render gradient to screen. + ctx.bindFramebuffer(gl.FRAMEBUFFER, null); + + rrUtil.drawQuad(ctx, gradShaderID, [-1, -1, 0], [1, 1, 0]); + + // Blit gradient from screen to fbo. + ctx.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo); + ctx.blitFramebuffer(0, 0, ctx.getWidth(), ctx.getHeight(), 0, 0, texW, texH, gl.COLOR_BUFFER_BIT, this.m_filter); + + // Fill left half of viewport with quad that uses texture. + ctx.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + ctx.clearBufferfv(gl.COLOR, 0, [1.0, 0.0, 0.0, 1.0]); + + rrUtil.drawQuad(ctx, texShaderID, [-1, -1, 0], [1, 1, 0]); + + // Blit fbo to right half. + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo); + ctx.blitFramebuffer(0, 0, texW, texH, Math.floor(ctx.getWidth() / 2), 0, ctx.getWidth(), ctx.getHeight(), gl.COLOR_BUFFER_BIT, this.m_filter); + + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + this.readPixels(dst, 0, 0, ctx.getWidth(), ctx.getHeight()); + + }; + + /** + * @protected + * @param {tcuSurface.Surface} reference + * @param {tcuSurface.Surface} result + */ + es3fFramebufferBlitTests.BlitDefaultFramebufferCase.prototype.compare = function(reference, result) { + /** @const {tcuRGBA.RGBA} */ + var threshold = tcuRGBA.max(es3fFboTestUtil.getFormatThreshold(this.m_format), tcuRGBA.newRGBAComponents(12, 12, 12, 12)); + + //m_testCtx.getLog() << TestLog::Message << 'Comparing images, threshold: ' << threshold << TestLog::EndMessage; + + return tcuImageCompare.bilinearCompare('Result', 'Image comparison result', reference.getAccess(), result.getAccess(), threshold); + }; + + /** @enum */ + es3fFramebufferBlitTests.BlitDirection = { + BLIT_DEFAULT_TO_TARGET: 0, + BLIT_TO_DEFAULT_FROM_TARGET: 1 + }; + + /** @enum */ + es3fFramebufferBlitTests.BlitArea = { + AREA_SCALE: 0, + AREA_OUT_OF_BOUNDS: 1 + }; + + /** + * @constructor + * @extends {es3fFramebufferBlitTests.BlitDefaultFramebufferCase} + * @param {string} name + * @param {string} desc + * @param {number} format + * @param {number} filter + * @param {es3fFramebufferBlitTests.BlitDirection} dir + * @param {es3fFramebufferBlitTests.BlitArea} area + */ + es3fFramebufferBlitTests.DefaultFramebufferBlitCase = function(name, desc, format, filter, dir, area) { + es3fFramebufferBlitTests.BlitDefaultFramebufferCase.call(this, name, desc, format, filter); + /** @const {es3fFramebufferBlitTests.BlitDirection} */ this.m_blitDir = dir; + /** @const {es3fFramebufferBlitTests.BlitArea} */ this.m_blitArea = area; + /** @type {Array<number>} */ this.m_srcRect = [-1, -1, -1, -1]; + /** @type {Array<number>} */ this.m_dstRect = [-1, -1, -1, -1]; + /** @type {Array<number>} */ this.m_interestingArea = [-1, -1, -1, -1]; + }; + + es3fFramebufferBlitTests.DefaultFramebufferBlitCase.prototype = Object.create(es3fFramebufferBlitTests.BlitDefaultFramebufferCase.prototype); + es3fFramebufferBlitTests.DefaultFramebufferBlitCase.prototype.constructor = es3fFramebufferBlitTests.DefaultFramebufferBlitCase; + + es3fFramebufferBlitTests.DefaultFramebufferBlitCase.prototype.init = function() { + // requirements + /** @const {number} */ var minViewportSize = 128; + if (gl.drawingBufferWidth < minViewportSize || + gl.drawingBufferHeight < minViewportSize) + throw new Error('Viewport size ' + minViewportSize + 'x' + minViewportSize + ' required'); + + // prevent viewport randoming + this.m_viewportWidth = gl.drawingBufferWidth; + this.m_viewportHeight = gl.drawingBufferHeight; + + // set proper areas + if (this.m_blitArea == es3fFramebufferBlitTests.BlitArea.AREA_SCALE) { + this.m_srcRect = [10, 20, 65, 100]; + this.m_dstRect = [25, 30, 125, 94]; + this.m_interestingArea = [0, 0, 128, 128]; + } else if (this.m_blitArea == es3fFramebufferBlitTests.BlitArea.AREA_OUT_OF_BOUNDS) { + /** @const {Array<number>} */ + var ubound = (this.m_blitDir == es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET) ? + ([128, 128]) : + ([gl.drawingBufferWidth, gl.drawingBufferHeight]); + + this.m_srcRect = [-10, -15, 100, 63]; + this.m_dstRect = deMath.add(deMath.swizzle(ubound, [0, 1, 0, 1]), [-75, -99, 8, 16]); + this.m_interestingArea = [ubound[0] - 128, ubound[1] - 128, ubound[0], ubound[1]]; + } + }; + + /** + * @param {tcuSurface.Surface} dst + */ + es3fFramebufferBlitTests.DefaultFramebufferBlitCase.prototype.render = function(dst) { + /** @type {es3fFboTestCase.Context} */ + var ctx = this.getCurrentContext(); + // TOOD: implement + /** @type {tcuTexture.TextureFormat} */ var colorFormat = gluTextureUtil.mapGLInternalFormat(this.m_format); + /** @type {gluTextureUtil.TransferFormat} */ var transferFmt = gluTextureUtil.getTransferFormat(colorFormat); + /** @const {tcuTexture.TextureChannelClass} */ + var targetClass = (this.m_blitDir == es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET) ? + (tcuTexture.getTextureChannelClass(colorFormat.type)) : + (tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT); + + var fbo; + var fboTex; + /** @const {number} */ var fboTexW = 128; + /** @const {number} */ var fboTexH = 128; + /** @const {number} */ var sourceWidth = (this.m_blitDir == es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET) ? (ctx.getWidth()) : (fboTexW); + /** @const {number} */ var sourceHeight = (this.m_blitDir == es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET) ? (ctx.getHeight()) : (fboTexH); + /** @const {number} */ var gridRenderWidth = Math.min(256, sourceWidth); + /** @const {number} */ var gridRenderHeight = Math.min(256, sourceHeight); + + var targetFbo; + var sourceFbo; + + // FBO + fbo = ctx.createFramebuffer(); + fboTex = ctx.createTexture(); + + ctx.bindTexture(gl.TEXTURE_2D, fboTex); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.m_filter); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.m_filter); + ctx.texImage2D(gl.TEXTURE_2D, 0, this.m_format, fboTexW, fboTexH, 0, transferFmt.format, transferFmt.dataType, null); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, fbo); + ctx.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fboTex, 0); + this.checkError(); + this.checkFramebufferStatus(gl.FRAMEBUFFER); + + targetFbo = (this.m_blitDir == es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET) ? (fbo) : (null); + sourceFbo = (this.m_blitDir == es3fFramebufferBlitTests.BlitDirection.BLIT_DEFAULT_TO_TARGET) ? (null) : (fbo); + + // Render grid to source framebuffer + /** @type {es3fFboTestUtil.Texture2DShader} */ + var texShader = new es3fFboTestUtil.Texture2DShader( + [gluShaderUtil.DataType.SAMPLER_2D], + gluShaderUtil.DataType.FLOAT_VEC4); + var texShaderID = this.getCurrentContext().createProgram(texShader); + /** @const {number} */ var internalFormat = gl.RGBA8; + /** @const {number} */ var format = gl.RGBA; + /** @const {number} */ var dataType = gl.UNSIGNED_BYTE; + /** @const {number} */ var gridTexW = 128; + /** @const {number} */ var gridTexH = 128; + /** @type {WebGLTexture|framework.opengl.simplereference.sglrReferenceContext.TextureContainer|null} */ + var gridTex = null; + /** @type {tcuTexture.TextureLevel} */ var data = new tcuTexture.TextureLevel(gluTextureUtil.mapGLTransferFormat(format, dataType), gridTexW, gridTexH, 1); + + tcuTextureUtil.fillWithGrid(data.getAccess(), 9, [0.9, 0.5, 0.1, 0.9], [0.2, 0.8, 0.2, 0.7]); + + gridTex = ctx.createTexture(); + ctx.bindTexture(gl.TEXTURE_2D, gridTex); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + ctx.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + ctx.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gridTexW, gridTexH, 0, format, dataType, data.getAccess().getDataPtr()); + + ctx.bindFramebuffer(gl.FRAMEBUFFER, sourceFbo); + ctx.viewport(0, 0, gridRenderWidth, gridRenderHeight); + ctx.clearBufferfv(gl.COLOR, 0, [1.0, 0.0, 0.0, 1.0]); + + texShader.setUniforms(this.getCurrentContext(), texShaderID); + + rrUtil.drawQuad(ctx, texShaderID, [-1, -1, 0], [1, 1, 0]); + + ctx.useProgram(null); + + // Blit source framebuffer to destination + + ctx.bindFramebuffer(gl.READ_FRAMEBUFFER, sourceFbo); + ctx.bindFramebuffer(gl.DRAW_FRAMEBUFFER, targetFbo); + this.checkError(); + + if (targetClass == tcuTexture.TextureChannelClass.SIGNED_FIXED_POINT || + targetClass == tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT || + targetClass == tcuTexture.TextureChannelClass.FLOATING_POINT) + ctx.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 0.0, 1.0]); + else if (targetClass == tcuTexture.TextureChannelClass.SIGNED_INTEGER) + ctx.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]); + else if (targetClass == tcuTexture.TextureChannelClass.UNSIGNED_INTEGER) + ctx.clearBufferuiv(gl.COLOR, 0, [0, 0, 0, 0]); + else + DE_ASSERT(false); + + ctx.blitFramebuffer(this.m_srcRect[0], this.m_srcRect[1], this.m_srcRect[2], this.m_srcRect[3], this.m_dstRect[0], this.m_dstRect[1], this.m_dstRect[2], this.m_dstRect[3], gl.COLOR_BUFFER_BIT, this.m_filter); + this.checkError(); + + // Read target + + ctx.bindFramebuffer(gl.FRAMEBUFFER, targetFbo); + + if (this.m_blitDir == es3fFramebufferBlitTests.BlitDirection.BLIT_TO_DEFAULT_FROM_TARGET) + this.readPixels(dst, this.m_interestingArea[0], this.m_interestingArea[1], this.m_interestingArea[2] - this.m_interestingArea[0], this.m_interestingArea[3] - this.m_interestingArea[1]); + else + this.readPixelsUsingFormat(dst, this.m_interestingArea[0], this.m_interestingArea[1], this.m_interestingArea[2] - this.m_interestingArea[0], this.m_interestingArea[3] - this.m_interestingArea[1], colorFormat, [1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]); + + this.checkError(); + }; + + es3fFramebufferBlitTests.run = function(context, range) { + gl = context; + //Set up root Test + var state = tcuTestCase.runner; + + var test = new es3fFramebufferBlitTests.FramebufferBlitTests(); + var testName = test.fullName(); + var testDescription = test.getDescription() || ''; + + state.testName = testName; + state.setRoot(test); + //Set up name and description of this test series. + setCurrentTestName(testName); + description(testDescription); + + try { + //Create test cases + test.init(); + if (range) + state.setRange(range); + //Run test cases + tcuTestCase.runTestCases(); + } + catch (err) { + bufferedLogToConsole(err); + testFailedOptions('Failed to es3fFramebufferBlitTests.run tests', false); + tcuTestCase.runner.terminate(); + } + }; + +}); |