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 ®ion = 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