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