• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 nullptr;
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 nullptr;
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::__anond77ee7f70411::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, const tcu::PixelBufferAccess &errorMask) = 0;
647 
648     tcu::IVec2 getViewportSize(void) const;
649     tcu::Vec4 getSurfaceThreshold(void) const;
650 
651     glu::DataType m_dataType;
652     glu::Precision m_precision;
653 
654     glu::DataType m_coordDataType;
655     glu::Precision m_coordPrecision;
656 
657     std::string m_fragmentSrc;
658 
659     tcu::Vec4 m_coordMin;
660     tcu::Vec4 m_coordMax;
661     tcu::Vec4 m_derivScale;
662     tcu::Vec4 m_derivBias;
663 
664     SurfaceType m_surfaceType;
665     int m_numSamples;
666     uint32_t m_hint;
667 
668     bool m_useAsymmetricCoords;
669 };
670 
TriangleDerivateCase(Context & context,const char * name,const char * description)671 TriangleDerivateCase::TriangleDerivateCase(Context &context, const char *name, const char *description)
672     : TestCase(context, name, description)
673     , m_dataType(glu::TYPE_LAST)
674     , m_precision(glu::PRECISION_LAST)
675     , m_coordDataType(glu::TYPE_LAST)
676     , m_coordPrecision(glu::PRECISION_LAST)
677     , m_surfaceType(SURFACETYPE_DEFAULT_FRAMEBUFFER)
678     , m_numSamples(0)
679     , m_hint(GL_DONT_CARE)
680     , m_useAsymmetricCoords(false)
681 {
682     DE_ASSERT(m_surfaceType != SURFACETYPE_DEFAULT_FRAMEBUFFER || m_numSamples == 0);
683 }
684 
~TriangleDerivateCase(void)685 TriangleDerivateCase::~TriangleDerivateCase(void)
686 {
687     TriangleDerivateCase::deinit();
688 }
689 
genVertexSource(glu::DataType coordType,glu::Precision precision)690 static std::string genVertexSource(glu::DataType coordType, glu::Precision precision)
691 {
692     DE_ASSERT(glu::isDataTypeFloatOrVec(coordType));
693 
694     const char *vertexTmpl = "#version 300 es\n"
695                              "in highp vec4 a_position;\n"
696                              "in ${PRECISION} ${DATATYPE} a_coord;\n"
697                              "out ${PRECISION} ${DATATYPE} v_coord;\n"
698                              "void main (void)\n"
699                              "{\n"
700                              "    gl_Position = a_position;\n"
701                              "    v_coord = a_coord;\n"
702                              "}\n";
703 
704     map<string, string> vertexParams;
705 
706     vertexParams["PRECISION"] = glu::getPrecisionName(precision);
707     vertexParams["DATATYPE"]  = glu::getDataTypeName(coordType);
708 
709     return tcu::StringTemplate(vertexTmpl).specialize(vertexParams);
710 }
711 
getViewportSize(void) const712 inline tcu::IVec2 TriangleDerivateCase::getViewportSize(void) const
713 {
714     if (m_surfaceType == SURFACETYPE_DEFAULT_FRAMEBUFFER)
715     {
716         const int width  = de::min<int>(m_context.getRenderTarget().getWidth(), VIEWPORT_WIDTH);
717         const int height = de::min<int>(m_context.getRenderTarget().getHeight(), VIEWPORT_HEIGHT);
718         return tcu::IVec2(width, height);
719     }
720     else
721         return tcu::IVec2(FBO_WIDTH, FBO_HEIGHT);
722 }
723 
iterate(void)724 TriangleDerivateCase::IterateResult TriangleDerivateCase::iterate(void)
725 {
726     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
727     const glu::ShaderProgram program(
728         m_context.getRenderContext(),
729         glu::makeVtxFragSources(genVertexSource(m_coordDataType, m_coordPrecision), m_fragmentSrc));
730     de::Random rnd(deStringHash(getName()) ^ 0xbbc24);
731     const bool useFbo             = m_surfaceType != SURFACETYPE_DEFAULT_FRAMEBUFFER;
732     const uint32_t fboFormat      = m_surfaceType == SURFACETYPE_FLOAT_FBO ? GL_RGBA32UI : GL_RGBA8;
733     const tcu::IVec2 viewportSize = getViewportSize();
734     const int viewportX = useFbo ? 0 : rnd.getInt(0, m_context.getRenderTarget().getWidth() - viewportSize.x());
735     const int viewportY = useFbo ? 0 : rnd.getInt(0, m_context.getRenderTarget().getHeight() - viewportSize.y());
736     AutoFbo fbo(gl);
737     AutoRbo rbo(gl);
738     tcu::TextureLevel result;
739 
740     m_testCtx.getLog() << program;
741 
742     if (!program.isOk())
743         TCU_FAIL("Compile failed");
744 
745     if (useFbo)
746     {
747         m_testCtx.getLog() << TestLog::Message << "Rendering to FBO, format = " << glu::getTextureFormatStr(fboFormat)
748                            << ", samples = " << m_numSamples << TestLog::EndMessage;
749 
750         fbo.gen();
751         rbo.gen();
752 
753         gl.bindRenderbuffer(GL_RENDERBUFFER, *rbo);
754         gl.renderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, fboFormat, viewportSize.x(), viewportSize.y());
755         gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
756         gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *rbo);
757         TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
758     }
759     else
760     {
761         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
762 
763         m_testCtx.getLog() << TestLog::Message << "Rendering to default framebuffer\n"
764                            << "\tColor depth: R=" << pixelFormat.redBits << ", G=" << pixelFormat.greenBits
765                            << ", B=" << pixelFormat.blueBits << ", A=" << pixelFormat.alphaBits << TestLog::EndMessage;
766     }
767 
768     m_testCtx.getLog() << TestLog::Message << "in: " << m_coordMin << " -> " << m_coordMax << "\n"
769                        << (m_useAsymmetricCoords ? "v_coord.x = in.x * (x+y)/2\n" : "v_coord.x = in.x * x\n")
770                        << (m_useAsymmetricCoords ? "v_coord.y = in.y * (x+y)/2\n" : "v_coord.y = in.y * y\n")
771                        << "v_coord.z = in.z * (x+y)/2\n"
772                        << "v_coord.w = in.w * (1 - (x+y)/2)\n"
773                        << TestLog::EndMessage << TestLog::Message << "u_scale: " << m_derivScale
774                        << ", u_bias: " << m_derivBias << " (displayed values have scale/bias removed)"
775                        << TestLog::EndMessage << TestLog::Message << "Viewport: " << viewportSize.x() << "x"
776                        << viewportSize.y() << TestLog::EndMessage << TestLog::Message
777                        << "GL_FRAGMENT_SHADER_DERIVATE_HINT: " << glu::getHintModeStr(m_hint) << TestLog::EndMessage;
778 
779     // Draw
780     {
781         const float positions[] = {-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
782                                    1.0f,  -1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f, 1.0f};
783         float coords[]          = {m_coordMin.x(),
784                                    m_coordMin.y(),
785                                    m_coordMin.z(),
786                                    m_coordMax.w(),
787                                    m_coordMin.x(),
788                                    m_coordMax.y(),
789                                    (m_coordMin.z() + m_coordMax.z()) * 0.5f,
790                                    (m_coordMin.w() + m_coordMax.w()) * 0.5f,
791                                    m_coordMax.x(),
792                                    m_coordMin.y(),
793                                    (m_coordMin.z() + m_coordMax.z()) * 0.5f,
794                                    (m_coordMin.w() + m_coordMax.w()) * 0.5f,
795                                    m_coordMax.x(),
796                                    m_coordMax.y(),
797                                    m_coordMax.z(),
798                                    m_coordMin.w()};
799 
800         // For linear tests we want varying data x and y to vary along both axes
801         // to get nonzero x for dfdy and nonzero y for dfdx. To make the gradient
802         // the same for both triangles we set vertices 2 and 3 to middle values.
803         // This way the values go from min -> (max+min) / 2 or (max+min) / 2 -> max
804         // depending on the triangle, but the derivative is the same for both.
805         if (m_useAsymmetricCoords)
806         {
807             coords[4] = coords[8] = (m_coordMin.x() + m_coordMax.x()) * 0.5f;
808             coords[5] = coords[9] = (m_coordMin.y() + m_coordMax.y()) * 0.5f;
809         }
810 
811         const glu::VertexArrayBinding vertexArrays[] = {glu::va::Float("a_position", 4, 4, 0, &positions[0]),
812                                                         glu::va::Float("a_coord", 4, 4, 0, &coords[0])};
813         const uint16_t indices[]                     = {0, 2, 1, 2, 3, 1};
814 
815         gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
816         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
817         gl.disable(GL_DITHER);
818 
819         gl.useProgram(program.getProgram());
820 
821         {
822             const int scaleLoc = gl.getUniformLocation(program.getProgram(), "u_scale");
823             const int biasLoc  = gl.getUniformLocation(program.getProgram(), "u_bias");
824 
825             switch (m_dataType)
826             {
827             case glu::TYPE_FLOAT:
828                 gl.uniform1f(scaleLoc, m_derivScale.x());
829                 gl.uniform1f(biasLoc, m_derivBias.x());
830                 break;
831 
832             case glu::TYPE_FLOAT_VEC2:
833                 gl.uniform2fv(scaleLoc, 1, m_derivScale.getPtr());
834                 gl.uniform2fv(biasLoc, 1, m_derivBias.getPtr());
835                 break;
836 
837             case glu::TYPE_FLOAT_VEC3:
838                 gl.uniform3fv(scaleLoc, 1, m_derivScale.getPtr());
839                 gl.uniform3fv(biasLoc, 1, m_derivBias.getPtr());
840                 break;
841 
842             case glu::TYPE_FLOAT_VEC4:
843                 gl.uniform4fv(scaleLoc, 1, m_derivScale.getPtr());
844                 gl.uniform4fv(biasLoc, 1, m_derivBias.getPtr());
845                 break;
846 
847             default:
848                 DE_ASSERT(false);
849             }
850         }
851 
852         gls::setupDefaultUniforms(m_context.getRenderContext(), program.getProgram());
853         setupRenderState(program.getProgram());
854 
855         gl.hint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, m_hint);
856         GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
857 
858         gl.viewport(viewportX, viewportY, viewportSize.x(), viewportSize.y());
859         glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays),
860                   &vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
861         GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
862     }
863 
864     // Read back results
865     {
866         const bool isMSAA = useFbo && m_numSamples > 0;
867         AutoFbo resFbo(gl);
868         AutoRbo resRbo(gl);
869 
870         // Resolve if necessary
871         if (isMSAA)
872         {
873             resFbo.gen();
874             resRbo.gen();
875 
876             gl.bindRenderbuffer(GL_RENDERBUFFER, *resRbo);
877             gl.renderbufferStorageMultisample(GL_RENDERBUFFER, 0, fboFormat, viewportSize.x(), viewportSize.y());
878             gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, *resFbo);
879             gl.framebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *resRbo);
880             TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
881 
882             gl.blitFramebuffer(0, 0, viewportSize.x(), viewportSize.y(), 0, 0, viewportSize.x(), viewportSize.y(),
883                                GL_COLOR_BUFFER_BIT, GL_NEAREST);
884             GLU_EXPECT_NO_ERROR(gl.getError(), "Resolve blit");
885 
886             gl.bindFramebuffer(GL_READ_FRAMEBUFFER, *resFbo);
887         }
888 
889         switch (m_surfaceType)
890         {
891         case SURFACETYPE_DEFAULT_FRAMEBUFFER:
892         case SURFACETYPE_UNORM_FBO:
893             result.setStorage(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
894                               viewportSize.x(), viewportSize.y());
895             glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, result);
896             break;
897 
898         case SURFACETYPE_FLOAT_FBO:
899         {
900             const tcu::TextureFormat dataFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT);
901             const tcu::TextureFormat transferFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT32);
902 
903             result.setStorage(dataFormat, viewportSize.x(), viewportSize.y());
904             glu::readPixels(m_context.getRenderContext(), viewportX, viewportY,
905                             tcu::PixelBufferAccess(transferFormat, result.getWidth(), result.getHeight(),
906                                                    result.getDepth(), result.getAccess().getDataPtr()));
907             break;
908         }
909 
910         default:
911             DE_ASSERT(false);
912         }
913 
914         GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
915     }
916 
917     // Verify
918     {
919         tcu::Surface errorMask(result.getWidth(), result.getHeight());
920         tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
921 
922         const qpTestResult testResult = verify(result.getAccess(), errorMask.getAccess());
923         const char *failStr           = "Fail";
924 
925         m_testCtx.getLog() << TestLog::ImageSet("Result", "Result images")
926                            << TestLog::Image("Rendered", "Rendered image", result);
927 
928         if (testResult != QP_TEST_RESULT_PASS)
929             m_testCtx.getLog() << TestLog::Image("ErrorMask", "Error mask", errorMask);
930 
931         m_testCtx.getLog() << TestLog::EndImageSet;
932 
933         if (testResult == QP_TEST_RESULT_PASS)
934             failStr = "Pass";
935         else if (testResult == QP_TEST_RESULT_QUALITY_WARNING)
936             failStr = "QualityWarning";
937 
938         m_testCtx.setTestResult(testResult, failStr);
939     }
940 
941     return STOP;
942 }
943 
getSurfaceThreshold(void) const944 tcu::Vec4 TriangleDerivateCase::getSurfaceThreshold(void) const
945 {
946     switch (m_surfaceType)
947     {
948     case SURFACETYPE_DEFAULT_FRAMEBUFFER:
949     {
950         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
951         const tcu::IVec4 channelBits(pixelFormat.redBits, pixelFormat.greenBits, pixelFormat.blueBits,
952                                      pixelFormat.alphaBits);
953         const tcu::IVec4 intThreshold = tcu::IVec4(1) << (8 - channelBits);
954         const tcu::Vec4 normThreshold = intThreshold.asFloat() / 255.0f;
955 
956         return normThreshold;
957     }
958 
959     case SURFACETYPE_UNORM_FBO:
960         return tcu::IVec4(1).asFloat() / 255.0f;
961     case SURFACETYPE_FLOAT_FBO:
962         return tcu::Vec4(0.0f);
963     default:
964         DE_ASSERT(false);
965         return tcu::Vec4(0.0f);
966     }
967 }
968 
969 // ConstantDerivateCase
970 
971 class ConstantDerivateCase : public TriangleDerivateCase
972 {
973 public:
974     ConstantDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
975                          glu::DataType type);
~ConstantDerivateCase(void)976     ~ConstantDerivateCase(void)
977     {
978     }
979 
980     void init(void);
981 
982 protected:
983     qpTestResult verify(const tcu::ConstPixelBufferAccess &result, const tcu::PixelBufferAccess &errorMask);
984 
985 private:
986     DerivateFunc m_func;
987 };
988 
ConstantDerivateCase(Context & context,const char * name,const char * description,DerivateFunc func,glu::DataType type)989 ConstantDerivateCase::ConstantDerivateCase(Context &context, const char *name, const char *description,
990                                            DerivateFunc func, glu::DataType type)
991     : TriangleDerivateCase(context, name, description)
992     , m_func(func)
993 {
994     m_dataType       = type;
995     m_precision      = glu::PRECISION_HIGHP;
996     m_coordDataType  = m_dataType;
997     m_coordPrecision = m_precision;
998 }
999 
init(void)1000 void ConstantDerivateCase::init(void)
1001 {
1002     const char *fragmentTmpl = "#version 300 es\n"
1003                                "layout(location = 0) out mediump vec4 o_color;\n"
1004                                "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1005                                "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1006                                "void main (void)\n"
1007                                "{\n"
1008                                "    ${PRECISION} ${DATATYPE} res = ${FUNC}(${VALUE}) * u_scale + u_bias;\n"
1009                                "    o_color = ${CAST_TO_OUTPUT};\n"
1010                                "}\n";
1011     map<string, string> fragmentParams;
1012     fragmentParams["PRECISION"]      = glu::getPrecisionName(m_precision);
1013     fragmentParams["DATATYPE"]       = glu::getDataTypeName(m_dataType);
1014     fragmentParams["FUNC"]           = getDerivateFuncName(m_func);
1015     fragmentParams["VALUE"]          = m_dataType == glu::TYPE_FLOAT_VEC4 ? "vec4(1.0, 7.2, -1e5, 0.0)" :
1016                                        m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec3(1e2, 8.0, 0.01)" :
1017                                        m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec2(-0.0, 2.7)" :
1018                                                                             /* TYPE_FLOAT */ "7.7";
1019     fragmentParams["CAST_TO_OUTPUT"] = m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1020                                        m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1021                                        m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1022                                                                             /* TYPE_FLOAT */ "vec4(res, 0.0, 0.0, 1.0)";
1023 
1024     m_fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
1025 
1026     m_derivScale = tcu::Vec4(1e3f, 1e3f, 1e3f, 1e3f);
1027     m_derivBias  = tcu::Vec4(0.5f, 0.5f, 0.5f, 0.5f);
1028 }
1029 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)1030 qpTestResult ConstantDerivateCase::verify(const tcu::ConstPixelBufferAccess &result,
1031                                           const tcu::PixelBufferAccess &errorMask)
1032 {
1033     const tcu::Vec4 reference(0.0f); // Derivate of constant argument should always be 0
1034     const tcu::Vec4 threshold = getSurfaceThreshold() / abs(m_derivScale);
1035 
1036     return verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, threshold, m_derivScale,
1037                                   m_derivBias);
1038 }
1039 
1040 // LinearDerivateCase
1041 
1042 class LinearDerivateCase : public TriangleDerivateCase
1043 {
1044 public:
1045     LinearDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1046                        glu::DataType type, glu::Precision precision, uint32_t hint, SurfaceType surfaceType,
1047                        int numSamples, const char *fragmentSrcTmpl);
~LinearDerivateCase(void)1048     ~LinearDerivateCase(void)
1049     {
1050     }
1051 
1052     void init(void);
1053 
1054 protected:
1055     qpTestResult verify(const tcu::ConstPixelBufferAccess &result, const tcu::PixelBufferAccess &errorMask);
1056 
1057 private:
1058     DerivateFunc m_func;
1059     std::string m_fragmentTmpl;
1060 };
1061 
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)1062 LinearDerivateCase::LinearDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1063                                        glu::DataType type, glu::Precision precision, uint32_t hint,
1064                                        SurfaceType surfaceType, int numSamples, const char *fragmentSrcTmpl)
1065     : TriangleDerivateCase(context, name, description)
1066     , m_func(func)
1067     , m_fragmentTmpl(fragmentSrcTmpl)
1068 {
1069     m_dataType            = type;
1070     m_precision           = precision;
1071     m_coordDataType       = m_dataType;
1072     m_coordPrecision      = m_precision;
1073     m_hint                = hint;
1074     m_surfaceType         = surfaceType;
1075     m_numSamples          = numSamples;
1076     m_useAsymmetricCoords = true;
1077 }
1078 
init(void)1079 void LinearDerivateCase::init(void)
1080 {
1081     const tcu::IVec2 viewportSize = getViewportSize();
1082     const float w                 = float(viewportSize.x());
1083     const float h                 = float(viewportSize.y());
1084     const bool packToInt          = m_surfaceType == SURFACETYPE_FLOAT_FBO;
1085     map<string, string> fragmentParams;
1086 
1087     fragmentParams["OUTPUT_TYPE"] = glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1088     fragmentParams["OUTPUT_PREC"] = glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_precision);
1089     fragmentParams["PRECISION"]   = glu::getPrecisionName(m_precision);
1090     fragmentParams["DATATYPE"]    = glu::getDataTypeName(m_dataType);
1091     fragmentParams["FUNC"]        = getDerivateFuncName(m_func);
1092 
1093     if (packToInt)
1094     {
1095         fragmentParams["CAST_TO_OUTPUT"] =
1096             m_dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1097             m_dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1098             m_dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1099                                                  /* TYPE_FLOAT */ "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1100     }
1101     else
1102     {
1103         fragmentParams["CAST_TO_OUTPUT"] =
1104             m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1105             m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1106             m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1107                                                  /* TYPE_FLOAT */ "vec4(res, 0.0, 0.0, 1.0)";
1108     }
1109 
1110     m_fragmentSrc = tcu::StringTemplate(m_fragmentTmpl.c_str()).specialize(fragmentParams);
1111 
1112     switch (m_precision)
1113     {
1114     case glu::PRECISION_HIGHP:
1115         m_coordMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1116         m_coordMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1117         break;
1118 
1119     case glu::PRECISION_MEDIUMP:
1120         m_coordMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1121         m_coordMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1122         break;
1123 
1124     case glu::PRECISION_LOWP:
1125         m_coordMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1126         m_coordMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1127         break;
1128 
1129     default:
1130         DE_ASSERT(false);
1131     }
1132 
1133     if (m_surfaceType == SURFACETYPE_FLOAT_FBO)
1134     {
1135         // No scale or bias used for accuracy.
1136         m_derivScale = tcu::Vec4(1.0f);
1137         m_derivBias  = tcu::Vec4(0.0f);
1138     }
1139     else
1140     {
1141         // Compute scale - bias that normalizes to 0..1 range.
1142         const tcu::Vec4 dx = (m_coordMax - m_coordMin) / tcu::Vec4(w, w, w * 0.5f, -w * 0.5f);
1143         const tcu::Vec4 dy = (m_coordMax - m_coordMin) / tcu::Vec4(h, h, h * 0.5f, -h * 0.5f);
1144 
1145         switch (m_func)
1146         {
1147         case DERIVATE_DFDX:
1148             m_derivScale = 0.5f / dx;
1149             break;
1150 
1151         case DERIVATE_DFDY:
1152             m_derivScale = 0.5f / dy;
1153             break;
1154 
1155         case DERIVATE_FWIDTH:
1156             m_derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1157             break;
1158 
1159         default:
1160             DE_ASSERT(false);
1161         }
1162 
1163         m_derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1164     }
1165 }
1166 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)1167 qpTestResult LinearDerivateCase::verify(const tcu::ConstPixelBufferAccess &result,
1168                                         const tcu::PixelBufferAccess &errorMask)
1169 {
1170     const tcu::Vec4 xScale = tcu::Vec4(0.5f, 0.5f, 0.5f, -0.5f);
1171     const tcu::Vec4 yScale = tcu::Vec4(0.5f, 0.5f, 0.5f, -0.5f);
1172 
1173     const tcu::Vec4 surfaceThreshold = getSurfaceThreshold() / abs(m_derivScale);
1174 
1175     if (m_func == DERIVATE_DFDX || m_func == DERIVATE_DFDY)
1176     {
1177         const bool isX               = m_func == DERIVATE_DFDX;
1178         const float div              = isX ? float(result.getWidth()) : float(result.getHeight());
1179         const tcu::Vec4 scale        = isX ? xScale : yScale;
1180         tcu::Vec4 reference          = ((m_coordMax - m_coordMin) / div);
1181         const tcu::Vec4 opThreshold  = getDerivateThreshold(m_precision, m_coordMin, m_coordMax, reference);
1182         const tcu::Vec4 opThresholdW = getDerivateThresholdWarning(m_precision, m_coordMin, m_coordMax, reference);
1183         const tcu::Vec4 threshold    = max(surfaceThreshold, opThreshold);
1184         const tcu::Vec4 thresholdW   = max(surfaceThreshold, opThresholdW);
1185         const int numComps           = glu::getDataTypeFloatScalars(m_dataType);
1186 
1187         /* adjust the reference value for the correct dfdx or dfdy sample adjacency */
1188         reference = reference * scale;
1189 
1190         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying result image.\n"
1191                            << "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold "
1192                            << LogVecComps(threshold, numComps) << tcu::TestLog::EndMessage;
1193 
1194         // short circuit if result is strictly within the normal value error bounds.
1195         // This improves performance significantly.
1196         if (verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, threshold,
1197                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1198         {
1199             m_testCtx.getLog() << tcu::TestLog::Message << "No incorrect derivatives found, result valid."
1200                                << tcu::TestLog::EndMessage;
1201 
1202             return QP_TEST_RESULT_PASS;
1203         }
1204 
1205         // Check with relaxed threshold value
1206         if (verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, thresholdW,
1207                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1208         {
1209             m_testCtx.getLog() << tcu::TestLog::Message
1210                                << "No incorrect derivatives found, result valid with quality warning."
1211                                << tcu::TestLog::EndMessage;
1212 
1213             return QP_TEST_RESULT_QUALITY_WARNING;
1214         }
1215 
1216         // some pixels exceed error bounds calculated for normal values. Verify that these
1217         // potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1218 
1219         m_testCtx.getLog() << tcu::TestLog::Message
1220                            << "Initial verification failed, verifying image by calculating accurate error bounds for "
1221                               "each result pixel.\n"
1222                            << "\tVerifying each result derivative is within its range of legal result values."
1223                            << tcu::TestLog::EndMessage;
1224 
1225         {
1226             const tcu::IVec2 viewportSize = getViewportSize();
1227             const float w                 = float(viewportSize.x());
1228             const float h                 = float(viewportSize.y());
1229             const tcu::Vec4 valueRamp     = (m_coordMax - m_coordMin);
1230             Linear2DFunctionEvaluator function;
1231 
1232             function.matrix.setRow(0,
1233                                    tcu::Vec3((valueRamp.x() / w) / 2.0f, (valueRamp.x() / h) / 2.0f, m_coordMin.x()));
1234             function.matrix.setRow(1,
1235                                    tcu::Vec3((valueRamp.y() / w) / 2.0f, (valueRamp.y() / h) / 2.0f, m_coordMin.y()));
1236             function.matrix.setRow(2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_coordMin.z() + m_coordMin.z()) /
1237                                           2.0f);
1238             function.matrix.setRow(
1239                 3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_coordMax.w() + m_coordMax.w()) / 2.0f);
1240 
1241             return reverifyConstantDerivateWithFlushRelaxations(m_testCtx.getLog(), result, errorMask, m_dataType,
1242                                                                 m_precision, m_derivScale, m_derivBias,
1243                                                                 surfaceThreshold, m_func, function);
1244         }
1245     }
1246     else
1247     {
1248         DE_ASSERT(m_func == DERIVATE_FWIDTH);
1249         const float w = float(result.getWidth());
1250         const float h = float(result.getHeight());
1251 
1252         const tcu::Vec4 dx          = ((m_coordMax - m_coordMin) / w) * xScale;
1253         const tcu::Vec4 dy          = ((m_coordMax - m_coordMin) / h) * yScale;
1254         const tcu::Vec4 reference   = tcu::abs(dx) + tcu::abs(dy);
1255         const tcu::Vec4 dxThreshold = getDerivateThreshold(m_precision, m_coordMin * xScale, m_coordMax * xScale, dx);
1256         const tcu::Vec4 dyThreshold = getDerivateThreshold(m_precision, m_coordMin * yScale, m_coordMax * yScale, dy);
1257         const tcu::Vec4 dxThresholdW =
1258             getDerivateThresholdWarning(m_precision, m_coordMin * xScale, m_coordMax * xScale, dx);
1259         const tcu::Vec4 dyThresholdW =
1260             getDerivateThresholdWarning(m_precision, m_coordMin * yScale, m_coordMax * yScale, dy);
1261         const tcu::Vec4 threshold  = max(surfaceThreshold, max(dxThreshold, dyThreshold));
1262         const tcu::Vec4 thresholdW = max(surfaceThreshold, max(dxThresholdW, dyThresholdW));
1263         qpTestResult testResult    = QP_TEST_RESULT_FAIL;
1264 
1265         testResult = verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, threshold,
1266                                             m_derivScale, m_derivBias);
1267 
1268         // return if result is pass
1269         if (testResult == QP_TEST_RESULT_PASS)
1270             return testResult;
1271 
1272         // re-check with relaxed threshold
1273         testResult = verifyConstantDerivate(m_testCtx.getLog(), result, errorMask, m_dataType, reference, thresholdW,
1274                                             m_derivScale, m_derivBias);
1275 
1276         // if with relaxed threshold test is passing then mark the result with quality warning.
1277         if (testResult == QP_TEST_RESULT_PASS)
1278             testResult = QP_TEST_RESULT_QUALITY_WARNING;
1279 
1280         return testResult;
1281     }
1282 }
1283 
1284 // TextureDerivateCase
1285 
1286 class TextureDerivateCase : public TriangleDerivateCase
1287 {
1288 public:
1289     TextureDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1290                         glu::DataType type, glu::Precision precision, uint32_t hint, SurfaceType surfaceType,
1291                         int numSamples);
1292     ~TextureDerivateCase(void);
1293 
1294     void init(void);
1295     void deinit(void);
1296 
1297 protected:
1298     void setupRenderState(uint32_t program);
1299     qpTestResult verify(const tcu::ConstPixelBufferAccess &result, const tcu::PixelBufferAccess &errorMask);
1300 
1301 private:
1302     DerivateFunc m_func;
1303 
1304     tcu::Vec4 m_texValueMin;
1305     tcu::Vec4 m_texValueMax;
1306     glu::Texture2D *m_texture;
1307 };
1308 
TextureDerivateCase(Context & context,const char * name,const char * description,DerivateFunc func,glu::DataType type,glu::Precision precision,uint32_t hint,SurfaceType surfaceType,int numSamples)1309 TextureDerivateCase::TextureDerivateCase(Context &context, const char *name, const char *description, DerivateFunc func,
1310                                          glu::DataType type, glu::Precision precision, uint32_t hint,
1311                                          SurfaceType surfaceType, int numSamples)
1312     : TriangleDerivateCase(context, name, description)
1313     , m_func(func)
1314     , m_texture(nullptr)
1315 {
1316     m_dataType       = type;
1317     m_precision      = precision;
1318     m_coordDataType  = glu::TYPE_FLOAT_VEC2;
1319     m_coordPrecision = glu::PRECISION_HIGHP;
1320     m_hint           = hint;
1321     m_surfaceType    = surfaceType;
1322     m_numSamples     = numSamples;
1323 }
1324 
~TextureDerivateCase(void)1325 TextureDerivateCase::~TextureDerivateCase(void)
1326 {
1327     delete m_texture;
1328 }
1329 
init(void)1330 void TextureDerivateCase::init(void)
1331 {
1332     // Generate shader
1333     {
1334         const char *fragmentTmpl = "#version 300 es\n"
1335                                    "in highp vec2 v_coord;\n"
1336                                    "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1337                                    "uniform ${PRECISION} sampler2D u_sampler;\n"
1338                                    "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1339                                    "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1340                                    "void main (void)\n"
1341                                    "{\n"
1342                                    "    ${PRECISION} vec4 tex = texture(u_sampler, v_coord);\n"
1343                                    "    ${PRECISION} ${DATATYPE} res = ${FUNC}(tex${SWIZZLE}) * u_scale + u_bias;\n"
1344                                    "    o_color = ${CAST_TO_OUTPUT};\n"
1345                                    "}\n";
1346 
1347         const bool packToInt = m_surfaceType == SURFACETYPE_FLOAT_FBO;
1348         map<string, string> fragmentParams;
1349 
1350         fragmentParams["OUTPUT_TYPE"] = glu::getDataTypeName(packToInt ? glu::TYPE_UINT_VEC4 : glu::TYPE_FLOAT_VEC4);
1351         fragmentParams["OUTPUT_PREC"] = glu::getPrecisionName(packToInt ? glu::PRECISION_HIGHP : m_precision);
1352         fragmentParams["PRECISION"]   = glu::getPrecisionName(m_precision);
1353         fragmentParams["DATATYPE"]    = glu::getDataTypeName(m_dataType);
1354         fragmentParams["FUNC"]        = getDerivateFuncName(m_func);
1355         fragmentParams["SWIZZLE"]     = m_dataType == glu::TYPE_FLOAT_VEC4 ? "" :
1356                                         m_dataType == glu::TYPE_FLOAT_VEC3 ? ".xyz" :
1357                                         m_dataType == glu::TYPE_FLOAT_VEC2 ? ".xy" :
1358                                                                              /* TYPE_FLOAT */ ".x";
1359 
1360         if (packToInt)
1361         {
1362             fragmentParams["CAST_TO_OUTPUT"] =
1363                 m_dataType == glu::TYPE_FLOAT_VEC4 ? "floatBitsToUint(res)" :
1364                 m_dataType == glu::TYPE_FLOAT_VEC3 ? "floatBitsToUint(vec4(res, 1.0))" :
1365                 m_dataType == glu::TYPE_FLOAT_VEC2 ? "floatBitsToUint(vec4(res, 0.0, 1.0))" :
1366                                                      /* TYPE_FLOAT */ "floatBitsToUint(vec4(res, 0.0, 0.0, 1.0))";
1367         }
1368         else
1369         {
1370             fragmentParams["CAST_TO_OUTPUT"] =
1371                 m_dataType == glu::TYPE_FLOAT_VEC4 ? "res" :
1372                 m_dataType == glu::TYPE_FLOAT_VEC3 ? "vec4(res, 1.0)" :
1373                 m_dataType == glu::TYPE_FLOAT_VEC2 ? "vec4(res, 0.0, 1.0)" :
1374                                                      /* TYPE_FLOAT */ "vec4(res, 0.0, 0.0, 1.0)";
1375         }
1376 
1377         m_fragmentSrc = tcu::StringTemplate(fragmentTmpl).specialize(fragmentParams);
1378     }
1379 
1380     // Texture size matches viewport and nearest sampling is used. Thus texture sampling
1381     // is equal to just interpolating the texture value range.
1382 
1383     // Determine value range for texture.
1384 
1385     switch (m_precision)
1386     {
1387     case glu::PRECISION_HIGHP:
1388         m_texValueMin = tcu::Vec4(-97.f, 0.2f, 71.f, 74.f);
1389         m_texValueMax = tcu::Vec4(-13.2f, -77.f, 44.f, 76.f);
1390         break;
1391 
1392     case glu::PRECISION_MEDIUMP:
1393         m_texValueMin = tcu::Vec4(-37.0f, 47.f, -7.f, 0.0f);
1394         m_texValueMax = tcu::Vec4(-1.0f, 12.f, 7.f, 19.f);
1395         break;
1396 
1397     case glu::PRECISION_LOWP:
1398         m_texValueMin = tcu::Vec4(0.0f, -1.0f, 0.0f, 1.0f);
1399         m_texValueMax = tcu::Vec4(1.0f, 1.0f, -1.0f, -1.0f);
1400         break;
1401 
1402     default:
1403         DE_ASSERT(false);
1404     }
1405 
1406     // Lowp and mediump cases use RGBA16F format, while highp uses RGBA32F.
1407     {
1408         const tcu::IVec2 viewportSize = getViewportSize();
1409         DE_ASSERT(!m_texture);
1410         m_texture = new glu::Texture2D(m_context.getRenderContext(),
1411                                        m_precision == glu::PRECISION_HIGHP ? GL_RGBA32F : GL_RGBA16F, viewportSize.x(),
1412                                        viewportSize.y());
1413         m_texture->getRefTexture().allocLevel(0);
1414     }
1415 
1416     // Texture coordinates
1417     m_coordMin = tcu::Vec4(0.0f);
1418     m_coordMax = tcu::Vec4(1.0f);
1419 
1420     // Fill with gradients.
1421     {
1422         const tcu::PixelBufferAccess level0 = m_texture->getRefTexture().getLevel(0);
1423         for (int y = 0; y < level0.getHeight(); y++)
1424         {
1425             for (int x = 0; x < level0.getWidth(); x++)
1426             {
1427                 const float xf = (float(x) + 0.5f) / float(level0.getWidth());
1428                 const float yf = (float(y) + 0.5f) / float(level0.getHeight());
1429                 // Make x and y data to have dependency to both axes so that dfdx(tex).y and dfdy(tex).x are nonzero.
1430                 const tcu::Vec4 s =
1431                     tcu::Vec4(xf + yf / 2.0f, yf + xf / 2.0f, (xf + yf) / 2.0f, 1.0f - (xf + yf) / 2.0f);
1432 
1433                 level0.setPixel(m_texValueMin + (m_texValueMax - m_texValueMin) * s, x, y);
1434             }
1435         }
1436     }
1437 
1438     m_texture->upload();
1439 
1440     if (m_surfaceType == SURFACETYPE_FLOAT_FBO)
1441     {
1442         // No scale or bias used for accuracy.
1443         m_derivScale = tcu::Vec4(1.0f);
1444         m_derivBias  = tcu::Vec4(0.0f);
1445     }
1446     else
1447     {
1448         // Compute scale - bias that normalizes to 0..1 range.
1449         const tcu::IVec2 viewportSize = getViewportSize();
1450         const float w                 = float(viewportSize.x());
1451         const float h                 = float(viewportSize.y());
1452         const tcu::Vec4 dx            = (m_texValueMax - m_texValueMin) / tcu::Vec4(w, w, w * 0.5f, -w * 0.5f);
1453         const tcu::Vec4 dy            = (m_texValueMax - m_texValueMin) / tcu::Vec4(h, h, h * 0.5f, -h * 0.5f);
1454 
1455         switch (m_func)
1456         {
1457         case DERIVATE_DFDX:
1458             m_derivScale = 0.5f / dx;
1459             break;
1460 
1461         case DERIVATE_DFDY:
1462             m_derivScale = 0.5f / dy;
1463             break;
1464 
1465         case DERIVATE_FWIDTH:
1466             m_derivScale = 0.5f / (tcu::abs(dx) + tcu::abs(dy));
1467             break;
1468 
1469         default:
1470             DE_ASSERT(false);
1471         }
1472 
1473         m_derivBias = tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
1474     }
1475 }
1476 
deinit(void)1477 void TextureDerivateCase::deinit(void)
1478 {
1479     delete m_texture;
1480     m_texture = nullptr;
1481 }
1482 
setupRenderState(uint32_t program)1483 void TextureDerivateCase::setupRenderState(uint32_t program)
1484 {
1485     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1486     const int texUnit        = 1;
1487 
1488     gl.activeTexture(GL_TEXTURE0 + texUnit);
1489     gl.bindTexture(GL_TEXTURE_2D, m_texture->getGLTexture());
1490     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1491     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1492     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1493     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1494 
1495     gl.uniform1i(gl.getUniformLocation(program, "u_sampler"), texUnit);
1496 }
1497 
verify(const tcu::ConstPixelBufferAccess & result,const tcu::PixelBufferAccess & errorMask)1498 qpTestResult TextureDerivateCase::verify(const tcu::ConstPixelBufferAccess &result,
1499                                          const tcu::PixelBufferAccess &errorMask)
1500 {
1501     // \note Edges are ignored in comparison
1502     if (result.getWidth() < 2 || result.getHeight() < 2)
1503         throw tcu::NotSupportedError("Too small viewport");
1504 
1505     tcu::ConstPixelBufferAccess compareArea =
1506         tcu::getSubregion(result, 1, 1, result.getWidth() - 2, result.getHeight() - 2);
1507     tcu::PixelBufferAccess maskArea =
1508         tcu::getSubregion(errorMask, 1, 1, errorMask.getWidth() - 2, errorMask.getHeight() - 2);
1509     const tcu::Vec4 xScale = tcu::Vec4(1.0f, 0.5f, 0.5f, -0.5f);
1510     const tcu::Vec4 yScale = tcu::Vec4(0.5f, 1.0f, 0.5f, -0.5f);
1511     const float w          = float(result.getWidth());
1512     const float h          = float(result.getHeight());
1513 
1514     const tcu::Vec4 surfaceThreshold = getSurfaceThreshold() / abs(m_derivScale);
1515 
1516     if (m_func == DERIVATE_DFDX || m_func == DERIVATE_DFDY)
1517     {
1518         const bool isX              = m_func == DERIVATE_DFDX;
1519         const float div             = isX ? w : h;
1520         const tcu::Vec4 scale       = isX ? xScale : yScale;
1521         tcu::Vec4 reference         = ((m_texValueMax - m_texValueMin) / div);
1522         const tcu::Vec4 opThreshold = getDerivateThreshold(m_precision, m_texValueMin, m_texValueMax, reference);
1523         const tcu::Vec4 opThresholdW =
1524             getDerivateThresholdWarning(m_precision, m_texValueMin, m_texValueMax, reference);
1525         const tcu::Vec4 threshold  = max(surfaceThreshold, opThreshold);
1526         const tcu::Vec4 thresholdW = max(surfaceThreshold, opThresholdW);
1527         const int numComps         = glu::getDataTypeFloatScalars(m_dataType);
1528 
1529         /* adjust the reference value for the correct dfdx or dfdy sample adjacency */
1530         reference = reference * scale;
1531 
1532         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying result image.\n"
1533                            << "\tValid derivative is " << LogVecComps(reference, numComps) << " with threshold "
1534                            << LogVecComps(threshold, numComps) << tcu::TestLog::EndMessage;
1535 
1536         // short circuit if result is strictly within the normal value error bounds.
1537         // This improves performance significantly.
1538         if (verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference, threshold,
1539                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1540         {
1541             m_testCtx.getLog() << tcu::TestLog::Message << "No incorrect derivatives found, result valid."
1542                                << tcu::TestLog::EndMessage;
1543 
1544             return QP_TEST_RESULT_PASS;
1545         }
1546 
1547         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying result image.\n"
1548                            << "\tValid derivative is " << LogVecComps(reference, numComps) << " with Warning threshold "
1549                            << LogVecComps(thresholdW, numComps) << tcu::TestLog::EndMessage;
1550 
1551         // Re-check with relaxed threshold
1552         if (verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference, thresholdW,
1553                                    m_derivScale, m_derivBias, LOG_NOTHING) == QP_TEST_RESULT_PASS)
1554         {
1555             m_testCtx.getLog() << tcu::TestLog::Message
1556                                << "No incorrect derivatives found, result valid with quality warning."
1557                                << tcu::TestLog::EndMessage;
1558 
1559             return QP_TEST_RESULT_QUALITY_WARNING;
1560         }
1561 
1562         // some pixels exceed error bounds calculated for normal values. Verify that these
1563         // potentially invalid pixels are in fact valid due to (for example) subnorm flushing.
1564 
1565         m_testCtx.getLog() << tcu::TestLog::Message
1566                            << "Initial verification failed, verifying image by calculating accurate error bounds for "
1567                               "each result pixel.\n"
1568                            << "\tVerifying each result derivative is within its range of legal result values."
1569                            << tcu::TestLog::EndMessage;
1570 
1571         {
1572             const tcu::Vec4 valueRamp = (m_texValueMax - m_texValueMin);
1573             Linear2DFunctionEvaluator function;
1574 
1575             function.matrix.setRow(0, tcu::Vec3(valueRamp.x() / w, (valueRamp.x() / h) / 2.0f, m_texValueMin.x()));
1576             function.matrix.setRow(1, tcu::Vec3((valueRamp.y() / w) / 2.0f, valueRamp.y() / h, m_texValueMin.y()));
1577             function.matrix.setRow(
1578                 2, tcu::Vec3(valueRamp.z() / w, valueRamp.z() / h, m_texValueMin.z() + m_texValueMin.z()) / 2.0f);
1579             function.matrix.setRow(
1580                 3, tcu::Vec3(-valueRamp.w() / w, -valueRamp.w() / h, m_texValueMax.w() + m_texValueMax.w()) / 2.0f);
1581 
1582             return reverifyConstantDerivateWithFlushRelaxations(m_testCtx.getLog(), compareArea, maskArea, m_dataType,
1583                                                                 m_precision, m_derivScale, m_derivBias,
1584                                                                 surfaceThreshold, m_func, function);
1585         }
1586     }
1587     else
1588     {
1589         DE_ASSERT(m_func == DERIVATE_FWIDTH);
1590         const tcu::Vec4 dx        = ((m_texValueMax - m_texValueMin) / w) * xScale;
1591         const tcu::Vec4 dy        = ((m_texValueMax - m_texValueMin) / h) * yScale;
1592         const tcu::Vec4 reference = tcu::abs(dx) + tcu::abs(dy);
1593         const tcu::Vec4 dxThreshold =
1594             getDerivateThreshold(m_precision, m_texValueMin * xScale, m_texValueMax * xScale, dx);
1595         const tcu::Vec4 dyThreshold =
1596             getDerivateThreshold(m_precision, m_texValueMin * yScale, m_texValueMax * yScale, dy);
1597         const tcu::Vec4 dxThresholdW =
1598             getDerivateThresholdWarning(m_precision, m_texValueMin * xScale, m_texValueMax * xScale, dx);
1599         const tcu::Vec4 dyThresholdW =
1600             getDerivateThresholdWarning(m_precision, m_texValueMin * yScale, m_texValueMax * yScale, dy);
1601         const tcu::Vec4 threshold  = max(surfaceThreshold, max(dxThreshold, dyThreshold));
1602         const tcu::Vec4 thresholdW = max(surfaceThreshold, max(dxThresholdW, dyThresholdW));
1603         qpTestResult testResult    = QP_TEST_RESULT_FAIL;
1604 
1605         testResult = verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference, threshold,
1606                                             m_derivScale, m_derivBias);
1607 
1608         if (testResult == QP_TEST_RESULT_PASS)
1609             return testResult;
1610 
1611         // Re-Check with relaxed threshold
1612         testResult = verifyConstantDerivate(m_testCtx.getLog(), compareArea, maskArea, m_dataType, reference,
1613                                             thresholdW, m_derivScale, m_derivBias);
1614 
1615         // If test is passing with relaxed threshold then mark quality warning
1616         if (testResult == QP_TEST_RESULT_PASS)
1617             testResult = QP_TEST_RESULT_QUALITY_WARNING;
1618 
1619         return testResult;
1620     }
1621 }
1622 
ShaderDerivateTests(Context & context)1623 ShaderDerivateTests::ShaderDerivateTests(Context &context)
1624     : TestCaseGroup(context, "derivate", "Derivate Function Tests")
1625 {
1626 }
1627 
~ShaderDerivateTests(void)1628 ShaderDerivateTests::~ShaderDerivateTests(void)
1629 {
1630 }
1631 
1632 struct FunctionSpec
1633 {
1634     std::string name;
1635     DerivateFunc function;
1636     glu::DataType dataType;
1637     glu::Precision precision;
1638 
FunctionSpecdeqp::gles3::Functional::FunctionSpec1639     FunctionSpec(const std::string &name_, DerivateFunc function_, glu::DataType dataType_, glu::Precision precision_)
1640         : name(name_)
1641         , function(function_)
1642         , dataType(dataType_)
1643         , precision(precision_)
1644     {
1645     }
1646 };
1647 
init(void)1648 void ShaderDerivateTests::init(void)
1649 {
1650     static const struct
1651     {
1652         const char *name;
1653         const char *description;
1654         const char *source;
1655     } s_linearDerivateCases[] = {
1656         {"linear", "Basic derivate of linearly interpolated argument",
1657 
1658          "#version 300 es\n"
1659          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1660          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1661          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1662          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1663          "void main (void)\n"
1664          "{\n"
1665          "    ${PRECISION} ${DATATYPE} res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1666          "    o_color = ${CAST_TO_OUTPUT};\n"
1667          "}\n"},
1668         {"in_function", "Derivate of linear function argument",
1669 
1670          "#version 300 es\n"
1671          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1672          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1673          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1674          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1675          "\n"
1676          "${PRECISION} ${DATATYPE} computeRes (${PRECISION} ${DATATYPE} value)\n"
1677          "{\n"
1678          "    return ${FUNC}(v_coord) * u_scale + u_bias;\n"
1679          "}\n"
1680          "\n"
1681          "void main (void)\n"
1682          "{\n"
1683          "    ${PRECISION} ${DATATYPE} res = computeRes(v_coord);\n"
1684          "    o_color = ${CAST_TO_OUTPUT};\n"
1685          "}\n"},
1686         {"static_if", "Derivate of linearly interpolated value in static if",
1687 
1688          "#version 300 es\n"
1689          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1690          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1691          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1692          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1693          "void main (void)\n"
1694          "{\n"
1695          "    ${PRECISION} ${DATATYPE} res;\n"
1696          "    if (false)\n"
1697          "        res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1698          "    else\n"
1699          "        res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1700          "    o_color = ${CAST_TO_OUTPUT};\n"
1701          "}\n"},
1702         {"static_loop", "Derivate of linearly interpolated value in static loop",
1703 
1704          "#version 300 es\n"
1705          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1706          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1707          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1708          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1709          "void main (void)\n"
1710          "{\n"
1711          "    ${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1712          "    for (int i = 0; i < 2; i++)\n"
1713          "        res += ${FUNC}(v_coord * float(i));\n"
1714          "    res = res * u_scale + u_bias;\n"
1715          "    o_color = ${CAST_TO_OUTPUT};\n"
1716          "}\n"},
1717         {"static_switch", "Derivate of linearly interpolated value in static switch",
1718 
1719          "#version 300 es\n"
1720          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1721          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1722          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1723          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1724          "void main (void)\n"
1725          "{\n"
1726          "    ${PRECISION} ${DATATYPE} res;\n"
1727          "    switch (1)\n"
1728          "    {\n"
1729          "        case 0: res = ${FUNC}(-v_coord) * u_scale + u_bias;    break;\n"
1730          "        case 1: res = ${FUNC}(v_coord) * u_scale + u_bias;    break;\n"
1731          "    }\n"
1732          "    o_color = ${CAST_TO_OUTPUT};\n"
1733          "}\n"},
1734         {"uniform_if", "Derivate of linearly interpolated value in uniform if",
1735 
1736          "#version 300 es\n"
1737          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1738          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1739          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1740          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1741          "uniform bool ub_true;\n"
1742          "void main (void)\n"
1743          "{\n"
1744          "    ${PRECISION} ${DATATYPE} res;\n"
1745          "    if (ub_true)"
1746          "        res = ${FUNC}(v_coord) * u_scale + u_bias;\n"
1747          "    else\n"
1748          "        res = ${FUNC}(-v_coord) * u_scale + u_bias;\n"
1749          "    o_color = ${CAST_TO_OUTPUT};\n"
1750          "}\n"},
1751         {"uniform_loop", "Derivate of linearly interpolated value in uniform loop",
1752 
1753          "#version 300 es\n"
1754          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1755          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1756          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1757          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1758          "uniform int ui_two;\n"
1759          "void main (void)\n"
1760          "{\n"
1761          "    ${PRECISION} ${DATATYPE} res = ${DATATYPE}(0.0);\n"
1762          "    for (int i = 0; i < ui_two; i++)\n"
1763          "        res += ${FUNC}(v_coord * float(i));\n"
1764          "    res = res * u_scale + u_bias;\n"
1765          "    o_color = ${CAST_TO_OUTPUT};\n"
1766          "}\n"},
1767         {"uniform_switch", "Derivate of linearly interpolated value in uniform switch",
1768 
1769          "#version 300 es\n"
1770          "in ${PRECISION} ${DATATYPE} v_coord;\n"
1771          "layout(location = 0) out ${OUTPUT_PREC} ${OUTPUT_TYPE} o_color;\n"
1772          "uniform ${PRECISION} ${DATATYPE} u_scale;\n"
1773          "uniform ${PRECISION} ${DATATYPE} u_bias;\n"
1774          "uniform int ui_one;\n"
1775          "void main (void)\n"
1776          "{\n"
1777          "    ${PRECISION} ${DATATYPE} res;\n"
1778          "    switch (ui_one)\n"
1779          "    {\n"
1780          "        case 0: res = ${FUNC}(-v_coord) * u_scale + u_bias;    break;\n"
1781          "        case 1: res = ${FUNC}(v_coord) * u_scale + u_bias;    break;\n"
1782          "    }\n"
1783          "    o_color = ${CAST_TO_OUTPUT};\n"
1784          "}\n"},
1785     };
1786 
1787     static const struct
1788     {
1789         const char *name;
1790         SurfaceType surfaceType;
1791         int numSamples;
1792     } s_fboConfigs[] = {
1793         {"fbo", SURFACETYPE_DEFAULT_FRAMEBUFFER, 0},
1794         {"fbo_msaa2", SURFACETYPE_UNORM_FBO, 2},
1795         {"fbo_msaa4", SURFACETYPE_UNORM_FBO, 4},
1796         {"fbo_float", SURFACETYPE_FLOAT_FBO, 0},
1797     };
1798 
1799     static const struct
1800     {
1801         const char *name;
1802         uint32_t hint;
1803     } s_hints[] = {
1804         {"fastest", GL_FASTEST},
1805         {"nicest", GL_NICEST},
1806     };
1807 
1808     static const struct
1809     {
1810         const char *name;
1811         SurfaceType surfaceType;
1812         int numSamples;
1813     } s_hintFboConfigs[] = {{"default", SURFACETYPE_DEFAULT_FRAMEBUFFER, 0},
1814                             {"fbo_msaa4", SURFACETYPE_UNORM_FBO, 4},
1815                             {"fbo_float", SURFACETYPE_FLOAT_FBO, 0}};
1816 
1817     static const struct
1818     {
1819         const char *name;
1820         SurfaceType surfaceType;
1821         int numSamples;
1822         uint32_t hint;
1823     } s_textureConfigs[] = {
1824         {"basic", SURFACETYPE_DEFAULT_FRAMEBUFFER, 0, GL_DONT_CARE},
1825         {"msaa4", SURFACETYPE_UNORM_FBO, 4, GL_DONT_CARE},
1826         {"float_fastest", SURFACETYPE_FLOAT_FBO, 0, GL_FASTEST},
1827         {"float_nicest", SURFACETYPE_FLOAT_FBO, 0, GL_NICEST},
1828     };
1829 
1830     // .dfdx, .dfdy, .fwidth
1831     for (int funcNdx = 0; funcNdx < DERIVATE_LAST; funcNdx++)
1832     {
1833         const DerivateFunc function = DerivateFunc(funcNdx);
1834         tcu::TestCaseGroup *const functionGroup =
1835             new tcu::TestCaseGroup(m_testCtx, getDerivateFuncCaseName(function), getDerivateFuncName(function));
1836         addChild(functionGroup);
1837 
1838         // .constant - no precision variants, checks that derivate of constant arguments is 0
1839         {
1840             tcu::TestCaseGroup *const constantGroup =
1841                 new tcu::TestCaseGroup(m_testCtx, "constant", "Derivate of constant argument");
1842             functionGroup->addChild(constantGroup);
1843 
1844             for (int vecSize = 1; vecSize <= 4; vecSize++)
1845             {
1846                 const glu::DataType dataType = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1847                 constantGroup->addChild(
1848                     new ConstantDerivateCase(m_context, glu::getDataTypeName(dataType), "", function, dataType));
1849             }
1850         }
1851 
1852         // Cases based on LinearDerivateCase
1853         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_linearDerivateCases); caseNdx++)
1854         {
1855             tcu::TestCaseGroup *const linearCaseGroup = new tcu::TestCaseGroup(
1856                 m_testCtx, s_linearDerivateCases[caseNdx].name, s_linearDerivateCases[caseNdx].description);
1857             const char *source = s_linearDerivateCases[caseNdx].source;
1858             functionGroup->addChild(linearCaseGroup);
1859 
1860             for (int vecSize = 1; vecSize <= 4; vecSize++)
1861             {
1862                 for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1863                 {
1864                     const glu::DataType dataType   = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1865                     const glu::Precision precision = glu::Precision(precNdx);
1866                     const SurfaceType surfaceType  = SURFACETYPE_DEFAULT_FRAMEBUFFER;
1867                     const int numSamples           = 0;
1868                     const uint32_t hint            = GL_DONT_CARE;
1869                     ostringstream caseName;
1870 
1871                     if (caseNdx != 0 && precision == glu::PRECISION_LOWP)
1872                         continue; // Skip as lowp doesn't actually produce any bits when rendered to default FB.
1873 
1874                     caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1875 
1876                     linearCaseGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function,
1877                                                                      dataType, precision, hint, surfaceType, numSamples,
1878                                                                      source));
1879                 }
1880             }
1881         }
1882 
1883         // Fbo cases
1884         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(s_fboConfigs); caseNdx++)
1885         {
1886             tcu::TestCaseGroup *const fboGroup =
1887                 new tcu::TestCaseGroup(m_testCtx, s_fboConfigs[caseNdx].name, "Derivate usage when rendering into FBO");
1888             const char *source            = s_linearDerivateCases[0].source; // use source from .linear group
1889             const SurfaceType surfaceType = s_fboConfigs[caseNdx].surfaceType;
1890             const int numSamples          = s_fboConfigs[caseNdx].numSamples;
1891             functionGroup->addChild(fboGroup);
1892 
1893             for (int vecSize = 1; vecSize <= 4; vecSize++)
1894             {
1895                 for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1896                 {
1897                     const glu::DataType dataType   = vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1898                     const glu::Precision precision = glu::Precision(precNdx);
1899                     const uint32_t hint            = GL_DONT_CARE;
1900                     ostringstream caseName;
1901 
1902                     if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1903                         continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1904 
1905                     caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1906 
1907                     fboGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function, dataType,
1908                                                               precision, hint, surfaceType, numSamples, source));
1909                 }
1910             }
1911         }
1912 
1913         // .fastest, .nicest
1914         for (int hintCaseNdx = 0; hintCaseNdx < DE_LENGTH_OF_ARRAY(s_hints); hintCaseNdx++)
1915         {
1916             tcu::TestCaseGroup *const hintGroup =
1917                 new tcu::TestCaseGroup(m_testCtx, s_hints[hintCaseNdx].name, "Shader derivate hints");
1918             const char *source  = s_linearDerivateCases[0].source; // use source from .linear group
1919             const uint32_t hint = s_hints[hintCaseNdx].hint;
1920             functionGroup->addChild(hintGroup);
1921 
1922             for (int fboCaseNdx = 0; fboCaseNdx < DE_LENGTH_OF_ARRAY(s_hintFboConfigs); fboCaseNdx++)
1923             {
1924                 tcu::TestCaseGroup *const fboGroup =
1925                     new tcu::TestCaseGroup(m_testCtx, s_hintFboConfigs[fboCaseNdx].name, "");
1926                 const SurfaceType surfaceType = s_hintFboConfigs[fboCaseNdx].surfaceType;
1927                 const int numSamples          = s_hintFboConfigs[fboCaseNdx].numSamples;
1928                 hintGroup->addChild(fboGroup);
1929 
1930                 for (int vecSize = 1; vecSize <= 4; vecSize++)
1931                 {
1932                     for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1933                     {
1934                         const glu::DataType dataType =
1935                             vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1936                         const glu::Precision precision = glu::Precision(precNdx);
1937                         ostringstream caseName;
1938 
1939                         if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1940                             continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1941 
1942                         caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1943 
1944                         fboGroup->addChild(new LinearDerivateCase(m_context, caseName.str().c_str(), "", function,
1945                                                                   dataType, precision, hint, surfaceType, numSamples,
1946                                                                   source));
1947                     }
1948                 }
1949             }
1950         }
1951 
1952         // .texture
1953         {
1954             tcu::TestCaseGroup *const textureGroup =
1955                 new tcu::TestCaseGroup(m_testCtx, "texture", "Derivate of texture lookup result");
1956             functionGroup->addChild(textureGroup);
1957 
1958             for (int texCaseNdx = 0; texCaseNdx < DE_LENGTH_OF_ARRAY(s_textureConfigs); texCaseNdx++)
1959             {
1960                 tcu::TestCaseGroup *const caseGroup =
1961                     new tcu::TestCaseGroup(m_testCtx, s_textureConfigs[texCaseNdx].name, "");
1962                 const SurfaceType surfaceType = s_textureConfigs[texCaseNdx].surfaceType;
1963                 const int numSamples          = s_textureConfigs[texCaseNdx].numSamples;
1964                 const uint32_t hint           = s_textureConfigs[texCaseNdx].hint;
1965                 textureGroup->addChild(caseGroup);
1966 
1967                 for (int vecSize = 1; vecSize <= 4; vecSize++)
1968                 {
1969                     for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
1970                     {
1971                         const glu::DataType dataType =
1972                             vecSize > 1 ? glu::getDataTypeFloatVec(vecSize) : glu::TYPE_FLOAT;
1973                         const glu::Precision precision = glu::Precision(precNdx);
1974                         ostringstream caseName;
1975 
1976                         if (surfaceType != SURFACETYPE_FLOAT_FBO && precision == glu::PRECISION_LOWP)
1977                             continue; // Skip as lowp doesn't actually produce any bits when rendered to U8 RT.
1978 
1979                         caseName << glu::getDataTypeName(dataType) << "_" << glu::getPrecisionName(precision);
1980 
1981                         caseGroup->addChild(new TextureDerivateCase(m_context, caseName.str().c_str(), "", function,
1982                                                                     dataType, precision, hint, surfaceType,
1983                                                                     numSamples));
1984                     }
1985                 }
1986             }
1987         }
1988     }
1989 }
1990 
1991 } // namespace Functional
1992 } // namespace gles3
1993 } // namespace deqp
1994