es3fMultisampleTests.js (82373B)
1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES Utilities 3 * ------------------------------------------------ 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21 'use strict'; 22 goog.provide('functional.gles3.es3fMultisampleTests'); 23 goog.require('framework.common.tcuTestCase'); 24 goog.require('framework.delibs.debase.deMath'); 25 goog.require('framework.delibs.debase.deRandom'); 26 goog.require('framework.delibs.debase.deString'); 27 goog.require('framework.opengl.gluShaderProgram'); 28 goog.require('framework.opengl.gluStrUtil'); 29 goog.require('framework.opengl.gluTextureUtil'); 30 goog.require('framework.common.tcuImageCompare'); 31 goog.require('framework.common.tcuLogImage'); 32 goog.require('framework.common.tcuRGBA'); 33 goog.require('framework.common.tcuSurface'); 34 goog.require('framework.common.tcuTexture'); 35 goog.require('modules.shared.glsTextureTestUtil'); 36 37 goog.scope(function() { 38 /** @type {?WebGL2RenderingContext} */ var gl; 39 var es3fMultisampleTests = functional.gles3.es3fMultisampleTests; 40 var deMath = framework.delibs.debase.deMath; 41 var deRandom = framework.delibs.debase.deRandom; 42 var deString = framework.delibs.debase.deString; 43 var gluShaderProgram = framework.opengl.gluShaderProgram; 44 var tcuRGBA = framework.common.tcuRGBA; 45 var tcuSurface = framework.common.tcuSurface; 46 var tcuTestCase = framework.common.tcuTestCase; 47 var tcuTexture = framework.common.tcuTexture; 48 var gluStrUtil = framework.opengl.gluStrUtil; 49 var glsTextureTestUtil = modules.shared.glsTextureTestUtil; 50 var tcuImageCompare = framework.common.tcuImageCompare; 51 var gluTextureUtil = framework.opengl.gluTextureUtil; 52 var tcuLogImage = framework.common.tcuLogImage; 53 54 /** 55 * @constructor 56 * @struct 57 * @param {Array<number>} p0_ 58 * @param {Array<number>} p1_ 59 * @param {Array<number>} p2_ 60 * @param {Array<number>} p3_ 61 */ 62 es3fMultisampleTests.QuadCorners = function(p0_, p1_, p2_, p3_) { 63 /** @type {Array<number>} */ this.p0 = p0_; 64 /** @type {Array<number>} */ this.p1 = p1_; 65 /** @type {Array<number>} */ this.p2 = p2_; 66 /** @type {Array<number>} */ this.p3 = p3_; 67 }; 68 69 /** 70 * @param {number} defaultCount 71 * @return {number} 72 */ 73 es3fMultisampleTests.getIterationCount = function(defaultCount) { 74 // The C++ test takes an argument from the command line. 75 // Leaving this function in case we want to be able to take an argument from the URL 76 return defaultCount; 77 }; 78 79 /** 80 * @param {Array<number>} point 81 * @param {Array<number>} p0 82 * @param {Array<number>} p1 83 * @param {Array<number>} p2 84 * @param {Array<number>} p3 85 * @return {boolean} 86 */ 87 es3fMultisampleTests.isInsideQuad = function(point, p0, p1, p2, p3) { 88 /** @type {number} */ var dot0 = (point[0] - p0[0]) * (p1[1] - p0[1]) + (point[1] - p0[1]) * (p0[0] - p1[0]); 89 /** @type {number} */ var dot1 = (point[0] - p1[0]) * (p2[1] - p1[1]) + (point[1] - p1[1]) * (p1[0] - p2[0]); 90 /** @type {number} */ var dot2 = (point[0] - p2[0]) * (p3[1] - p2[1]) + (point[1] - p2[1]) * (p2[0] - p3[0]); 91 /** @type {number} */ var dot3 = (point[0] - p3[0]) * (p0[1] - p3[1]) + (point[1] - p3[1]) * (p3[0] - p0[0]); 92 93 return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0); 94 }; 95 96 /** 97 * Check if a region in an image is unicolored. 98 * 99 * Checks if the pixels in img inside the convex quadilateral defined by 100 * p0, p1, p2 and p3 are all (approximately) of the same color. 101 * 102 * @param {tcuSurface.Surface} img 103 * @param {Array<number>} p0 104 * @param {Array<number>} p1 105 * @param {Array<number>} p2 106 * @param {Array<number>} p3 107 * @return {boolean} 108 */ 109 es3fMultisampleTests.isPixelRegionUnicolored = function(img, p0, p1, p2, p3) { 110 /** @type {number} */ var xMin = deMath.clamp(Math.min(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1); 111 /** @type {number} */ var yMin = deMath.clamp(Math.min(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1); 112 /** @type {number} */ var xMax = deMath.clamp(Math.max(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1); 113 /** @type {number} */ var yMax = deMath.clamp(Math.max(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1); 114 /** @type {boolean} */ var insideEncountered = false; //!< Whether we have already seen at least one pixel inside the region. 115 /** @type {tcuRGBA.RGBA} */ var insideColor; //!< Color of the first pixel inside the region. 116 /** @type {tcuRGBA.RGBA} */ var threshold = tcuRGBA.newRGBAComponents(3, 3, 3, 3); 117 for (var y = yMin; y <= yMax; y++) 118 for (var x = xMin; x <= xMax; x++) 119 if (es3fMultisampleTests.isInsideQuad([x, y], p0, p1, p2, p3)) { 120 /** @type {tcuRGBA.RGBA} */ var pixColor = new tcuRGBA.RGBA(img.getPixel(x, y)); 121 122 if (insideEncountered) 123 if (!tcuRGBA.compareThreshold(pixColor, insideColor, threshold)) // Pixel color differs from already-detected color inside same region - region not unicolored. 124 return false; 125 else { 126 insideEncountered = true; 127 insideColor = pixColor; 128 } 129 } 130 return true; 131 }; 132 133 /** 134 * [drawUnicolorTestErrors description] 135 * @param {tcuSurface.Surface} img 136 * @param {tcuTexture.PixelBufferAccess} errorImg 137 * @param {Array<number>} p0 138 * @param {Array<number>} p1 139 * @param {Array<number>} p2 140 * @param {Array<number>} p3 141 * @return {boolean} 142 */ 143 es3fMultisampleTests.drawUnicolorTestErrors = function(img, errorImg, p0, p1, p2, p3) { 144 /** @type {number} */ var xMin = deMath.clamp(Math.min(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1); 145 /** @type {number} */ var yMin = deMath.clamp(Math.min(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1); 146 /** @type {number} */ var xMax = deMath.clamp(Math.max(p0[0], p1[0], p2[0], p3[0]), 0, img.getWidth() - 1); 147 /** @type {number} */ var yMax = deMath.clamp(Math.max(p0[1], p1[1], p2[1], p3[1]), 0, img.getHeight() - 1); 148 /** @type {tcuRGBA.RGBA} */ var refColor = new tcuRGBA.RGBA(img.getPixel(Math.floor((xMin + xMax) / 2), Math.floor((yMin + yMax) / 2))); 149 /** @type {tcuRGBA.RGBA} */ var threshold = tcuRGBA.newRGBAComponents(3, 3, 3, 3); 150 for (var y = yMin; y <= yMax; y++) 151 for (var x = xMin; x <= xMax; x++) 152 if (es3fMultisampleTests.isInsideQuad([x, y], p0, p1, p2, p3)) { 153 if (!tcuRGBA.compareThreshold(new tcuRGBA.RGBA(img.getPixel(x, y)), refColor, threshold)) { 154 img.setPixel(x, y, tcuRGBA.RGBA.red.toVec()); // TODO: this might also be toIVec() 155 errorImg.setPixel([1.0, 0.0, 0.0, 1.0], x, y); 156 } 157 } 158 return true; 159 }; 160 161 /** 162 * @constructor 163 * @struct 164 * @param {number=} numSamples_ 165 * @param {boolean=} useDepth_ 166 * @param {boolean=} useStencil_ 167 */ 168 es3fMultisampleTests.FboParams = function(numSamples_, useDepth_, useStencil_) { 169 /** @type {boolean} */ var useFbo_ = true; 170 if (numSamples_ === undefined && useDepth_ === undefined && useStencil_ === undefined) 171 useFbo_ = false; 172 /** @type {boolean} */ this.useFbo = useFbo_; 173 /** @type {number} */ this.numSamples = numSamples_ === undefined ? -1 : numSamples_; 174 /** @type {boolean} */ this.useDepth = useDepth_ === undefined ? false : useDepth_; 175 /** @type {boolean} */ this.useStencil = useStencil_ === undefined ? false : useStencil_; 176 177 }; 178 179 /** 180 * @constructor 181 * @extends {tcuTestCase.DeqpTest} 182 * @param {string} name 183 * @param {string} desc 184 * @param {number} desiredViewportSize 185 * @param {es3fMultisampleTests.FboParams} fboParams 186 */ 187 es3fMultisampleTests.MultisampleCase = function(name, desc, desiredViewportSize, fboParams) { 188 tcuTestCase.DeqpTest.call(this, name, desc); 189 /** @type {number} */ this.m_numSamples = 0; 190 /** @type {number} */ this.m_viewportSize = 0; 191 /** @type {number} */ this.m_desiredViewportSize = desiredViewportSize; 192 /** @type {es3fMultisampleTests.FboParams} */ this.m_fboParams = fboParams; 193 /** @type {WebGLRenderbuffer} */ this.m_msColorRbo = null; 194 /** @type {WebGLRenderbuffer} */ this.m_msDepthStencilRbo = null; 195 /** @type {WebGLRenderbuffer} */ this.m_resolveColorRbo = null; 196 /** @type {WebGLFramebuffer} */ this.m_msFbo = null; 197 /** @type {WebGLFramebuffer} */ this.m_resolveFbo = null; 198 /** @type {gluShaderProgram.ShaderProgram} */ this.m_program = null; 199 /** @type {number} */ this.m_attrPositionLoc = -1; 200 /** @type {number} */ this.m_attrColorLoc = -1; 201 /** @type {number} */ this.m_renderWidth = fboParams.useFbo ? 2 * desiredViewportSize : gl.drawingBufferWidth; 202 /** @type {number} */ this.m_renderHeight = fboParams.useFbo ? 2 * desiredViewportSize : gl.drawingBufferHeight; 203 /** @type {number} */ this.m_viewportX = 0; 204 /** @type {number} */ this.m_viewportY = 0; 205 /** @type {deRandom.Random} */ this.m_rnd = new deRandom.Random(deString.deStringHash(this.name)); 206 if (this.m_fboParams.useFbo) 207 assertMsgOptions(this.m_fboParams.numSamples >= 0, 'fboParams.numSamples < 0', false, true); 208 }; 209 210 es3fMultisampleTests.MultisampleCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype); 211 212 /** Copy the constructor */ 213 es3fMultisampleTests.MultisampleCase.prototype.constructor = es3fMultisampleTests.MultisampleCase; 214 215 /* Rest states */ 216 es3fMultisampleTests.MultisampleCase.prototype.deinit = function() { 217 gl.colorMask(true, true, true, true); 218 gl.depthMask(true); 219 220 gl.clearColor(0.0, 0.0, 0.0, 0.0); 221 gl.clearDepth(1.0); 222 gl.clearStencil(0); 223 224 gl.disable(gl.STENCIL_TEST); 225 gl.disable(gl.DEPTH_TEST); 226 gl.disable(gl.BLEND) 227 gl.disable(gl.SAMPLE_COVERAGE); 228 gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); 229 230 if (this.m_program) { 231 gl.deleteProgram(this.m_program.getProgram()); 232 this.m_program = null; 233 } 234 if (this.m_msColorRbo) { 235 gl.deleteRenderbuffer(this.m_msColorRbo); 236 this.m_msColorRbo = null; 237 } 238 if (this.m_msDepthStencilRbo) { 239 gl.deleteRenderbuffer(this.m_msDepthStencilRbo); 240 this.m_msDepthStencilRbo = null; 241 } 242 if (this.m_resolveColorRbo) { 243 gl.deleteRenderbuffer(this.m_resolveColorRbo); 244 this.m_resolveColorRbo = null; 245 } 246 247 if (this.m_msFbo) { 248 gl.deleteFramebuffer(this.m_msFbo); 249 this.m_msFbo = null; 250 } 251 if (this.m_resolveFbo) { 252 gl.deleteFramebuffer(this.m_resolveFbo); 253 this.m_resolveFbo = null; 254 } 255 256 gl.bindRenderbuffer(gl.RENDERBUFFER, null); 257 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 258 } 259 260 /** 261 * @protected 262 * @param {Array<number>} p0 263 * @param {Array<number>} p1 264 * @param {Array<number>} p2 265 * @param {Array<number>} c0 266 * @param {Array<number>} c1 267 * @param {Array<number>} c2 268 */ 269 es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec3cAsVec4 = function(p0, p1, p2, c0, c1, c2) { 270 /** @type {Array<number>} */ var vertexPositions = [ 271 p0[0], p0[1], p0[2], 1.0, 272 p1[0], p1[1], p1[2], 1.0, 273 p2[0], p2[1], p2[2], 1.0 274 ]; 275 /** @type {Array<number>} */ var vertexColors = [ 276 c0[0], c0[1], c0[2], c0[3], 277 c1[0], c1[1], c1[2], c1[3], 278 c2[0], c2[1], c2[2], c2[3] 279 ]; 280 281 var posGLBuffer = gl.createBuffer(); 282 /** @type {ArrayBufferView} */ var posBuffer = new Float32Array(vertexPositions); 283 gl.bindBuffer(gl.ARRAY_BUFFER, posGLBuffer); 284 gl.bufferData(gl.ARRAY_BUFFER, posBuffer, gl.STATIC_DRAW); 285 286 gl.enableVertexAttribArray(this.m_attrPositionLoc); 287 gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0); 288 289 var colGLBuffer = gl.createBuffer(); 290 /** @type {ArrayBufferView} */ var colBuffer = new Float32Array(vertexColors); 291 gl.bindBuffer(gl.ARRAY_BUFFER, colGLBuffer); 292 gl.bufferData(gl.ARRAY_BUFFER, colBuffer, gl.STATIC_DRAW); 293 294 gl.enableVertexAttribArray(this.m_attrColorLoc); 295 gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0); 296 297 gl.useProgram(this.m_program.getProgram()); 298 gl.drawArrays(gl.TRIANGLES, 0, 3); 299 300 gl.bindBuffer(gl.ARRAY_BUFFER, null); 301 gl.deleteBuffer(colGLBuffer); 302 gl.deleteBuffer(posGLBuffer); 303 }; 304 305 /** 306 * @protected 307 * @param {Array<number>} p0 308 * @param {Array<number>} p1 309 * @param {Array<number>} p2 310 * @param {Array<number>} color 311 */ 312 es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec3WithColor = function(p0, p1, p2, color) { 313 this.renderTriangle_pAsVec3cAsVec4(p0, p1, p2, color, color, color); 314 }; 315 316 /** 317 * @protected 318 * @param {Array<number>} p0 319 * @param {Array<number>} p1 320 * @param {Array<number>} p2 321 * @param {Array<number>} c0 322 * @param {Array<number>} c1 323 * @param {Array<number>} c2 324 */ 325 es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2 = function(p0, p1, p2, c0, c1, c2) { 326 this.renderTriangle_pAsVec3cAsVec4( 327 [p0[0], p0[1], 0.0], 328 [p1[0], p1[1], 0.0], 329 [p2[0], p2[1], 0.0], 330 c0, c1, c2); 331 }; 332 333 /** 334 * @protected 335 * @param {Array<number>} p0 336 * @param {Array<number>} p1 337 * @param {Array<number>} p2 338 * @param {Array<number>} color 339 */ 340 es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2WithColor = function(p0, p1, p2, color) { 341 this.renderTriangle_pAsVec2(p0, p1, p2, color, color, color); 342 }; 343 344 /** 345 * @protected 346 * @param {Array<number>} p0 347 * @param {Array<number>} p1 348 * @param {Array<number>} p2 349 * @param {Array<number>} p3 350 * @param {Array<number>} c0 351 * @param {Array<number>} c1 352 * @param {Array<number>} c2 353 * @param {Array<number>} c3 354 */ 355 es3fMultisampleTests.MultisampleCase.prototype.renderQuad = function(p0, p1, p2, p3, c0, c1, c2, c3) { 356 this.renderTriangle_pAsVec2(p0, p1, p2, c0, c1, c2); 357 this.renderTriangle_pAsVec2(p2, p1, p3, c2, c1, c3); 358 }; 359 360 /** 361 * @protected 362 * @param {Array<number>} p0 363 * @param {Array<number>} p1 364 * @param {Array<number>} p2 365 * @param {Array<number>} p3 366 * @param {Array<number>} color 367 */ 368 es3fMultisampleTests.MultisampleCase.prototype.renderQuad_WithColor = function(p0, p1, p2, p3, color) { 369 this.renderQuad(p0, p1, p2, p3, color, color, color, color); 370 }; 371 372 /** 373 * @protected 374 * @param {Array<number>} p0 375 * @param {Array<number>} p1 376 * @param {Array<number>} color 377 */ 378 es3fMultisampleTests.MultisampleCase.prototype.renderLine = function(p0, p1, color) { 379 /** @type {Array<number>} */ var vertexPositions = [ 380 p0[0], p0[1], 0.0, 1.0, 381 p1[0], p1[1], 0.0, 1.0 382 ]; 383 /** @type {Array<number>} */ var vertexColors = [ 384 color[0], color[1], color[2], color[3], 385 color[0], color[1], color[2], color[3] 386 ]; 387 388 var posGLBuffer = gl.createBuffer(); 389 /** @type {ArrayBufferView} */ var posBuffer = new Float32Array(vertexPositions); 390 gl.bindBuffer(gl.ARRAY_BUFFER, posGLBuffer); 391 gl.bufferData(gl.ARRAY_BUFFER, posBuffer, gl.STATIC_DRAW); 392 393 gl.enableVertexAttribArray(this.m_attrPositionLoc); 394 gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0); 395 396 var colGLBuffer = gl.createBuffer(); 397 /** @type {ArrayBufferView} */ var colBuffer = new Float32Array(vertexColors); 398 gl.bindBuffer(gl.ARRAY_BUFFER, colGLBuffer); 399 gl.bufferData(gl.ARRAY_BUFFER, colBuffer, gl.STATIC_DRAW); 400 401 gl.enableVertexAttribArray(this.m_attrColorLoc); 402 gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0); 403 404 gl.useProgram(this.m_program.getProgram()); 405 gl.drawArrays(gl.LINES, 0, 2); 406 407 gl.bindBuffer(gl.ARRAY_BUFFER, null); 408 gl.deleteBuffer(colGLBuffer); 409 gl.deleteBuffer(posGLBuffer); 410 }; 411 412 /** 413 * @protected 414 */ 415 es3fMultisampleTests.MultisampleCase.prototype.randomizeViewport = function() { 416 this.m_viewportX = this.m_rnd.getInt(0, this.m_renderWidth - this.m_viewportSize); 417 this.m_viewportY = this.m_rnd.getInt(0, this.m_renderHeight - this.m_viewportSize); 418 419 gl.viewport(this.m_viewportX, this.m_viewportY, this.m_viewportSize, this.m_viewportSize); 420 }; 421 422 /** 423 * @protected 424 * @return {tcuSurface.Surface} 425 */ 426 es3fMultisampleTests.MultisampleCase.prototype.readImage = function() { 427 /** @type {tcuSurface.Surface} */ 428 var dst = new tcuSurface.Surface(this.m_viewportSize, this.m_viewportSize); 429 /** @type {number} */ var pixelSize = dst.getAccess().getFormat().getPixelSize(); 430 /** @type {number} */ var param = deMath.deIsPowerOfTwo32(pixelSize) ? Math.min(pixelSize, 8) : 1; 431 /** @type {gluTextureUtil.TransferFormat} */ var format = gluTextureUtil.getTransferFormat(dst.getAccess().getFormat()); 432 /** @type {number} */ var width = dst.getAccess().getWidth(); 433 /** @type {number} */ var height = dst.getAccess().getHeight(); 434 if (this.m_fboParams.useFbo) { 435 gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.m_resolveFbo); 436 gl.blitFramebuffer(0, 0, this.m_renderWidth, this.m_renderHeight, 0, 0, this.m_renderWidth, this.m_renderHeight, gl.COLOR_BUFFER_BIT, gl.NEAREST); 437 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.m_resolveFbo); 438 439 gl.pixelStorei(gl.PACK_ALIGNMENT, param); 440 gl.readPixels(this.m_viewportX, this.m_viewportY, width, height, format.format, format.dataType, dst.getAccess().getDataPtr()); 441 442 gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo); 443 } 444 else { 445 gl.pixelStorei(gl.PACK_ALIGNMENT, param); 446 gl.readPixels(this.m_viewportX, this.m_viewportY, width, height, format.format, format.dataType, dst.getAccess().getDataPtr()); 447 } 448 return dst; 449 }; 450 451 es3fMultisampleTests.MultisampleCase.prototype.init = function() { 452 /** @type {string} */ var vertShaderSource = '' + 453 '#version 300 es\n' + 454 'in highp vec4 a_position;\n' + 455 'in mediump vec4 a_color;\n' + 456 'out mediump vec4 v_color;\n' + 457 'void main()\n' + 458 '{\n' + 459 ' gl_Position = a_position;\n' + 460 ' v_color = a_color;\n' + 461 '}\n'; 462 463 /** @type {string} */ var fragShaderSource = '' + 464 '#version 300 es\n' + 465 'in mediump vec4 v_color;\n' + 466 'layout(location = 0) out mediump vec4 o_color;\n' + 467 'void main()\n' + 468 '{\n' + 469 ' o_color = v_color;\n' + 470 '}\n'; 471 472 var numSamples = /** @type {number} */ (gl.getParameter(gl.SAMPLES)); 473 if (!this.m_fboParams.useFbo && numSamples <= 1) { 474 var msg = 'No multisample buffers'; 475 checkMessage(false, msg); 476 return false; 477 } 478 479 if (this.m_fboParams.useFbo) { 480 if (this.m_fboParams.numSamples > 0) 481 this.m_numSamples = this.m_fboParams.numSamples; 482 else { 483 bufferedLogToConsole('Querying maximum number of samples for ' + gluStrUtil.getPixelFormatName(gl.RGBA8) + ' with gl.getInternalformatParameter()'); 484 var supportedSampleCountArray = /** @type {Int32Array} */ (gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES)); 485 if (supportedSampleCountArray.length == 0) { 486 var msg = 'No supported sample counts'; 487 checkMessage(false, msg); 488 return false; 489 } 490 this.m_numSamples = supportedSampleCountArray[0]; 491 } 492 493 bufferedLogToConsole('Using FBO of size (' + this.m_renderWidth + ', ' + this.m_renderHeight + ') with ' + this.m_numSamples + ' samples'); 494 } 495 else { 496 // Query and log number of samples per pixel. 497 this.m_numSamples = numSamples; 498 bufferedLogToConsole('gl.SAMPLES =' + this.m_numSamples); 499 } 500 501 // Prepare program. 502 503 assertMsgOptions(!this.m_program, 'Program loaded when it should not be.', false, true); 504 505 this.m_program = new gluShaderProgram.ShaderProgram( 506 gl, 507 gluShaderProgram.makeVtxFragSources(vertShaderSource, fragShaderSource)); 508 509 if (!this.m_program.isOk()) 510 throw new Error('Failed to compile program'); 511 512 this.m_attrPositionLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_position'); 513 this.m_attrColorLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_color'); 514 515 if (this.m_attrPositionLoc < 0 || this.m_attrColorLoc < 0) { 516 this.m_program = null; 517 throw new Error('Invalid attribute locations'); 518 } 519 520 if (this.m_fboParams.useFbo) { 521 // Setup ms color RBO. 522 this.m_msColorRbo = gl.createRenderbuffer(); 523 gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_msColorRbo); 524 525 /** @type {Int32Array} */ var supportedSampleCountArray = /** @type {Int32Array} */ (gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES)); 526 var maxSampleCount = supportedSampleCountArray[0]; 527 if (maxSampleCount < this.m_numSamples) { 528 bufferedLogToConsole('skipping test: ' + this.m_numSamples + ' samples not supported; max is ' + maxSampleCount); 529 return false; 530 } 531 532 assertMsgOptions(gl.getError() === gl.NO_ERROR, 'should be no GL error before renderbufferStorageMultisample'); 533 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.m_numSamples, gl.RGBA8, this.m_renderWidth, this.m_renderHeight); 534 assertMsgOptions(gl.getError() === gl.NO_ERROR, 'should be no GL error after renderbufferStorageMultisample'); 535 536 537 if (this.m_fboParams.useDepth || this.m_fboParams.useStencil) { 538 // Setup ms depth & stencil RBO. 539 this.m_msDepthStencilRbo = gl.createRenderbuffer(); 540 gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_msDepthStencilRbo); 541 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, this.m_numSamples, gl.DEPTH24_STENCIL8, this.m_renderWidth, this.m_renderHeight); 542 } 543 544 // Setup ms FBO. 545 this.m_msFbo = gl.createFramebuffer(); 546 gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo); 547 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.m_msColorRbo); 548 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.m_msDepthStencilRbo); 549 550 // Setup resolve color RBO. 551 this.m_resolveColorRbo = gl.createRenderbuffer(); 552 gl.bindRenderbuffer(gl.RENDERBUFFER, this.m_resolveColorRbo); 553 gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, this.m_renderWidth, this.m_renderHeight); 554 555 // Setup resolve FBO. 556 this.m_resolveFbo = gl.createFramebuffer(); 557 gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_resolveFbo); 558 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, this.m_resolveColorRbo); 559 560 // Use ms FBO. 561 gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo); 562 } 563 564 // Get suitable viewport size. 565 566 this.m_viewportSize = Math.min(this.m_desiredViewportSize, this.m_renderWidth, this.m_renderHeight); 567 this.randomizeViewport(); 568 return true; 569 }; 570 571 /** 572 * Base class for cases testing the value of sample count. 573 * 574 * Draws a test pattern (defined by renderPattern() of an inheriting class) 575 * and counts the number of distinct colors in the resulting image. That 576 * number should be at least the value of sample count plus one. This is 577 * repeated with increased values of m_currentIteration until this correct 578 * number of colors is detected or m_currentIteration reaches 579 * m_maxNumIterations. 580 * 581 * @extends {es3fMultisampleTests.MultisampleCase} 582 * @constructor 583 * @param {string} name 584 * @param {string} desc 585 * @param {es3fMultisampleTests.FboParams} fboParams 586 */ 587 es3fMultisampleTests.NumSamplesCase = function(name, desc, fboParams) { 588 es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, fboParams); 589 /** @type {number} */ var DEFAULT_MAX_NUM_ITERATIONS = 16; 590 /** @type {number} */ this.m_currentIteration = 0; 591 /** @type {number} */ this.m_maxNumIterations = es3fMultisampleTests.getIterationCount(DEFAULT_MAX_NUM_ITERATIONS); 592 /** @type {Array<tcuRGBA.RGBA>} */ this.m_detectedColors = []; 593 }; 594 595 es3fMultisampleTests.NumSamplesCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype); 596 597 /** Copy the constructor */ 598 es3fMultisampleTests.NumSamplesCase.prototype.constructor = es3fMultisampleTests.NumSamplesCase; 599 600 /** 601 * @return {tcuTestCase.IterateResult} 602 */ 603 es3fMultisampleTests.NumSamplesCase.prototype.iterate = function() { 604 this.randomizeViewport(); 605 606 gl.clearColor(0.0, 0.0, 0.0, 1.0); 607 gl.clear(gl.COLOR_BUFFER_BIT); 608 609 this.renderPattern(); 610 611 // Read and log rendered image. 612 613 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage(); 614 tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess()); 615 616 // Detect new, previously unseen colors from image. 617 618 /** @type {number} */ var requiredNumDistinctColors = this.m_numSamples + 1; 619 620 // If the number of samples is high (64 or more), we need to lower the threshold for detecting unique colors, otherwise two expected unique colors would be treated as the same color. 621 var threshold = Math.min(3, Math.floor(255 / this.m_numSamples) - 1); 622 var thresholdRGBA = tcuRGBA.newRGBAComponents(threshold, threshold, threshold, threshold); 623 624 for (var y = 0; y < renderedImg.getHeight() && this.m_detectedColors.length < requiredNumDistinctColors; y++) 625 for (var x = 0; x < renderedImg.getWidth() && this.m_detectedColors.length < requiredNumDistinctColors; x++) { 626 /** @type {tcuRGBA.RGBA} */ var color = new tcuRGBA.RGBA(renderedImg.getPixel(x, y)); 627 628 /** @type {number} */ var i; 629 for (i = 0; i < this.m_detectedColors.length; i++) { 630 if (tcuRGBA.compareThreshold(color, this.m_detectedColors[i], thresholdRGBA)) 631 break; 632 } 633 634 if (i === this.m_detectedColors.length) 635 this.m_detectedColors.push(color); // Color not previously detected. 636 } 637 638 // Log results. 639 640 bufferedLogToConsole('Number of distinct colors detected so far: ' + (this.m_detectedColors.length >= requiredNumDistinctColors ? 'at least ' : '') + this.m_detectedColors.length); 641 642 643 if (this.m_detectedColors.length < requiredNumDistinctColors) { 644 // Haven't detected enough different colors yet. 645 646 this.m_currentIteration++; 647 648 if (this.m_currentIteration >= this.m_maxNumIterations) { 649 testFailedOptions('Failure: Number of distinct colors detected is lower than sample count+1', false); 650 return tcuTestCase.IterateResult.STOP; 651 } 652 else { 653 bufferedLogToConsole('The number of distinct colors detected is lower than sample count+1 - trying again with a slightly altered pattern'); 654 return tcuTestCase.IterateResult.CONTINUE; 655 } 656 } 657 else { 658 testPassedOptions('Success: The number of distinct colors detected is at least sample count+1', true); 659 return tcuTestCase.IterateResult.STOP; 660 } 661 }; 662 663 /** 664 * @extends {es3fMultisampleTests.NumSamplesCase} 665 * @constructor 666 * @param {string} name 667 * @param {string} desc 668 * @param {number=} numFboSamples 669 */ 670 es3fMultisampleTests.PolygonNumSamplesCase = function(name, desc, numFboSamples) { 671 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 672 /** @type {es3fMultisampleTests.FboParams} */ 673 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams(); 674 es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params); 675 }; 676 677 es3fMultisampleTests.PolygonNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype); 678 679 /** Copy the constructor */ 680 es3fMultisampleTests.PolygonNumSamplesCase.prototype.constructor = es3fMultisampleTests.PolygonNumSamplesCase; 681 682 es3fMultisampleTests.PolygonNumSamplesCase.prototype.renderPattern = function() { 683 // The test pattern consists of several triangles with edges at different angles. 684 685 /** @type {number} */ var numTriangles = 25; 686 for (var i = 0; i < numTriangles; i++) { 687 /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles + 0.001 * this.m_currentIteration; 688 /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles + 0.001 * this.m_currentIteration; 689 690 this.renderTriangle_pAsVec2WithColor( 691 [0.0, 0.0], 692 [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95], 693 [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95], 694 [1.0, 1.0, 1.0, 1.0]); 695 } 696 }; 697 698 /** 699 * @extends {es3fMultisampleTests.NumSamplesCase} 700 * @constructor 701 * @param {string} name 702 * @param {string} desc 703 * @param {number=} numFboSamples 704 */ 705 es3fMultisampleTests.LineNumSamplesCase = function(name, desc, numFboSamples) { 706 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 707 /** @type {es3fMultisampleTests.FboParams} */ 708 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams(); 709 es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params); 710 }; 711 712 es3fMultisampleTests.LineNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype); 713 714 /** Copy the constructor */ 715 es3fMultisampleTests.LineNumSamplesCase.prototype.constructor = es3fMultisampleTests.LineNumSamplesCase; 716 717 es3fMultisampleTests.LineNumSamplesCase.prototype.renderPattern = function() { 718 // The test pattern consists of several lines at different angles. 719 720 // We scale the number of lines based on the viewport size. This is because a gl line's thickness is 721 // constant in pixel units, i.e. they get relatively thicker as viewport size decreases. Thus we must 722 // decrease the number of lines in order to decrease the extent of overlap among the lines in the 723 // center of the pattern. 724 /** @type {number} */ var numLines = Math.floor(100.0 * Math.sqrt(this.m_viewportSize / 256.0)); 725 726 for (var i = 0; i < numLines; i++) { 727 /** @type {number} */ var angle = 2.0 * Math.PI * i / numLines + 0.001 * this.m_currentIteration; 728 this.renderLine([0.0, 0.0], [Math.cos(angle) * 0.95, Math.sin(angle) * 0.95], [1.0, 1.0, 1.0, 1.0]); 729 } 730 }; 731 732 /** 733 * Case testing behaviour of common edges when multisampling. 734 * 735 * Draws a number of test patterns, each with a number of quads, each made 736 * of two triangles, rotated at different angles. The inner edge inside the 737 * quad (i.e. the common edge of the two triangles) still should not be 738 * visible, despite multisampling - i.e. the two triangles forming the quad 739 * should never get any common coverage bits in any pixel. 740 * 741 * @extends {es3fMultisampleTests.MultisampleCase} 742 * @constructor 743 * @param {string} name 744 * @param {string} desc 745 * @param {es3fMultisampleTests.CommonEdgeCase.CaseType} caseType 746 * @param {number} numFboSamples 747 */ 748 es3fMultisampleTests.CommonEdgeCase = function(name, desc, caseType, numFboSamples) { 749 /** @type {number} */ var cases = caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS ? 128 : 32; 750 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 751 /** @type {es3fMultisampleTests.FboParams} */ 752 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams(); 753 754 es3fMultisampleTests.MultisampleCase.call(this, name, desc, cases, params); 755 /** @type {number} */ var DEFAULT_SMALL_QUADS_ITERATIONS = 16; 756 /** @type {number} */ var DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS = 64; // 8*8 757 /** @type {es3fMultisampleTests.CommonEdgeCase.CaseType} */ this.m_caseType = caseType; 758 /** @type {number} */ this.m_currentIteration = 0; 759 /** @type {number} */ 760 this.m_numIterations = caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS ? es3fMultisampleTests.getIterationCount(DEFAULT_SMALL_QUADS_ITERATIONS) : 761 caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD ? es3fMultisampleTests.getIterationCount(DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS) : 762 8; 763 }; 764 765 es3fMultisampleTests.CommonEdgeCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype); 766 767 /** Copy the constructor */ 768 es3fMultisampleTests.CommonEdgeCase.prototype.constructor = es3fMultisampleTests.CommonEdgeCase; 769 770 /** 771 * @enum {number} 772 */ 773 es3fMultisampleTests.CommonEdgeCase.CaseType = { 774 SMALL_QUADS: 0, //!< Draw several small quads per iteration. 775 BIGGER_THAN_VIEWPORT_QUAD: 1, //!< Draw one bigger-than-viewport quad per iteration. 776 FIT_VIEWPORT_QUAD: 2 //!< Draw one exactly viewport-sized, axis aligned quad per iteration. 777 }; 778 779 es3fMultisampleTests.CommonEdgeCase.prototype.init = function() { 780 var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this); 781 if (!inited) { 782 return false; 783 } 784 785 if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS) { 786 // Check for a big enough viewport. With too small viewports the test case can't analyze the resulting image well enough. 787 788 /** @type {number} */ var minViewportSize = 32; 789 790 if (this.m_viewportSize < minViewportSize) 791 throw new Error('Render target width or height too low (is ' + this.m_viewportSize + ', should be at least ' + minViewportSize + ')'); 792 } 793 794 gl.enable(gl.BLEND); 795 gl.blendEquation(gl.FUNC_ADD); 796 gl.blendFunc(gl.ONE, gl.ONE); 797 bufferedLogToConsole('Additive blending enabled in order to detect (erroneously) overlapping samples'); 798 }; 799 800 /** 801 * @return {tcuTestCase.IterateResult} 802 */ 803 es3fMultisampleTests.CommonEdgeCase.prototype.iterate = function() { 804 /** @type {tcuSurface.Surface} */ var errorImg = new tcuSurface.Surface(this.m_viewportSize, this.m_viewportSize); 805 806 this.randomizeViewport(); 807 808 gl.clearColor(0.0, 0.0, 0.0, 1.0); 809 gl.clear(gl.COLOR_BUFFER_BIT); 810 811 // Draw test pattern. Test patterns consist of quads formed with two triangles. 812 // After drawing the pattern, we check that the interior pixels of each quad are 813 // all the same color - this is meant to verify that there are no artifacts on the inner edge. 814 815 /** @type {Array<es3fMultisampleTests.QuadCorners>} */ var unicoloredRegions = []; 816 817 /** @type {Array<Array<number>>} */ var corners; 818 /** @type {number} */ var angleCos; 819 /** @type {number} */ var angleSin; 820 /** @type {number} */ var angle; 821 /** @type {number} */ var quadDiagLen; 822 /** @type {number} */ var unicolorRegionScale; 823 /** @type {number} */ var quadBaseAngleNdx 824 /** @type {number} */ var quadSubAngleNdx; 825 826 if (this.m_caseType == es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS) { 827 // Draw several quads, rotated at different angles. 828 829 quadDiagLen = 2.0 / 3.0 * 0.9; // \note Fit 3 quads in both x and y directions. 830 831 832 // \note First and second iteration get exact 0 (and 90, 180, 270) and 45 (and 135, 225, 315) angle quads, as they are kind of a special case. 833 834 if (this.m_currentIteration === 0) { 835 angleCos = 1.0; 836 angleSin = 0.0; 837 } 838 else if (this.m_currentIteration === 1) { 839 angleCos = Math.SQRT1_2; 840 angleSin = Math.SQRT1_2; 841 } 842 else { 843 angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1); 844 angleCos = Math.cos(angle); 845 angleSin = Math.sin(angle); 846 } 847 848 corners = [ 849 deMath.scale([angleCos, angleSin], 0.5 * quadDiagLen), 850 deMath.scale([-angleSin, angleCos], 0.5 * quadDiagLen), 851 deMath.scale([-angleCos, -angleSin], 0.5 * quadDiagLen), 852 deMath.scale([angleSin, -angleCos], 0.5 * quadDiagLen) 853 ]; 854 855 // Draw 8 quads. 856 // First four are rotated at angles angle+0, angle+90, angle+180 and angle+270. 857 // Last four are rotated the same angles as the first four, but the ordering of the last triangle's vertices is reversed. 858 859 for (var quadNdx = 0; quadNdx < 8; quadNdx++) { 860 /** @type {Array<number>} */ 861 var center = deMath.addScalar( 862 deMath.scale([quadNdx % 3, quadNdx / 3], (2.0 - quadDiagLen)/ 2.0), 863 (-0.5 * (2.0 - quadDiagLen))); 864 865 this.renderTriangle_pAsVec2WithColor( 866 deMath.add(corners[(0 + quadNdx) % 4], center), 867 deMath.add(corners[(1 + quadNdx) % 4], center), 868 deMath.add(corners[(2 + quadNdx) % 4], center), 869 [0.5, 0.5, 0.5, 1.0]); 870 871 if (quadNdx >= 4) { 872 this.renderTriangle_pAsVec2WithColor( 873 deMath.add(corners[(3 + quadNdx) % 4], center), 874 deMath.add(corners[(2 + quadNdx) % 4], center), 875 deMath.add(corners[(0 + quadNdx) % 4], center), 876 [0.5, 0.5, 0.5, 1.0]); 877 } 878 else { 879 this.renderTriangle_pAsVec2WithColor( 880 deMath.add(corners[(0 + quadNdx) % 4], center), 881 deMath.add(corners[(2 + quadNdx) % 4], center), 882 deMath.add(corners[(3 + quadNdx) % 4], center), 883 [0.5, 0.5, 0.5, 1.0]); 884 } 885 886 // The size of the 'interior' of a quad is assumed to be approximately unicolorRegionScale*<actual size of quad>. 887 // By 'interior' we here mean the region of non-boundary pixels of the rendered quad for which we can safely assume 888 // that it has all coverage bits set to 1, for every pixel. 889 unicolorRegionScale = 1.0 - 6.0 * 2.0 / this.m_viewportSize / quadDiagLen; 890 unicoloredRegions.push( 891 new es3fMultisampleTests.QuadCorners( 892 deMath.add(center, deMath.scale(corners[0], unicolorRegionScale)), 893 deMath.add(center, deMath.scale(corners[1], unicolorRegionScale)), 894 deMath.add(center, deMath.scale(corners[2], unicolorRegionScale)), 895 deMath.add(center, deMath.scale(corners[3], unicolorRegionScale)))); 896 } 897 } 898 else if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD) { 899 // Draw a bigger-than-viewport quad, rotated at an angle depending on m_currentIteration. 900 901 quadBaseAngleNdx = Math.floor(this.m_currentIteration / 8); 902 quadSubAngleNdx = this.m_currentIteration % 8; 903 904 if (quadBaseAngleNdx === 0) { 905 angleCos = 1.0; 906 angleSin = 0.0; 907 } 908 else if (quadBaseAngleNdx === 1) { 909 angleCos = Math.SQRT1_2; 910 angleSin = Math.SQRT1_2; 911 } 912 else { 913 angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1); 914 angleCos = Math.cos(angle); 915 angleSin = Math.sin(angle); 916 } 917 918 quadDiagLen = 2.5 / Math.max(angleCos, angleSin); 919 920 corners = [ 921 deMath.scale([angleCos, angleSin], 0.5 * quadDiagLen), 922 deMath.scale([-angleSin, angleCos], 0.5 * quadDiagLen), 923 deMath.scale([-angleCos, -angleSin], 0.5 * quadDiagLen), 924 deMath.scale([angleSin, -angleCos], 0.5 * quadDiagLen) 925 ]; 926 927 this.renderTriangle_pAsVec2WithColor( 928 corners[(0 + quadSubAngleNdx) % 4], 929 corners[(1 + quadSubAngleNdx) % 4], 930 corners[(2 + quadSubAngleNdx) % 4], 931 [0.5, 0.5, 0.5, 1.0]); 932 933 if (quadSubAngleNdx >= 4) { 934 this.renderTriangle_pAsVec2WithColor( 935 corners[(3 + quadSubAngleNdx) % 4], 936 corners[(2 + quadSubAngleNdx) % 4], 937 corners[(0 + quadSubAngleNdx) % 4], 938 [0.5, 0.5, 0.5, 1.0]); 939 } 940 else { 941 this.renderTriangle_pAsVec2WithColor( 942 corners[(0 + quadSubAngleNdx) % 4], 943 corners[(2 + quadSubAngleNdx) % 4], 944 corners[(3 + quadSubAngleNdx) % 4], 945 [0.5, 0.5, 0.5, 1.0]); 946 } 947 948 unicolorRegionScale = 1.0 - 6.0 * 2.0 / this.m_viewportSize / quadDiagLen; 949 unicoloredRegions.push( 950 new es3fMultisampleTests.QuadCorners( 951 deMath.scale(corners[0], unicolorRegionScale), 952 deMath.scale(corners[1], unicolorRegionScale), 953 deMath.scale(corners[2], unicolorRegionScale), 954 deMath.scale(corners[3], unicolorRegionScale))); 955 } 956 else if (this.m_caseType === es3fMultisampleTests.CommonEdgeCase.CaseType.FIT_VIEWPORT_QUAD) { 957 // Draw an exactly viewport-sized quad, rotated by multiples of 90 degrees angle depending on m_currentIteration. 958 959 quadSubAngleNdx = this.m_currentIteration % 8; 960 961 corners = [ 962 [1.0, 1.0], 963 [-1.0, 1.0], 964 [-1.0, -1.0], 965 [1.0, -1.0] 966 ]; 967 968 this.renderTriangle_pAsVec2WithColor( 969 corners[(0 + quadSubAngleNdx) % 4], 970 corners[(1 + quadSubAngleNdx) % 4], 971 corners[(2 + quadSubAngleNdx) % 4], 972 [0.5, 0.5, 0.5, 1.0]); 973 974 if (quadSubAngleNdx >= 4) { 975 this.renderTriangle_pAsVec2WithColor( 976 corners[(3 + quadSubAngleNdx) % 4], 977 corners[(2 + quadSubAngleNdx) % 4], 978 corners[(0 + quadSubAngleNdx) % 4], 979 [0.5, 0.5, 0.5, 1.0]); 980 } 981 else { 982 this.renderTriangle_pAsVec2WithColor( 983 corners[(0 + quadSubAngleNdx) % 4], 984 corners[(2 + quadSubAngleNdx) % 4], 985 corners[(3 + quadSubAngleNdx) % 4], 986 [0.5, 0.5, 0.5, 1.0]); 987 } 988 989 unicoloredRegions.push(new es3fMultisampleTests.QuadCorners(corners[0], corners[1], corners[2], corners[3])); 990 } 991 else 992 throw new Error('CaseType not supported.'); 993 994 // Read pixels and check unicolored regions. 995 996 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage(); 997 998 errorImg.getAccess().clear([0.0, 1.0, 0.0, 1.0]); 999 tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess()); 1000 1001 /** @type {boolean} */ var errorsDetected = false; 1002 for (var i = 0; i < unicoloredRegions.length; i++) { 1003 /** @type {es3fMultisampleTests.QuadCorners} */ var region = unicoloredRegions[i]; 1004 /** @type {Array<number>} */ var p0Win = deMath.scale(deMath.addScalar(region.p0, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5); 1005 /** @type {Array<number>} */ var p1Win = deMath.scale(deMath.addScalar(region.p1, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5); 1006 /** @type {Array<number>} */ var p2Win = deMath.scale(deMath.addScalar(region.p2, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5); 1007 /** @type {Array<number>} */ var p3Win = deMath.scale(deMath.addScalar(region.p3, 1.0), 0.5 * (this.m_viewportSize - 1) + 0.5); 1008 /** @type {boolean} */ var errorsInCurrentRegion = !es3fMultisampleTests.isPixelRegionUnicolored(renderedImg, p0Win, p1Win, p2Win, p3Win); 1009 1010 if (errorsInCurrentRegion) 1011 es3fMultisampleTests.drawUnicolorTestErrors(renderedImg, errorImg.getAccess(), p0Win, p1Win, p2Win, p3Win); 1012 1013 errorsDetected = errorsDetected || errorsInCurrentRegion; 1014 } 1015 1016 this.m_currentIteration++; 1017 1018 if (errorsDetected) { 1019 bufferedLogToConsole('Failure: Not all quad interiors seem unicolored - common-edge artifacts?'); 1020 bufferedLogToConsole('Erroneous pixels are drawn red in the following image'); 1021 tcuLogImage.logImage('RenderedImageWithErrors', 'Rendered image with errors marked', renderedImg.getAccess()); 1022 tcuLogImage.logImage('ErrorsOnly', 'Image with error pixels only', errorImg.getAccess()); 1023 testFailedOptions('Failed: iteration ' + (this.m_currentIteration - 1), false); 1024 return tcuTestCase.IterateResult.STOP; 1025 } 1026 else if (this.m_currentIteration < this.m_numIterations) { 1027 bufferedLogToConsole('Quads seem OK - moving on to next pattern'); 1028 return tcuTestCase.IterateResult.CONTINUE; 1029 } 1030 else { 1031 bufferedLogToConsole('Success: All quad interiors seem unicolored (no common-edge artifacts)'); 1032 testPassedOptions('Passed: iteration ' + (this.m_currentIteration - 1), true); 1033 return tcuTestCase.IterateResult.STOP; 1034 } 1035 }; 1036 1037 /** 1038 * Test that depth values are per-sample. 1039 * 1040 * Draws intersecting, differently-colored polygons and checks that there 1041 * are at least sample count+1 distinct colors present, due to some of the 1042 * samples at the intersection line belonging to one and some to another 1043 * polygon. 1044 * 1045 * @extends {es3fMultisampleTests.NumSamplesCase} 1046 * @constructor 1047 * @param {string} name 1048 * @param {string} desc 1049 * @param {number=} numFboSamples 1050 */ 1051 es3fMultisampleTests.SampleDepthCase = function(name, desc, numFboSamples) { 1052 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 1053 /** @type {es3fMultisampleTests.FboParams} */ 1054 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, true, false) : new es3fMultisampleTests.FboParams(); 1055 es3fMultisampleTests.NumSamplesCase.call(this, name, desc, params); 1056 }; 1057 1058 es3fMultisampleTests.SampleDepthCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype); 1059 1060 /** Copy the constructor */ 1061 es3fMultisampleTests.SampleDepthCase.prototype.constructor = es3fMultisampleTests.SampleDepthCase; 1062 1063 es3fMultisampleTests.SampleDepthCase.prototype.init = function() { 1064 var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this); 1065 if (!inited) { 1066 return false; 1067 } 1068 1069 gl.enable(gl.DEPTH_TEST); 1070 gl.depthFunc(gl.LESS); 1071 1072 bufferedLogToConsole('Depth test enabled, depth func is gl.LESS'); 1073 bufferedLogToConsole('Drawing several bigger-than-viewport black or white polygons intersecting each other'); 1074 }; 1075 1076 es3fMultisampleTests.SampleDepthCase.prototype.renderPattern = function() { 1077 gl.clearColor(0.0, 0.0, 0.0, 0.0); 1078 gl.clearDepth(1.0); 1079 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 1080 1081 /** @type {number} */ var numPolygons = 50; 1082 1083 for (var i = 0; i < numPolygons; i++) { 1084 /** @type {Array<number>} */ var color = i % 2 == 0 ? [1.0, 1.0, 1.0, 1.0] : [0.0, 0.0, 0.0, 1.0]; 1085 /** @type {number} */ var angle = 2.0 * Math.PI * i / numPolygons + 0.001 * this.m_currentIteration; 1086 /** @type {Array<number>} */ var pt0 = [3.0 * Math.cos(angle + 2.0 * Math.PI * 0.0 / 3.0), 3.0 * Math.sin(angle + 2.0 * Math.PI * 0.0 / 3.0), 1.0]; 1087 /** @type {Array<number>} */ var pt1 = [3.0 * Math.cos(angle + 2.0 * Math.PI * 1.0 / 3.0), 3.0 * Math.sin(angle + 2.0 * Math.PI * 1.0 / 3.0), 0.0]; 1088 /** @type {Array<number>} */ var pt2 = [3.0 * Math.cos(angle + 2.0 * Math.PI * 2.0 / 3.0), 3.0 * Math.sin(angle + 2.0 * Math.PI * 2.0 / 3.0), 0.0]; 1089 1090 this.renderTriangle_pAsVec3WithColor(pt0, pt1, pt2, color); 1091 } 1092 }; 1093 1094 /** 1095 * Test that stencil buffer values are per-sample. 1096 * 1097 * Draws a unicolored pattern and marks drawn samples in stencil buffer; 1098 * then clears and draws a viewport-size quad with that color and with 1099 * proper stencil test such that the resulting image should be exactly the 1100 * same as after the pattern was first drawn. 1101 * 1102 * @extends {es3fMultisampleTests.MultisampleCase} 1103 * @constructor 1104 * @param {string} name 1105 * @param {string} desc 1106 * @param {number=} numFboSamples 1107 */ 1108 es3fMultisampleTests.SampleStencilCase = function(name, desc, numFboSamples) { 1109 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 1110 /** @type {es3fMultisampleTests.FboParams} */ 1111 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, true) : new es3fMultisampleTests.FboParams(); 1112 es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params); 1113 }; 1114 1115 es3fMultisampleTests.SampleStencilCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype); 1116 1117 /** Copy the constructor */ 1118 es3fMultisampleTests.SampleStencilCase.prototype.constructor = es3fMultisampleTests.SampleStencilCase; 1119 1120 /** 1121 * @return {tcuTestCase.IterateResult} 1122 */ 1123 es3fMultisampleTests.SampleStencilCase.prototype.iterate = function() { 1124 this.randomizeViewport(); 1125 1126 gl.clearColor(0.0, 0.0, 0.0, 1.0); 1127 gl.clearStencil(0); 1128 gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); 1129 gl.enable(gl.STENCIL_TEST); 1130 gl.stencilFunc(gl.ALWAYS, 1, 1); 1131 gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); 1132 1133 bufferedLogToConsole('Drawing a pattern with gl.stencilFunc(gl.ALWAYS, 1, 1) and gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)'); 1134 1135 /** @type {number} */ var numTriangles = 25; 1136 for (var i = 0; i < numTriangles; i++) { 1137 /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles; 1138 /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles; 1139 1140 this.renderTriangle_pAsVec2WithColor( 1141 [0.0, 0.0], 1142 [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95], 1143 [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95], 1144 [1.0, 1.0, 1.0, 1.0]); 1145 } 1146 1147 /** @type {tcuSurface.Surface} */ var renderedImgFirst = this.readImage(); 1148 tcuLogImage.logImage('RenderedImgFirst', 'First image rendered', renderedImgFirst.getAccess()); 1149 bufferedLogToConsole('Clearing color buffer to black'); 1150 1151 gl.clear(gl.COLOR_BUFFER_BIT); 1152 gl.stencilFunc(gl.EQUAL, 1, 1); 1153 gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); 1154 1155 bufferedLogToConsole('Checking that color buffer was actually cleared to black'); 1156 1157 /** @type {tcuSurface.Surface} */ var clearedImg = this.readImage(); 1158 1159 for (var y = 0; y < clearedImg.getHeight(); y++) 1160 for (var x = 0; x < clearedImg.getWidth(); x++) { 1161 /** @type {tcuRGBA.RGBA} */ var clr = new tcuRGBA.RGBA(clearedImg.getPixel(x, y)); 1162 if (!clr.equals(tcuRGBA.RGBA.black)) { 1163 bufferedLogToConsole('Failure: first non-black pixel, color ' + clr.toString() + ', detected at coordinates (' + x + ', ' + y + ')'); 1164 tcuLogImage.logImage('ClearedImg', 'Image after clearing, erroneously non-black', clearedImg.getAccess()); 1165 testFailedOptions('Failed', false); 1166 return tcuTestCase.IterateResult.STOP; 1167 } 1168 } 1169 1170 bufferedLogToConsole('Drawing a viewport-sized quad with gl.stencilFunc(gl.EQUAL, 1, 1) and gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP) - should result in same image as the first'); 1171 1172 this.renderQuad_WithColor( 1173 [-1.0, -1.0], 1174 [1.0, -1.0], 1175 [-1.0, 1.0], 1176 [1.0, 1.0], 1177 [1.0, 1.0, 1.0, 1.0]); 1178 1179 /** @type {tcuSurface.Surface} */ var renderedImgSecond = this.readImage(); 1180 tcuLogImage.logImage('RenderedImgSecond', 'Second image rendered', renderedImgSecond.getAccess()); 1181 /** @type {boolean} */ 1182 var passed = tcuImageCompare.pixelThresholdCompare( 1183 'ImageCompare', 1184 'Image comparison', 1185 renderedImgFirst, 1186 renderedImgSecond, 1187 [0,0,0,0]); 1188 1189 if (passed) { 1190 bufferedLogToConsole('Success: The two images rendered are identical'); 1191 testPassedOptions('Passed', true); 1192 } 1193 else 1194 testFailedOptions('Failed', false); 1195 1196 return tcuTestCase.IterateResult.STOP; 1197 }; 1198 1199 /** 1200 * Tests coverage mask generation proportionality property. 1201 * 1202 * Tests that the number of coverage bits in a coverage mask created by 1203 * gl.SAMPLE_ALPHA_TO_COVERAGE or gl.SAMPLE_COVERAGE is, on average, 1204 * proportional to the alpha or coverage value, respectively. Draws 1205 * multiple frames, each time increasing the alpha or coverage value used, 1206 * and checks that the average color is changing appropriately. 1207 * 1208 * @extends {es3fMultisampleTests.MultisampleCase} 1209 * @constructor 1210 * @param {string} name 1211 * @param {string} desc 1212 * @param {es3fMultisampleTests.MaskProportionalityCase.CaseType} type 1213 * @param {number=} numFboSamples 1214 */ 1215 es3fMultisampleTests.MaskProportionalityCase = function(name, desc, type, numFboSamples) { 1216 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 1217 /** @type {es3fMultisampleTests.FboParams} */ 1218 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams(); 1219 es3fMultisampleTests.MultisampleCase.call(this, name, desc, 32, params); 1220 /** @type {es3fMultisampleTests.MaskProportionalityCase.CaseType} */ this.m_type = type; 1221 /** @type {number} */ this.m_numIterations; 1222 /** @type {number} */ this.m_currentIteration = 0; 1223 /** @type {number} */ this.m_previousIterationColorSum = -1; 1224 }; 1225 1226 es3fMultisampleTests.MaskProportionalityCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype); 1227 1228 /** Copy the constructor */ 1229 es3fMultisampleTests.MaskProportionalityCase.prototype.constructor = es3fMultisampleTests.MaskProportionalityCase; 1230 1231 /** 1232 * @enum {number} 1233 */ 1234 es3fMultisampleTests.MaskProportionalityCase.CaseType = { 1235 ALPHA_TO_COVERAGE: 0, 1236 SAMPLE_COVERAGE: 1, 1237 SAMPLE_COVERAGE_INVERTED: 2 1238 }; 1239 1240 es3fMultisampleTests.MaskProportionalityCase.prototype.init = function() { 1241 var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this); 1242 if (!inited) { 1243 return false; 1244 } 1245 1246 if (this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) { 1247 gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE); 1248 bufferedLogToConsole('gl.SAMPLE_ALPHA_TO_COVERAGE is enabled'); 1249 } 1250 else { 1251 assertMsgOptions( 1252 this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE || 1253 this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED, 1254 'CaseType should be SAMPLE_COVERAGE or SAMPLE_COVERAGE_INVERTED', false, true); 1255 1256 gl.enable(gl.SAMPLE_COVERAGE); 1257 bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled'); 1258 } 1259 1260 this.m_numIterations = Math.max(2, es3fMultisampleTests.getIterationCount(this.m_numSamples * 5)); 1261 1262 this.randomizeViewport(); // \note Using the same viewport for every iteration since coverage mask may depend on window-relative pixel coordinate. 1263 }; 1264 1265 /** 1266 * @return {tcuTestCase.IterateResult} 1267 */ 1268 es3fMultisampleTests.MaskProportionalityCase.prototype.iterate = function() { 1269 bufferedLogToConsole('Clearing color to black'); 1270 gl.colorMask(true, true, true, true); 1271 gl.clearColor(0.0, 0.0, 0.0, 1.0); 1272 gl.clear(gl.COLOR_BUFFER_BIT); 1273 1274 if (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) { 1275 gl.colorMask(true, true, true, false); 1276 bufferedLogToConsole('Using color mask TRUE, TRUE, TRUE, FALSE'); 1277 } 1278 1279 // Draw quad. 1280 1281 /** @type {Array<number>} */ var pt0 = [-1.0, -1.0]; 1282 /** @type {Array<number>} */ var pt1 = [1.0, -1.0]; 1283 /** @type {Array<number>} */ var pt2 = [-1.0, 1.0]; 1284 /** @type {Array<number>} */ var pt3 = [1.0, 1.0]; 1285 /** @type {Array<number>} */ var quadColor = [1.0, 0.0, 0.0, 1.0]; 1286 /** @type {number} */ var alphaOrCoverageValue = this.m_currentIteration / (this.m_numIterations-1); 1287 1288 if (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE) { 1289 bufferedLogToConsole('Drawing a red quad using alpha value ' + alphaOrCoverageValue); 1290 quadColor[3] = alphaOrCoverageValue; 1291 } 1292 else { 1293 assertMsgOptions( 1294 this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE || 1295 this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED, 1296 'CaseType should be SAMPLE_COVERAGE or SAMPLE_COVERAGE_INVERTED', false, true); 1297 1298 /** @type {boolean} */ var isInverted = (this.m_type === es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED); 1299 /** @type {number} */ var coverageValue = isInverted ? 1.0 - alphaOrCoverageValue : alphaOrCoverageValue; 1300 bufferedLogToConsole('Drawing a red quad using sample coverage value ' + coverageValue + (isInverted ? ' (inverted)' : '')); 1301 gl.sampleCoverage(coverageValue, isInverted ? true : false); 1302 } 1303 1304 this.renderQuad_WithColor(pt0, pt1, pt2, pt3, quadColor); 1305 1306 // Read and log image. 1307 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage(); 1308 /** @type {number} */ var numPixels = renderedImg.getWidth() * renderedImg.getHeight(); 1309 1310 tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess()); 1311 // Compute average red component in rendered image. 1312 1313 /** @type {number} */ var sumRed = 0; 1314 1315 for (var y = 0; y < renderedImg.getHeight(); y++) 1316 for (var x = 0; x < renderedImg.getWidth(); x++) 1317 sumRed += new tcuRGBA.RGBA(renderedImg.getPixel(x, y)).getRed(); 1318 1319 bufferedLogToConsole('Average red color component: ' + (sumRed / 255.0 / numPixels)); 1320 1321 // Check if average color has decreased from previous frame's color. 1322 1323 if (sumRed < this.m_previousIterationColorSum) { 1324 bufferedLogToConsole('Failure: Current average red color component is lower than previous'); 1325 testFailedOptions('Failed', false); 1326 return tcuTestCase.IterateResult.STOP; 1327 } 1328 1329 // Check if coverage mask is not all-zeros if alpha or coverage value is 0 (or 1, if inverted). 1330 1331 if (this.m_currentIteration == 0 && sumRed != 0) 1332 { 1333 bufferedLogToConsole('Failure: Image should be completely black'); 1334 testFailedOptions('Failed', false); 1335 return tcuTestCase.IterateResult.STOP; 1336 } 1337 1338 if (this.m_currentIteration == this.m_numIterations-1 && sumRed != 0xff*numPixels) 1339 { 1340 bufferedLogToConsole('Failure: Image should be completely red'); 1341 1342 testFailedOptions('Failed', false); 1343 return tcuTestCase.IterateResult.STOP; 1344 } 1345 1346 this.m_previousIterationColorSum = sumRed; 1347 1348 this.m_currentIteration++; 1349 1350 if (this.m_currentIteration >= this.m_numIterations) 1351 { 1352 bufferedLogToConsole('Success: Number of coverage mask bits set appears to be, on average, proportional to ' + 1353 (this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE ? 'alpha' : 1354 this.m_type == es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE ? 'sample coverage value' : 1355 'inverted sample coverage value')); 1356 1357 testPassedOptions('Passed', true); 1358 return tcuTestCase.IterateResult.STOP; 1359 } 1360 else 1361 return tcuTestCase.IterateResult.CONTINUE; 1362 }; 1363 1364 /** 1365 * Tests coverage mask generation constancy property. 1366 * 1367 * Tests that the coverage mask created by gl.SAMPLE_ALPHA_TO_COVERAGE or 1368 * gl.SAMPLE_COVERAGE is constant at given pixel coordinates, with a given 1369 * alpha component or coverage value, respectively. Draws two quads, with 1370 * the second one fully overlapping the first one such that at any given 1371 * pixel, both quads have the same alpha or coverage value. This way, if 1372 * the constancy property is fulfilled, only the second quad should be 1373 * visible. 1374 * @extends {es3fMultisampleTests.MultisampleCase} 1375 * @constructor 1376 * @param {string} name 1377 * @param {string} desc 1378 * @param {es3fMultisampleTests.MaskConstancyCase.CaseType} type 1379 * @param {number=} numFboSamples 1380 */ 1381 es3fMultisampleTests.MaskConstancyCase = function(name, desc, type, numFboSamples) { 1382 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 1383 /** @type {es3fMultisampleTests.FboParams} */ 1384 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams(); 1385 es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params); 1386 var CaseType = es3fMultisampleTests.MaskConstancyCase.CaseType; 1387 /** @type {boolean} */ this.m_isAlphaToCoverageCase = (type === CaseType.ALPHA_TO_COVERAGE || type === CaseType.BOTH || type === CaseType.BOTH_INVERTED); 1388 /** @type {boolean} */ this.m_isSampleCoverageCase = (type === CaseType.SAMPLE_COVERAGE || type === CaseType.SAMPLE_COVERAGE_INVERTED || type === CaseType.BOTH || type === CaseType.BOTH_INVERTED); 1389 /** @type {boolean} */ this.m_isInvertedSampleCoverageCase = (type === CaseType.SAMPLE_COVERAGE_INVERTED || type === CaseType.BOTH_INVERTED); 1390 }; 1391 1392 es3fMultisampleTests.MaskConstancyCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype); 1393 1394 /** Copy the constructor */ 1395 es3fMultisampleTests.MaskConstancyCase.prototype.constructor = es3fMultisampleTests.MaskConstancyCase; 1396 1397 /** 1398 * @enum {number} 1399 */ 1400 es3fMultisampleTests.MaskConstancyCase.CaseType = { 1401 ALPHA_TO_COVERAGE: 0, //!< Use only alpha-to-coverage. 1402 SAMPLE_COVERAGE: 1, //!< Use only sample coverage. 1403 SAMPLE_COVERAGE_INVERTED: 2, //!< Use only inverted sample coverage. 1404 BOTH: 3, //!< Use both alpha-to-coverage and sample coverage. 1405 BOTH_INVERTED: 4 //!< Use both alpha-to-coverage and inverted sample coverage. 1406 }; 1407 1408 /** 1409 * @return {tcuTestCase.IterateResult} 1410 */ 1411 es3fMultisampleTests.MaskConstancyCase.prototype.iterate = function() { 1412 this.randomizeViewport(); 1413 1414 bufferedLogToConsole('Clearing color to black'); 1415 gl.clearColor(0.0, 0.0, 0.0, 1.0); 1416 gl.clear(gl.COLOR_BUFFER_BIT); 1417 1418 if (this.m_isAlphaToCoverageCase) { 1419 gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE); 1420 gl.colorMask(true, true, true, false); 1421 bufferedLogToConsole('gl.SAMPLE_ALPHA_TO_COVERAGE is enabled'); 1422 bufferedLogToConsole('Color mask is TRUE, TRUE, TRUE, FALSE'); 1423 } 1424 1425 if (this.m_isSampleCoverageCase) { 1426 gl.enable(gl.SAMPLE_COVERAGE); 1427 bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled'); 1428 } 1429 1430 bufferedLogToConsole('Drawing several green quads, each fully overlapped by a red quad with the same ' + 1431 (this.m_isAlphaToCoverageCase ? 'alpha' : '') + 1432 (this.m_isAlphaToCoverageCase && this.m_isSampleCoverageCase ? ' and ' : '') + 1433 (this.m_isInvertedSampleCoverageCase ? 'inverted ' : '') + 1434 (this.m_isSampleCoverageCase ? 'sample coverage' : '') + 1435 ' values'); 1436 1437 /** @type {number} */ var numQuadRowsCols = this.m_numSamples * 4; 1438 1439 for (var row = 0; row < numQuadRowsCols; row++) { 1440 for (var col = 0; col < numQuadRowsCols; col++) { 1441 /** @type {number} */ var x0 = (col + 0) / numQuadRowsCols * 2.0 - 1.0; 1442 /** @type {number} */ var x1 = (col + 1) / numQuadRowsCols * 2.0 - 1.0; 1443 /** @type {number} */ var y0 = (row + 0) / numQuadRowsCols * 2.0 - 1.0; 1444 /** @type {number} */ var y1 = (row + 1) / numQuadRowsCols * 2.0 - 1.0; 1445 /** @type {Array<number>} */ var baseGreen = [0.0, 1.0, 0.0, 0.0]; 1446 /** @type {Array<number>} */ var baseRed = [1.0, 0.0, 0.0, 0.0]; 1447 /** @type {Array<number>} */ var alpha0 = [0.0, 0.0, 0.0, this.m_isAlphaToCoverageCase ? col / (numQuadRowsCols - 1) : 1.0]; 1448 /** @type {Array<number>} */ var alpha1 = [0.0, 0.0, 0.0, this.m_isAlphaToCoverageCase ? row / (numQuadRowsCols - 1) : 1.0]; 1449 1450 if (this.m_isSampleCoverageCase) { 1451 /** @type {number} */ var value = (row*numQuadRowsCols + col) / (numQuadRowsCols*numQuadRowsCols - 1); 1452 gl.sampleCoverage(this.m_isInvertedSampleCoverageCase ? 1.0 - value : value, this.m_isInvertedSampleCoverageCase ? true : false); 1453 } 1454 1455 this.renderQuad([x0, y0], [x1, y0], [x0, y1], [x1, y1], 1456 deMath.add(baseGreen, alpha0), deMath.add(baseGreen, alpha1), 1457 deMath.add(baseGreen, alpha0), deMath.add(baseGreen, alpha1)); 1458 this.renderQuad([x0, y0], [x1, y0], [x0, y1], [x1, y1], 1459 deMath.add(baseRed, alpha0), deMath.add(baseRed, alpha1), 1460 deMath.add(baseRed, alpha0), deMath.add(baseRed, alpha1)); 1461 } 1462 } 1463 1464 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage(); 1465 1466 tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess()); 1467 for (var y = 0; y < renderedImg.getHeight(); y++) 1468 for (var x = 0; x < renderedImg.getWidth(); x++) { 1469 if (new tcuRGBA.RGBA(renderedImg.getPixel(x, y)).getGreen() > 0) { 1470 bufferedLogToConsole('Failure: Non-zero green color component detected - should have been completely overwritten by red quad'); 1471 testFailedOptions('Failed', false); 1472 return tcuTestCase.IterateResult.STOP; 1473 } 1474 } 1475 1476 bufferedLogToConsole('Success: Coverage mask appears to be constant at a given pixel coordinate with a given ' + 1477 (this.m_isAlphaToCoverageCase ? 'alpha' : '') + 1478 (this.m_isAlphaToCoverageCase && this.m_isSampleCoverageCase ? ' and ' : '') + 1479 (this.m_isSampleCoverageCase ? 'coverage value' : '')); 1480 1481 testPassedOptions('Passed', true); 1482 1483 return tcuTestCase.IterateResult.STOP; 1484 } 1485 1486 1487 /** 1488 * Tests coverage mask inversion validity. 1489 * 1490 * Tests that the coverage masks obtained by glSampleCoverage(..., true) 1491 * and glSampleCoverage(..., false) are indeed each others' inverses. 1492 * This is done by drawing a pattern, with varying coverage values, 1493 * overlapped by a pattern that has inverted masks and is otherwise 1494 * identical. The resulting image is compared to one obtained by drawing 1495 * the same pattern but with all-ones coverage masks. 1496 * @extends {es3fMultisampleTests.MultisampleCase} 1497 * @constructor 1498 * @param {string} name 1499 * @param {string} desc 1500 * @param {number=} numFboSamples 1501 */ 1502 es3fMultisampleTests.CoverageMaskInvertCase = function(name, desc, numFboSamples) { 1503 numFboSamples = numFboSamples === undefined ? 0 : numFboSamples; 1504 /** @type {es3fMultisampleTests.FboParams} */ 1505 var params = numFboSamples >= 0 ? new es3fMultisampleTests.FboParams(numFboSamples, false, false) : new es3fMultisampleTests.FboParams(); 1506 es3fMultisampleTests.MultisampleCase.call(this, name, desc, 256, params); 1507 }; 1508 1509 es3fMultisampleTests.CoverageMaskInvertCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype); 1510 1511 /** Copy the constructor */ 1512 es3fMultisampleTests.CoverageMaskInvertCase.prototype.constructor = es3fMultisampleTests.CoverageMaskInvertCase; 1513 1514 /** 1515 * @param {boolean} invertSampleCoverage 1516 */ 1517 es3fMultisampleTests.CoverageMaskInvertCase.prototype.drawPattern = function(invertSampleCoverage) { 1518 /** @type {number} */ var numTriangles = 25; 1519 for (var i = 0; i < numTriangles; i++) { 1520 gl.sampleCoverage(i / (numTriangles - 1), invertSampleCoverage ? true : false); 1521 1522 /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles; 1523 /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles; 1524 1525 this.renderTriangle_pAsVec2WithColor( 1526 [0.0, 0.0], 1527 [Math.cos(angle0) * 0.95, Math.sin(angle0) * 0.95], 1528 [Math.cos(angle1) * 0.95, Math.sin(angle1) * 0.95], 1529 [0.4 + i / numTriangles * 0.6, 1530 0.5 + i / numTriangles * 0.3, 1531 0.6 - i / numTriangles * 0.5, 1532 0.7 - i / numTriangles * 0.7]); 1533 } 1534 }; 1535 1536 /** 1537 * @return {tcuTestCase.IterateResult} 1538 */ 1539 es3fMultisampleTests.CoverageMaskInvertCase.prototype.iterate = function() { 1540 this.randomizeViewport(); 1541 1542 gl.enable(gl.BLEND); 1543 gl.blendEquation(gl.FUNC_ADD); 1544 gl.blendFunc(gl.ONE, gl.ONE); 1545 bufferedLogToConsole('Additive blending enabled in order to detect (erroneously) overlapping samples'); 1546 1547 bufferedLogToConsole('Clearing color to all-zeros'); 1548 gl.clearColor(0.0, 0.0, 0.0, 0.0); 1549 gl.clear(gl.COLOR_BUFFER_BIT); 1550 bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE disabled'); 1551 this.drawPattern(false); 1552 /** @type {tcuSurface.Surface} */ var renderedImgNoSampleCoverage = this.readImage(); 1553 1554 tcuLogImage.logImage('RenderedImageNoSampleCoverage', 'Rendered image with gl.SAMPLE_COVERAGE disabled', renderedImgNoSampleCoverage.getAccess()); 1555 bufferedLogToConsole('Clearing color to all-zeros'); 1556 gl.clear(gl.COLOR_BUFFER_BIT); 1557 gl.enable(gl.SAMPLE_COVERAGE); 1558 bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE enabled, using non-inverted masks'); 1559 this.drawPattern(false); 1560 bufferedLogToConsole('Drawing the pattern with gl.SAMPLE_COVERAGE enabled, using same sample coverage values but inverted masks'); 1561 this.drawPattern(true); 1562 /** @type {tcuSurface.Surface} */ var renderedImgSampleCoverage = this.readImage(); 1563 1564 tcuLogImage.logImage('RenderedImageSampleCoverage', 'Rendered image with gl.SAMPLE_COVERAGE enabled', renderedImgSampleCoverage.getAccess()); 1565 /** @type {boolean} */ var passed = tcuImageCompare.pixelThresholdCompare( 1566 'CoverageVsNoCoverage', 1567 'Comparison of same pattern with gl.SAMPLE_COVERAGE disabled and enabled', 1568 renderedImgNoSampleCoverage, 1569 renderedImgSampleCoverage, 1570 [0, 0, 0, 0]); 1571 1572 if (passed) { 1573 bufferedLogToConsole('Success: The two images rendered are identical'); 1574 testPassedOptions('Passed', true); 1575 } 1576 else { 1577 testFailedOptions('Failed', false); 1578 } 1579 1580 return tcuTestCase.IterateResult.STOP; 1581 }; 1582 1583 es3fMultisampleTests.init = function() { 1584 var testGroup = tcuTestCase.runner.testCases; 1585 /** 1586 * @enum {number} 1587 */ 1588 var CaseType = { 1589 DEFAULT_FRAMEBUFFER: 0, 1590 FBO_4_SAMPLES: 1, 1591 FBO_8_SAMPLES: 2, 1592 FBO_MAX_SAMPLES: 3 1593 }; 1594 1595 for (var caseTypeI in CaseType) { 1596 /** @type {CaseType} */ var caseType = CaseType[caseTypeI]; 1597 /** @type {number} */ 1598 var numFboSamples = caseType === CaseType.DEFAULT_FRAMEBUFFER ? -1 : 1599 caseType === CaseType.FBO_4_SAMPLES ? 4 : 1600 caseType === CaseType.FBO_8_SAMPLES ? 8 : 1601 caseType === CaseType.FBO_MAX_SAMPLES ? 0 : 1602 -2; 1603 1604 /** @type {?string} */ 1605 var name = caseType === CaseType.DEFAULT_FRAMEBUFFER ? 'default_framebuffer' : 1606 caseType === CaseType.FBO_4_SAMPLES ? 'fbo_4_samples' : 1607 caseType === CaseType.FBO_8_SAMPLES ? 'fbo_8_samples' : 1608 caseType === CaseType.FBO_MAX_SAMPLES ? 'fbo_max_samples' : 1609 null; 1610 /** @type {?string} */ 1611 var desc = caseType === CaseType.DEFAULT_FRAMEBUFFER ? 'Render into default framebuffer' : 1612 caseType === CaseType.FBO_4_SAMPLES ? 'Render into a framebuffer object with 4 samples' : 1613 caseType === CaseType.FBO_8_SAMPLES ? 'Render into a framebuffer object with 8 samples' : 1614 caseType === CaseType.FBO_MAX_SAMPLES ? 'Render into a framebuffer object with the maximum number of samples' : 1615 null; 1616 1617 /** @type {tcuTestCase.DeqpTest} */ var group = tcuTestCase.newTest(name, desc); 1618 1619 assertMsgOptions(group.name != null, 'Error: No Test Name', false, true); 1620 assertMsgOptions(group.description != null, 'Error: No Test Description', false, true); 1621 assertMsgOptions(numFboSamples >= -1, 'Assert Failed: numFboSamples >= -1', false, true); 1622 testGroup.addChild(group); 1623 1624 group.addChild(new es3fMultisampleTests.PolygonNumSamplesCase( 1625 'num_samples_polygon', 1626 'Test sanity of the sample count, with polygons', 1627 numFboSamples)); 1628 1629 group.addChild(new es3fMultisampleTests.LineNumSamplesCase( 1630 'num_samples_line', 1631 'Test sanity of the sample count, with lines', 1632 numFboSamples)); 1633 1634 group.addChild(new es3fMultisampleTests.CommonEdgeCase( 1635 'common_edge_small_quads', 1636 'Test polygons\'s common edges with small quads', 1637 es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS, 1638 numFboSamples)); 1639 1640 group.addChild(new es3fMultisampleTests.CommonEdgeCase( 1641 'common_edge_big_quad', 1642 'Test polygon\'s common edges with bigger-than-viewport quads', 1643 es3fMultisampleTests.CommonEdgeCase.CaseType.BIGGER_THAN_VIEWPORT_QUAD, 1644 numFboSamples)); 1645 1646 group.addChild(new es3fMultisampleTests.CommonEdgeCase( 1647 'common_edge_viewport_quad', 1648 'Test polygons\' common edges with exactly viewport-sized quads', 1649 es3fMultisampleTests.CommonEdgeCase.CaseType.FIT_VIEWPORT_QUAD, 1650 numFboSamples)); 1651 1652 group.addChild(new es3fMultisampleTests.SampleDepthCase( 1653 'depth', 1654 'Test that depth values are per-sample', 1655 numFboSamples)); 1656 1657 group.addChild(new es3fMultisampleTests.SampleStencilCase( 1658 'stencil', 1659 'Test that stencil values are per-sample', 1660 numFboSamples)); 1661 1662 group.addChild(new es3fMultisampleTests.CoverageMaskInvertCase( 1663 'sample_coverage_invert', 1664 'Test that non-inverted and inverted sample coverage masks are each other\'s negations', 1665 numFboSamples)); 1666 1667 1668 group.addChild(new es3fMultisampleTests.MaskProportionalityCase( 1669 'proportionality_alpha_to_coverage', 1670 'Test the proportionality property of GL_SAMPLE_ALPHA_TO_COVERAGE', 1671 es3fMultisampleTests.MaskProportionalityCase.CaseType.ALPHA_TO_COVERAGE, 1672 numFboSamples)); 1673 group.addChild(new es3fMultisampleTests.MaskProportionalityCase( 1674 'proportionality_sample_coverage', 1675 'Test the proportionality property of GL_SAMPLE_COVERAGE', 1676 es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE, 1677 numFboSamples)); 1678 group.addChild(new es3fMultisampleTests.MaskProportionalityCase( 1679 'proportionality_sample_coverage_inverted', 1680 'Test the proportionality property of inverted-mask GL_SAMPLE_COVERAGE', 1681 es3fMultisampleTests.MaskProportionalityCase.CaseType.SAMPLE_COVERAGE_INVERTED, 1682 numFboSamples)); 1683 1684 group.addChild(new es3fMultisampleTests.MaskConstancyCase( 1685 'constancy_alpha_to_coverage', 1686 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE', 1687 es3fMultisampleTests.MaskConstancyCase.CaseType.ALPHA_TO_COVERAGE, 1688 numFboSamples)); 1689 group.addChild(new es3fMultisampleTests.MaskConstancyCase( 1690 'constancy_sample_coverage', 1691 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_COVERAGE', 1692 es3fMultisampleTests.MaskConstancyCase.CaseType.SAMPLE_COVERAGE, 1693 numFboSamples)); 1694 group.addChild(new es3fMultisampleTests.MaskConstancyCase( 1695 'constancy_sample_coverage_inverted', 1696 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using inverted-mask GL_SAMPLE_COVERAGE', 1697 es3fMultisampleTests.MaskConstancyCase.CaseType.SAMPLE_COVERAGE_INVERTED, 1698 numFboSamples)); 1699 group.addChild(new es3fMultisampleTests.MaskConstancyCase( 1700 'constancy_both', 1701 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and GL_SAMPLE_COVERAGE', 1702 es3fMultisampleTests.MaskConstancyCase.CaseType.BOTH, 1703 numFboSamples)); 1704 group.addChild(new es3fMultisampleTests.MaskConstancyCase( 1705 'constancy_both_inverted', 1706 'Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and inverted-mask GL_SAMPLE_COVERAGE', 1707 es3fMultisampleTests.MaskConstancyCase.CaseType.BOTH_INVERTED, 1708 numFboSamples)); 1709 } 1710 }; 1711 1712 /** 1713 * Run test 1714 * @param {WebGL2RenderingContext} context 1715 */ 1716 es3fMultisampleTests.run = function(context, range) { 1717 gl = context; 1718 //Set up Test Root parameters 1719 var testName = 'multisample'; 1720 var testDescription = 'Multisample Tests'; 1721 var state = tcuTestCase.runner; 1722 1723 state.testName = testName; 1724 state.setRoot(tcuTestCase.newTest(testName, testDescription, null)); 1725 1726 //Set up name and description of this test series. 1727 setCurrentTestName(testName); 1728 description(testDescription); 1729 1730 try { 1731 //Create test cases 1732 es3fMultisampleTests.init(); 1733 if (range) 1734 state.setRange(range); 1735 //Run test cases 1736 tcuTestCase.runTestCases(); 1737 } 1738 catch (err) { 1739 testFailedOptions('Failed to es3fMultisampleTests.run tests', false); 1740 tcuTestCase.runner.terminate(); 1741 } 1742 }; 1743 1744 });