1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
3 * ------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
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');
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;
57 * @param {Array<number>} p0_
58 * @param {Array<number>} p1_
59 * @param {Array<number>} p2_
60 * @param {Array<number>} p3_
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_;
70 * @param {number} defaultCount
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
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
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]);
93 return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0);
97 * Check if a region in an image is unicolored.
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.
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
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));
122 if (insideEncountered)
123 if (!tcuRGBA.compareThreshold(pixColor, insideColor, threshold)) // Pixel color differs from already-detected color inside same region - region not unicolored.
126 insideEncountered = true;
127 insideColor = pixColor;
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
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);
164 * @param {number=} numSamples_
165 * @param {boolean=} useDepth_
166 * @param {boolean=} useStencil_
168 es3fMultisampleTests.FboParams = function(numSamples_, useDepth_, useStencil_) {
169 /** @type {boolean} */ var useFbo_ = true;
170 if (numSamples_ === undefined && useDepth_ === undefined && useStencil_ === undefined)
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_;
181 * @extends {tcuTestCase.DeqpTest}
182 * @param {string} name
183 * @param {string} desc
184 * @param {number} desiredViewportSize
185 * @param {es3fMultisampleTests.FboParams} fboParams
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);
210 es3fMultisampleTests.MultisampleCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
212 /** Copy the constructor */
213 es3fMultisampleTests.MultisampleCase.prototype.constructor = es3fMultisampleTests.MultisampleCase;
216 es3fMultisampleTests.MultisampleCase.prototype.deinit = function() {
217 gl.colorMask(true, true, true, true);
220 gl.clearColor(0.0, 0.0, 0.0, 0.0);
224 gl.disable(gl.STENCIL_TEST);
225 gl.disable(gl.DEPTH_TEST);
227 gl.disable(gl.SAMPLE_COVERAGE);
228 gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
230 if (this.m_program) {
231 gl.deleteProgram(this.m_program.getProgram());
232 this.m_program = null;
234 if (this.m_msColorRbo) {
235 gl.deleteRenderbuffer(this.m_msColorRbo);
236 this.m_msColorRbo = null;
238 if (this.m_msDepthStencilRbo) {
239 gl.deleteRenderbuffer(this.m_msDepthStencilRbo);
240 this.m_msDepthStencilRbo = null;
242 if (this.m_resolveColorRbo) {
243 gl.deleteRenderbuffer(this.m_resolveColorRbo);
244 this.m_resolveColorRbo = null;
248 gl.deleteFramebuffer(this.m_msFbo);
251 if (this.m_resolveFbo) {
252 gl.deleteFramebuffer(this.m_resolveFbo);
253 this.m_resolveFbo = null;
256 gl.bindRenderbuffer(gl.RENDERBUFFER, null);
257 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
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
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
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]
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);
286 gl.enableVertexAttribArray(this.m_attrPositionLoc);
287 gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0);
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);
294 gl.enableVertexAttribArray(this.m_attrColorLoc);
295 gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0);
297 gl.useProgram(this.m_program.getProgram());
298 gl.drawArrays(gl.TRIANGLES, 0, 3);
300 gl.bindBuffer(gl.ARRAY_BUFFER, null);
301 gl.deleteBuffer(colGLBuffer);
302 gl.deleteBuffer(posGLBuffer);
307 * @param {Array<number>} p0
308 * @param {Array<number>} p1
309 * @param {Array<number>} p2
310 * @param {Array<number>} color
312 es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec3WithColor = function(p0, p1, p2, color) {
313 this.renderTriangle_pAsVec3cAsVec4(p0, p1, p2, color, color, color);
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
325 es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2 = function(p0, p1, p2, c0, c1, c2) {
326 this.renderTriangle_pAsVec3cAsVec4(
335 * @param {Array<number>} p0
336 * @param {Array<number>} p1
337 * @param {Array<number>} p2
338 * @param {Array<number>} color
340 es3fMultisampleTests.MultisampleCase.prototype.renderTriangle_pAsVec2WithColor = function(p0, p1, p2, color) {
341 this.renderTriangle_pAsVec2(p0, p1, p2, color, color, color);
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
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);
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
368 es3fMultisampleTests.MultisampleCase.prototype.renderQuad_WithColor = function(p0, p1, p2, p3, color) {
369 this.renderQuad(p0, p1, p2, p3, color, color, color, color);
374 * @param {Array<number>} p0
375 * @param {Array<number>} p1
376 * @param {Array<number>} color
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
383 /** @type {Array<number>} */ var vertexColors = [
384 color[0], color[1], color[2], color[3],
385 color[0], color[1], color[2], color[3]
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);
393 gl.enableVertexAttribArray(this.m_attrPositionLoc);
394 gl.vertexAttribPointer(this.m_attrPositionLoc, 4, gl.FLOAT, false, 0, 0);
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);
401 gl.enableVertexAttribArray(this.m_attrColorLoc);
402 gl.vertexAttribPointer(this.m_attrColorLoc, 4, gl.FLOAT, false, 0, 0);
404 gl.useProgram(this.m_program.getProgram());
405 gl.drawArrays(gl.LINES, 0, 2);
407 gl.bindBuffer(gl.ARRAY_BUFFER, null);
408 gl.deleteBuffer(colGLBuffer);
409 gl.deleteBuffer(posGLBuffer);
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);
419 gl.viewport(this.m_viewportX, this.m_viewportY, this.m_viewportSize, this.m_viewportSize);
424 * @return {tcuSurface.Surface}
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);
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());
442 gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
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());
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' +
459 ' gl_Position = a_position;\n' +
460 ' v_color = a_color;\n' +
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' +
469 ' o_color = v_color;\n' +
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);
479 if (this.m_fboParams.useFbo) {
480 if (this.m_fboParams.numSamples > 0)
481 this.m_numSamples = this.m_fboParams.numSamples;
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);
490 this.m_numSamples = supportedSampleCountArray[0];
493 bufferedLogToConsole('Using FBO of size (' + this.m_renderWidth + ', ' + this.m_renderHeight + ') with ' + this.m_numSamples + ' samples');
496 // Query and log number of samples per pixel.
497 this.m_numSamples = numSamples;
498 bufferedLogToConsole('gl.SAMPLES =' + this.m_numSamples);
503 assertMsgOptions(!this.m_program, 'Program loaded when it should not be.', false, true);
505 this.m_program = new gluShaderProgram.ShaderProgram(
507 gluShaderProgram.makeVtxFragSources(vertShaderSource, fragShaderSource));
509 if (!this.m_program.isOk())
510 throw new Error('Failed to compile program');
512 this.m_attrPositionLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_position');
513 this.m_attrColorLoc = gl.getAttribLocation(this.m_program.getProgram(), 'a_color');
515 if (this.m_attrPositionLoc < 0 || this.m_attrColorLoc < 0) {
516 this.m_program = null;
517 throw new Error('Invalid attribute locations');
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);
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);
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');
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);
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);
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);
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);
561 gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
564 // Get suitable viewport size.
566 this.m_viewportSize = Math.min(this.m_desiredViewportSize, this.m_renderWidth, this.m_renderHeight);
567 this.randomizeViewport();
572 * Base class for cases testing the value of sample count.
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.
581 * @extends {es3fMultisampleTests.MultisampleCase}
583 * @param {string} name
584 * @param {string} desc
585 * @param {es3fMultisampleTests.FboParams} fboParams
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 = [];
595 es3fMultisampleTests.NumSamplesCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
597 /** Copy the constructor */
598 es3fMultisampleTests.NumSamplesCase.prototype.constructor = es3fMultisampleTests.NumSamplesCase;
601 * @return {tcuTestCase.IterateResult}
603 es3fMultisampleTests.NumSamplesCase.prototype.iterate = function() {
604 this.randomizeViewport();
606 gl.clearColor(0.0, 0.0, 0.0, 1.0);
607 gl.clear(gl.COLOR_BUFFER_BIT);
609 this.renderPattern();
611 // Read and log rendered image.
613 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
614 tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
616 // Detect new, previously unseen colors from image.
618 /** @type {number} */ var requiredNumDistinctColors = this.m_numSamples + 1;
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);
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));
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))
634 if (i === this.m_detectedColors.length)
635 this.m_detectedColors.push(color); // Color not previously detected.
640 bufferedLogToConsole('Number of distinct colors detected so far: ' + (this.m_detectedColors.length >= requiredNumDistinctColors ? 'at least ' : '') + this.m_detectedColors.length);
643 if (this.m_detectedColors.length < requiredNumDistinctColors) {
644 // Haven't detected enough different colors yet.
646 this.m_currentIteration++;
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;
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;
658 testPassedOptions('Success: The number of distinct colors detected is at least sample count+1', true);
659 return tcuTestCase.IterateResult.STOP;
664 * @extends {es3fMultisampleTests.NumSamplesCase}
666 * @param {string} name
667 * @param {string} desc
668 * @param {number=} numFboSamples
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);
677 es3fMultisampleTests.PolygonNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
679 /** Copy the constructor */
680 es3fMultisampleTests.PolygonNumSamplesCase.prototype.constructor = es3fMultisampleTests.PolygonNumSamplesCase;
682 es3fMultisampleTests.PolygonNumSamplesCase.prototype.renderPattern = function() {
683 // The test pattern consists of several triangles with edges at different angles.
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;
690 this.renderTriangle_pAsVec2WithColor(
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]);
699 * @extends {es3fMultisampleTests.NumSamplesCase}
701 * @param {string} name
702 * @param {string} desc
703 * @param {number=} numFboSamples
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);
712 es3fMultisampleTests.LineNumSamplesCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
714 /** Copy the constructor */
715 es3fMultisampleTests.LineNumSamplesCase.prototype.constructor = es3fMultisampleTests.LineNumSamplesCase;
717 es3fMultisampleTests.LineNumSamplesCase.prototype.renderPattern = function() {
718 // The test pattern consists of several lines at different angles.
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));
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]);
733 * Case testing behaviour of common edges when multisampling.
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.
741 * @extends {es3fMultisampleTests.MultisampleCase}
743 * @param {string} name
744 * @param {string} desc
745 * @param {es3fMultisampleTests.CommonEdgeCase.CaseType} caseType
746 * @param {number} numFboSamples
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();
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) :
765 es3fMultisampleTests.CommonEdgeCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
767 /** Copy the constructor */
768 es3fMultisampleTests.CommonEdgeCase.prototype.constructor = es3fMultisampleTests.CommonEdgeCase;
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.
779 es3fMultisampleTests.CommonEdgeCase.prototype.init = function() {
780 var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
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.
788 /** @type {number} */ var minViewportSize = 32;
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 + ')');
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');
801 * @return {tcuTestCase.IterateResult}
803 es3fMultisampleTests.CommonEdgeCase.prototype.iterate = function() {
804 /** @type {tcuSurface.Surface} */ var errorImg = new tcuSurface.Surface(this.m_viewportSize, this.m_viewportSize);
806 this.randomizeViewport();
808 gl.clearColor(0.0, 0.0, 0.0, 1.0);
809 gl.clear(gl.COLOR_BUFFER_BIT);
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.
815 /** @type {Array<es3fMultisampleTests.QuadCorners>} */ var unicoloredRegions = [];
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;
826 if (this.m_caseType == es3fMultisampleTests.CommonEdgeCase.CaseType.SMALL_QUADS) {
827 // Draw several quads, rotated at different angles.
829 quadDiagLen = 2.0 / 3.0 * 0.9; // \note Fit 3 quads in both x and y directions.
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.
834 if (this.m_currentIteration === 0) {
838 else if (this.m_currentIteration === 1) {
839 angleCos = Math.SQRT1_2;
840 angleSin = Math.SQRT1_2;
843 angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1);
844 angleCos = Math.cos(angle);
845 angleSin = Math.sin(angle);
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)
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.
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)));
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]);
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]);
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]);
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))));
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.
901 quadBaseAngleNdx = Math.floor(this.m_currentIteration / 8);
902 quadSubAngleNdx = this.m_currentIteration % 8;
904 if (quadBaseAngleNdx === 0) {
908 else if (quadBaseAngleNdx === 1) {
909 angleCos = Math.SQRT1_2;
910 angleSin = Math.SQRT1_2;
913 angle = 0.5 * Math.PI * (this.m_currentIteration - 1) / (this.m_numIterations - 1);
914 angleCos = Math.cos(angle);
915 angleSin = Math.sin(angle);
918 quadDiagLen = 2.5 / Math.max(angleCos, angleSin);
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)
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]);
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]);
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]);
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)));
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.
959 quadSubAngleNdx = this.m_currentIteration % 8;
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]);
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]);
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]);
989 unicoloredRegions.push(new es3fMultisampleTests.QuadCorners(corners[0], corners[1], corners[2], corners[3]));
992 throw new Error('CaseType not supported.');
994 // Read pixels and check unicolored regions.
996 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
998 errorImg.getAccess().clear([0.0, 1.0, 0.0, 1.0]);
999 tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
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);
1010 if (errorsInCurrentRegion)
1011 es3fMultisampleTests.drawUnicolorTestErrors(renderedImg, errorImg.getAccess(), p0Win, p1Win, p2Win, p3Win);
1013 errorsDetected = errorsDetected || errorsInCurrentRegion;
1016 this.m_currentIteration++;
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;
1026 else if (this.m_currentIteration < this.m_numIterations) {
1027 bufferedLogToConsole('Quads seem OK - moving on to next pattern');
1028 return tcuTestCase.IterateResult.CONTINUE;
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;
1038 * Test that depth values are per-sample.
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
1045 * @extends {es3fMultisampleTests.NumSamplesCase}
1047 * @param {string} name
1048 * @param {string} desc
1049 * @param {number=} numFboSamples
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);
1058 es3fMultisampleTests.SampleDepthCase.prototype = Object.create(es3fMultisampleTests.NumSamplesCase.prototype);
1060 /** Copy the constructor */
1061 es3fMultisampleTests.SampleDepthCase.prototype.constructor = es3fMultisampleTests.SampleDepthCase;
1063 es3fMultisampleTests.SampleDepthCase.prototype.init = function() {
1064 var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
1069 gl.enable(gl.DEPTH_TEST);
1070 gl.depthFunc(gl.LESS);
1072 bufferedLogToConsole('Depth test enabled, depth func is gl.LESS');
1073 bufferedLogToConsole('Drawing several bigger-than-viewport black or white polygons intersecting each other');
1076 es3fMultisampleTests.SampleDepthCase.prototype.renderPattern = function() {
1077 gl.clearColor(0.0, 0.0, 0.0, 0.0);
1079 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
1081 /** @type {number} */ var numPolygons = 50;
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];
1090 this.renderTriangle_pAsVec3WithColor(pt0, pt1, pt2, color);
1095 * Test that stencil buffer values are per-sample.
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.
1102 * @extends {es3fMultisampleTests.MultisampleCase}
1104 * @param {string} name
1105 * @param {string} desc
1106 * @param {number=} numFboSamples
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);
1115 es3fMultisampleTests.SampleStencilCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1117 /** Copy the constructor */
1118 es3fMultisampleTests.SampleStencilCase.prototype.constructor = es3fMultisampleTests.SampleStencilCase;
1121 * @return {tcuTestCase.IterateResult}
1123 es3fMultisampleTests.SampleStencilCase.prototype.iterate = function() {
1124 this.randomizeViewport();
1126 gl.clearColor(0.0, 0.0, 0.0, 1.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);
1133 bufferedLogToConsole('Drawing a pattern with gl.stencilFunc(gl.ALWAYS, 1, 1) and gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)');
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;
1140 this.renderTriangle_pAsVec2WithColor(
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]);
1147 /** @type {tcuSurface.Surface} */ var renderedImgFirst = this.readImage();
1148 tcuLogImage.logImage('RenderedImgFirst', 'First image rendered', renderedImgFirst.getAccess());
1149 bufferedLogToConsole('Clearing color buffer to black');
1151 gl.clear(gl.COLOR_BUFFER_BIT);
1152 gl.stencilFunc(gl.EQUAL, 1, 1);
1153 gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
1155 bufferedLogToConsole('Checking that color buffer was actually cleared to black');
1157 /** @type {tcuSurface.Surface} */ var clearedImg = this.readImage();
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;
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');
1172 this.renderQuad_WithColor(
1177 [1.0, 1.0, 1.0, 1.0]);
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(
1190 bufferedLogToConsole('Success: The two images rendered are identical');
1191 testPassedOptions('Passed', true);
1194 testFailedOptions('Failed', false);
1196 return tcuTestCase.IterateResult.STOP;
1200 * Tests coverage mask generation proportionality property.
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.
1208 * @extends {es3fMultisampleTests.MultisampleCase}
1210 * @param {string} name
1211 * @param {string} desc
1212 * @param {es3fMultisampleTests.MaskProportionalityCase.CaseType} type
1213 * @param {number=} numFboSamples
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;
1226 es3fMultisampleTests.MaskProportionalityCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1228 /** Copy the constructor */
1229 es3fMultisampleTests.MaskProportionalityCase.prototype.constructor = es3fMultisampleTests.MaskProportionalityCase;
1234 es3fMultisampleTests.MaskProportionalityCase.CaseType = {
1235 ALPHA_TO_COVERAGE: 0,
1237 SAMPLE_COVERAGE_INVERTED: 2
1240 es3fMultisampleTests.MaskProportionalityCase.prototype.init = function() {
1241 var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
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');
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);
1256 gl.enable(gl.SAMPLE_COVERAGE);
1257 bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
1260 this.m_numIterations = Math.max(2, es3fMultisampleTests.getIterationCount(this.m_numSamples * 5));
1262 this.randomizeViewport(); // \note Using the same viewport for every iteration since coverage mask may depend on window-relative pixel coordinate.
1266 * @return {tcuTestCase.IterateResult}
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);
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');
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);
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;
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);
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);
1304 this.renderQuad_WithColor(pt0, pt1, pt2, pt3, quadColor);
1306 // Read and log image.
1307 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
1308 /** @type {number} */ var numPixels = renderedImg.getWidth() * renderedImg.getHeight();
1310 tcuLogImage.logImage('RenderedImage', 'Rendered image', renderedImg.getAccess());
1311 // Compute average red component in rendered image.
1313 /** @type {number} */ var sumRed = 0;
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();
1319 bufferedLogToConsole('Average red color component: ' + (sumRed / 255.0 / numPixels));
1321 // Check if average color has decreased from previous frame's color.
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;
1329 // Check if coverage mask is not all-zeros if alpha or coverage value is 0 (or 1, if inverted).
1331 if (this.m_currentIteration == 0 && sumRed != 0)
1333 bufferedLogToConsole('Failure: Image should be completely black');
1334 testFailedOptions('Failed', false);
1335 return tcuTestCase.IterateResult.STOP;
1338 if (this.m_currentIteration == this.m_numIterations-1 && sumRed != 0xff*numPixels)
1340 bufferedLogToConsole('Failure: Image should be completely red');
1342 testFailedOptions('Failed', false);
1343 return tcuTestCase.IterateResult.STOP;
1346 this.m_previousIterationColorSum = sumRed;
1348 this.m_currentIteration++;
1350 if (this.m_currentIteration >= this.m_numIterations)
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'));
1357 testPassedOptions('Passed', true);
1358 return tcuTestCase.IterateResult.STOP;
1361 return tcuTestCase.IterateResult.CONTINUE;
1365 * Tests coverage mask generation constancy property.
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
1374 * @extends {es3fMultisampleTests.MultisampleCase}
1376 * @param {string} name
1377 * @param {string} desc
1378 * @param {es3fMultisampleTests.MaskConstancyCase.CaseType} type
1379 * @param {number=} numFboSamples
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);
1392 es3fMultisampleTests.MaskConstancyCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1394 /** Copy the constructor */
1395 es3fMultisampleTests.MaskConstancyCase.prototype.constructor = es3fMultisampleTests.MaskConstancyCase;
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.
1409 * @return {tcuTestCase.IterateResult}
1411 es3fMultisampleTests.MaskConstancyCase.prototype.iterate = function() {
1412 this.randomizeViewport();
1414 bufferedLogToConsole('Clearing color to black');
1415 gl.clearColor(0.0, 0.0, 0.0, 1.0);
1416 gl.clear(gl.COLOR_BUFFER_BIT);
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');
1425 if (this.m_isSampleCoverageCase) {
1426 gl.enable(gl.SAMPLE_COVERAGE);
1427 bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
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' : '') +
1437 /** @type {number} */ var numQuadRowsCols = this.m_numSamples * 4;
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];
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);
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));
1464 /** @type {tcuSurface.Surface} */ var renderedImg = this.readImage();
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;
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' : ''));
1481 testPassedOptions('Passed', true);
1483 return tcuTestCase.IterateResult.STOP;
1488 * Tests coverage mask inversion validity.
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}
1498 * @param {string} name
1499 * @param {string} desc
1500 * @param {number=} numFboSamples
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);
1509 es3fMultisampleTests.CoverageMaskInvertCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1511 /** Copy the constructor */
1512 es3fMultisampleTests.CoverageMaskInvertCase.prototype.constructor = es3fMultisampleTests.CoverageMaskInvertCase;
1515 * @param {boolean} invertSampleCoverage
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);
1522 /** @type {number} */ var angle0 = 2.0 * Math.PI * i / numTriangles;
1523 /** @type {number} */ var angle1 = 2.0 * Math.PI * (i + 0.5) / numTriangles;
1525 this.renderTriangle_pAsVec2WithColor(
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]);
1537 * @return {tcuTestCase.IterateResult}
1539 es3fMultisampleTests.CoverageMaskInvertCase.prototype.iterate = function() {
1540 this.randomizeViewport();
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');
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();
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();
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,
1573 bufferedLogToConsole('Success: The two images rendered are identical');
1574 testPassedOptions('Passed', true);
1577 testFailedOptions('Failed', false);
1580 return tcuTestCase.IterateResult.STOP;
1583 es3fMultisampleTests.init = function() {
1584 var testGroup = tcuTestCase.runner.testCases;
1589 DEFAULT_FRAMEBUFFER: 0,
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 :
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' :
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' :
1617 /** @type {tcuTestCase.DeqpTest} */ var group = tcuTestCase.newTest(name, desc);
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);
1624 group.addChild(new es3fMultisampleTests.PolygonNumSamplesCase(
1625 'num_samples_polygon',
1626 'Test sanity of the sample count, with polygons',
1629 group.addChild(new es3fMultisampleTests.LineNumSamplesCase(
1631 'Test sanity of the sample count, with lines',
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,
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,
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,
1652 group.addChild(new es3fMultisampleTests.SampleDepthCase(
1654 'Test that depth values are per-sample',
1657 group.addChild(new es3fMultisampleTests.SampleStencilCase(
1659 'Test that stencil values are per-sample',
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',
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,
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,
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,
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,
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,
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,
1699 group.addChild(new es3fMultisampleTests.MaskConstancyCase(
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,
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,
1714 * @param {WebGL2RenderingContext} context
1716 es3fMultisampleTests.run = function(context, range) {
1718 //Set up Test Root parameters
1719 var testName = 'multisample';
1720 var testDescription = 'Multisample Tests';
1721 var state = tcuTestCase.runner;
1723 state.testName = testName;
1724 state.setRoot(tcuTestCase.newTest(testName, testDescription, null));
1726 //Set up name and description of this test series.
1727 setCurrentTestName(testName);
1728 description(testDescription);
1732 es3fMultisampleTests.init();
1734 state.setRange(range);
1736 tcuTestCase.runTestCases();
1739 testFailedOptions('Failed to es3fMultisampleTests.run tests', false);
1740 tcuTestCase.runner.terminate();