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