eglGetFrameTimestamps: Consider timestamps of 0 as invalid.
[platform/upstream/VK-GL-CTS.git] / modules / gles3 / functional / es3fDitheringTests.cpp
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 Dithering tests.
22  *//*--------------------------------------------------------------------*/
23
24 #include "es3fDitheringTests.hpp"
25 #include "gluRenderContext.hpp"
26 #include "gluDefs.hpp"
27 #include "glsFragmentOpUtil.hpp"
28 #include "gluPixelTransfer.hpp"
29 #include "tcuRenderTarget.hpp"
30 #include "tcuRGBA.hpp"
31 #include "tcuVector.hpp"
32 #include "tcuPixelFormat.hpp"
33 #include "tcuTestLog.hpp"
34 #include "tcuSurface.hpp"
35 #include "tcuCommandLine.hpp"
36 #include "deRandom.hpp"
37 #include "deStringUtil.hpp"
38 #include "deString.h"
39 #include "deMath.h"
40
41 #include <string>
42 #include <algorithm>
43
44 #include "glw.h"
45
46 namespace deqp
47 {
48
49 using tcu::Vec4;
50 using tcu::IVec4;
51 using tcu::TestLog;
52 using gls::FragmentOpUtil::QuadRenderer;
53 using gls::FragmentOpUtil::Quad;
54 using tcu::PixelFormat;
55 using tcu::Surface;
56 using de::Random;
57 using std::vector;
58 using std::string;
59
60 namespace gles3
61 {
62 namespace Functional
63 {
64
65 static const char* const s_channelNames[4] = { "red", "green", "blue", "alpha" };
66
67 static inline IVec4 pixelFormatToIVec4 (const PixelFormat& format)
68 {
69         return IVec4(format.redBits, format.greenBits, format.blueBits, format.alphaBits);
70 }
71
72 template<typename T>
73 static inline string choiceListStr (const vector<T>& choices)
74 {
75         string result;
76         for (int i = 0; i < (int)choices.size(); i++)
77         {
78                 if (i == (int)choices.size()-1)
79                         result += " or ";
80                 else if (i > 0)
81                         result += ", ";
82                 result += de::toString(choices[i]);
83         }
84         return result;
85 }
86
87 class DitheringCase : public tcu::TestCase
88 {
89 public:
90         enum PatternType
91         {
92                 PATTERNTYPE_GRADIENT = 0,
93                 PATTERNTYPE_UNICOLORED_QUAD,
94
95                 PATTERNTYPE_LAST
96         };
97
98                                                                                         DitheringCase                           (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, bool isEnabled, PatternType patternType, const tcu::Vec4& color);
99                                                                                         ~DitheringCase                          (void);
100
101         IterateResult                                                   iterate                                         (void);
102         void                                                                    init                                            (void);
103         void                                                                    deinit                                          (void);
104
105         static const char*                                              getPatternTypeName                      (PatternType type);
106
107 private:
108         bool                                                                    checkColor                                      (const tcu::Vec4& inputClr, const tcu::RGBA& renderedClr, bool logErrors) const;
109
110         bool                                                                    drawAndCheckGradient            (bool isVerticallyIncreasing, const tcu::Vec4& highColor) const;
111         bool                                                                    drawAndCheckUnicoloredQuad      (const tcu::Vec4& color) const;
112
113         const glu::RenderContext&                               m_renderCtx;
114
115         const bool                                                              m_ditheringEnabled;
116         const PatternType                                               m_patternType;
117         const tcu::Vec4                                                 m_color;
118
119         const tcu::PixelFormat                                  m_renderFormat;
120
121         const QuadRenderer*                                             m_renderer;
122         int                                                                             m_iteration;
123 };
124
125 const char* DitheringCase::getPatternTypeName (const PatternType type)
126 {
127         switch (type)
128         {
129                 case PATTERNTYPE_GRADIENT:                      return "gradient";
130                 case PATTERNTYPE_UNICOLORED_QUAD:       return "unicolored_quad";
131                 default:
132                         DE_ASSERT(false);
133                         return DE_NULL;
134         }
135 }
136
137
138 DitheringCase::DitheringCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* const name, const char* const description, const bool ditheringEnabled, const PatternType patternType, const Vec4& color)
139         : TestCase                              (testCtx, name, description)
140         , m_renderCtx                   (renderCtx)
141         , m_ditheringEnabled    (ditheringEnabled)
142         , m_patternType                 (patternType)
143         , m_color                               (color)
144         , m_renderFormat                (renderCtx.getRenderTarget().getPixelFormat())
145         , m_renderer                    (DE_NULL)
146         , m_iteration                   (0)
147 {
148 }
149
150 DitheringCase::~DitheringCase (void)
151 {
152         DitheringCase::deinit();
153 }
154
155 void DitheringCase::init (void)
156 {
157         DE_ASSERT(!m_renderer);
158         m_renderer = new QuadRenderer(m_renderCtx, glu::GLSL_VERSION_300_ES);
159         m_iteration = 0;
160 }
161
162 void DitheringCase::deinit (void)
163 {
164         delete m_renderer;
165         m_renderer = DE_NULL;
166 }
167
168 bool DitheringCase::checkColor (const Vec4& inputClr, const tcu::RGBA& renderedClr, const bool logErrors) const
169 {
170         const IVec4             channelBits             = pixelFormatToIVec4(m_renderFormat);
171         bool                    allChannelsOk   = true;
172
173         for (int chanNdx = 0; chanNdx < 4; chanNdx++)
174         {
175                 if (channelBits[chanNdx] == 0)
176                         continue;
177
178                 const int               channelMax                      = (1 << channelBits[chanNdx]) - 1;
179                 const float             scaledInput                     = inputClr[chanNdx] * (float)channelMax;
180                 const bool              useRoundingMargin       = deFloatAbs(scaledInput - deFloatRound(scaledInput)) < 0.0001f;
181                 vector<int>             channelChoices;
182
183                 channelChoices.push_back(de::min(channelMax,    (int)deFloatCeil(scaledInput)));
184                 channelChoices.push_back(de::max(0,                             (int)deFloatCeil(scaledInput) - 1));
185
186                 // If the input color results in a scaled value that is very close to an integer, account for a little bit of possible inaccuracy.
187                 if (useRoundingMargin)
188                 {
189                         if (scaledInput > deFloatRound(scaledInput))
190                                 channelChoices.push_back((int)deFloatCeil(scaledInput) - 2);
191                         else
192                                 channelChoices.push_back((int)deFloatCeil(scaledInput) + 1);
193                 }
194
195                 std::sort(channelChoices.begin(), channelChoices.end());
196
197                 {
198                         const int               renderedClrInFormat     = (int)deFloatRound((float)(renderedClr.toIVec()[chanNdx] * channelMax) / 255.0f);
199                         bool                    goodChannel                     = false;
200
201                         for (int i = 0; i < (int)channelChoices.size(); i++)
202                         {
203                                 if (renderedClrInFormat == channelChoices[i])
204                                 {
205                                         goodChannel = true;
206                                         break;
207                                 }
208                         }
209
210                         if (!goodChannel)
211                         {
212                                 if (logErrors)
213                                 {
214                                         m_testCtx.getLog() << TestLog::Message
215                                                                            << "Failure: " << channelBits[chanNdx] << "-bit " << s_channelNames[chanNdx] << " channel is " << renderedClrInFormat
216                                                                            << ", should be " << choiceListStr(channelChoices)
217                                                                            << " (corresponding fragment color channel is " << inputClr[chanNdx] << ")"
218                                                                            << TestLog::EndMessage
219                                                                            << TestLog::Message
220                                                                            << "Note: " << inputClr[chanNdx] << " * (" << channelMax + 1 << "-1) = " << scaledInput
221                                                                            << TestLog::EndMessage;
222
223                                         if (useRoundingMargin)
224                                         {
225                                                 m_testCtx.getLog() << TestLog::Message
226                                                                                    << "Note: one extra color candidate was allowed because fragmentColorChannel * (2^bits-1) is close to an integer"
227                                                                                    << TestLog::EndMessage;
228                                         }
229                                 }
230
231                                 allChannelsOk = false;
232                         }
233                 }
234         }
235
236         return allChannelsOk;
237 }
238
239 bool DitheringCase::drawAndCheckGradient (const bool isVerticallyIncreasing, const Vec4& highColor) const
240 {
241         TestLog&                                        log                                     = m_testCtx.getLog();
242         Random                                          rnd                                     (deStringHash(getName()));
243         const int                                       maxViewportWid          = 256;
244         const int                                       maxViewportHei          = 256;
245         const int                                       viewportWid                     = de::min(m_renderCtx.getRenderTarget().getWidth(), maxViewportWid);
246         const int                                       viewportHei                     = de::min(m_renderCtx.getRenderTarget().getHeight(), maxViewportHei);
247         const int                                       viewportX                       = rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportWid);
248         const int                                       viewportY                       = rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportHei);
249         const Vec4                                      quadClr0                        (0.0f, 0.0f, 0.0f, 0.0f);
250         const Vec4&                                     quadClr1                        = highColor;
251         Quad                                            quad;
252         Surface                                         renderedImg                     (viewportWid, viewportHei);
253
254         GLU_CHECK_CALL(glViewport(viewportX, viewportY, viewportWid, viewportHei));
255
256         log << TestLog::Message << "Dithering is " << (m_ditheringEnabled ? "enabled" : "disabled") << TestLog::EndMessage;
257
258         if (m_ditheringEnabled)
259                 GLU_CHECK_CALL(glEnable(GL_DITHER));
260         else
261                 GLU_CHECK_CALL(glDisable(GL_DITHER));
262
263         log << TestLog::Message << "Drawing a " << (isVerticallyIncreasing ? "vertically" : "horizontally") << " increasing gradient" << TestLog::EndMessage;
264
265         quad.color[0] = quadClr0;
266         quad.color[1] = isVerticallyIncreasing ? quadClr1 : quadClr0;
267         quad.color[2] = isVerticallyIncreasing ? quadClr0 : quadClr1;
268         quad.color[3] = quadClr1;
269
270         m_renderer->render(quad);
271
272         glu::readPixels(m_renderCtx, viewportX, viewportY, renderedImg.getAccess());
273         GLU_CHECK_MSG("glReadPixels()");
274
275         log << TestLog::Image(isVerticallyIncreasing ? "VerGradient"            : "HorGradient",
276                                                   isVerticallyIncreasing ? "Vertical gradient"  : "Horizontal gradient",
277                                                   renderedImg);
278
279         // Validate, at each pixel, that each color channel is one of its two allowed values.
280
281         {
282                 Surface         errorMask               (viewportWid, viewportHei);
283                 bool            colorChoicesOk  = true;
284
285                 for (int y = 0; y < renderedImg.getHeight(); y++)
286                 {
287                         for (int x = 0; x < renderedImg.getWidth(); x++)
288                         {
289                                 const float             inputF          = ((float)(isVerticallyIncreasing ? y : x) + 0.5f) / (float)(isVerticallyIncreasing ? renderedImg.getHeight() : renderedImg.getWidth());
290                                 const Vec4              inputClr        = (1.0f-inputF)*quadClr0 + inputF*quadClr1;
291
292                                 if (!checkColor(inputClr, renderedImg.getPixel(x, y), colorChoicesOk))
293                                 {
294                                         errorMask.setPixel(x, y, tcu::RGBA::red());
295
296                                         if (colorChoicesOk)
297                                         {
298                                                 log << TestLog::Message << "First failure at pixel (" << x << ", " << y << ") (not printing further errors)" << TestLog::EndMessage;
299                                                 colorChoicesOk = false;
300                                         }
301                                 }
302                                 else
303                                         errorMask.setPixel(x, y, tcu::RGBA::green());
304                         }
305                 }
306
307                 if (!colorChoicesOk)
308                 {
309                         log << TestLog::Image("ColorChoiceErrorMask", "Error mask for color choices", errorMask);
310                         return false;
311                 }
312         }
313
314         // When dithering is disabled, the color selection must be coordinate-independent - i.e. the colors must be constant in the gradient's constant direction.
315
316         if (!m_ditheringEnabled)
317         {
318                 const int       increasingDirectionSize = isVerticallyIncreasing ? renderedImg.getHeight() : renderedImg.getWidth();
319                 const int       constantDirectionSize   = isVerticallyIncreasing ? renderedImg.getWidth() : renderedImg.getHeight();
320
321                 for (int incrPos = 0; incrPos < increasingDirectionSize; incrPos++)
322                 {
323                         bool            colorHasChanged                 = false;
324                         tcu::RGBA       prevConstantDirectionPix;
325
326                         for (int constPos = 0; constPos < constantDirectionSize; constPos++)
327                         {
328                                 const int                       x               = isVerticallyIncreasing ? constPos : incrPos;
329                                 const int                       y               = isVerticallyIncreasing ? incrPos : constPos;
330                                 const tcu::RGBA         clr             = renderedImg.getPixel(x, y);
331
332                                 if (constPos > 0 && clr != prevConstantDirectionPix)
333                                 {
334                                         if (colorHasChanged)
335                                         {
336                                                 log << TestLog::Message
337                                                         << "Failure: colors should be constant per " << (isVerticallyIncreasing ? "row" : "column")
338                                                         << " (since dithering is disabled), but the color at position (" << x << ", " << y << ") is " << clr
339                                                         << " and does not equal the color at (" << (isVerticallyIncreasing ? x-1 : x) << ", " << (isVerticallyIncreasing ? y : y-1) << "), which is " << prevConstantDirectionPix
340                                                         << TestLog::EndMessage;
341
342                                                 return false;
343                                         }
344                                         else
345                                                 colorHasChanged = true;
346                                 }
347
348                                 prevConstantDirectionPix = clr;
349                         }
350                 }
351         }
352
353         return true;
354 }
355
356 bool DitheringCase::drawAndCheckUnicoloredQuad (const Vec4& quadColor) const
357 {
358         TestLog&                                        log                                     = m_testCtx.getLog();
359         Random                                          rnd                                     (deStringHash(getName()));
360         const int                                       maxViewportWid          = 32;
361         const int                                       maxViewportHei          = 32;
362         const int                                       viewportWid                     = de::min(m_renderCtx.getRenderTarget().getWidth(), maxViewportWid);
363         const int                                       viewportHei                     = de::min(m_renderCtx.getRenderTarget().getHeight(), maxViewportHei);
364         const int                                       viewportX                       = rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportWid);
365         const int                                       viewportY                       = rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportHei);
366         Quad                                            quad;
367         Surface                                         renderedImg                     (viewportWid, viewportHei);
368
369         GLU_CHECK_CALL(glViewport(viewportX, viewportY, viewportWid, viewportHei));
370
371         log << TestLog::Message << "Dithering is " << (m_ditheringEnabled ? "enabled" : "disabled") << TestLog::EndMessage;
372
373         if (m_ditheringEnabled)
374                 GLU_CHECK_CALL(glEnable(GL_DITHER));
375         else
376                 GLU_CHECK_CALL(glDisable(GL_DITHER));
377
378         log << TestLog::Message << "Drawing an unicolored quad with color " << quadColor << TestLog::EndMessage;
379
380         quad.color[0] = quadColor;
381         quad.color[1] = quadColor;
382         quad.color[2] = quadColor;
383         quad.color[3] = quadColor;
384
385         m_renderer->render(quad);
386
387         glu::readPixels(m_renderCtx, viewportX, viewportY, renderedImg.getAccess());
388         GLU_CHECK_MSG("glReadPixels()");
389
390         log << TestLog::Image(("Quad" + de::toString(m_iteration)).c_str(), ("Quad " + de::toString(m_iteration)).c_str(), renderedImg);
391
392         // Validate, at each pixel, that each color channel is one of its two allowed values.
393
394         {
395                 Surface         errorMask               (viewportWid, viewportHei);
396                 bool            colorChoicesOk  = true;
397
398                 for (int y = 0; y < renderedImg.getHeight(); y++)
399                 {
400                         for (int x = 0; x < renderedImg.getWidth(); x++)
401                         {
402                                 if (!checkColor(quadColor, renderedImg.getPixel(x, y), colorChoicesOk))
403                                 {
404                                         errorMask.setPixel(x, y, tcu::RGBA::red());
405
406                                         if (colorChoicesOk)
407                                         {
408                                                 log << TestLog::Message << "First failure at pixel (" << x << ", " << y << ") (not printing further errors)" << TestLog::EndMessage;
409                                                 colorChoicesOk = false;
410                                         }
411                                 }
412                                 else
413                                         errorMask.setPixel(x, y, tcu::RGBA::green());
414                         }
415                 }
416
417                 if (!colorChoicesOk)
418                 {
419                         log << TestLog::Image("ColorChoiceErrorMask", "Error mask for color choices", errorMask);
420                         return false;
421                 }
422         }
423
424         // When dithering is disabled, the color selection must be coordinate-independent - i.e. the entire rendered image must be unicolored.
425
426         if (!m_ditheringEnabled)
427         {
428                 const tcu::RGBA renderedClr00 = renderedImg.getPixel(0, 0);
429
430                 for (int y = 0; y < renderedImg.getHeight(); y++)
431                 {
432                         for (int x = 0; x < renderedImg.getWidth(); x++)
433                         {
434                                 const tcu::RGBA curClr = renderedImg.getPixel(x, y);
435
436                                 if (curClr != renderedClr00)
437                                 {
438                                         log << TestLog::Message
439                                                 << "Failure: color at (" << x << ", " << y << ") is " << curClr
440                                                 << " and does not equal the color at (0, 0), which is " << renderedClr00
441                                                 << TestLog::EndMessage;
442
443                                         return false;
444                                 }
445                         }
446                 }
447         }
448
449         return true;
450 }
451
452 DitheringCase::IterateResult DitheringCase::iterate (void)
453 {
454         if (m_patternType == PATTERNTYPE_GRADIENT)
455         {
456                 // Draw horizontal and vertical gradients.
457
458                 DE_ASSERT(m_iteration < 2);
459
460                 const bool success = drawAndCheckGradient(m_iteration == 1, m_color);
461
462                 if (!success)
463                 {
464                         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
465                         return STOP;
466                 }
467
468                 if (m_iteration == 1)
469                 {
470                         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
471                         return STOP;
472                 }
473         }
474         else if (m_patternType == PATTERNTYPE_UNICOLORED_QUAD)
475         {
476                 const int numQuads = m_testCtx.getCommandLine().getTestIterationCount() > 0 ? m_testCtx.getCommandLine().getTestIterationCount() : 30;
477
478                 DE_ASSERT(m_iteration < numQuads);
479
480                 const Vec4 quadColor    = (float)m_iteration / (float)(numQuads-1) * m_color;
481                 const bool success              =  drawAndCheckUnicoloredQuad(quadColor);
482
483                 if (!success)
484                 {
485                         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
486                         return STOP;
487                 }
488
489                 if (m_iteration == numQuads - 1)
490                 {
491                         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
492                         return STOP;
493                 }
494         }
495         else
496                 DE_ASSERT(false);
497
498         m_iteration++;
499
500         return CONTINUE;
501 }
502
503 DitheringTests::DitheringTests (Context& context)
504         : TestCaseGroup(context, "dither", "Dithering tests")
505 {
506 }
507
508 DitheringTests::~DitheringTests (void)
509 {
510 }
511
512 void DitheringTests::init (void)
513 {
514         static const struct
515         {
516                 const char*             name;
517                 Vec4                    color;
518         } caseColors[] =
519         {
520                 { "white",              Vec4(1.0f, 1.0f, 1.0f, 1.0f) },
521                 { "red",                Vec4(1.0f, 0.0f, 0.0f, 1.0f) },
522                 { "green",              Vec4(0.0f, 1.0f, 0.0f, 1.0f) },
523                 { "blue",               Vec4(0.0f, 0.0f, 1.0f, 1.0f) },
524                 { "alpha",              Vec4(0.0f, 0.0f, 0.0f, 1.0f) }
525         };
526
527         for (int ditheringEnabledI = 0; ditheringEnabledI <= 1; ditheringEnabledI++)
528         {
529                 const bool                              ditheringEnabled        = ditheringEnabledI != 0;
530                 TestCaseGroup* const    group                           = new TestCaseGroup(m_context, ditheringEnabled ? "enabled" : "disabled", "");
531                 addChild(group);
532
533                 for (int patternTypeI = 0; patternTypeI < DitheringCase::PATTERNTYPE_LAST; patternTypeI++)
534                 {
535                         for (int caseColorNdx = 0; caseColorNdx < DE_LENGTH_OF_ARRAY(caseColors); caseColorNdx++)
536                         {
537                                 const DitheringCase::PatternType        patternType             = (DitheringCase::PatternType)patternTypeI;
538                                 const string                                            caseName                = string("") + DitheringCase::getPatternTypeName(patternType) + "_" + caseColors[caseColorNdx].name;
539
540                                 group->addChild(new DitheringCase(m_context.getTestContext(), m_context.getRenderContext(), caseName.c_str(), "", ditheringEnabled, patternType, caseColors[caseColorNdx].color));
541                         }
542                 }
543         }
544 }
545
546 } // Functional
547 } // gles3
548 } // deqp