Backed out changeset 7272b7396c78 (bug 1932758) for causing fenix debug failures...
[gecko.git] / dom / canvas / test / webgl-conf / checkout / deqp / functional / gles3 / es3fMultisampleTests.js
blob47e6267d35d3727f39239d026eede5b4042bcea3
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  */
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');
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;
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     };
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     };
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]);
93         return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0);
94     };
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));
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     };
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     };
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_;
177     };
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     };
210     es3fMultisampleTests.MultisampleCase.prototype = Object.create(tcuTestCase.DeqpTest.prototype);
212     /** Copy the constructor */
213     es3fMultisampleTests.MultisampleCase.prototype.constructor = es3fMultisampleTests.MultisampleCase;
215     /* Rest states */
216     es3fMultisampleTests.MultisampleCase.prototype.deinit = function() {
217         gl.colorMask(true, true, true, true);
218         gl.depthMask(true);
220         gl.clearColor(0.0, 0.0, 0.0, 0.0);
221         gl.clearDepth(1.0);
222         gl.clearStencil(0);
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);
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         }
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         }
256         gl.bindRenderbuffer(gl.RENDERBUFFER, null);
257         gl.bindFramebuffer(gl.FRAMEBUFFER, null);
258     }
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         ];
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);
303     };
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     };
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     };
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     };
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     };
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     };
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         ];
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);
410     };
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);
419         gl.viewport(this.m_viewportX, this.m_viewportY, this.m_viewportSize, this.m_viewportSize);
420     };
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);
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);
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     };
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';
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';
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         }
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             }
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         }
501         // Prepare program.
503         assertMsgOptions(!this.m_program, 'Program loaded when it should not be.', false, true);
505         this.m_program = new gluShaderProgram.ShaderProgram(
506             gl,
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');
518         }
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);
529                 return false;
530             }
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);
542             }
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);
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);
560             // Use ms FBO.
561             gl.bindFramebuffer(gl.FRAMEBUFFER, this.m_msFbo);
562         }
564         // Get suitable viewport size.
566         this.m_viewportSize = Math.min(this.m_desiredViewportSize, this.m_renderWidth, this.m_renderHeight);
567         this.randomizeViewport();
568         return true;
569     };
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     };
595     es3fMultisampleTests.NumSamplesCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
597     /** Copy the constructor */
598     es3fMultisampleTests.NumSamplesCase.prototype.constructor = es3fMultisampleTests.NumSamplesCase;
600     /**
601      * @return {tcuTestCase.IterateResult}
602      */
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))
631                     break;
632             }
634             if (i === this.m_detectedColors.length)
635                 this.m_detectedColors.push(color); // Color not previously detected.
636         }
638         // Log results.
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;
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     };
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     };
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(
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     };
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     };
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]);
729         }
730     };
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();
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     };
765     es3fMultisampleTests.CommonEdgeCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
767     /** Copy the constructor */
768     es3fMultisampleTests.CommonEdgeCase.prototype.constructor = es3fMultisampleTests.CommonEdgeCase;
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     };
779     es3fMultisampleTests.CommonEdgeCase.prototype.init = function() {
780         var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
781         if (!inited) {
782             return false;
783         }
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 + ')');
792         }
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     };
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);
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) {
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             }
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             ];
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.
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]);
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                 }
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.
901             quadBaseAngleNdx = Math.floor(this.m_currentIteration / 8);
902             quadSubAngleNdx = this.m_currentIteration % 8;
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             }
918             quadDiagLen = 2.5 / Math.max(angleCos, angleSin);
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             ];
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]);
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             }
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.
959             quadSubAngleNdx = this.m_currentIteration % 8;
961             corners = [
962                 [1.0, 1.0],
963                 [-1.0, 1.0],
964                 [-1.0, -1.0],
965                 [1.0, -1.0]
966             ];
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]);
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             }
989             unicoloredRegions.push(new es3fMultisampleTests.QuadCorners(corners[0], corners[1], corners[2], corners[3]));
990         }
991         else
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;
1014         }
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;
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     };
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     };
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);
1065         if (!inited) {
1066             return false;
1067         }
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');
1074     };
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);
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);
1091         }
1092     };
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     };
1115     es3fMultisampleTests.SampleStencilCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1117     /** Copy the constructor */
1118     es3fMultisampleTests.SampleStencilCase.prototype.constructor = es3fMultisampleTests.SampleStencilCase;
1120     /**
1121      * @return {tcuTestCase.IterateResult}
1122      */
1123     es3fMultisampleTests.SampleStencilCase.prototype.iterate = function() {
1124         this.randomizeViewport();
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);
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(
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         }
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;
1167             }
1168         }
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(
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]);
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]);
1189         if (passed) {
1190             bufferedLogToConsole('Success: The two images rendered are identical');
1191             testPassedOptions('Passed', true);
1192         }
1193         else
1194             testFailedOptions('Failed', false);
1196         return tcuTestCase.IterateResult.STOP;
1197     };
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     };
1226     es3fMultisampleTests.MaskProportionalityCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1228     /** Copy the constructor */
1229     es3fMultisampleTests.MaskProportionalityCase.prototype.constructor = es3fMultisampleTests.MaskProportionalityCase;
1231     /**
1232      * @enum {number}
1233      */
1234     es3fMultisampleTests.MaskProportionalityCase.CaseType = {
1235         ALPHA_TO_COVERAGE: 0,
1236         SAMPLE_COVERAGE: 1,
1237         SAMPLE_COVERAGE_INVERTED: 2
1238     };
1240     es3fMultisampleTests.MaskProportionalityCase.prototype.init = function() {
1241         var inited = es3fMultisampleTests.MultisampleCase.prototype.init.call(this);
1242         if (!inited) {
1243             return false;
1244         }
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);
1256             gl.enable(gl.SAMPLE_COVERAGE);
1257             bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
1258         }
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.
1263     };
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);
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         }
1279         // Draw quad.
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;
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);
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         }
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;
1327         }
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)
1332         {
1333             bufferedLogToConsole('Failure: Image should be completely black');
1334             testFailedOptions('Failed', false);
1335             return tcuTestCase.IterateResult.STOP;
1336         }
1338         if (this.m_currentIteration == this.m_numIterations-1 && sumRed != 0xff*numPixels)
1339         {
1340             bufferedLogToConsole('Failure: Image should be completely red');
1342             testFailedOptions('Failed', false);
1343             return tcuTestCase.IterateResult.STOP;
1344         }
1346         this.m_previousIterationColorSum = sumRed;
1348         this.m_currentIteration++;
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'));
1357             testPassedOptions('Passed', true);
1358             return tcuTestCase.IterateResult.STOP;
1359         }
1360         else
1361             return tcuTestCase.IterateResult.CONTINUE;
1362     };
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     };
1392     es3fMultisampleTests.MaskConstancyCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1394     /** Copy the constructor */
1395     es3fMultisampleTests.MaskConstancyCase.prototype.constructor = es3fMultisampleTests.MaskConstancyCase;
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     };
1408     /**
1409      * @return {tcuTestCase.IterateResult}
1410      */
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');
1423         }
1425         if (this.m_isSampleCoverageCase) {
1426             gl.enable(gl.SAMPLE_COVERAGE);
1427             bufferedLogToConsole('gl.SAMPLE_COVERAGE is enabled');
1428         }
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');
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);
1453                 }
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         }
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;
1473             }
1474         }
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;
1484     }
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     };
1509     es3fMultisampleTests.CoverageMaskInvertCase.prototype = Object.create(es3fMultisampleTests.MultisampleCase.prototype);
1511     /** Copy the constructor */
1512     es3fMultisampleTests.CoverageMaskInvertCase.prototype.constructor = es3fMultisampleTests.CoverageMaskInvertCase;
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);
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(
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     };
1536     /**
1537     * @return {tcuTestCase.IterateResult}
1538     */
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,
1570             [0, 0, 0, 0]);
1572         if (passed) {
1573             bufferedLogToConsole('Success: The two images rendered are identical');
1574             testPassedOptions('Passed', true);
1575         }
1576         else {
1577             testFailedOptions('Failed', false);
1578         }
1580         return tcuTestCase.IterateResult.STOP;
1581     };
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         };
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;
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;
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',
1627                 numFboSamples));
1629             group.addChild(new es3fMultisampleTests.LineNumSamplesCase(
1630                 'num_samples_line',
1631                 'Test sanity of the sample count, with lines',
1632                 numFboSamples));
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));
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));
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));
1652             group.addChild(new es3fMultisampleTests.SampleDepthCase(
1653                 'depth',
1654                 'Test that depth values are per-sample',
1655                 numFboSamples));
1657             group.addChild(new es3fMultisampleTests.SampleStencilCase(
1658                 'stencil',
1659                 'Test that stencil values are per-sample',
1660                 numFboSamples));
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));
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));
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     };
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;
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);
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     };