xref: /aosp_15_r20/external/deqp/modules/gles3/functional/es3fShaderDerivateTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Shader derivate function tests.
22  *
23  * \todo [2013-06-25 pyry] Missing features:
24  *  - lines and points
25  *  - projected coordinates
26  *  - continous non-trivial functions (sin, exp)
27  *  - non-continous functions (step)
28  *//*--------------------------------------------------------------------*/
29 
30 #include "es3fShaderDerivateTests.hpp"
31 #include "gluShaderProgram.hpp"
32 #include "gluRenderContext.hpp"
33 #include "gluDrawUtil.hpp"
34 #include "gluPixelTransfer.hpp"
35 #include "gluShaderUtil.hpp"
36 #include "gluStrUtil.hpp"
37 #include "gluTextureUtil.hpp"
38 #include "gluTexture.hpp"
39 #include "tcuStringTemplate.hpp"
40 #include "tcuRenderTarget.hpp"
41 #include "tcuSurface.hpp"
42 #include "tcuTestLog.hpp"
43 #include "tcuVectorUtil.hpp"
44 #include "tcuTextureUtil.hpp"
45 #include "tcuRGBA.hpp"
46 #include "tcuFloat.hpp"
47 #include "tcuInterval.hpp"
48 #include "deRandom.hpp"
49 #include "deUniquePtr.hpp"
50 #include "deString.h"
51 #include "glwEnums.hpp"
52 #include "glwFunctions.hpp"
53 #include "glsShaderRenderCase.hpp" // gls::setupDefaultUniforms()
54 
55 #include <sstream>
56 
57 namespace deqp
58 {
59 namespace gles3
60 {
61 namespace Functional
62 {
63 
64 using std::map;
65 using std::ostringstream;
66 using std::string;
67 using std::vector;
68 using tcu::TestLog;
69 
70 enum
71 {
72     VIEWPORT_WIDTH      = 167,
73     VIEWPORT_HEIGHT     = 103,
74     FBO_WIDTH           = 99,
75     FBO_HEIGHT          = 133,
76     MAX_FAILED_MESSAGES = 10
77 };
78 
79 enum DerivateFunc
80 {
81     DERIVATE_DFDX = 0,
82     DERIVATE_DFDY,
83     DERIVATE_FWIDTH,
84 
85     DERIVATE_LAST
86 };
87 
88 enum SurfaceType
89 {
90     SURFACETYPE_DEFAULT_FRAMEBUFFER = 0,
91     SURFACETYPE_UNORM_FBO,
92     SURFACETYPE_FLOAT_FBO, // \note Uses RGBA32UI fbo actually, since FP rendertargets are not in core spec.
93 
94     SURFACETYPE_LAST
95 };
96 
97 // Utilities
98 
99 namespace
100 {
101 
102 class AutoFbo
103 {
104 public:
AutoFbo(const glw::Functions & gl)105     AutoFbo(const glw::Functions &gl) : m_gl(gl), m_fbo(0)
106     {
107     }
108 
~AutoFbo(void)109     ~AutoFbo(void)
110     {
111         if (m_fbo)
112             m_gl.deleteFramebuffers(1, &m_fbo);
113     }
114 
gen(void)115     void gen(void)
116     {
117         DE_ASSERT(!m_fbo);
118         m_gl.genFramebuffers(1, &m_fbo);
119     }
120 
operator *(void) const121     uint32_t operator*(void) const
122     {
123         return m_fbo;
124     }
125 
126 private:
127     const glw::Functions &m_gl;
128     uint32_t m_fbo;
129 };
130 
131 class AutoRbo
132 {
133 public:
AutoRbo(const glw::Functions & gl)134     AutoRbo(const glw::Functions &gl) : m_gl(gl), m_rbo(0)
135     {
136     }
137 
~AutoRbo(void)138     ~AutoRbo(void)
139     {
140         if (m_rbo)
141             m_gl.deleteRenderbuffers(1, &m_rbo);
142     }
143 
gen(void)144     void gen(void)
145     {
146         DE_ASSERT(!m_rbo);
147         m_gl.genRenderbuffers(1, &m_rbo);
148     }
149 
operator *(void) const150     uint32_t operator*(void) const
151     {
152         return m_rbo;
153     }
154 
155 private:
156     const glw::Functions &m_gl;
157     uint32_t m_rbo;
158 };
159 
160 } // namespace
161 
getDerivateFuncName(DerivateFunc func)162 static const char *getDerivateFuncName(DerivateFunc func)
163 {
164     switch (func)
165     {
166     case DERIVATE_DFDX:
167         return "dFdx";
168     case DERIVATE_DFDY:
169         return "dFdy";
170     case DERIVATE_FWIDTH:
171         return "fwidth";
172     default:
173         DE_ASSERT(false);
174         return DE_NULL;
175     }
176 }
177 
getDerivateFuncCaseName(DerivateFunc func)178 static const char *getDerivateFuncCaseName(DerivateFunc func)
179 {
180     switch (func)
181     {
182     case DERIVATE_DFDX:
183         return "dfdx";
184     case DERIVATE_DFDY:
185         return "dfdy";
186     case DERIVATE_FWIDTH:
187         return "fwidth";
188     default:
189         DE_ASSERT(false);
190         return DE_NULL;
191     }
192 }
193 
getDerivateMask(glu::DataType type)194 static inline tcu::BVec4 getDerivateMask(glu::DataType type)
195 {
196     switch (type)
197     {
198     case glu::TYPE_FLOAT:
199         return tcu::BVec4(true, false, false, false);
200     case glu::TYPE_FLOAT_VEC2:
201         return tcu::BVec4(true, true, false, false);
202     case glu::TYPE_FLOAT_VEC3:
203         return tcu::BVec4(true, true, true, false);
204     case glu::TYPE_FLOAT_VEC4:
205         return tcu::BVec4(true, true, true, true);
206     default:
207         DE_ASSERT(false);
208         return tcu::BVec4(true);
209     }
210 }
211 
readDerivate(const tcu::ConstPixelBufferAccess & surface,const tcu::Vec4 & derivScale,const tcu::Vec4 & derivBias,int x,int y)212 static inline tcu::Vec4 readDerivate(const tcu::ConstPixelBufferAccess &surface, const tcu::Vec4 &derivScale,
213                                      const tcu::Vec4 &derivBias, int x, int y)
214 {
215     return (surface.getPixel(x, y) - derivBias) / derivScale;
216 }
217 
getCompExpBits(const tcu::Vec4 & v)218 static inline tcu::UVec4 getCompExpBits(const tcu::Vec4 &v)
219 {
220     return tcu::UVec4(tcu::Float32(v[0]).exponentBits(), tcu::Float32(v[1]).exponentBits(),
221                       tcu::Float32(v[2]).exponentBits(), tcu::Float32(v[3]).exponentBits());
222 }
223 
computeFloatingPointError(const float value,const int numAccurateBits)224 float computeFloatingPointError(const float value, const int numAccurateBits)
225 {
226     const int numGarbageBits = 23 - numAccurateBits;
227     const uint32_t mask      = (1u << numGarbageBits) - 1u;
228     const int exp            = (tcu::Float32(value).exponent() < -3) ? -3 : tcu::Float32(value).exponent();
229 
230     return tcu::Float32::construct(+1, exp, (1u << 23) | mask).asFloat() -
231            tcu::Float32::construct(+1, exp, 1u << 23).asFloat();
232 }
233 
getNumMantissaBits(const glu::Precision precision)234 static int getNumMantissaBits(const glu::Precision precision)
235 {
236     switch (precision)
237     {
238     case glu::PRECISION_HIGHP:
239         return 23;
240     case glu::PRECISION_MEDIUMP:
241         return 10;
242     case glu::PRECISION_LOWP:
243         return 6;
244     default:
245         DE_ASSERT(false);
246         return 0;
247     }
248 }
249 
getMinExponent(const glu::Precision precision)250 static int getMinExponent(const glu::Precision precision)
251 {
252     switch (precision)
253     {
254     case glu::PRECISION_HIGHP:
255         return -126;
256     case glu::PRECISION_MEDIUMP:
257         return -14;
258     case glu::PRECISION_LOWP:
259         return -8;
260     default:
261         DE_ASSERT(false);
262         return 0;
263     }
264 }
265 
getSingleULPForExponent(int exp,int numMantissaBits)266 static float getSingleULPForExponent(int exp, int numMantissaBits)
267 {
268     if (numMantissaBits > 0)
269     {
270         DE_ASSERT(numMantissaBits <= 23);
271 
272         const int ulpBitNdx = 23 - numMantissaBits;
273         return tcu::Float32::construct(+1, exp, (1 << 23) | (1 << ulpBitNdx)).asFloat() -
274                tcu::Float32::construct(+1, exp, (1 << 23)).asFloat();
275     }
276     else
277     {
278         DE_ASSERT(numMantissaBits == 0);
279         return tcu::Float32::construct(+1, exp, (1 << 23)).asFloat();
280     }
281 }
282 
getSingleULPForValue(float value,int numMantissaBits)283 static float getSingleULPForValue(float value, int numMantissaBits)
284 {
285     const int exp = tcu::Float32(value).exponent();
286     return getSingleULPForExponent(exp, numMantissaBits);
287 }
288 
convertFloatFlushToZeroRtn(float value,int minExponent,int numAccurateBits)289 static float convertFloatFlushToZeroRtn(float value, int minExponent, int numAccurateBits)
290 {
291     if (value == 0.0f)
292     {
293         return 0.0f;
294     }
295     else
296     {
297         const tcu::Float32 inputFloat = tcu::Float32(value);
298         const int numTruncatedBits    = 23 - numAccurateBits;
299         const uint32_t truncMask      = (1u << numTruncatedBits) - 1u;
300 
301         if (value > 0.0f)
302         {
303             if (value > 0.0f && tcu::Float32(value).exponent() < minExponent)
304             {
305                 // flush to zero if possible
306                 return 0.0f;
307             }
308             else
309             {
310                 // just mask away non-representable bits
311                 return tcu::Float32::construct(+1, inputFloat.exponent(), inputFloat.mantissa() & ~truncMask).asFloat();
312             }
313         }
314         else
315         {
316             if (inputFloat.mantissa() & truncMask)
317             {
318                 // decrement one ulp if truncated bits are non-zero (i.e. if value is not representable)
319                 return tcu::Float32::construct(-1, inputFloat.exponent(), inputFloat.mantissa() & ~truncMask)
320                            .asFloat() -
321                        getSingleULPForExponent(inputFloat.exponent(), numAccurateBits);
322             }
323             else
324             {
325                 // value is representable, no need to do anything
326                 return value;
327             }
328         }
329     }
330 }
331 
convertFloatFlushToZeroRtp(float value,int minExponent,int numAccurateBits)332 static float convertFloatFlushToZeroRtp(float value, int minExponent, int numAccurateBits)
333 {
334     return -convertFloatFlushToZeroRtn(-value, minExponent, numAccurateBits);
335 }
336 
addErrorUlp(float value,float numUlps,int numMantissaBits)337 static float addErrorUlp(float value, float numUlps, int numMantissaBits)
338 {
339     return value + numUlps * getSingleULPForValue(value, numMantissaBits);
340 }
341 
342 enum
343 {
344     INTERPOLATION_LOST_BITS = 3, // number mantissa of bits allowed to be lost in varying interpolation
345 };
346 
getInterpolationLostBitsWarning(const glu::Precision precision)347 static int getInterpolationLostBitsWarning(const glu::Precision precision)
348 {
349     // number mantissa of bits allowed to be lost in varying interpolation
350     switch (precision)
351     {
352     case glu::PRECISION_HIGHP:
353         return 9;
354     case glu::PRECISION_MEDIUMP:
355         return 3;
356     case glu::PRECISION_LOWP:
357         return 3;
358     default:
359         DE_ASSERT(false);
360         return 0;
361     }
362 }
363 
getDerivateThreshold(const glu::Precision precision,const tcu::Vec4 & valueMin,const tcu::Vec4 & valueMax,const tcu::Vec4 & expectedDerivate)364 static inline tcu::Vec4 getDerivateThreshold(const glu::Precision precision, const tcu::Vec4 &valueMin,
365                                              const tcu::Vec4 &valueMax, const tcu::Vec4 &expectedDerivate)
366 {
367     const int baseBits           = getNumMantissaBits(precision);
368     const tcu::UVec4 derivExp    = getCompExpBits(expectedDerivate);
369     const tcu::UVec4 maxValueExp = max(getCompExpBits(valueMin), getCompExpBits(valueMax));
370     const tcu::UVec4 numBitsLost = maxValueExp - min(maxValueExp, derivExp);
371     const tcu::IVec4 numAccurateBits =
372         max(baseBits - numBitsLost.asInt() - (int)INTERPOLATION_LOST_BITS, tcu::IVec4(0));
373 
374     return tcu::Vec4(computeFloatingPointError(expectedDerivate[0], numAccurateBits[0]),
375                      computeFloatingPointError(expectedDerivate[1], numAccurateBits[1]),
376                      computeFloatingPointError(expectedDerivate[2], numAccurateBits[2]),
377                      computeFloatingPointError(expectedDerivate[3], numAccurateBits[3]));
378 }
379 
getDerivateThresholdWarning(const glu::Precision precision,const tcu::Vec4 & valueMin,const tcu::Vec4 & valueMax,const tcu::Vec4 & expectedDerivate)380 static inline tcu::Vec4 getDerivateThresholdWarning(const glu::Precision precision, const tcu::Vec4 &valueMin,
381                                                     const tcu::Vec4 &valueMax, const tcu::Vec4 &expectedDerivate)
382 {
383     const int baseBits           = getNumMantissaBits(precision);
384     const tcu::UVec4 derivExp    = getCompExpBits(expectedDerivate);
385     const tcu::UVec4 maxValueExp = max(getCompExpBits(valueMin), getCompExpBits(valueMax));
386     const tcu::UVec4 numBitsLost = maxValueExp - min(maxValueExp, derivExp);
387     const tcu::IVec4 numAccurateBits =
388         max(baseBits - numBitsLost.asInt() - getInterpolationLostBitsWarning(precision), tcu::IVec4(0));
389 
390     return tcu::Vec4(computeFloatingPointError(expectedDerivate[0], numAccurateBits[0]),
391                      computeFloatingPointError(expectedDerivate[1], numAccurateBits[1]),
392                      computeFloatingPointError(expectedDerivate[2], numAccurateBits[2]),
393                      computeFloatingPointError(expectedDerivate[3], numAccurateBits[3]));
394 }
395 
396 namespace
397 {
398 
399 struct LogVecComps
400 {
401     const tcu::Vec4 &v;
402     int numComps;
403 
LogVecCompsdeqp::gles3::Functional::__anonf22f2d770411::LogVecComps404     LogVecComps(const tcu::Vec4 &v_, int numComps_) : v(v_), numComps(numComps_)
405     {
406     }
407 };
408 
operator <<(std::ostream & str,const LogVecComps & v)409 std::ostream &operator<<(std::ostream &str, const LogVecComps &v)
410 {
411     DE_ASSERT(de::inRange(v.numComps, 1, 4));
412     if (v.numComps == 1)
413         return str << v.v[0];
414     else if (v.numComps == 2)
415         return str << v.v.toWidth<2>();
416     else if (v.numComps == 3)
417         return str << v.v.toWidth<3>();
418     else
419         return str << v.v;
420 }
421 
422 } // namespace
423 
424 enum VerificationLogging
425 {
426     LOG_ALL = 0,
427     LOG_NOTHING
428 };
429 
verifyConstantDerivate(tcu::TestLog & log,const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask,glu::DataType dataType,const tcu::Vec4 & reference,const tcu::Vec4 & threshold,const tcu::Vec4 & scale,const tcu::Vec4 & bias,VerificationLogging logPolicy=LOG_ALL)430 static qpTestResult verifyConstantDerivate(tcu::TestLog &log, const tcu::ConstPixelBufferAccess &result,
431                                            const tcu::PixelBufferAccess &errorMask, glu::DataType dataType,
432                                            const tcu::Vec4 &reference, const tcu::Vec4 &threshold,
433                                            const tcu::Vec4 &scale, const tcu::Vec4 &bias,
434                                            VerificationLogging logPolicy = LOG_ALL)
435 {
436     const int numComps    = glu::getDataTypeFloatScalars(dataType);
437     const tcu::BVec4 mask = tcu::logicalNot(getDerivateMask(dataType));
438     int numFailedPixels   = 0;
439 
440     if (logPolicy == LOG_ALL)
441         log << TestLog::Message << "Expecting " << LogVecComps(reference, numComps) << " with threshold "
442             << LogVecComps(threshold, numComps) << TestLog::EndMessage;
443 
444     for (int y = 0; y < result.getHeight(); y++)
445     {
446         for (int x = 0; x < result.getWidth(); x++)
447         {
448             const tcu::Vec4 resDerivate = readDerivate(result, scale, bias, x, y);
449             const bool isOk =
450                 tcu::allEqual(tcu::logicalOr(tcu::lessThanEqual(tcu::abs(reference - resDerivate), threshold), mask),
451                               tcu::BVec4(true));
452 
453             if (!isOk)
454             {
455                 if (numFailedPixels < MAX_FAILED_MESSAGES && logPolicy == LOG_ALL)
456                     log << TestLog::Message << "FAIL: got " << LogVecComps(resDerivate, numComps)
457                         << ", diff = " << LogVecComps(tcu::abs(reference - resDerivate), numComps) << ", at x = " << x
458                         << ", y = " << y << TestLog::EndMessage;
459                 numFailedPixels += 1;
460                 errorMask.setPixel(tcu::RGBA::red().toVec(), x, y);
461             }
462         }
463     }
464 
465     if (numFailedPixels >= MAX_FAILED_MESSAGES && logPolicy == LOG_ALL)
466         log << TestLog::Message << "..." << TestLog::EndMessage;
467 
468     if (numFailedPixels > 0 && logPolicy == LOG_ALL)
469         log << TestLog::Message << "FAIL: found " << numFailedPixels << " failed pixels" << TestLog::EndMessage;
470 
471     return (numFailedPixels == 0) ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
472 }
473 
474 struct Linear2DFunctionEvaluator
475 {
476     tcu::Matrix<float, 4, 3> matrix;
477 
478     //      .-----.
479     //      | s_x |
480     //  M x | s_y |
481     //      | 1.0 |
482     //      '-----'
483     tcu::Vec4 evaluateAt(float screenX, float screenY) const;
484 };
485 
evaluateAt(float screenX,float screenY) const486 tcu::Vec4 Linear2DFunctionEvaluator::evaluateAt(float screenX, float screenY) const
487 {
488     const tcu::Vec3 position(screenX, screenY, 1.0f);
489     return matrix * position;
490 }
491 
reverifyConstantDerivateWithFlushRelaxations(tcu::TestLog & log,const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask,glu::DataType dataType,glu::Precision precision,const tcu::Vec4 & derivScale,const tcu::Vec4 & derivBias,const tcu::Vec4 & surfaceThreshold,DerivateFunc derivateFunc,const Linear2DFunctionEvaluator & function)492 static qpTestResult reverifyConstantDerivateWithFlushRelaxations(
493     tcu::TestLog &log, const tcu::ConstPixelBufferAccess &result, const tcu::PixelBufferAccess &errorMask,
494     glu::DataType dataType, glu::Precision precision, const tcu::Vec4 &derivScale, const tcu::Vec4 &derivBias,
495     const tcu::Vec4 &surfaceThreshold, DerivateFunc derivateFunc, const Linear2DFunctionEvaluator &function)
496 {
497     DE_ASSERT(result.getWidth() == errorMask.getWidth());
498     DE_ASSERT(result.getHeight() == errorMask.getHeight());
499     DE_ASSERT(derivateFunc == DERIVATE_DFDX || derivateFunc == DERIVATE_DFDY);
500 
501     const tcu::IVec4 red(255, 0, 0, 255);
502     const tcu::IVec4 green(0, 255, 0, 255);
503     const float divisionErrorUlps = 2.5f;
504 
505     const int numComponents = glu::getDataTypeFloatScalars(dataType);
506     const int numBits       = getNumMantissaBits(precision);
507     const int minExponent   = getMinExponent(precision);
508 
509     const int numVaryingSampleBits = numBits - INTERPOLATION_LOST_BITS;
510     int numFailedPixels            = 0;
511 
512     tcu::clear(errorMask, green);
513 
514     // search for failed pixels
515     for (int y = 0; y < result.getHeight(); ++y)
516         for (int x = 0; x < result.getWidth(); ++x)
517         {
518             //                 flushToZero?(f2z?(functionValueCurrent) - f2z?(functionValueBefore))
519             // flushToZero? ( ------------------------------------------------------------------------ +- 2.5 ULP )
520             //                                                  dx
521 
522             const tcu::Vec4 resultDerivative = readDerivate(result, derivScale, derivBias, x, y);
523 
524             // sample at the front of the back pixel and the back of the front pixel to cover the whole area of
525             // legal sample positions. In general case this is NOT OK, but we know that the target function is
526             // (mostly*) linear which allows us to take the sample points at arbitrary points. This gets us the
527             // maximum difference possible in exponents which are used in error bound calculations.
528             // * non-linearity may happen around zero or with very high function values due to subnorms not
529             //   behaving well.
530             const tcu::Vec4 functionValueForward  = (derivateFunc == DERIVATE_DFDX) ?
531                                                         (function.evaluateAt((float)x + 2.0f, (float)y + 0.5f)) :
532                                                         (function.evaluateAt((float)x + 0.5f, (float)y + 2.0f));
533             const tcu::Vec4 functionValueBackward = (derivateFunc == DERIVATE_DFDX) ?
534                                                         (function.evaluateAt((float)x - 1.0f, (float)y + 0.5f)) :
535                                                         (function.evaluateAt((float)x + 0.5f, (float)y - 1.0f));
536 
537             bool anyComponentFailed = false;
538 
539             // check components separately
540             for (int c = 0; c < numComponents; ++c)
541             {
542                 // Simulate interpolation. Add allowed interpolation error and round to target precision. Allow one half ULP (i.e. correct rounding)
543                 const tcu::Interval forwardComponent(
544                     convertFloatFlushToZeroRtn(addErrorUlp((float)functionValueForward[c], -0.5f, numVaryingSampleBits),
545                                                minExponent, numBits),
546                     convertFloatFlushToZeroRtp(addErrorUlp((float)functionValueForward[c], +0.5f, numVaryingSampleBits),
547                                                minExponent, numBits));
548                 const tcu::Interval backwardComponent(
549                     convertFloatFlushToZeroRtn(
550                         addErrorUlp((float)functionValueBackward[c], -0.5f, numVaryingSampleBits), minExponent,
551                         numBits),
552                     convertFloatFlushToZeroRtp(
553                         addErrorUlp((float)functionValueBackward[c], +0.5f, numVaryingSampleBits), minExponent,
554                         numBits));
555                 const int maxValueExp = de::max(de::max(tcu::Float32(forwardComponent.lo()).exponent(),
556                                                         tcu::Float32(forwardComponent.hi()).exponent()),
557                                                 de::max(tcu::Float32(backwardComponent.lo()).exponent(),
558                                                         tcu::Float32(backwardComponent.hi()).exponent()));
559 
560                 // subtraction in numerator will likely cause a cancellation of the most
561                 // significant bits. Apply error bounds.
562 
563                 const tcu::Interval numerator(forwardComponent - backwardComponent);
564                 const int numeratorLoExp      = tcu::Float32(numerator.lo()).exponent();
565                 const int numeratorHiExp      = tcu::Float32(numerator.hi()).exponent();
566                 const int numeratorLoBitsLost = de::max(
567                     0,
568                     maxValueExp -
569                         numeratorLoExp); //!< must clamp to zero since if forward and backward components have different
570                 const int numeratorHiBitsLost = de::max(
571                     0, maxValueExp - numeratorHiExp); //!< sign, numerator might have larger exponent than its operands.
572                 const int numeratorLoBits = de::max(0, numBits - numeratorLoBitsLost);
573                 const int numeratorHiBits = de::max(0, numBits - numeratorHiBitsLost);
574 
575                 const tcu::Interval numeratorRange(
576                     convertFloatFlushToZeroRtn((float)numerator.lo(), minExponent, numeratorLoBits),
577                     convertFloatFlushToZeroRtp((float)numerator.hi(), minExponent, numeratorHiBits));
578 
579                 const tcu::Interval divisionRange =
580                     numeratorRange /
581                     3.0f; // legal sample area is anywhere within this and neighboring pixels (i.e. size = 3)
582                 const tcu::Interval divisionResultRange(
583                     convertFloatFlushToZeroRtn(addErrorUlp((float)divisionRange.lo(), -divisionErrorUlps, numBits),
584                                                minExponent, numBits),
585                     convertFloatFlushToZeroRtp(addErrorUlp((float)divisionRange.hi(), +divisionErrorUlps, numBits),
586                                                minExponent, numBits));
587                 const tcu::Interval finalResultRange(divisionResultRange.lo() - surfaceThreshold[c],
588                                                      divisionResultRange.hi() + surfaceThreshold[c]);
589 
590                 if (resultDerivative[c] >= finalResultRange.lo() && resultDerivative[c] <= finalResultRange.hi())
591                 {
592                     // value ok
593                 }
594                 else
595                 {
596                     if (numFailedPixels < MAX_FAILED_MESSAGES)
597                         log << tcu::TestLog::Message << "Error in pixel at " << x << ", " << y << " with component "
598                             << c << " (channel " << ("rgba"[c]) << ")\n"
599                             << "\tGot pixel value " << result.getPixelInt(x, y) << "\n"
600                             << "\t\tdFd" << ((derivateFunc == DERIVATE_DFDX) ? ('x') : ('y'))
601                             << " ~= " << resultDerivative[c] << "\n"
602                             << "\t\tdifference to a valid range: "
603                             << ((resultDerivative[c] < finalResultRange.lo()) ? ("-") : ("+"))
604                             << ((resultDerivative[c] < finalResultRange.lo()) ?
605                                     (finalResultRange.lo() - resultDerivative[c]) :
606                                     (resultDerivative[c] - finalResultRange.hi()))
607                             << "\n"
608                             << "\tDerivative value range:\n"
609                             << "\t\tMin: " << finalResultRange.lo() << "\n"
610                             << "\t\tMax: " << finalResultRange.hi() << "\n"
611                             << tcu::TestLog::EndMessage;
612 
613                     ++numFailedPixels;
614                     anyComponentFailed = true;
615                 }
616             }
617 
618             if (anyComponentFailed)
619                 errorMask.setPixel(red, x, y);
620         }
621 
622     if (numFailedPixels >= MAX_FAILED_MESSAGES)
623         log << TestLog::Message << "..." << TestLog::EndMessage;
624 
625     if (numFailedPixels > 0)
626         log << TestLog::Message << "FAIL: found " << numFailedPixels << " failed pixels" << TestLog::EndMessage;
627 
628     return (numFailedPixels == 0) ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
629 }
630 
631 // TriangleDerivateCase
632 
633 class TriangleDerivateCase : public TestCase
634 {
635 public:
636     TriangleDerivateCase(Context &context, const char *name, const char *description);
637     ~TriangleDerivateCase(void);
638 
639     IterateResult iterate(void);
640 
641 protected:
setupRenderState(uint32_t program)642     virtual void setupRenderState(uint32_t program)
643     {
644         DE_UNREF(program);
645     }
646     virtual qpTestResult verify(const tcu::ConstPixelBufferAccess &result,
647                                 const tcu::PixelBufferAccess &errorMask) = DE_NULL;
648 
649     tcu::IVec2 getViewportSize(void) const;
650     tcu::Vec4 getSurfaceThreshold(void) const;
651 
652     glu::DataType m_dataType;
653     glu::Precision m_precision;
654 
655     glu::DataType m_coordDataType;
656     glu::Precision m_coordPrecision;
657 
658     std::string m_fragmentSrc;
659 
660     tcu::Vec4 m_coordMin;
661     tcu::Vec4 m_coordMax;
662     tcu::Vec4 m_derivScale;
663     tcu::Vec4 m_derivBias;
664 
665     SurfaceType m_surfaceType;
666     int m_numSamples;
667     uint32_t m_hint;
668 
669     bool m_useAsymmetricCoords;
670 };
671 
TriangleDerivateCase(Context & context,const char * name,const char * description)672 TriangleDerivateCase::TriangleDerivateCase(Context &context, const char *name, const char *description)
673     : TestCase(context, name, description)
674     , m_dataType(glu::TYPE_LAST)
675     , m_precision(glu::PRECISION_LAST)
676     , m_coordDataType(glu::TYPE_LAST)
677     , m_coordPrecision(glu::PRECISION_LAST)
678     , m_surfaceType(SURFACETYPE_DEFAULT_FRAMEBUFFER)
679     , m_numSamples(0)
680     , m_hint(GL_DONT_CARE)
681     , m_useAsymmetricCoords(false)
682 {
683     DE_ASSERT(m_surfaceType != SURFACETYPE_DEFAULT_FRAMEBUFFER || m_numSamples == 0);
684 }
685 
~TriangleDerivateCase(void)686 TriangleDerivateCase::~TriangleDerivateCase(void)
687 {
688     TriangleDerivateCase::deinit();
689 }
690 
genVertexSource(glu::DataType coordType,glu::Precision precision)691 static std::string genVertexSource(glu::DataType coordType, glu::Precision precision)
692 {
693     DE_ASSERT(glu::isDataTypeFloatOrVec(coordType));
694 
695     const char *vertexTmpl = "#version 300 es\n"
696                              "in highp vec4 a_position;\n"
697                              "in ${PRECISION} ${DATATYPE} a_coord;\n"
698                              "out ${PRECISION} ${DATATYPE} v_coord;\n"
699                              "void main (void)\n"
700                              "{\n"
701                              "    gl_Position = a_position;\n"
702                              "    v_coord = a_coord;\n"
703                              "}\n";
704 
705     map<string, string> vertexParams;
706 
707     vertexParams["PRECISION"] = glu::getPrecisionName(precision);
708     vertexParams["DATATYPE"]  = glu::getDataTypeName(coordType);
709 
710     return tcu::StringTemplate(vertexTmpl).specialize(vertexParams);
711 }
712 
getViewportSize(void) const713 inline tcu::IVec2 TriangleDerivateCase::getViewportSize(void) const
714 {
715     if (m_surfaceType == SURFACETYPE_DEFAULT_FRAMEBUFFER)
716     {
717         const int width  = de::min<int>(m_context.getRenderTarget().getWidth(), VIEWPORT_WIDTH);
718         const int height = de::min<int>(m_context.getRenderTarget().getHeight(), VIEWPORT_HEIGHT);
719         return tcu::IVec2(width, height);
720     }
721     else
722         return tcu::IVec2(FBO_WIDTH, FBO_HEIGHT);
723 }
724 
iterate(void)725 TriangleDerivateCase::IterateResult TriangleDerivateCase::iterate(void)
726 {
727     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
728     const glu::ShaderProgram program(
729         m_context.getRenderContext(),
730         glu::makeVtxFragSources(genVertexSource(m_coordDataType, m_coordPrecision), m_fragmentSrc));
731     de::Random rnd(deStringHash(getName()) ^ 0xbbc24);
732     const bool useFbo             = m_surfaceType != SURFACETYPE_DEFAULT_FRAMEBUFFER;
733     const uint32_t fboFormat      = m_surfaceType == SURFACETYPE_FLOAT_FBO ? GL_RGBA32UI : GL_RGBA8;
734     const tcu::IVec2 viewportSize = getViewportSize();
735     const int viewportX = useFbo ? 0 : rnd.getInt(0, m_context.getRenderTarget().getWidth() - viewportSize.x());
736     const int viewportY = useFbo ? 0 : rnd.getInt(0, m_context.getRenderTarget().getHeight() - viewportSize.y());
737     AutoFbo fbo(gl);
738     AutoRbo rbo(gl);
739     tcu::TextureLevel result;
740 
741     m_testCtx.getLog() << program;
742 
743     if (!program.isOk())
744         TCU_FAIL("Compile failed");
745 
746     if (useFbo)
747     {
748         m_testCtx.getLog() << TestLog::Message << "Rendering to FBO, format = " << glu::getTextureFormatStr(fboFormat)
749                            << ", samples = " << m_numSamples << TestLog::EndMessage;
750 
751         fbo.gen();
752         rbo.gen();
753 
754         gl.bindRenderbuffer(GL_RENDERBUFFER, *rbo);
755         gl.renderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, fboFormat, viewportSize.x(), viewportSize.y());
756         gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
757         gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *rbo);
758         TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
759     }
760     else
761     {
762         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
763 
764         m_testCtx.getLog() << TestLog::Message << "Rendering to default framebuffer\n"
765                            << "\tColor depth: R=" << pixelFormat.redBits << ", G=" << pixelFormat.greenBits
766                            << ", B=" << pixelFormat.blueBits << ", A=" << pixelFormat.alphaBits << TestLog::EndMessage;
767     }
768 
769     m_testCtx.getLog() << TestLog::Message << "in: " << m_coordMin << " -> " << m_coordMax << "\n"
770                        << (m_useAsymmetricCoords ? "v_coord.x = in.x * (x+y)/2\n" : "v_coord.x = in.x * x\n")
771                        << (m_useAsymmetricCoords ? "v_coord.y = in.y * (x+y)/2\n" : "v_coord.y = in.y * y\n")
772                        << "v_coord.z = in.z * (x+y)/2\n"
773                        << "v_coord.w = in.w * (1 - (x+y)/2)\n"
774                        << TestLog::EndMessage << TestLog::Message << "u_scale: " << m_derivScale
775                        << ", u_bias: " << m_derivBias << " (displayed values have scale/bias removed)"
776                        << TestLog::EndMessage << TestLog::Message << "Viewport: " << viewportSize.x() << "x"
777                        << viewportSize.y() << TestLog::EndMessage << TestLog::Message
778                        << "GL_FRAGMENT_SHADER_DERIVATE_HINT: " << glu::getHintModeStr(m_hint) << TestLog::EndMessage;
779 
780     // Draw
781     {
782         const float positions[] = {-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
783                                    1.0f,  -1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f, 1.0f};
784         float coords[]          = {m_coordMin.x(),
785                                    m_coordMin.y(),
786                                    m_coordMin.z(),
787                                    m_coordMax.w(),
788                                    m_coordMin.x(),
789                                    m_coordMax.y(),
790                                    (m_coordMin.z() + m_coordMax.z()) * 0.5f,
791                                    (m_coordMin.w() + m_coordMax.w()) * 0.5f,
792                                    m_coordMax.x(),
793                                    m_coordMin.y(),
794                                    (m_coordMin.z() + m_coordMax.z()) * 0.5f,
795                                    (m_coordMin.w() + m_coordMax.w()) * 0.5f,
796                                    m_coordMax.x(),
797                                    m_coordMax.y(),
798                                    m_coordMax.z(),
799                                    m_coordMin.w()};
800 
801         // For linear tests we want varying data x and y to vary along both axes
802         // to get nonzero x for dfdy and nonzero y for dfdx. To make the gradient
803         // the same for both triangles we set vertices 2 and 3 to middle values.
804         // This way the values go from min -> (max+min) / 2 or (max+min) / 2 -> max
805         // depending on the triangle, but the derivative is the same for both.
806         if (m_useAsymmetricCoords)
807         {
808             coords[4] = coords[8] = (m_coordMin.x() + m_coordMax.x()) * 0.5f;
809             coords[5] = coords[9] = (m_coordMin.y() + m_coordMax.y()) * 0.5f;
810         }
811 
812         const glu::VertexArrayBinding vertexArrays[] = {glu::va::Float("a_position", 4, 4, 0, &positions[0]),
813                                                         glu::va::Float("a_coord", 4, 4, 0, &coords[0])};
814         const uint16_t indices[]                     = {0, 2, 1, 2, 3, 1};
815 
816         gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
817         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
818         gl.disable(GL_DITHER);
819 
820         gl.useProgram(program.getProgram());
821 
822         {
823             const int scaleLoc = gl.getUniformLocation(program.getProgram(), "u_scale");
824             const int biasLoc  = gl.getUniformLocation(program.getProgram(), "u_bias");
825 
826             switch (m_dataType)
827             {
828             case glu::TYPE_FLOAT:
829                 gl.uniform1f(scaleLoc, m_derivScale.x());
830                 gl.uniform1f(biasLoc, m_derivBias.x());
831                 break;
832 
833             case glu::TYPE_FLOAT_VEC2:
834                 gl.uniform2fv(scaleLoc, 1, m_derivScale.getPtr());
835                 gl.uniform2fv(biasLoc, 1, m_derivBias.getPtr());
836                 break;
837 
838             case glu::TYPE_FLOAT_VEC3:
839                 gl.uniform3fv(scaleLoc, 1, m_derivScale.getPtr());
840                 gl.uniform3fv(biasLoc, 1, m_derivBias.getPtr());
841                 break;
842 
843             case glu::TYPE_FLOAT_VEC4:
844                 gl.uniform4fv(scaleLoc, 1, m_derivScale.getPtr());
845                 gl.uniform4fv(biasLoc, 1, m_derivBias.getPtr());
846                 break;
847 
848             default:
849                 DE_ASSERT(false);
850             }
851         }
852 
853         gls::setupDefaultUniforms(m_context.getRenderContext(), program.getProgram());
854         setupRenderState(program.getProgram());
855 
856         gl.hint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, m_hint);
857         GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
858 
859         gl.viewport(viewportX, viewportY, viewportSize.x(), viewportSize.y());
860         glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays),
861                   &vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
862         GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
863     }
864 
865     // Read back results
866     {
867         const bool isMSAA = useFbo && m_numSamples > 0;
868         AutoFbo resFbo(gl);
869         AutoRbo resRbo(gl);
870 
871         // Resolve if necessary
872         if (isMSAA)
873         {
874             resFbo.gen();
875             resRbo.gen();
876 
877             gl.bindRenderbuffer(GL_RENDERBUFFER, *resRbo);
878             gl.renderbufferStorageMultisample(GL_RENDERBUFFER, 0, fboFormat, viewportSize.x(), viewportSize.y());
879             gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, *resFbo);
880             gl.framebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *resRbo);
881             TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
882 
883             gl.blitFramebuffer(0, 0, viewportSize.x(), viewportSize.y(), 0, 0, viewportSize.x(), viewportSize.y(),
884                                GL_COLOR_BUFFER_BIT, GL_NEAREST);
885             GLU_EXPECT_NO_ERROR(gl.getError(), "Resolve blit");
886 
887             gl.bindFramebuffer(GL_READ_FRAMEBUFFER, *resFbo);
888         }
889 
890         switch (m_surfaceType)
891         {
892         case SURFACETYPE_DEFAULT_FRAMEBUFFER:
893         case SURFACETYPE_UNORM_FBO:
894             result.setStorage(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
895                               viewportSize.x(), viewportSize.y());
896             glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, result);
897             break;
898 
899         case SURFACETYPE_FLOAT_FBO:
900         {
901             const tcu::TextureFormat dataFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT);
902             const tcu::TextureFormat transferFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT32);
903 
904             result.setStorage(dataFormat, viewportSize.x(), viewportSize.y());
905             glu::readPixels(m_context.getRenderContext(), viewportX, viewportY,
906                             tcu::PixelBufferAccess(transferFormat, result.getWidth(), result.getHeight(),
907                                                    result.getDepth(), result.getAccess().getDataPtr()));
908             break;
909         }
910 
911         default:
912             DE_ASSERT(false);
913         }
914 
915         GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
916     }
917 
918     // Verify
919     {
920         tcu::Surface errorMask(result.getWidth(), result.getHeight());
921         tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
922 
923         const qpTestResult testResult = verify(result.getAccess(), errorMask.getAccess());
924         const char *failStr           = "Fail";
925 
926         m_testCtx.getLog() << TestLog::ImageSet("Result", "Result images")
927                            << TestLog::Image("Rendered", "Rendered image", result);
928 
929         if (testResult != QP_TEST_RESULT_PASS)
930             m_testCtx.getLog() << TestLog::Image("ErrorMask", "Error mask", errorMask);
931 
932         m_testCtx.getLog() << TestLog::EndImageSet;
933 
934         if (testResult == QP_TEST_RESULT_PASS)
935             failStr = "Pass";
936         else if (testResult == QP_TEST_RESULT_QUALITY_WARNING)
937             failStr = "QualityWarning";
938 
939         m_testCtx.setTestResult(testResult, failStr);
940     }
941 
942     return STOP;
943 }
944 
getSurfaceThreshold(void) const945 tcu::Vec4 TriangleDerivateCase::getSurfaceThreshold(void) const
946 {
947     switch (m_surfaceType)
948     {
949     case SURFACETYPE_DEFAULT_FRAMEBUFFER:
950     {
951         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
952         const tcu::IVec4 channelBits(pixelFormat.redBits, pixelFormat.greenBits, pixelFormat.blueBits,
953                                      pixelFormat.alphaBits);
954         const tcu::IVec4 intThreshold = tcu::IVec4(1) << (8 - channelBits);
955         const tcu::Vec4 normThreshold = intThreshold.asFloat() / 255.0f;
956 
957         return normThreshold;
958     }
959 
960     case SURFACETYPE_UNORM_FBO:
961         return tcu::IVec4(1).asFloat() / 255.0f;
962     case SURFACETYPE_FLOAT_FBO:
963         return tcu::Vec4(0.0f);
964     default:
965         DE_ASSERT(false);
966         return tcu::Vec4(0.0f);
967     }
968 }
969 
970 // ConstantDerivateCase
971 
972 class ConstantDerivateCase : public TriangleDerivateCase
973 {
974 public:
975     ConstantDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
976                          glu::DataType type);
~ConstantDerivateCase(void)977     ~ConstantDerivateCase(void)
978     {
979     }
980 
981     void init(void);
982 
983 protected:
984     qpTestResult verify(const tcu::ConstPixelBufferAccess &result, const tcu::PixelBufferAccess &errorMask);
985 
986 private:
987     DerivateFunc m_func;
988 };
989 
ConstantDerivateCase(Context & context,const char * name,const char * description,DerivateFunc func,glu::DataType type)990 ConstantDerivateCase::ConstantDerivateCase(Context &context, const char *name, const char *description,
991                                            DerivateFunc func, glu::DataType type)
992     : TriangleDerivateCase(context, name, description)
993     , m_func(func)
994 {
995     m_dataType       = type;
996     m_precision      = glu::PRECISION_HIGHP;
997     m_coordDataType  = m_dataType;
998     m_coordPrecision = m_precision;
999 }
1000 
init(void)1001 void ConstantDerivateCase::init(void)
1002 {
1003     const char *fragmentTmpl = "#version 300 es\n"
1004                                "layout(location = 0) out mediump vec4 o_color;\n"
1005                                "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1006                                "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1007                                "void main (void)\n"
1008                                "{\n"
1009                                "    ${PRECISION} ${DATATYPE} res = ${FUNC}(${VALUE}) * u_scale + u_bias;\n"
1010                                "    o_color = ${CAST_TO_OUTPUT};\n"
1011                                "}\n";
1012     map<string, string> fragmentParams;
1013     fragmentParams["PRECISION"]      = glu::getPrecisionName(m_precision);
1014     fragmentParams["DATATYPE"]       = glu::getDataTypeName(m_dataType);
1015     fragmentParams["FUNC"]           = getDerivateFuncName(m_func);
1016     fragmentParams["VALUE"]          = m_dataType == glu::TYPE_FLOAT_VEC4 ? "vec4(1.0, 7.2, -1e5, 0.0)" :
1017                                        m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec3(1e2, 8.0, 0.01)" :
1018                                        m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec2(-0.0, 2.7)" :
1019                                                                             /* TYPE_FLOAT */ "7.7";
1020     fragmentParams["CAST_TO_OUTPUT"] = m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1021                                        m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1022                                        m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1023                                                                             /* TYPE_FLOAT */ "vec4(res, 0.0, 0.0, 1.0)";
1024 
1025     m_fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
1026 
1027     m_derivScale = tcu::Vec4(1e3f, 1e3f, 1e3f, 1e3f);
1028     m_derivBias  = tcu::Vec4(0.5f, 0.5f, 0.5f, 0.5f);
1029 }
1030 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)1031 qpTestResult ConstantDerivateCase::verify(const tcu::ConstPixelBufferAccess &result,
1032                                           const tcu::PixelBufferAccess &errorMask)
1033 {
1034     const tcu::Vec4 reference(0.0f); // Derivate of constant argument should always be 0
1035     const tcu::Vec4 threshold = getSurfaceThreshold() / abs(m_derivScale);
1036 
1037     return verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, threshold, m_derivScale,
1038                                   m_derivBias);
1039 }
1040 
1041 // LinearDerivateCase
1042 
1043 class LinearDerivateCase : public TriangleDerivateCase
1044 {
1045 public:
1046     LinearDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1047                        glu::DataType type, glu::Precision precision, uint32_t hint, SurfaceType surfaceType,
1048                        int numSamples, const char *fragmentSrcTmpl);
~LinearDerivateCase(void)1049     ~LinearDerivateCase(void)
1050     {
1051     }
1052 
1053     void init(void);
1054 
1055 protected:
1056     qpTestResult verify(const tcu::ConstPixelBufferAccess &result, const tcu::PixelBufferAccess &errorMask);
1057 
1058 private:
1059     DerivateFunc m_func;
1060     std::string m_fragmentTmpl;
1061 };
1062 
LinearDerivateCase(Context & context,const char * name,const char * description,DerivateFunc func,glu::DataType type,glu::Precision precision,uint32_t hint,SurfaceType surfaceType,int numSamples,const char * fragmentSrcTmpl)1063 LinearDerivateCase::LinearDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1064                                        glu::DataType type, glu::Precision precision, uint32_t hint,
1065                                        SurfaceType surfaceType, int numSamples, const char *fragmentSrcTmpl)
1066     : TriangleDerivateCase(context, name, description)
1067     , m_func(func)
1068     , m_fragmentTmpl(fragmentSrcTmpl)
1069 {
1070     m_dataType            = type;
1071     m_precision           = precision;
1072     m_coordDataType       = m_dataType;
1073     m_coordPrecision      = m_precision;
1074     m_hint                = hint;
1075     m_surfaceType         = surfaceType;
1076     m_numSamples          = numSamples;
1077     m_useAsymmetricCoords = true;
1078 }
1079 
init(void)1080 void LinearDerivateCase::init(void)
1081 {
1082     const tcu::IVec2 viewportSize = getViewportSize();
1083     const float w                 = float(viewportSize.x());
1084     const float h                 = float(viewportSize.y());
1085     const bool packToInt          = m_surfaceType == SURFACETYPE_FLOAT_FBO;
1086     map<string, string> fragmentParams;
1087 
1088     fragmentParams["OUTPUT_TYPE"] = glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1089     fragmentParams["OUTPUT_PREC"] = glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_precision);
1090     fragmentParams["PRECISION"]   = glu::getPrecisionName(m_precision);
1091     fragmentParams["DATATYPE"]    = glu::getDataTypeName(m_dataType);
1092     fragmentParams["FUNC"]        = getDerivateFuncName(m_func);
1093 
1094     if (packToInt)
1095     {
1096         fragmentParams["CAST_TO_OUTPUT"] =
1097             m_dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1098             m_dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1099             m_dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1100                                                  /* TYPE_FLOAT */ "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1101     }
1102     else
1103     {
1104         fragmentParams["CAST_TO_OUTPUT"] =
1105             m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1106             m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1107             m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1108                                                  /* TYPE_FLOAT */ "vec4(res, 0.0, 0.0, 1.0)";
1109     }
1110 
1111     m_fragmentSrc = tcu::StringTemplate(m_fragmentTmpl.c_str()).specialize(fragmentParams);
1112 
1113     switch (m_precision)
1114     {
1115     case glu::PRECISION_HIGHP:
1116         m_coordMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1117         m_coordMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1118         break;
1119 
1120     case glu::PRECISION_MEDIUMP:
1121         m_coordMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1122         m_coordMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1123         break;
1124 
1125     case glu::PRECISION_LOWP:
1126         m_coordMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1127         m_coordMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1128         break;
1129 
1130     default:
1131         DE_ASSERT(false);
1132     }
1133 
1134     if (m_surfaceType == SURFACETYPE_FLOAT_FBO)
1135     {
1136         // No scale or bias used for accuracy.
1137         m_derivScale = tcu::Vec4(1.0f);
1138         m_derivBias  = tcu::Vec4(0.0f);
1139     }
1140     else
1141     {
1142         // Compute scale - bias that normalizes to 0..1 range.
1143         const tcu::Vec4 dx = (m_coordMax - m_coordMin) / tcu::Vec4(w, w, w * 0.5f, -w * 0.5f);
1144         const tcu::Vec4 dy = (m_coordMax - m_coordMin) / tcu::Vec4(h, h, h * 0.5f, -h * 0.5f);
1145 
1146         switch (m_func)
1147         {
1148         case DERIVATE_DFDX:
1149             m_derivScale = 0.5f / dx;
1150             break;
1151 
1152         case DERIVATE_DFDY:
1153             m_derivScale = 0.5f / dy;
1154             break;
1155 
1156         case DERIVATE_FWIDTH:
1157             m_derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1158             break;
1159 
1160         default:
1161             DE_ASSERT(false);
1162         }
1163 
1164         m_derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1165     }
1166 }
1167 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)1168 qpTestResult LinearDerivateCase::verify(const tcu::ConstPixelBufferAccess &result,
1169                                         const tcu::PixelBufferAccess &errorMask)
1170 {
1171     const tcu::Vec4 xScale = tcu::Vec4(0.5f, 0.5f, 0.5f, -0.5f);
1172     const tcu::Vec4 yScale = tcu::Vec4(0.5f, 0.5f, 0.5f, -0.5f);
1173 
1174     const tcu::Vec4 surfaceThreshold = getSurfaceThreshold() / abs(m_derivScale);
1175 
1176     if (m_func == DERIVATE_DFDX || m_func == DERIVATE_DFDY)
1177     {
1178         const bool isX               = m_func == DERIVATE_DFDX;
1179         const float div              = isX ? float(result.getWidth()) : float(result.getHeight());
1180         const tcu::Vec4 scale        = isX ? xScale : yScale;
1181         tcu::Vec4 reference          = ((m_coordMax - m_coordMin) / div);
1182         const tcu::Vec4 opThreshold  = getDerivateThreshold(m_precision, m_coordMin, m_coordMax, reference);
1183         const tcu::Vec4 opThresholdW = getDerivateThresholdWarning(m_precision, m_coordMin, m_coordMax, reference);
1184         const tcu::Vec4 threshold    = max(surfaceThreshold, opThreshold);
1185         const tcu::Vec4 thresholdW   = max(surfaceThreshold, opThresholdW);
1186         const int numComps           = glu::getDataTypeFloatScalars(m_dataType);
1187 
1188         /* adjust the reference value for the correct dfdx or dfdy sample adjacency */
1189         reference = reference * scale;
1190 
1191         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying result image.\n"
1192                            << "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold "
1193                            << LogVecComps(threshold, numComps) << tcu::TestLog::EndMessage;
1194 
1195         // short circuit if result is strictly within the normal value error bounds.
1196         // This improves performance significantly.
1197         if (verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, threshold,
1198                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1199         {
1200             m_testCtx.getLog() << tcu::TestLog::Message << "No incorrect derivatives found, result valid."
1201                                << tcu::TestLog::EndMessage;
1202 
1203             return QP_TEST_RESULT_PASS;
1204         }
1205 
1206         // Check with relaxed threshold value
1207         if (verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, thresholdW,
1208                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1209         {
1210             m_testCtx.getLog() << tcu::TestLog::Message
1211                                << "No incorrect derivatives found, result valid with quality warning."
1212                                << tcu::TestLog::EndMessage;
1213 
1214             return QP_TEST_RESULT_QUALITY_WARNING;
1215         }
1216 
1217         // some pixels exceed error bounds calculated for normal values. Verify that these
1218         // potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1219 
1220         m_testCtx.getLog() << tcu::TestLog::Message
1221                            << "Initial verification failed, verifying image by calculating accurate error bounds for "
1222                               "each result pixel.\n"
1223                            << "\tVerifying each result derivative is within its range of legal result values."
1224                            << tcu::TestLog::EndMessage;
1225 
1226         {
1227             const tcu::IVec2 viewportSize = getViewportSize();
1228             const float w                 = float(viewportSize.x());
1229             const float h                 = float(viewportSize.y());
1230             const tcu::Vec4 valueRamp     = (m_coordMax - m_coordMin);
1231             Linear2DFunctionEvaluator function;
1232 
1233             function.matrix.setRow(0,
1234                                    tcu::Vec3((valueRamp.x() / w) / 2.0f, (valueRamp.x() / h) / 2.0f, m_coordMin.x()));
1235             function.matrix.setRow(1,
1236                                    tcu::Vec3((valueRamp.y() / w) / 2.0f, (valueRamp.y() / h) / 2.0f, m_coordMin.y()));
1237             function.matrix.setRow(2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_coordMin.z() + m_coordMin.z()) /
1238                                           2.0f);
1239             function.matrix.setRow(
1240                 3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_coordMax.w() + m_coordMax.w()) / 2.0f);
1241 
1242             return reverifyConstantDerivateWithFlushRelaxations(m_testCtx.getLog(), result, errorMask, m_dataType,
1243                                                                 m_precision, m_derivScale, m_derivBias,
1244                                                                 surfaceThreshold, m_func, function);
1245         }
1246     }
1247     else
1248     {
1249         DE_ASSERT(m_func == DERIVATE_FWIDTH);
1250         const float w = float(result.getWidth());
1251         const float h = float(result.getHeight());
1252 
1253         const tcu::Vec4 dx          = ((m_coordMax - m_coordMin) / w) * xScale;
1254         const tcu::Vec4 dy          = ((m_coordMax - m_coordMin) / h) * yScale;
1255         const tcu::Vec4 reference   = tcu::abs(dx) + tcu::abs(dy);
1256         const tcu::Vec4 dxThreshold = getDerivateThreshold(m_precision, m_coordMin * xScale, m_coordMax * xScale, dx);
1257         const tcu::Vec4 dyThreshold = getDerivateThreshold(m_precision, m_coordMin * yScale, m_coordMax * yScale, dy);
1258         const tcu::Vec4 dxThresholdW =
1259             getDerivateThresholdWarning(m_precision, m_coordMin * xScale, m_coordMax * xScale, dx);
1260         const tcu::Vec4 dyThresholdW =
1261             getDerivateThresholdWarning(m_precision, m_coordMin * yScale, m_coordMax * yScale, dy);
1262         const tcu::Vec4 threshold  = max(surfaceThreshold, max(dxThreshold, dyThreshold));
1263         const tcu::Vec4 thresholdW = max(surfaceThreshold, max(dxThresholdW, dyThresholdW));
1264         qpTestResult testResult    = QP_TEST_RESULT_FAIL;
1265 
1266         testResult = verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, threshold,
1267                                             m_derivScale, m_derivBias);
1268 
1269         // return if result is pass
1270         if (testResult == QP_TEST_RESULT_PASS)
1271             return testResult;
1272 
1273         // re-check with relaxed threshold
1274         testResult = verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, thresholdW,
1275                                             m_derivScale, m_derivBias);
1276 
1277         // if with relaxed threshold test is passing then mark the result with quality warning.
1278         if (testResult == QP_TEST_RESULT_PASS)
1279             testResult = QP_TEST_RESULT_QUALITY_WARNING;
1280 
1281         return testResult;
1282     }
1283 }
1284 
1285 // TextureDerivateCase
1286 
1287 class TextureDerivateCase : public TriangleDerivateCase
1288 {
1289 public:
1290     TextureDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1291                         glu::DataType type, glu::Precision precision, uint32_t hint, SurfaceType surfaceType,
1292                         int numSamples);
1293     ~TextureDerivateCase(void);
1294 
1295     void init(void);
1296     void deinit(void);
1297 
1298 protected:
1299     void setupRenderState(uint32_t program);
1300     qpTestResult verify(const tcu::ConstPixelBufferAccess &result, const tcu::PixelBufferAccess &errorMask);
1301 
1302 private:
1303     DerivateFunc m_func;
1304 
1305     tcu::Vec4 m_texValueMin;
1306     tcu::Vec4 m_texValueMax;
1307     glu::Texture2D *m_texture;
1308 };
1309 
TextureDerivateCase(Context & context,const char * name,const char * description,DerivateFunc func,glu::DataType type,glu::Precision precision,uint32_t hint,SurfaceType surfaceType,int numSamples)1310 TextureDerivateCase::TextureDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1311                                          glu::DataType type, glu::Precision precision, uint32_t hint,
1312                                          SurfaceType surfaceType, int numSamples)
1313     : TriangleDerivateCase(context, name, description)
1314     , m_func(func)
1315     , m_texture(DE_NULL)
1316 {
1317     m_dataType       = type;
1318     m_precision      = precision;
1319     m_coordDataType  = glu::TYPE_FLOAT_VEC2;
1320     m_coordPrecision = glu::PRECISION_HIGHP;
1321     m_hint           = hint;
1322     m_surfaceType    = surfaceType;
1323     m_numSamples     = numSamples;
1324 }
1325 
~TextureDerivateCase(void)1326 TextureDerivateCase::~TextureDerivateCase(void)
1327 {
1328     delete m_texture;
1329 }
1330 
init(void)1331 void TextureDerivateCase::init(void)
1332 {
1333     // Generate shader
1334     {
1335         const char *fragmentTmpl = "#version 300 es\n"
1336                                    "in highp vec2 v_coord;\n"
1337                                    "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1338                                    "uniform ${PRECISION} sampler2D u_sampler;\n"
1339                                    "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1340                                    "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1341                                    "void main (void)\n"
1342                                    "{\n"
1343                                    "    ${PRECISION} vec4 tex = texture(u_sampler, v_coord);\n"
1344                                    "    ${PRECISION} ${DATATYPE} res = ${FUNC}(tex${SWIZZLE}) * u_scale + u_bias;\n"
1345                                    "    o_color = ${CAST_TO_OUTPUT};\n"
1346                                    "}\n";
1347 
1348         const bool packToInt = m_surfaceType == SURFACETYPE_FLOAT_FBO;
1349         map<string, string> fragmentParams;
1350 
1351         fragmentParams["OUTPUT_TYPE"] = glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1352         fragmentParams["OUTPUT_PREC"] = glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_precision);
1353         fragmentParams["PRECISION"]   = glu::getPrecisionName(m_precision);
1354         fragmentParams["DATATYPE"]    = glu::getDataTypeName(m_dataType);
1355         fragmentParams["FUNC"]        = getDerivateFuncName(m_func);
1356         fragmentParams["SWIZZLE"]     = m_dataType == glu::TYPE_FLOAT_VEC4 ? "" :
1357                                         m_dataType == glu::TYPE_FLOAT_VEC3 ? ".xyz" :
1358                                         m_dataType == glu::TYPE_FLOAT_VEC2 ? ".xy" :
1359                                                                              /* TYPE_FLOAT */ ".x";
1360 
1361         if (packToInt)
1362         {
1363             fragmentParams["CAST_TO_OUTPUT"] =
1364                 m_dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1365                 m_dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1366                 m_dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1367                                                      /* TYPE_FLOAT */ "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1368         }
1369         else
1370         {
1371             fragmentParams["CAST_TO_OUTPUT"] =
1372                 m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1373                 m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1374                 m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1375                                                      /* TYPE_FLOAT */ "vec4(res, 0.0, 0.0, 1.0)";
1376         }
1377 
1378         m_fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
1379     }
1380 
1381     // Texture size matches viewport and nearest sampling is used. Thus texture sampling
1382     // is equal to just interpolating the texture value range.
1383 
1384     // Determine value range for texture.
1385 
1386     switch (m_precision)
1387     {
1388     case glu::PRECISION_HIGHP:
1389         m_texValueMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1390         m_texValueMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1391         break;
1392 
1393     case glu::PRECISION_MEDIUMP:
1394         m_texValueMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1395         m_texValueMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1396         break;
1397 
1398     case glu::PRECISION_LOWP:
1399         m_texValueMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1400         m_texValueMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1401         break;
1402 
1403     default:
1404         DE_ASSERT(false);
1405     }
1406 
1407     // Lowp and mediump cases use RGBA16F format, while highp uses RGBA32F.
1408     {
1409         const tcu::IVec2 viewportSize = getViewportSize();
1410         DE_ASSERT(!m_texture);
1411         m_texture = new glu::Texture2D(m_context.getRenderContext(),
1412                                        m_precision == glu::PRECISION_HIGHP ? GL_RGBA32F : GL_RGBA16F, viewportSize.x(),
1413                                        viewportSize.y());
1414         m_texture->getRefTexture().allocLevel(0);
1415     }
1416 
1417     // Texture coordinates
1418     m_coordMin = tcu::Vec4(0.0f);
1419     m_coordMax = tcu::Vec4(1.0f);
1420 
1421     // Fill with gradients.
1422     {
1423         const tcu::PixelBufferAccess level0 = m_texture->getRefTexture().getLevel(0);
1424         for (int y = 0; y < level0.getHeight(); y++)
1425         {
1426             for (int x = 0; x < level0.getWidth(); x++)
1427             {
1428                 const float xf = (float(x) + 0.5f) / float(level0.getWidth());
1429                 const float yf = (float(y) + 0.5f) / float(level0.getHeight());
1430                 // Make x and y data to have dependency to both axes so that dfdx(tex).y and dfdy(tex).x are nonzero.
1431                 const tcu::Vec4 s =
1432                     tcu::Vec4(xf + yf / 2.0f, yf + xf / 2.0f, (xf + yf) / 2.0f, 1.0f - (xf + yf) / 2.0f);
1433 
1434                 level0.setPixel(m_texValueMin + (m_texValueMax - m_texValueMin) * s, x, y);
1435             }
1436         }
1437     }
1438 
1439     m_texture->upload();
1440 
1441     if (m_surfaceType == SURFACETYPE_FLOAT_FBO)
1442     {
1443         // No scale or bias used for accuracy.
1444         m_derivScale = tcu::Vec4(1.0f);
1445         m_derivBias  = tcu::Vec4(0.0f);
1446     }
1447     else
1448     {
1449         // Compute scale - bias that normalizes to 0..1 range.
1450         const tcu::IVec2 viewportSize = getViewportSize();
1451         const float w                 = float(viewportSize.x());
1452         const float h                 = float(viewportSize.y());
1453         const tcu::Vec4 dx            = (m_texValueMax - m_texValueMin) / tcu::Vec4(w, w, w * 0.5f, -w * 0.5f);
1454         const tcu::Vec4 dy            = (m_texValueMax - m_texValueMin) / tcu::Vec4(h, h, h * 0.5f, -h * 0.5f);
1455 
1456         switch (m_func)
1457         {
1458         case DERIVATE_DFDX:
1459             m_derivScale = 0.5f / dx;
1460             break;
1461 
1462         case DERIVATE_DFDY:
1463             m_derivScale = 0.5f / dy;
1464             break;
1465 
1466         case DERIVATE_FWIDTH:
1467             m_derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1468             break;
1469 
1470         default:
1471             DE_ASSERT(false);
1472         }
1473 
1474         m_derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1475     }
1476 }
1477 
deinit(void)1478 void TextureDerivateCase::deinit(void)
1479 {
1480     delete m_texture;
1481     m_texture = DE_NULL;
1482 }
1483 
setupRenderState(uint32_t program)1484 void TextureDerivateCase::setupRenderState(uint32_t program)
1485 {
1486     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1487     const int texUnit        = 1;
1488 
1489     gl.activeTexture(GL_TEXTURE0 + texUnit);
1490     gl.bindTexture(GL_TEXTURE_2D, m_texture->getGLTexture());
1491     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1492     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1493     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1494     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1495 
1496     gl.uniform1i(gl.getUniformLocation(program, "u_sampler"), texUnit);
1497 }
1498 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)1499 qpTestResult TextureDerivateCase::verify(const tcu::ConstPixelBufferAccess &result,
1500                                          const tcu::PixelBufferAccess &errorMask)
1501 {
1502     // \note Edges are ignored in comparison
1503     if (result.getWidth() < 2 || result.getHeight() < 2)
1504         throw tcu::NotSupportedError("Too small viewport");
1505 
1506     tcu::ConstPixelBufferAccess compareArea =
1507         tcu::getSubregion(result, 1, 1, result.getWidth() - 2, result.getHeight() - 2);
1508     tcu::PixelBufferAccess maskArea =
1509         tcu::getSubregion(errorMask, 1, 1, errorMask.getWidth() - 2, errorMask.getHeight() - 2);
1510     const tcu::Vec4 xScale = tcu::Vec4(1.0f, 0.5f, 0.5f, -0.5f);
1511     const tcu::Vec4 yScale = tcu::Vec4(0.5f, 1.0f, 0.5f, -0.5f);
1512     const float w          = float(result.getWidth());
1513     const float h          = float(result.getHeight());
1514 
1515     const tcu::Vec4 surfaceThreshold = getSurfaceThreshold() / abs(m_derivScale);
1516 
1517     if (m_func == DERIVATE_DFDX || m_func == DERIVATE_DFDY)
1518     {
1519         const bool isX              = m_func == DERIVATE_DFDX;
1520         const float div             = isX ? w : h;
1521         const tcu::Vec4 scale       = isX ? xScale : yScale;
1522         tcu::Vec4 reference         = ((m_texValueMax - m_texValueMin) / div);
1523         const tcu::Vec4 opThreshold = getDerivateThreshold(m_precision, m_texValueMin, m_texValueMax, reference);
1524         const tcu::Vec4 opThresholdW =
1525             getDerivateThresholdWarning(m_precision, m_texValueMin, m_texValueMax, reference);
1526         const tcu::Vec4 threshold  = max(surfaceThreshold, opThreshold);
1527         const tcu::Vec4 thresholdW = max(surfaceThreshold, opThresholdW);
1528         const int numComps         = glu::getDataTypeFloatScalars(m_dataType);
1529 
1530         /* adjust the reference value for the correct dfdx or dfdy sample adjacency */
1531         reference = reference * scale;
1532 
1533         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying result image.\n"
1534                            << "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold "
1535                            << LogVecComps(threshold, numComps) << tcu::TestLog::EndMessage;
1536 
1537         // short circuit if result is strictly within the normal value error bounds.
1538         // This improves performance significantly.
1539         if (verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference, threshold,
1540                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1541         {
1542             m_testCtx.getLog() << tcu::TestLog::Message << "No incorrect derivatives found, result valid."
1543                                << tcu::TestLog::EndMessage;
1544 
1545             return QP_TEST_RESULT_PASS;
1546         }
1547 
1548         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying result image.\n"
1549                            << "\tValid derivative is " << LogVecComps(reference, numComps) << " with Warning threshold "
1550                            << LogVecComps(thresholdW, numComps) << tcu::TestLog::EndMessage;
1551 
1552         // Re-check with relaxed threshold
1553         if (verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference, thresholdW,
1554                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1555         {
1556             m_testCtx.getLog() << tcu::TestLog::Message
1557                                << "No incorrect derivatives found, result valid with quality warning."
1558                                << tcu::TestLog::EndMessage;
1559 
1560             return QP_TEST_RESULT_QUALITY_WARNING;
1561         }
1562 
1563         // some pixels exceed error bounds calculated for normal values. Verify that these
1564         // potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1565 
1566         m_testCtx.getLog() << tcu::TestLog::Message
1567                            << "Initial verification failed, verifying image by calculating accurate error bounds for "
1568                               "each result pixel.\n"
1569                            << "\tVerifying each result derivative is within its range of legal result values."
1570                            << tcu::TestLog::EndMessage;
1571 
1572         {
1573             const tcu::Vec4 valueRamp = (m_texValueMax - m_texValueMin);
1574             Linear2DFunctionEvaluator function;
1575 
1576             function.matrix.setRow(0, tcu::Vec3(valueRamp.x() / w, (valueRamp.x() / h) / 2.0f, m_texValueMin.x()));
1577             function.matrix.setRow(1, tcu::Vec3((valueRamp.y() / w) / 2.0f, valueRamp.y() / h, m_texValueMin.y()));
1578             function.matrix.setRow(
1579                 2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_texValueMin.z() + m_texValueMin.z()) / 2.0f);
1580             function.matrix.setRow(
1581                 3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_texValueMax.w() + m_texValueMax.w()) / 2.0f);
1582 
1583             return reverifyConstantDerivateWithFlushRelaxations(m_testCtx.getLog(), compareArea, maskArea, m_dataType,
1584                                                                 m_precision, m_derivScale, m_derivBias,
1585                                                                 surfaceThreshold, m_func, function);
1586         }
1587     }
1588     else
1589     {
1590         DE_ASSERT(m_func == DERIVATE_FWIDTH);
1591         const tcu::Vec4 dx        = ((m_texValueMax - m_texValueMin) / w) * xScale;
1592         const tcu::Vec4 dy        = ((m_texValueMax - m_texValueMin) / h) * yScale;
1593         const tcu::Vec4 reference = tcu::abs(dx) + tcu::abs(dy);
1594         const tcu::Vec4 dxThreshold =
1595             getDerivateThreshold(m_precision, m_texValueMin * xScale, m_texValueMax * xScale, dx);
1596         const tcu::Vec4 dyThreshold =
1597             getDerivateThreshold(m_precision, m_texValueMin * yScale, m_texValueMax * yScale, dy);
1598         const tcu::Vec4 dxThresholdW =
1599             getDerivateThresholdWarning(m_precision, m_texValueMin * xScale, m_texValueMax * xScale, dx);
1600         const tcu::Vec4 dyThresholdW =
1601             getDerivateThresholdWarning(m_precision, m_texValueMin * yScale, m_texValueMax * yScale, dy);
1602         const tcu::Vec4 threshold  = max(surfaceThreshold, max(dxThreshold, dyThreshold));
1603         const tcu::Vec4 thresholdW = max(surfaceThreshold, max(dxThresholdW, dyThresholdW));
1604         qpTestResult testResult    = QP_TEST_RESULT_FAIL;
1605 
1606         testResult = verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference, threshold,
1607                                             m_derivScale, m_derivBias);
1608 
1609         if (testResult == QP_TEST_RESULT_PASS)
1610             return testResult;
1611 
1612         // Re-Check with relaxed threshold
1613         testResult = verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference,
1614                                             thresholdW, m_derivScale, m_derivBias);
1615 
1616         // If test is passing with relaxed threshold then mark quality warning
1617         if (testResult == QP_TEST_RESULT_PASS)
1618             testResult = QP_TEST_RESULT_QUALITY_WARNING;
1619 
1620         return testResult;
1621     }
1622 }
1623 
ShaderDerivateTests(Context & context)1624 ShaderDerivateTests::ShaderDerivateTests(Context &context)
1625     : TestCaseGroup(context, "derivate", "Derivate Function Tests")
1626 {
1627 }
1628 
~ShaderDerivateTests(void)1629 ShaderDerivateTests::~ShaderDerivateTests(void)
1630 {
1631 }
1632 
1633 struct FunctionSpec
1634 {
1635     std::string name;
1636     DerivateFunc function;
1637     glu::DataType dataType;
1638     glu::Precision precision;
1639 
FunctionSpecdeqp::gles3::Functional::FunctionSpec1640     FunctionSpec(const std::string &name_, DerivateFunc function_, glu::DataType dataType_, glu::Precision precision_)
1641         : name(name_)
1642         , function(function_)
1643         , dataType(dataType_)
1644         , precision(precision_)
1645     {
1646     }
1647 };
1648 
init(void)1649 void ShaderDerivateTests::init(void)
1650 {
1651     static const struct
1652     {
1653         const char *name;
1654         const char *description;
1655         const char *source;
1656     } s_linearDerivateCases[] = {
1657         {"linear", "Basic derivate of linearly interpolated argument",
1658 
1659          "#version 300 es\n"
1660          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1661          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1662          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1663          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1664          "void main (void)\n"
1665          "{\n"
1666          "    ${PRECISION} ${DATATYPE} res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1667          "    o_color = ${CAST_TO_OUTPUT};\n"
1668          "}\n"},
1669         {"in_function", "Derivate of linear function argument",
1670 
1671          "#version 300 es\n"
1672          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1673          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1674          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1675          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1676          "\n"
1677          "${PRECISION} ${DATATYPE} computeRes (${PRECISION} ${DATATYPE} value)\n"
1678          "{\n"
1679          "    return ${FUNC}(v_coord) * u_scale + u_bias;\n"
1680          "}\n"
1681          "\n"
1682          "void main (void)\n"
1683          "{\n"
1684          "    ${PRECISION} ${DATATYPE} res = computeRes(v_coord);\n"
1685          "    o_color = ${CAST_TO_OUTPUT};\n"
1686          "}\n"},
1687         {"static_if", "Derivate of linearly interpolated value in static if",
1688 
1689          "#version 300 es\n"
1690          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1691          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1692          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1693          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1694          "void main (void)\n"
1695          "{\n"
1696          "    ${PRECISION} ${DATATYPE} res;\n"
1697          "    if (false)\n"
1698          "        res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1699          "    else\n"
1700          "        res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1701          "    o_color = ${CAST_TO_OUTPUT};\n"
1702          "}\n"},
1703         {"static_loop", "Derivate of linearly interpolated value in static loop",
1704 
1705          "#version 300 es\n"
1706          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1707          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1708          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1709          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1710          "void main (void)\n"
1711          "{\n"
1712          "    ${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1713          "    for (int i = 0; i < 2; i++)\n"
1714          "        res += ${FUNC}(v_coord * float(i));\n"
1715          "    res = res * u_scale + u_bias;\n"
1716          "    o_color = ${CAST_TO_OUTPUT};\n"
1717          "}\n"},
1718         {"static_switch", "Derivate of linearly interpolated value in static switch",
1719 
1720          "#version 300 es\n"
1721          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1722          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1723          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1724          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1725          "void main (void)\n"
1726          "{\n"
1727          "    ${PRECISION} ${DATATYPE} res;\n"
1728          "    switch (1)\n"
1729          "    {\n"
1730          "        case 0: res = ${FUNC}(-v_coord) * u_scale + u_bias;    break;\n"
1731          "        case 1: res = ${FUNC}(v_coord) * u_scale + u_bias;    break;\n"
1732          "    }\n"
1733          "    o_color = ${CAST_TO_OUTPUT};\n"
1734          "}\n"},
1735         {"uniform_if", "Derivate of linearly interpolated value in uniform if",
1736 
1737          "#version 300 es\n"
1738          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1739          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1740          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1741          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1742          "uniform bool ub_true;\n"
1743          "void main (void)\n"
1744          "{\n"
1745          "    ${PRECISION} ${DATATYPE} res;\n"
1746          "    if (ub_true)"
1747          "        res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1748          "    else\n"
1749          "        res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1750          "    o_color = ${CAST_TO_OUTPUT};\n"
1751          "}\n"},
1752         {"uniform_loop", "Derivate of linearly interpolated value in uniform loop",
1753 
1754          "#version 300 es\n"
1755          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1756          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1757          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1758          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1759          "uniform int ui_two;\n"
1760          "void main (void)\n"
1761          "{\n"
1762          "    ${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1763          "    for (int i = 0; i < ui_two; i++)\n"
1764          "        res += ${FUNC}(v_coord * float(i));\n"
1765          "    res = res * u_scale + u_bias;\n"
1766          "    o_color = ${CAST_TO_OUTPUT};\n"
1767          "}\n"},
1768         {"uniform_switch", "Derivate of linearly interpolated value in uniform switch",
1769 
1770          "#version 300 es\n"
1771          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1772          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1773          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1774          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1775          "uniform int ui_one;\n"
1776          "void main (void)\n"
1777          "{\n"
1778          "    ${PRECISION} ${DATATYPE} res;\n"
1779          "    switch (ui_one)\n"
1780          "    {\n"
1781          "        case 0: res = ${FUNC}(-v_coord) * u_scale + u_bias;    break;\n"
1782          "        case 1: res = ${FUNC}(v_coord) * u_scale + u_bias;    break;\n"
1783          "    }\n"
1784          "    o_color = ${CAST_TO_OUTPUT};\n"
1785          "}\n"},
1786     };
1787 
1788     static const struct
1789     {
1790         const char *name;
1791         SurfaceType surfaceType;
1792         int numSamples;
1793     } s_fboConfigs[] = {
1794         {"fbo", SURFACETYPE_DEFAULT_FRAMEBUFFER, 0},
1795         {"fbo_msaa2", SURFACETYPE_UNORM_FBO, 2},
1796         {"fbo_msaa4", SURFACETYPE_UNORM_FBO, 4},
1797         {"fbo_float", SURFACETYPE_FLOAT_FBO, 0},
1798     };
1799 
1800     static const struct
1801     {
1802         const char *name;
1803         uint32_t hint;
1804     } s_hints[] = {
1805         {"fastest", GL_FASTEST},
1806         {"nicest", GL_NICEST},
1807     };
1808 
1809     static const struct
1810     {
1811         const char *name;
1812         SurfaceType surfaceType;
1813         int numSamples;
1814     } s_hintFboConfigs[] = {{"default", SURFACETYPE_DEFAULT_FRAMEBUFFER, 0},
1815                             {"fbo_msaa4", SURFACETYPE_UNORM_FBO, 4},
1816                             {"fbo_float", SURFACETYPE_FLOAT_FBO, 0}};
1817 
1818     static const struct
1819     {
1820         const char *name;
1821         SurfaceType surfaceType;
1822         int numSamples;
1823         uint32_t hint;
1824     } s_textureConfigs[] = {
1825         {"basic", SURFACETYPE_DEFAULT_FRAMEBUFFER, 0, GL_DONT_CARE},
1826         {"msaa4", SURFACETYPE_UNORM_FBO, 4, GL_DONT_CARE},
1827         {"float_fastest", SURFACETYPE_FLOAT_FBO, 0, GL_FASTEST},
1828         {"float_nicest", SURFACETYPE_FLOAT_FBO, 0, GL_NICEST},
1829     };
1830 
1831     // .dfdx, .dfdy, .fwidth
1832     for (int funcNdx = 0; funcNdx < DERIVATE_LAST; funcNdx++)
1833     {
1834         const DerivateFunc function = DerivateFunc(funcNdx);
1835         tcu::TestCaseGroup *const functionGroup =
1836             new tcu::TestCaseGroup(m_testCtx, getDerivateFuncCaseName(function), getDerivateFuncName(function));
1837         addChild(functionGroup);
1838 
1839         // .constant - no precision variants, checks that derivate of constant arguments is 0
1840         {
1841             tcu::TestCaseGroup *const constantGroup =
1842                 new tcu::TestCaseGroup(m_testCtx, "constant", "Derivate of constant argument");
1843             functionGroup->addChild(constantGroup);
1844 
1845             for (int vecSize = 1; vecSize <= 4; vecSize++)
1846             {
1847                 const glu::DataType dataType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1848                 constantGroup->addChild(
1849                     new ConstantDerivateCase(m_context, glu::getDataTypeName(dataType), "", function, dataType));
1850             }
1851         }
1852 
1853         // Cases based on LinearDerivateCase
1854         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_linearDerivateCases); caseNdx++)
1855         {
1856             tcu::TestCaseGroup *const linearCaseGroup = new tcu::TestCaseGroup(
1857                 m_testCtx, s_linearDerivateCases[caseNdx].name, s_linearDerivateCases[caseNdx].description);
1858             const char *source = s_linearDerivateCases[caseNdx].source;
1859             functionGroup->addChild(linearCaseGroup);
1860 
1861             for (int vecSize = 1; vecSize <= 4; vecSize++)
1862             {
1863                 for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1864                 {
1865                     const glu::DataType dataType   = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1866                     const glu::Precision precision = glu::Precision(precNdx);
1867                     const SurfaceType surfaceType  = SURFACETYPE_DEFAULT_FRAMEBUFFER;
1868                     const int numSamples           = 0;
1869                     const uint32_t hint            = GL_DONT_CARE;
1870                     ostringstream caseName;
1871 
1872                     if (caseNdx != 0 && precision == glu::PRECISION_LOWP)
1873                         continue; // Skip as lowp doesn't actually produce any bits when rendered to default FB.
1874 
1875                     caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1876 
1877                     linearCaseGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function,
1878                                                                      dataType, precision, hint, surfaceType, numSamples,
1879                                                                      source));
1880                 }
1881             }
1882         }
1883 
1884         // Fbo cases
1885         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_fboConfigs); caseNdx++)
1886         {
1887             tcu::TestCaseGroup *const fboGroup =
1888                 new tcu::TestCaseGroup(m_testCtx, s_fboConfigs[caseNdx].name, "Derivate usage when rendering into FBO");
1889             const char *source            = s_linearDerivateCases[0].source; // use source from .linear group
1890             const SurfaceType surfaceType = s_fboConfigs[caseNdx].surfaceType;
1891             const int numSamples          = s_fboConfigs[caseNdx].numSamples;
1892             functionGroup->addChild(fboGroup);
1893 
1894             for (int vecSize = 1; vecSize <= 4; vecSize++)
1895             {
1896                 for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1897                 {
1898                     const glu::DataType dataType   = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1899                     const glu::Precision precision = glu::Precision(precNdx);
1900                     const uint32_t hint            = GL_DONT_CARE;
1901                     ostringstream caseName;
1902 
1903                     if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1904                         continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1905 
1906                     caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1907 
1908                     fboGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function, dataType,
1909                                                               precision, hint, surfaceType, numSamples, source));
1910                 }
1911             }
1912         }
1913 
1914         // .fastest, .nicest
1915         for (int hintCaseNdx = 0; hintCaseNdx < DE_LENGTH_OF_ARRAY(s_hints); hintCaseNdx++)
1916         {
1917             tcu::TestCaseGroup *const hintGroup =
1918                 new tcu::TestCaseGroup(m_testCtx, s_hints[hintCaseNdx].name, "Shader derivate hints");
1919             const char *source  = s_linearDerivateCases[0].source; // use source from .linear group
1920             const uint32_t hint = s_hints[hintCaseNdx].hint;
1921             functionGroup->addChild(hintGroup);
1922 
1923             for (int fboCaseNdx = 0; fboCaseNdx < DE_LENGTH_OF_ARRAY(s_hintFboConfigs); fboCaseNdx++)
1924             {
1925                 tcu::TestCaseGroup *const fboGroup =
1926                     new tcu::TestCaseGroup(m_testCtx, s_hintFboConfigs[fboCaseNdx].name, "");
1927                 const SurfaceType surfaceType = s_hintFboConfigs[fboCaseNdx].surfaceType;
1928                 const int numSamples          = s_hintFboConfigs[fboCaseNdx].numSamples;
1929                 hintGroup->addChild(fboGroup);
1930 
1931                 for (int vecSize = 1; vecSize <= 4; vecSize++)
1932                 {
1933                     for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1934                     {
1935                         const glu::DataType dataType =
1936                             vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1937                         const glu::Precision precision = glu::Precision(precNdx);
1938                         ostringstream caseName;
1939 
1940                         if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1941                             continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1942 
1943                         caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1944 
1945                         fboGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function,
1946                                                                   dataType, precision, hint, surfaceType, numSamples,
1947                                                                   source));
1948                     }
1949                 }
1950             }
1951         }
1952 
1953         // .texture
1954         {
1955             tcu::TestCaseGroup *const textureGroup =
1956                 new tcu::TestCaseGroup(m_testCtx, "texture", "Derivate of texture lookup result");
1957             functionGroup->addChild(textureGroup);
1958 
1959             for (int texCaseNdx = 0; texCaseNdx < DE_LENGTH_OF_ARRAY(s_textureConfigs); texCaseNdx++)
1960             {
1961                 tcu::TestCaseGroup *const caseGroup =
1962                     new tcu::TestCaseGroup(m_testCtx, s_textureConfigs[texCaseNdx].name, "");
1963                 const SurfaceType surfaceType = s_textureConfigs[texCaseNdx].surfaceType;
1964                 const int numSamples          = s_textureConfigs[texCaseNdx].numSamples;
1965                 const uint32_t hint           = s_textureConfigs[texCaseNdx].hint;
1966                 textureGroup->addChild(caseGroup);
1967 
1968                 for (int vecSize = 1; vecSize <= 4; vecSize++)
1969                 {
1970                     for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1971                     {
1972                         const glu::DataType dataType =
1973                             vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1974                         const glu::Precision precision = glu::Precision(precNdx);
1975                         ostringstream caseName;
1976 
1977                         if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1978                             continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1979 
1980                         caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1981 
1982                         caseGroup->addChild(new TextureDerivateCase(m_context, caseName.str().c_str(), "", function,
1983                                                                     dataType, precision, hint, surfaceType,
1984                                                                     numSamples));
1985                     }
1986                 }
1987             }
1988         }
1989     }
1990 }
1991 
1992 } // namespace Functional
1993 } // namespace gles3
1994 } // namespace deqp
1995