Add new framebuffer fetch extension tests am: 2a609fb223
[platform/upstream/VK-GL-CTS.git] / modules / gles2 / functional / es2fDitheringTests.cpp
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 2.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 "es2fDitheringTests.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 "glw.h"
42
43 #include <string>
44 #include <algorithm>
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 gles2
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_100_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                                         // Allow color to change once to take into account possibly
335                                         // discontinuity between triangles
336                                         if (colorHasChanged)
337                                         {
338                                                 log << TestLog::Message
339                                                         << "Failure: colors should be constant per " << (isVerticallyIncreasing ? "row" : "column")
340                                                         << " (since dithering is disabled), but the color at position (" << x << ", " << y << ") is " << clr
341                                                         << " and does not equal the color at (" << (isVerticallyIncreasing ? x-1 : x) << ", " << (isVerticallyIncreasing ? y : y-1) << "), which is " << prevConstantDirectionPix
342                                                         << TestLog::EndMessage;
343
344                                                 return false;
345                                         }
346                                         else
347                                                 colorHasChanged = true;
348                                 }
349
350                                 prevConstantDirectionPix = clr;
351                         }
352                 }
353         }
354
355         return true;
356 }
357
358 bool DitheringCase::drawAndCheckUnicoloredQuad (const Vec4& quadColor) const
359 {
360         TestLog&                                        log                                     = m_testCtx.getLog();
361         Random                                          rnd                                     (deStringHash(getName()));
362         const int                                       maxViewportWid          = 32;
363         const int                                       maxViewportHei          = 32;
364         const int                                       viewportWid                     = de::min(m_renderCtx.getRenderTarget().getWidth(), maxViewportWid);
365         const int                                       viewportHei                     = de::min(m_renderCtx.getRenderTarget().getHeight(), maxViewportHei);
366         const int                                       viewportX                       = rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth() - viewportWid);
367         const int                                       viewportY                       = rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight() - viewportHei);
368         Quad                                            quad;
369         Surface                                         renderedImg                     (viewportWid, viewportHei);
370
371         GLU_CHECK_CALL(glViewport(viewportX, viewportY, viewportWid, viewportHei));
372
373         log << TestLog::Message << "Dithering is " << (m_ditheringEnabled ? "enabled" : "disabled") << TestLog::EndMessage;
374
375         if (m_ditheringEnabled)
376                 GLU_CHECK_CALL(glEnable(GL_DITHER));
377         else
378                 GLU_CHECK_CALL(glDisable(GL_DITHER));
379
380         log << TestLog::Message << "Drawing an unicolored quad with color " << quadColor << TestLog::EndMessage;
381
382         quad.color[0] = quadColor;
383         quad.color[1] = quadColor;
384         quad.color[2] = quadColor;
385         quad.color[3] = quadColor;
386
387         m_renderer->render(quad);
388
389         glu::readPixels(m_renderCtx, viewportX, viewportY, renderedImg.getAccess());
390         GLU_CHECK_MSG("glReadPixels()");
391
392         log << TestLog::Image(("Quad" + de::toString(m_iteration)).c_str(), ("Quad " + de::toString(m_iteration)).c_str(), renderedImg);
393
394         // Validate, at each pixel, that each color channel is one of its two allowed values.
395
396         {
397                 Surface         errorMask               (viewportWid, viewportHei);
398                 bool            colorChoicesOk  = true;
399
400                 for (int y = 0; y < renderedImg.getHeight(); y++)
401                 {
402                         for (int x = 0; x < renderedImg.getWidth(); x++)
403                         {
404                                 if (!checkColor(quadColor, renderedImg.getPixel(x, y), colorChoicesOk))
405                                 {
406                                         errorMask.setPixel(x, y, tcu::RGBA::red());
407
408                                         if (colorChoicesOk)
409                                         {
410                                                 log << TestLog::Message << "First failure at pixel (" << x << ", " << y << ") (not printing further errors)" << TestLog::EndMessage;
411                                                 colorChoicesOk = false;
412                                         }
413                                 }
414                                 else
415                                         errorMask.setPixel(x, y, tcu::RGBA::green());
416                         }
417                 }
418
419                 if (!colorChoicesOk)
420                 {
421                         log << TestLog::Image("ColorChoiceErrorMask", "Error mask for color choices", errorMask);
422                         return false;
423                 }
424         }
425
426         // When dithering is disabled, the color selection must be coordinate-independent - i.e. the entire rendered image must be unicolored.
427
428         if (!m_ditheringEnabled)
429         {
430                 const tcu::RGBA renderedClr00 = renderedImg.getPixel(0, 0);
431
432                 for (int y = 0; y < renderedImg.getHeight(); y++)
433                 {
434                         for (int x = 0; x < renderedImg.getWidth(); x++)
435                         {
436                                 const tcu::RGBA curClr = renderedImg.getPixel(x, y);
437
438                                 if (curClr != renderedClr00)
439                                 {
440                                         log << TestLog::Message
441                                                 << "Failure: color at (" << x << ", " << y << ") is " << curClr
442                                                 << " and does not equal the color at (0, 0), which is " << renderedClr00
443                                                 << TestLog::EndMessage;
444
445                                         return false;
446                                 }
447                         }
448                 }
449         }
450
451         return true;
452 }
453
454 DitheringCase::IterateResult DitheringCase::iterate (void)
455 {
456         if (m_patternType == PATTERNTYPE_GRADIENT)
457         {
458                 // Draw horizontal and vertical gradients.
459
460                 DE_ASSERT(m_iteration < 2);
461
462                 const bool success = drawAndCheckGradient(m_iteration == 1, m_color);
463
464                 if (!success)
465                 {
466                         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
467                         return STOP;
468                 }
469
470                 if (m_iteration == 1)
471                 {
472                         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
473                         return STOP;
474                 }
475         }
476         else if (m_patternType == PATTERNTYPE_UNICOLORED_QUAD)
477         {
478                 const int numQuads = m_testCtx.getCommandLine().getTestIterationCount() > 0 ? m_testCtx.getCommandLine().getTestIterationCount() : 30;
479
480                 DE_ASSERT(m_iteration < numQuads);
481
482                 const Vec4 quadColor    = (float)m_iteration / (float)(numQuads-1) * m_color;
483                 const bool success              =  drawAndCheckUnicoloredQuad(quadColor);
484
485                 if (!success)
486                 {
487                         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
488                         return STOP;
489                 }
490
491                 if (m_iteration == numQuads - 1)
492                 {
493                         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
494                         return STOP;
495                 }
496         }
497         else
498                 DE_ASSERT(false);
499
500         m_iteration++;
501
502         return CONTINUE;
503 }
504
505 DitheringTests::DitheringTests (Context& context)
506         : TestCaseGroup(context, "dither", "Dithering tests")
507 {
508 }
509
510 DitheringTests::~DitheringTests (void)
511 {
512 }
513
514 void DitheringTests::init (void)
515 {
516         static const struct
517         {
518                 const char*             name;
519                 Vec4                    color;
520         } caseColors[] =
521         {
522                 { "white",              Vec4(1.0f, 1.0f, 1.0f, 1.0f) },
523                 { "red",                Vec4(1.0f, 0.0f, 0.0f, 1.0f) },
524                 { "green",              Vec4(0.0f, 1.0f, 0.0f, 1.0f) },
525                 { "blue",               Vec4(0.0f, 0.0f, 1.0f, 1.0f) },
526                 { "alpha",              Vec4(0.0f, 0.0f, 0.0f, 1.0f) }
527         };
528
529         for (int ditheringEnabledI = 0; ditheringEnabledI <= 1; ditheringEnabledI++)
530         {
531                 const bool                              ditheringEnabled        = ditheringEnabledI != 0;
532                 TestCaseGroup* const    group                           = new TestCaseGroup(m_context, ditheringEnabled ? "enabled" : "disabled", "");
533                 addChild(group);
534
535                 for (int patternTypeI = 0; patternTypeI < DitheringCase::PATTERNTYPE_LAST; patternTypeI++)
536                 {
537                         for (int caseColorNdx = 0; caseColorNdx < DE_LENGTH_OF_ARRAY(caseColors); caseColorNdx++)
538                         {
539                                 const DitheringCase::PatternType        patternType             = (DitheringCase::PatternType)patternTypeI;
540                                 const string                                            caseName                = string("") + DitheringCase::getPatternTypeName(patternType) + "_" + caseColors[caseColorNdx].name;
541
542                                 group->addChild(new DitheringCase(m_context.getTestContext(), m_context.getRenderContext(), caseName.c_str(), "", ditheringEnabled, patternType, caseColors[caseColorNdx].color));
543                         }
544                 }
545         }
546 }
547
548 } // Functional
549 } // gles2
550 } // deqp