1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 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 GLSL textureGather[Offset[s]] tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es31fTextureGatherTests.hpp"
25 #include "glsTextureTestUtil.hpp"
26 #include "gluShaderProgram.hpp"
27 #include "gluTexture.hpp"
28 #include "gluDrawUtil.hpp"
29 #include "gluPixelTransfer.hpp"
30 #include "gluTextureUtil.hpp"
31 #include "gluStrUtil.hpp"
32 #include "gluObjectWrapper.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "tcuStringTemplate.hpp"
35 #include "tcuSurface.hpp"
36 #include "tcuTestLog.hpp"
37 #include "tcuVectorUtil.hpp"
38 #include "tcuTexLookupVerifier.hpp"
39 #include "tcuTexCompareVerifier.hpp"
40 #include "tcuCommandLine.hpp"
41 #include "deUniquePtr.hpp"
42 #include "deStringUtil.hpp"
43 #include "deRandom.hpp"
44 #include "deString.h"
45
46 #include "glwEnums.hpp"
47 #include "glwFunctions.hpp"
48
49 using de::MovePtr;
50 using glu::ShaderProgram;
51 using tcu::ConstPixelBufferAccess;
52 using tcu::IVec2;
53 using tcu::IVec3;
54 using tcu::IVec4;
55 using tcu::PixelBufferAccess;
56 using tcu::TestLog;
57 using tcu::UVec4;
58 using tcu::Vec2;
59 using tcu::Vec3;
60 using tcu::Vec4;
61
62 using std::string;
63 using std::vector;
64
65 namespace deqp
66 {
67
68 using glu::TextureTestUtil::TextureType;
69 using glu::TextureTestUtil::TEXTURETYPE_2D;
70 using glu::TextureTestUtil::TEXTURETYPE_2D_ARRAY;
71 using glu::TextureTestUtil::TEXTURETYPE_CUBE;
72
73 namespace gles31
74 {
75 namespace Functional
76 {
77
78 namespace
79 {
80
specializeShader(Context & context,const char * code)81 static std::string specializeShader(Context &context, const char *code)
82 {
83 const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(context.getRenderContext().getType());
84 std::map<std::string, std::string> specializationMap;
85
86 specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion);
87
88 if (glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
89 glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::core(4, 5)))
90 specializationMap["GPU_SHADER5_REQUIRE"] = "";
91 else
92 specializationMap["GPU_SHADER5_REQUIRE"] = "#extension GL_EXT_gpu_shader5 : require";
93
94 return tcu::StringTemplate(code).specialize(specializationMap);
95 }
96
97 // Round-to-zero int division, because pre-c++11 it's somewhat implementation-defined for negative values.
divRoundToZero(int a,int b)98 static inline int divRoundToZero(int a, int b)
99 {
100 return de::abs(a) / de::abs(b) * deSign32(a) * deSign32(b);
101 }
102
fillWithRandomColorTiles(const PixelBufferAccess & dst,const Vec4 & minVal,const Vec4 & maxVal,uint32_t seed)103 static void fillWithRandomColorTiles(const PixelBufferAccess &dst, const Vec4 &minVal, const Vec4 &maxVal,
104 uint32_t seed)
105 {
106 const int numCols = dst.getWidth() >= 7 ? 7 : dst.getWidth();
107 const int numRows = dst.getHeight() >= 5 ? 5 : dst.getHeight();
108 de::Random rnd(seed);
109
110 for (int slice = 0; slice < dst.getDepth(); slice++)
111 for (int row = 0; row < numRows; row++)
112 for (int col = 0; col < numCols; col++)
113 {
114 const int yBegin = (row + 0) * dst.getHeight() / numRows;
115 const int yEnd = (row + 1) * dst.getHeight() / numRows;
116 const int xBegin = (col + 0) * dst.getWidth() / numCols;
117 const int xEnd = (col + 1) * dst.getWidth() / numCols;
118 Vec4 color;
119 for (int i = 0; i < 4; i++)
120 color[i] = rnd.getFloat(minVal[i], maxVal[i]);
121 tcu::clear(tcu::getSubregion(dst, xBegin, yBegin, slice, xEnd - xBegin, yEnd - yBegin, 1), color);
122 }
123 }
124
isDepthFormat(const tcu::TextureFormat & fmt)125 static inline bool isDepthFormat(const tcu::TextureFormat &fmt)
126 {
127 return fmt.order == tcu::TextureFormat::D || fmt.order == tcu::TextureFormat::DS;
128 }
129
isUnormFormatType(tcu::TextureFormat::ChannelType type)130 static inline bool isUnormFormatType(tcu::TextureFormat::ChannelType type)
131 {
132 return type == tcu::TextureFormat::UNORM_INT8 || type == tcu::TextureFormat::UNORM_INT16 ||
133 type == tcu::TextureFormat::UNORM_INT32;
134 }
135
isSIntFormatType(tcu::TextureFormat::ChannelType type)136 static inline bool isSIntFormatType(tcu::TextureFormat::ChannelType type)
137 {
138 return type == tcu::TextureFormat::SIGNED_INT8 || type == tcu::TextureFormat::SIGNED_INT16 ||
139 type == tcu::TextureFormat::SIGNED_INT32;
140 }
141
isUIntFormatType(tcu::TextureFormat::ChannelType type)142 static inline bool isUIntFormatType(tcu::TextureFormat::ChannelType type)
143 {
144 return type == tcu::TextureFormat::UNSIGNED_INT8 || type == tcu::TextureFormat::UNSIGNED_INT16 ||
145 type == tcu::TextureFormat::UNSIGNED_INT32;
146 }
147
getPixels(const glu::RenderContext & renderCtx,const IVec2 & size,const tcu::TextureFormat & colorBufferFormat)148 static tcu::TextureLevel getPixels(const glu::RenderContext &renderCtx, const IVec2 &size,
149 const tcu::TextureFormat &colorBufferFormat)
150 {
151 tcu::TextureLevel result(colorBufferFormat, size.x(), size.y());
152
153 // only a few pixel formats are guaranteed to be valid targets for readPixels, convert the rest
154 if (colorBufferFormat.order == tcu::TextureFormat::RGBA &&
155 (colorBufferFormat.type == tcu::TextureFormat::UNORM_INT8 ||
156 colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT32 ||
157 colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT32))
158 {
159 // valid as is
160 glu::readPixels(renderCtx, 0, 0, result.getAccess());
161 }
162 else if (colorBufferFormat.order == tcu::TextureFormat::RGBA &&
163 (isSIntFormatType(colorBufferFormat.type) || isUIntFormatType(colorBufferFormat.type)))
164 {
165 // signed and unsigned integers must be read using 32-bit values
166 const bool isSigned = isSIntFormatType(colorBufferFormat.type);
167 tcu::TextureLevel readResult(
168 tcu::TextureFormat(tcu::TextureFormat::RGBA,
169 (isSigned) ? (tcu::TextureFormat::SIGNED_INT32) : (tcu::TextureFormat::UNSIGNED_INT32)),
170 size.x(), size.y());
171
172 glu::readPixels(renderCtx, 0, 0, readResult.getAccess());
173 tcu::copy(result.getAccess(), readResult.getAccess());
174 }
175 else
176 {
177 // unreadable format
178 DE_ASSERT(false);
179 }
180
181 return result;
182 }
183
184 enum TextureSwizzleComponent
185 {
186 TEXTURESWIZZLECOMPONENT_R = 0,
187 TEXTURESWIZZLECOMPONENT_G,
188 TEXTURESWIZZLECOMPONENT_B,
189 TEXTURESWIZZLECOMPONENT_A,
190 TEXTURESWIZZLECOMPONENT_ZERO,
191 TEXTURESWIZZLECOMPONENT_ONE,
192
193 TEXTURESWIZZLECOMPONENT_LAST
194 };
195
operator <<(std::ostream & stream,TextureSwizzleComponent comp)196 static std::ostream &operator<<(std::ostream &stream, TextureSwizzleComponent comp)
197 {
198 switch (comp)
199 {
200 case TEXTURESWIZZLECOMPONENT_R:
201 return stream << "RED";
202 case TEXTURESWIZZLECOMPONENT_G:
203 return stream << "GREEN";
204 case TEXTURESWIZZLECOMPONENT_B:
205 return stream << "BLUE";
206 case TEXTURESWIZZLECOMPONENT_A:
207 return stream << "ALPHA";
208 case TEXTURESWIZZLECOMPONENT_ZERO:
209 return stream << "ZERO";
210 case TEXTURESWIZZLECOMPONENT_ONE:
211 return stream << "ONE";
212 default:
213 DE_ASSERT(false);
214 return stream;
215 }
216 }
217
218 struct MaybeTextureSwizzle
219 {
220 public:
221 static MaybeTextureSwizzle createNoneTextureSwizzle(void);
222 static MaybeTextureSwizzle createSomeTextureSwizzle(void);
223
224 bool isSome(void) const;
225 bool isNone(void) const;
226 bool isIdentitySwizzle(void) const;
227
228 tcu::Vector<TextureSwizzleComponent, 4> &getSwizzle(void);
229 const tcu::Vector<TextureSwizzleComponent, 4> &getSwizzle(void) const;
230
231 private:
232 MaybeTextureSwizzle(void);
233
234 tcu::Vector<TextureSwizzleComponent, 4> m_swizzle;
235 bool m_isSome;
236 };
237
operator <<(std::ostream & stream,const MaybeTextureSwizzle & comp)238 static std::ostream &operator<<(std::ostream &stream, const MaybeTextureSwizzle &comp)
239 {
240 if (comp.isNone())
241 stream << "[default swizzle state]";
242 else
243 stream << "(" << comp.getSwizzle()[0] << ", " << comp.getSwizzle()[1] << ", " << comp.getSwizzle()[2] << ", "
244 << comp.getSwizzle()[3] << ")";
245
246 return stream;
247 }
248
createNoneTextureSwizzle(void)249 MaybeTextureSwizzle MaybeTextureSwizzle::createNoneTextureSwizzle(void)
250 {
251 MaybeTextureSwizzle swizzle;
252
253 swizzle.m_swizzle[0] = TEXTURESWIZZLECOMPONENT_LAST;
254 swizzle.m_swizzle[1] = TEXTURESWIZZLECOMPONENT_LAST;
255 swizzle.m_swizzle[2] = TEXTURESWIZZLECOMPONENT_LAST;
256 swizzle.m_swizzle[3] = TEXTURESWIZZLECOMPONENT_LAST;
257 swizzle.m_isSome = false;
258
259 return swizzle;
260 }
261
createSomeTextureSwizzle(void)262 MaybeTextureSwizzle MaybeTextureSwizzle::createSomeTextureSwizzle(void)
263 {
264 MaybeTextureSwizzle swizzle;
265
266 swizzle.m_swizzle[0] = TEXTURESWIZZLECOMPONENT_R;
267 swizzle.m_swizzle[1] = TEXTURESWIZZLECOMPONENT_G;
268 swizzle.m_swizzle[2] = TEXTURESWIZZLECOMPONENT_B;
269 swizzle.m_swizzle[3] = TEXTURESWIZZLECOMPONENT_A;
270 swizzle.m_isSome = true;
271
272 return swizzle;
273 }
274
isSome(void) const275 bool MaybeTextureSwizzle::isSome(void) const
276 {
277 return m_isSome;
278 }
279
isNone(void) const280 bool MaybeTextureSwizzle::isNone(void) const
281 {
282 return !m_isSome;
283 }
284
isIdentitySwizzle(void) const285 bool MaybeTextureSwizzle::isIdentitySwizzle(void) const
286 {
287 return m_isSome && m_swizzle[0] == TEXTURESWIZZLECOMPONENT_R && m_swizzle[1] == TEXTURESWIZZLECOMPONENT_G &&
288 m_swizzle[2] == TEXTURESWIZZLECOMPONENT_B && m_swizzle[3] == TEXTURESWIZZLECOMPONENT_A;
289 }
290
getSwizzle(void)291 tcu::Vector<TextureSwizzleComponent, 4> &MaybeTextureSwizzle::getSwizzle(void)
292 {
293 return m_swizzle;
294 }
295
getSwizzle(void) const296 const tcu::Vector<TextureSwizzleComponent, 4> &MaybeTextureSwizzle::getSwizzle(void) const
297 {
298 return m_swizzle;
299 }
300
MaybeTextureSwizzle(void)301 MaybeTextureSwizzle::MaybeTextureSwizzle(void)
302 : m_swizzle(TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST, TEXTURESWIZZLECOMPONENT_LAST,
303 TEXTURESWIZZLECOMPONENT_LAST)
304 , m_isSome(false)
305 {
306 }
307
getGLTextureSwizzleComponent(TextureSwizzleComponent c)308 static uint32_t getGLTextureSwizzleComponent(TextureSwizzleComponent c)
309 {
310 switch (c)
311 {
312 case TEXTURESWIZZLECOMPONENT_R:
313 return GL_RED;
314 case TEXTURESWIZZLECOMPONENT_G:
315 return GL_GREEN;
316 case TEXTURESWIZZLECOMPONENT_B:
317 return GL_BLUE;
318 case TEXTURESWIZZLECOMPONENT_A:
319 return GL_ALPHA;
320 case TEXTURESWIZZLECOMPONENT_ZERO:
321 return GL_ZERO;
322 case TEXTURESWIZZLECOMPONENT_ONE:
323 return GL_ONE;
324 default:
325 DE_ASSERT(false);
326 return (uint32_t)-1;
327 }
328 }
329
330 template <typename T>
swizzleColorChannel(const tcu::Vector<T,4> & src,TextureSwizzleComponent swizzle)331 static inline T swizzleColorChannel(const tcu::Vector<T, 4> &src, TextureSwizzleComponent swizzle)
332 {
333 switch (swizzle)
334 {
335 case TEXTURESWIZZLECOMPONENT_R:
336 return src[0];
337 case TEXTURESWIZZLECOMPONENT_G:
338 return src[1];
339 case TEXTURESWIZZLECOMPONENT_B:
340 return src[2];
341 case TEXTURESWIZZLECOMPONENT_A:
342 return src[3];
343 case TEXTURESWIZZLECOMPONENT_ZERO:
344 return (T)0;
345 case TEXTURESWIZZLECOMPONENT_ONE:
346 return (T)1;
347 default:
348 DE_ASSERT(false);
349 return (T)-1;
350 }
351 }
352
353 template <typename T>
swizzleColor(const tcu::Vector<T,4> & src,const MaybeTextureSwizzle & swizzle)354 static inline tcu::Vector<T, 4> swizzleColor(const tcu::Vector<T, 4> &src, const MaybeTextureSwizzle &swizzle)
355 {
356 DE_ASSERT(swizzle.isSome());
357
358 tcu::Vector<T, 4> result;
359 for (int i = 0; i < 4; i++)
360 result[i] = swizzleColorChannel(src, swizzle.getSwizzle()[i]);
361 return result;
362 }
363
364 template <typename T>
swizzlePixels(const PixelBufferAccess & dst,const ConstPixelBufferAccess & src,const MaybeTextureSwizzle & swizzle)365 static void swizzlePixels(const PixelBufferAccess &dst, const ConstPixelBufferAccess &src,
366 const MaybeTextureSwizzle &swizzle)
367 {
368 DE_ASSERT(dst.getWidth() == src.getWidth() && dst.getHeight() == src.getHeight() &&
369 dst.getDepth() == src.getDepth());
370 for (int z = 0; z < src.getDepth(); z++)
371 for (int y = 0; y < src.getHeight(); y++)
372 for (int x = 0; x < src.getWidth(); x++)
373 dst.setPixel(swizzleColor(src.getPixelT<T>(x, y, z), swizzle), x, y, z);
374 }
375
swizzlePixels(const PixelBufferAccess & dst,const ConstPixelBufferAccess & src,const MaybeTextureSwizzle & swizzle)376 static void swizzlePixels(const PixelBufferAccess &dst, const ConstPixelBufferAccess &src,
377 const MaybeTextureSwizzle &swizzle)
378 {
379 if (isDepthFormat(dst.getFormat()))
380 DE_ASSERT(swizzle.isNone() || swizzle.isIdentitySwizzle());
381
382 if (swizzle.isNone() || swizzle.isIdentitySwizzle())
383 tcu::copy(dst, src);
384 else if (isUnormFormatType(dst.getFormat().type))
385 swizzlePixels<float>(dst, src, swizzle);
386 else if (isUIntFormatType(dst.getFormat().type))
387 swizzlePixels<uint32_t>(dst, src, swizzle);
388 else if (isSIntFormatType(dst.getFormat().type))
389 swizzlePixels<int32_t>(dst, src, swizzle);
390 else
391 DE_ASSERT(false);
392 }
393
swizzleTexture(tcu::Texture2D & dst,const tcu::Texture2D & src,const MaybeTextureSwizzle & swizzle)394 static void swizzleTexture(tcu::Texture2D &dst, const tcu::Texture2D &src, const MaybeTextureSwizzle &swizzle)
395 {
396 dst = tcu::Texture2D(src.getFormat(), src.getWidth(), src.getHeight());
397 for (int levelNdx = 0; levelNdx < src.getNumLevels(); levelNdx++)
398 {
399 if (src.isLevelEmpty(levelNdx))
400 continue;
401 dst.allocLevel(levelNdx);
402 swizzlePixels(dst.getLevel(levelNdx), src.getLevel(levelNdx), swizzle);
403 }
404 }
405
swizzleTexture(tcu::Texture2DArray & dst,const tcu::Texture2DArray & src,const MaybeTextureSwizzle & swizzle)406 static void swizzleTexture(tcu::Texture2DArray &dst, const tcu::Texture2DArray &src, const MaybeTextureSwizzle &swizzle)
407 {
408 dst = tcu::Texture2DArray(src.getFormat(), src.getWidth(), src.getHeight(), src.getNumLayers());
409 for (int levelNdx = 0; levelNdx < src.getNumLevels(); levelNdx++)
410 {
411 if (src.isLevelEmpty(levelNdx))
412 continue;
413 dst.allocLevel(levelNdx);
414 swizzlePixels(dst.getLevel(levelNdx), src.getLevel(levelNdx), swizzle);
415 }
416 }
417
swizzleTexture(tcu::TextureCube & dst,const tcu::TextureCube & src,const MaybeTextureSwizzle & swizzle)418 static void swizzleTexture(tcu::TextureCube &dst, const tcu::TextureCube &src, const MaybeTextureSwizzle &swizzle)
419 {
420 dst = tcu::TextureCube(src.getFormat(), src.getSize());
421 for (int faceI = 0; faceI < tcu::CUBEFACE_LAST; faceI++)
422 {
423 const tcu::CubeFace face = (tcu::CubeFace)faceI;
424 for (int levelNdx = 0; levelNdx < src.getNumLevels(); levelNdx++)
425 {
426 if (src.isLevelEmpty(face, levelNdx))
427 continue;
428 dst.allocLevel(face, levelNdx);
429 swizzlePixels(dst.getLevelFace(levelNdx, face), src.getLevelFace(levelNdx, face), swizzle);
430 }
431 }
432 }
433
getOneLevelSubView(const tcu::Texture2DView & view,int level)434 static tcu::Texture2DView getOneLevelSubView(const tcu::Texture2DView &view, int level)
435 {
436 return tcu::Texture2DView(1, view.getLevels() + level);
437 }
438
getOneLevelSubView(const tcu::Texture2DArrayView & view,int level)439 static tcu::Texture2DArrayView getOneLevelSubView(const tcu::Texture2DArrayView &view, int level)
440 {
441 return tcu::Texture2DArrayView(1, view.getLevels() + level);
442 }
443
getOneLevelSubView(const tcu::TextureCubeView & view,int level)444 static tcu::TextureCubeView getOneLevelSubView(const tcu::TextureCubeView &view, int level)
445 {
446 const tcu::ConstPixelBufferAccess *levels[tcu::CUBEFACE_LAST];
447
448 for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
449 levels[face] = view.getFaceLevels((tcu::CubeFace)face) + level;
450
451 return tcu::TextureCubeView(1, levels);
452 }
453
454 class PixelOffsets
455 {
456 public:
457 virtual void operator()(const IVec2 &pixCoord, IVec2 (&dst)[4]) const = 0;
~PixelOffsets(void)458 virtual ~PixelOffsets(void)
459 {
460 }
461 };
462
463 class MultiplePixelOffsets : public PixelOffsets
464 {
465 public:
MultiplePixelOffsets(const IVec2 & a,const IVec2 & b,const IVec2 & c,const IVec2 & d)466 MultiplePixelOffsets(const IVec2 &a, const IVec2 &b, const IVec2 &c, const IVec2 &d)
467 {
468 m_offsets[0] = a;
469 m_offsets[1] = b;
470 m_offsets[2] = c;
471 m_offsets[3] = d;
472 }
473
operator ()(const IVec2 &,IVec2 (& dst)[4]) const474 void operator()(const IVec2 & /* pixCoord */, IVec2 (&dst)[4]) const
475 {
476 for (int i = 0; i < DE_LENGTH_OF_ARRAY(dst); i++)
477 dst[i] = m_offsets[i];
478 }
479
480 private:
481 IVec2 m_offsets[4];
482 };
483
484 class SinglePixelOffsets : public MultiplePixelOffsets
485 {
486 public:
SinglePixelOffsets(const IVec2 & offset)487 SinglePixelOffsets(const IVec2 &offset)
488 : MultiplePixelOffsets(offset + IVec2(0, 1), offset + IVec2(1, 1), offset + IVec2(1, 0), offset + IVec2(0, 0))
489 {
490 }
491 };
492
493 class DynamicSinglePixelOffsets : public PixelOffsets
494 {
495 public:
DynamicSinglePixelOffsets(const IVec2 & offsetRange)496 DynamicSinglePixelOffsets(const IVec2 &offsetRange) : m_offsetRange(offsetRange)
497 {
498 }
499
operator ()(const IVec2 & pixCoord,IVec2 (& dst)[4]) const500 void operator()(const IVec2 &pixCoord, IVec2 (&dst)[4]) const
501 {
502 const int offsetRangeSize = m_offsetRange.y() - m_offsetRange.x() + 1;
503 SinglePixelOffsets(tcu::mod(pixCoord.swizzle(1, 0), IVec2(offsetRangeSize)) + m_offsetRange.x())(IVec2(), dst);
504 }
505
506 private:
507 IVec2 m_offsetRange;
508 };
509
510 template <typename T>
triQuadInterpolate(const T (& values)[4],float xFactor,float yFactor)511 static inline T triQuadInterpolate(const T (&values)[4], float xFactor, float yFactor)
512 {
513 if (xFactor + yFactor < 1.0f)
514 return values[0] + (values[2] - values[0]) * xFactor + (values[1] - values[0]) * yFactor;
515 else
516 return values[3] + (values[1] - values[3]) * (1.0f - xFactor) + (values[2] - values[3]) * (1.0f - yFactor);
517 }
518
519 template <int N>
computeTexCoordVecs(const vector<float> & texCoords,tcu::Vector<float,N> (& dst)[4])520 static inline void computeTexCoordVecs(const vector<float> &texCoords, tcu::Vector<float, N> (&dst)[4])
521 {
522 DE_ASSERT((int)texCoords.size() == 4 * N);
523 for (int i = 0; i < 4; i++)
524 for (int j = 0; j < N; j++)
525 dst[i][j] = texCoords[i * N + j];
526 }
527
528 #if defined(DE_DEBUG)
529 // Whether offsets correspond to the sample offsets used with plain textureGather().
isZeroOffsetOffsets(const IVec2 (& offsets)[4])530 static inline bool isZeroOffsetOffsets(const IVec2 (&offsets)[4])
531 {
532 IVec2 ref[4];
533 SinglePixelOffsets(IVec2(0))(IVec2(), ref);
534 return std::equal(DE_ARRAY_BEGIN(offsets), DE_ARRAY_END(offsets), DE_ARRAY_BEGIN(ref));
535 }
536 #endif
537
538 template <typename ColorScalarType>
gatherOffsets(const tcu::Texture2DView & texture,const tcu::Sampler & sampler,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4])539 static tcu::Vector<ColorScalarType, 4> gatherOffsets(const tcu::Texture2DView &texture, const tcu::Sampler &sampler,
540 const Vec2 &coord, int componentNdx, const IVec2 (&offsets)[4])
541 {
542 return texture.gatherOffsets(sampler, coord.x(), coord.y(), componentNdx, offsets).cast<ColorScalarType>();
543 }
544
545 template <typename ColorScalarType>
gatherOffsets(const tcu::Texture2DArrayView & texture,const tcu::Sampler & sampler,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4])546 static tcu::Vector<ColorScalarType, 4> gatherOffsets(const tcu::Texture2DArrayView &texture,
547 const tcu::Sampler &sampler, const Vec3 &coord, int componentNdx,
548 const IVec2 (&offsets)[4])
549 {
550 return texture.gatherOffsets(sampler, coord.x(), coord.y(), coord.z(), componentNdx, offsets)
551 .cast<ColorScalarType>();
552 }
553
554 template <typename ColorScalarType>
gatherOffsets(const tcu::TextureCubeView & texture,const tcu::Sampler & sampler,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4])555 static tcu::Vector<ColorScalarType, 4> gatherOffsets(const tcu::TextureCubeView &texture, const tcu::Sampler &sampler,
556 const Vec3 &coord, int componentNdx, const IVec2 (&offsets)[4])
557 {
558 DE_ASSERT(isZeroOffsetOffsets(offsets));
559 DE_UNREF(offsets);
560 return texture.gather(sampler, coord.x(), coord.y(), coord.z(), componentNdx).cast<ColorScalarType>();
561 }
562
gatherOffsetsCompare(const tcu::Texture2DView & texture,const tcu::Sampler & sampler,float refZ,const Vec2 & coord,const IVec2 (& offsets)[4])563 static Vec4 gatherOffsetsCompare(const tcu::Texture2DView &texture, const tcu::Sampler &sampler, float refZ,
564 const Vec2 &coord, const IVec2 (&offsets)[4])
565 {
566 return texture.gatherOffsetsCompare(sampler, refZ, coord.x(), coord.y(), offsets);
567 }
568
gatherOffsetsCompare(const tcu::Texture2DArrayView & texture,const tcu::Sampler & sampler,float refZ,const Vec3 & coord,const IVec2 (& offsets)[4])569 static Vec4 gatherOffsetsCompare(const tcu::Texture2DArrayView &texture, const tcu::Sampler &sampler, float refZ,
570 const Vec3 &coord, const IVec2 (&offsets)[4])
571 {
572 return texture.gatherOffsetsCompare(sampler, refZ, coord.x(), coord.y(), coord.z(), offsets);
573 }
574
gatherOffsetsCompare(const tcu::TextureCubeView & texture,const tcu::Sampler & sampler,float refZ,const Vec3 & coord,const IVec2 (& offsets)[4])575 static Vec4 gatherOffsetsCompare(const tcu::TextureCubeView &texture, const tcu::Sampler &sampler, float refZ,
576 const Vec3 &coord, const IVec2 (&offsets)[4])
577 {
578 DE_ASSERT(isZeroOffsetOffsets(offsets));
579 DE_UNREF(offsets);
580 return texture.gatherCompare(sampler, refZ, coord.x(), coord.y(), coord.z());
581 }
582
583 template <typename PrecType, typename ColorScalarT>
isGatherOffsetsResultValid(const tcu::TextureCubeView & texture,const tcu::Sampler & sampler,const PrecType & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const tcu::Vector<ColorScalarT,4> & result)584 static bool isGatherOffsetsResultValid(const tcu::TextureCubeView &texture, const tcu::Sampler &sampler,
585 const PrecType &prec, const Vec3 &coord, int componentNdx,
586 const IVec2 (&offsets)[4], const tcu::Vector<ColorScalarT, 4> &result)
587 {
588 DE_ASSERT(isZeroOffsetOffsets(offsets));
589 DE_UNREF(offsets);
590 return tcu::isGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
591 }
592
isGatherOffsetsCompareResultValid(const tcu::TextureCubeView & texture,const tcu::Sampler & sampler,const tcu::TexComparePrecision & prec,const Vec3 & coord,const IVec2 (& offsets)[4],float cmpReference,const Vec4 & result)593 static bool isGatherOffsetsCompareResultValid(const tcu::TextureCubeView &texture, const tcu::Sampler &sampler,
594 const tcu::TexComparePrecision &prec, const Vec3 &coord,
595 const IVec2 (&offsets)[4], float cmpReference, const Vec4 &result)
596 {
597 DE_ASSERT(isZeroOffsetOffsets(offsets));
598 DE_UNREF(offsets);
599 return tcu::isGatherCompareResultValid(texture, sampler, prec, coord, cmpReference, result);
600 }
601
602 template <typename ColorScalarType, typename PrecType, typename TexViewT, typename TexCoordT>
verifyGatherOffsets(TestLog & log,const ConstPixelBufferAccess & result,const TexViewT & texture,const TexCoordT (& texCoords)[4],const tcu::Sampler & sampler,const PrecType & lookupPrec,int componentNdx,const PixelOffsets & getPixelOffsets)603 static bool verifyGatherOffsets(TestLog &log, const ConstPixelBufferAccess &result, const TexViewT &texture,
604 const TexCoordT (&texCoords)[4], const tcu::Sampler &sampler,
605 const PrecType &lookupPrec, int componentNdx, const PixelOffsets &getPixelOffsets)
606 {
607 typedef tcu::Vector<ColorScalarType, 4> ColorVec;
608
609 const int width = result.getWidth();
610 const int height = result.getWidth();
611 tcu::TextureLevel ideal(result.getFormat(), width, height);
612 const PixelBufferAccess idealAccess = ideal.getAccess();
613 tcu::Surface errorMask(width, height);
614 bool success = true;
615
616 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
617
618 for (int py = 0; py < height; py++)
619 for (int px = 0; px < width; px++)
620 {
621 IVec2 offsets[4];
622 getPixelOffsets(IVec2(px, py), offsets);
623
624 const Vec2 viewportCoord = (Vec2((float)px, (float)py) + 0.5f) / Vec2((float)width, (float)height);
625 const TexCoordT texCoord = triQuadInterpolate(texCoords, viewportCoord.x(), viewportCoord.y());
626 const ColorVec resultPix = result.getPixelT<ColorScalarType>(px, py);
627 const ColorVec idealPix = gatherOffsets<ColorScalarType>(texture, sampler, texCoord, componentNdx, offsets);
628
629 idealAccess.setPixel(idealPix, px, py);
630
631 if (tcu::boolAny(
632 tcu::logicalAnd(lookupPrec.colorMask,
633 tcu::greaterThan(tcu::absDiff(resultPix, idealPix),
634 lookupPrec.colorThreshold.template cast<ColorScalarType>()))))
635 {
636 if (!isGatherOffsetsResultValid(texture, sampler, lookupPrec, texCoord, componentNdx, offsets,
637 resultPix))
638 {
639 errorMask.setPixel(px, py, tcu::RGBA::red());
640 success = false;
641 }
642 }
643 }
644
645 log << TestLog::ImageSet("VerifyResult", "Verification result")
646 << TestLog::Image("Rendered", "Rendered image", result);
647
648 if (!success)
649 {
650 log << TestLog::Image("Reference", "Ideal reference image", ideal)
651 << TestLog::Image("ErrorMask", "Error mask", errorMask);
652 }
653
654 log << TestLog::EndImageSet;
655
656 return success;
657 }
658
659 class PixelCompareRefZ
660 {
661 public:
662 virtual float operator()(const IVec2 &pixCoord) const = 0;
663 };
664
665 class PixelCompareRefZDefault : public PixelCompareRefZ
666 {
667 public:
PixelCompareRefZDefault(const IVec2 & renderSize)668 PixelCompareRefZDefault(const IVec2 &renderSize) : m_renderSize(renderSize)
669 {
670 }
671
operator ()(const IVec2 & pixCoord) const672 float operator()(const IVec2 &pixCoord) const
673 {
674 return ((float)pixCoord.x() + 0.5f) / (float)m_renderSize.x();
675 }
676
677 private:
678 IVec2 m_renderSize;
679 };
680
681 template <typename TexViewT, typename TexCoordT>
verifyGatherOffsetsCompare(TestLog & log,const ConstPixelBufferAccess & result,const TexViewT & texture,const TexCoordT (& texCoords)[4],const tcu::Sampler & sampler,const tcu::TexComparePrecision & compPrec,const PixelCompareRefZ & getPixelRefZ,const PixelOffsets & getPixelOffsets)682 static bool verifyGatherOffsetsCompare(TestLog &log, const ConstPixelBufferAccess &result, const TexViewT &texture,
683 const TexCoordT (&texCoords)[4], const tcu::Sampler &sampler,
684 const tcu::TexComparePrecision &compPrec, const PixelCompareRefZ &getPixelRefZ,
685 const PixelOffsets &getPixelOffsets)
686 {
687 const int width = result.getWidth();
688 const int height = result.getWidth();
689 tcu::Surface ideal(width, height);
690 const PixelBufferAccess idealAccess = ideal.getAccess();
691 tcu::Surface errorMask(width, height);
692 bool success = true;
693
694 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
695
696 for (int py = 0; py < height; py++)
697 for (int px = 0; px < width; px++)
698 {
699 IVec2 offsets[4];
700 getPixelOffsets(IVec2(px, py), offsets);
701
702 const Vec2 viewportCoord = (Vec2((float)px, (float)py) + 0.5f) / Vec2((float)width, (float)height);
703 const TexCoordT texCoord = triQuadInterpolate(texCoords, viewportCoord.x(), viewportCoord.y());
704 const float refZ = getPixelRefZ(IVec2(px, py));
705 const Vec4 resultPix = result.getPixel(px, py);
706 const Vec4 idealPix = gatherOffsetsCompare(texture, sampler, refZ, texCoord, offsets);
707
708 idealAccess.setPixel(idealPix, px, py);
709
710 if (!tcu::boolAll(tcu::equal(resultPix, idealPix)))
711 {
712 if (!isGatherOffsetsCompareResultValid(texture, sampler, compPrec, texCoord, offsets, refZ, resultPix))
713 {
714 errorMask.setPixel(px, py, tcu::RGBA::red());
715 success = false;
716 }
717 }
718 }
719
720 log << TestLog::ImageSet("VerifyResult", "Verification result")
721 << TestLog::Image("Rendered", "Rendered image", result);
722
723 if (!success)
724 {
725 log << TestLog::Image("Reference", "Ideal reference image", ideal)
726 << TestLog::Image("ErrorMask", "Error mask", errorMask);
727 }
728
729 log << TestLog::EndImageSet;
730
731 return success;
732 }
733
verifySingleColored(TestLog & log,const ConstPixelBufferAccess & result,const Vec4 & refColor)734 static bool verifySingleColored(TestLog &log, const ConstPixelBufferAccess &result, const Vec4 &refColor)
735 {
736 const int width = result.getWidth();
737 const int height = result.getWidth();
738 tcu::Surface ideal(width, height);
739 const PixelBufferAccess idealAccess = ideal.getAccess();
740 tcu::Surface errorMask(width, height);
741 bool success = true;
742
743 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toVec());
744 tcu::clear(idealAccess, refColor);
745
746 for (int py = 0; py < height; py++)
747 for (int px = 0; px < width; px++)
748 {
749 if (result.getPixel(px, py) != refColor)
750 {
751 errorMask.setPixel(px, py, tcu::RGBA::red());
752 success = false;
753 }
754 }
755
756 log << TestLog::ImageSet("VerifyResult", "Verification result")
757 << TestLog::Image("Rendered", "Rendered image", result);
758
759 if (!success)
760 {
761 log << TestLog::Image("Reference", "Ideal reference image", ideal)
762 << TestLog::Image("ErrorMask", "Error mask", errorMask);
763 }
764
765 log << TestLog::EndImageSet;
766
767 return success;
768 }
769
770 enum GatherType
771 {
772 GATHERTYPE_BASIC = 0,
773 GATHERTYPE_OFFSET,
774 GATHERTYPE_OFFSET_DYNAMIC,
775 GATHERTYPE_OFFSETS,
776
777 GATHERTYPE_LAST
778 };
779
780 enum GatherCaseFlags
781 {
782 GATHERCASE_MIPMAP_INCOMPLETE = (1 << 0), //!< Excercise special case of sampling mipmap-incomplete texture
783 GATHERCASE_DONT_SAMPLE_CUBE_CORNERS = (1 << 1) //!< For cube map cases: do not sample cube corners
784 };
785
gatherTypeName(GatherType type)786 static inline const char *gatherTypeName(GatherType type)
787 {
788 switch (type)
789 {
790 case GATHERTYPE_BASIC:
791 return "basic";
792 case GATHERTYPE_OFFSET:
793 return "offset";
794 case GATHERTYPE_OFFSET_DYNAMIC:
795 return "offset_dynamic";
796 case GATHERTYPE_OFFSETS:
797 return "offsets";
798 default:
799 DE_ASSERT(false);
800 return DE_NULL;
801 }
802 }
803
gatherTypeDescription(GatherType type)804 static inline const char *gatherTypeDescription(GatherType type)
805 {
806 switch (type)
807 {
808 case GATHERTYPE_BASIC:
809 return "textureGather";
810 case GATHERTYPE_OFFSET:
811 return "textureGatherOffset";
812 case GATHERTYPE_OFFSET_DYNAMIC:
813 return "textureGatherOffset with dynamic offsets";
814 case GATHERTYPE_OFFSETS:
815 return "textureGatherOffsets";
816 default:
817 DE_ASSERT(false);
818 return DE_NULL;
819 }
820 }
821
requireGpuShader5(GatherType gatherType)822 static inline bool requireGpuShader5(GatherType gatherType)
823 {
824 return gatherType == GATHERTYPE_OFFSET_DYNAMIC || gatherType == GATHERTYPE_OFFSETS;
825 }
826
827 struct GatherArgs
828 {
829 int componentNdx; // If negative, implicit component index 0 is used (i.e. the parameter is not given).
830 IVec2 offsets
831 [4]; // \note Unless GATHERTYPE_OFFSETS is used, only offsets[0] is relevant; also, for GATHERTYPE_OFFSET_DYNAMIC, none are relevant.
832
GatherArgsdeqp::gles31::Functional::__anon1bb6c87a0111::GatherArgs833 GatherArgs(void) : componentNdx(-1)
834 {
835 std::fill(DE_ARRAY_BEGIN(offsets), DE_ARRAY_END(offsets), IVec2());
836 }
837
GatherArgsdeqp::gles31::Functional::__anon1bb6c87a0111::GatherArgs838 GatherArgs(int comp, const IVec2 &off0 = IVec2(), const IVec2 &off1 = IVec2(), const IVec2 &off2 = IVec2(),
839 const IVec2 &off3 = IVec2())
840 : componentNdx(comp)
841 {
842 offsets[0] = off0;
843 offsets[1] = off1;
844 offsets[2] = off2;
845 offsets[3] = off3;
846 }
847 };
848
makePixelOffsetsFunctor(GatherType gatherType,const GatherArgs & gatherArgs,const IVec2 & offsetRange)849 static MovePtr<PixelOffsets> makePixelOffsetsFunctor(GatherType gatherType, const GatherArgs &gatherArgs,
850 const IVec2 &offsetRange)
851 {
852 if (gatherType == GATHERTYPE_BASIC || gatherType == GATHERTYPE_OFFSET)
853 {
854 const IVec2 offset = gatherType == GATHERTYPE_BASIC ? IVec2(0) : gatherArgs.offsets[0];
855 return MovePtr<PixelOffsets>(new SinglePixelOffsets(offset));
856 }
857 else if (gatherType == GATHERTYPE_OFFSET_DYNAMIC)
858 {
859 return MovePtr<PixelOffsets>(new DynamicSinglePixelOffsets(offsetRange));
860 }
861 else if (gatherType == GATHERTYPE_OFFSETS)
862 return MovePtr<PixelOffsets>(new MultiplePixelOffsets(gatherArgs.offsets[0], gatherArgs.offsets[1],
863 gatherArgs.offsets[2], gatherArgs.offsets[3]));
864 else
865 {
866 DE_ASSERT(false);
867 return MovePtr<PixelOffsets>(DE_NULL);
868 }
869 }
870
getSamplerType(TextureType textureType,const tcu::TextureFormat & format)871 static inline glu::DataType getSamplerType(TextureType textureType, const tcu::TextureFormat &format)
872 {
873 if (isDepthFormat(format))
874 {
875 switch (textureType)
876 {
877 case TEXTURETYPE_2D:
878 return glu::TYPE_SAMPLER_2D_SHADOW;
879 case TEXTURETYPE_2D_ARRAY:
880 return glu::TYPE_SAMPLER_2D_ARRAY_SHADOW;
881 case TEXTURETYPE_CUBE:
882 return glu::TYPE_SAMPLER_CUBE_SHADOW;
883 default:
884 DE_ASSERT(false);
885 return glu::TYPE_LAST;
886 }
887 }
888 else
889 {
890 switch (textureType)
891 {
892 case TEXTURETYPE_2D:
893 return glu::getSampler2DType(format);
894 case TEXTURETYPE_2D_ARRAY:
895 return glu::getSampler2DArrayType(format);
896 case TEXTURETYPE_CUBE:
897 return glu::getSamplerCubeType(format);
898 default:
899 DE_ASSERT(false);
900 return glu::TYPE_LAST;
901 }
902 }
903 }
904
getSamplerGatherResultType(glu::DataType samplerType)905 static inline glu::DataType getSamplerGatherResultType(glu::DataType samplerType)
906 {
907 switch (samplerType)
908 {
909 case glu::TYPE_SAMPLER_2D_SHADOW:
910 case glu::TYPE_SAMPLER_2D_ARRAY_SHADOW:
911 case glu::TYPE_SAMPLER_CUBE_SHADOW:
912 case glu::TYPE_SAMPLER_2D:
913 case glu::TYPE_SAMPLER_2D_ARRAY:
914 case glu::TYPE_SAMPLER_CUBE:
915 return glu::TYPE_FLOAT_VEC4;
916
917 case glu::TYPE_INT_SAMPLER_2D:
918 case glu::TYPE_INT_SAMPLER_2D_ARRAY:
919 case glu::TYPE_INT_SAMPLER_CUBE:
920 return glu::TYPE_INT_VEC4;
921
922 case glu::TYPE_UINT_SAMPLER_2D:
923 case glu::TYPE_UINT_SAMPLER_2D_ARRAY:
924 case glu::TYPE_UINT_SAMPLER_CUBE:
925 return glu::TYPE_UINT_VEC4;
926
927 default:
928 DE_ASSERT(false);
929 return glu::TYPE_LAST;
930 }
931 }
932
getNumTextureSamplingDimensions(TextureType type)933 static inline int getNumTextureSamplingDimensions(TextureType type)
934 {
935 switch (type)
936 {
937 case TEXTURETYPE_2D:
938 return 2;
939 case TEXTURETYPE_2D_ARRAY:
940 return 3;
941 case TEXTURETYPE_CUBE:
942 return 3;
943 default:
944 DE_ASSERT(false);
945 return -1;
946 }
947 }
948
getGLTextureType(TextureType type)949 static uint32_t getGLTextureType(TextureType type)
950 {
951 switch (type)
952 {
953 case TEXTURETYPE_2D:
954 return GL_TEXTURE_2D;
955 case TEXTURETYPE_2D_ARRAY:
956 return GL_TEXTURE_2D_ARRAY;
957 case TEXTURETYPE_CUBE:
958 return GL_TEXTURE_CUBE_MAP;
959 default:
960 DE_ASSERT(false);
961 return (uint32_t)-1;
962 }
963 }
964
965 enum OffsetSize
966 {
967 OFFSETSIZE_NONE = 0,
968 OFFSETSIZE_MINIMUM_REQUIRED,
969 OFFSETSIZE_IMPLEMENTATION_MAXIMUM,
970
971 OFFSETSIZE_LAST
972 };
973
isMipmapFilter(tcu::Sampler::FilterMode filter)974 static inline bool isMipmapFilter(tcu::Sampler::FilterMode filter)
975 {
976 switch (filter)
977 {
978 case tcu::Sampler::NEAREST:
979 case tcu::Sampler::LINEAR:
980 return false;
981
982 case tcu::Sampler::NEAREST_MIPMAP_NEAREST:
983 case tcu::Sampler::NEAREST_MIPMAP_LINEAR:
984 case tcu::Sampler::LINEAR_MIPMAP_NEAREST:
985 case tcu::Sampler::LINEAR_MIPMAP_LINEAR:
986 return true;
987
988 default:
989 DE_ASSERT(false);
990 return false;
991 }
992 }
993
994 class TextureGatherCase : public TestCase
995 {
996 public:
997 TextureGatherCase(Context &context, const char *name, const char *description, TextureType textureType,
998 GatherType gatherType, OffsetSize offsetSize, tcu::TextureFormat textureFormat,
999 tcu::Sampler::CompareMode
1000 shadowCompareMode, //!< Should be COMPAREMODE_NONE iff textureFormat is a depth format.
1001 tcu::Sampler::WrapMode wrapS, tcu::Sampler::WrapMode wrapT, const MaybeTextureSwizzle &texSwizzle,
1002 // \note Filter modes have no effect on gather (except when it comes to
1003 // texture completeness); these are supposed to test just that.
1004 tcu::Sampler::FilterMode minFilter, tcu::Sampler::FilterMode magFilter, int baseLevel,
1005 uint32_t flags);
1006
1007 void init(void);
1008 void deinit(void);
1009 IterateResult iterate(void);
1010
1011 protected:
1012 IVec2 getOffsetRange(void) const;
1013
1014 template <typename TexViewT, typename TexCoordT>
1015 bool verify(const ConstPixelBufferAccess &rendered, const TexViewT &texture, const TexCoordT (&bottomLeft)[4],
1016 const GatherArgs &gatherArgs) const;
1017
1018 virtual void generateIterations(void) = 0;
1019 virtual void createAndUploadTexture(void) = 0;
1020 virtual int getNumIterations(void) const = 0;
1021 virtual GatherArgs getGatherArgs(int iterationNdx) const = 0;
1022 virtual vector<float> computeQuadTexCoord(int iterationNdx) const = 0;
1023 virtual bool verify(int iterationNdx, const ConstPixelBufferAccess &rendered) const = 0;
1024
1025 const GatherType m_gatherType;
1026 const OffsetSize m_offsetSize;
1027 const tcu::TextureFormat m_textureFormat;
1028 const tcu::Sampler::CompareMode m_shadowCompareMode;
1029 const tcu::Sampler::WrapMode m_wrapS;
1030 const tcu::Sampler::WrapMode m_wrapT;
1031 const MaybeTextureSwizzle m_textureSwizzle;
1032 const tcu::Sampler::FilterMode m_minFilter;
1033 const tcu::Sampler::FilterMode m_magFilter;
1034 const int m_baseLevel;
1035 const uint32_t m_flags;
1036
1037 private:
1038 enum
1039 {
1040 SPEC_MAX_MIN_OFFSET = -8,
1041 SPEC_MIN_MAX_OFFSET = 7
1042 };
1043
1044 static const IVec2 RENDER_SIZE;
1045
1046 glu::VertexSource genVertexShaderSource(bool requireGpuShader5, int numTexCoordComponents,
1047 bool useNormalizedCoordInput);
1048 glu::FragmentSource genFragmentShaderSource(bool requireGpuShader5, int numTexCoordComponents,
1049 glu::DataType samplerType, const string &funcCall,
1050 bool useNormalizedCoordInput, bool usePixCoord);
1051 string genGatherFuncCall(GatherType, const tcu::TextureFormat &, const GatherArgs &, const string &refZExpr,
1052 const IVec2 &offsetRange, int indentationDepth);
1053 glu::ProgramSources genProgramSources(GatherType, TextureType, const tcu::TextureFormat &, const GatherArgs &,
1054 const string &refZExpr, const IVec2 &offsetRange);
1055
1056 const TextureType m_textureType;
1057
1058 const tcu::TextureFormat m_colorBufferFormat;
1059 MovePtr<glu::Renderbuffer> m_colorBuffer;
1060 MovePtr<glu::Framebuffer> m_fbo;
1061
1062 int m_currentIteration;
1063 MovePtr<ShaderProgram> m_program;
1064 };
1065
1066 const IVec2 TextureGatherCase::RENDER_SIZE = IVec2(64, 64);
1067
TextureGatherCase(Context & context,const char * name,const char * description,TextureType textureType,GatherType gatherType,OffsetSize offsetSize,tcu::TextureFormat textureFormat,tcu::Sampler::CompareMode shadowCompareMode,tcu::Sampler::WrapMode wrapS,tcu::Sampler::WrapMode wrapT,const MaybeTextureSwizzle & textureSwizzle,tcu::Sampler::FilterMode minFilter,tcu::Sampler::FilterMode magFilter,int baseLevel,uint32_t flags)1068 TextureGatherCase::TextureGatherCase(
1069 Context &context, const char *name, const char *description, TextureType textureType, GatherType gatherType,
1070 OffsetSize offsetSize, tcu::TextureFormat textureFormat,
1071 tcu::Sampler::CompareMode shadowCompareMode, //!< Should be COMPAREMODE_NONE iff textureType == TEXTURETYPE_NORMAL.
1072 tcu::Sampler::WrapMode wrapS, tcu::Sampler::WrapMode wrapT, const MaybeTextureSwizzle &textureSwizzle,
1073 tcu::Sampler::FilterMode minFilter, tcu::Sampler::FilterMode magFilter, int baseLevel, uint32_t flags)
1074 : TestCase(context, name, description)
1075 , m_gatherType(gatherType)
1076 , m_offsetSize(offsetSize)
1077 , m_textureFormat(textureFormat)
1078 , m_shadowCompareMode(shadowCompareMode)
1079 , m_wrapS(wrapS)
1080 , m_wrapT(wrapT)
1081 , m_textureSwizzle(textureSwizzle)
1082 , m_minFilter(minFilter)
1083 , m_magFilter(magFilter)
1084 , m_baseLevel(baseLevel)
1085 , m_flags(flags)
1086 , m_textureType(textureType)
1087 , m_colorBufferFormat(tcu::TextureFormat(
1088 tcu::TextureFormat::RGBA, isDepthFormat(textureFormat) ? tcu::TextureFormat::UNORM_INT8 : textureFormat.type))
1089 , m_currentIteration(0)
1090 {
1091 DE_ASSERT((m_gatherType == GATHERTYPE_BASIC) == (m_offsetSize == OFFSETSIZE_NONE));
1092 DE_ASSERT((m_shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE) == isDepthFormat(m_textureFormat));
1093 DE_ASSERT(isUnormFormatType(m_colorBufferFormat.type) ||
1094 m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8 ||
1095 m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT16 ||
1096 m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8 ||
1097 m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT16);
1098 DE_ASSERT(glu::isGLInternalColorFormatFilterable(glu::getInternalFormat(m_colorBufferFormat)) ||
1099 (m_magFilter == tcu::Sampler::NEAREST &&
1100 (m_minFilter == tcu::Sampler::NEAREST || m_minFilter == tcu::Sampler::NEAREST_MIPMAP_NEAREST)));
1101 DE_ASSERT(isMipmapFilter(m_minFilter) || !(m_flags & GATHERCASE_MIPMAP_INCOMPLETE));
1102 DE_ASSERT(m_textureType == TEXTURETYPE_CUBE || !(m_flags & GATHERCASE_DONT_SAMPLE_CUBE_CORNERS));
1103 DE_ASSERT(!((m_flags & GATHERCASE_MIPMAP_INCOMPLETE) &&
1104 isDepthFormat(m_textureFormat))); // It's not clear what shadow textures should return when incomplete.
1105 }
1106
getOffsetRange(void) const1107 IVec2 TextureGatherCase::getOffsetRange(void) const
1108 {
1109 switch (m_offsetSize)
1110 {
1111 case OFFSETSIZE_NONE:
1112 return IVec2(0);
1113
1114 case OFFSETSIZE_MINIMUM_REQUIRED:
1115 // \note Defined by spec.
1116 return IVec2(SPEC_MAX_MIN_OFFSET, SPEC_MIN_MAX_OFFSET);
1117
1118 case OFFSETSIZE_IMPLEMENTATION_MAXIMUM:
1119 return IVec2(m_context.getContextInfo().getInt(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET),
1120 m_context.getContextInfo().getInt(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET));
1121
1122 default:
1123 DE_ASSERT(false);
1124 return IVec2(-1);
1125 }
1126 }
1127
genVertexShaderSource(bool requireGpuShader5,int numTexCoordComponents,bool useNormalizedCoordInput)1128 glu::VertexSource TextureGatherCase::genVertexShaderSource(bool requireGpuShader5, int numTexCoordComponents,
1129 bool useNormalizedCoordInput)
1130 {
1131 DE_ASSERT(numTexCoordComponents == 2 || numTexCoordComponents == 3);
1132 const string texCoordType = "vec" + de::toString(numTexCoordComponents);
1133 std::string vertexSource = "${GLSL_VERSION_DECL}\n" + string(requireGpuShader5 ? "${GPU_SHADER5_REQUIRE}\n" : "") +
1134 "\n"
1135 "in highp vec2 a_position;\n"
1136 "in highp " +
1137 texCoordType + " a_texCoord;\n" +
1138 (useNormalizedCoordInput ? "in highp vec2 a_normalizedCoord; // (0,0) to (1,1)\n" : "") +
1139 "\n"
1140 "out highp " +
1141 texCoordType + " v_texCoord;\n" +
1142 (useNormalizedCoordInput ? "out highp vec2 v_normalizedCoord;\n" : "") +
1143 "\n"
1144 "void main (void)\n"
1145 "{\n"
1146 " gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);\n"
1147 " v_texCoord = a_texCoord;\n" +
1148 (useNormalizedCoordInput ? "\tv_normalizedCoord = a_normalizedCoord;\n" : "") + "}\n";
1149 return glu::VertexSource(specializeShader(m_context, vertexSource.c_str()));
1150 }
1151
genFragmentShaderSource(bool requireGpuShader5,int numTexCoordComponents,glu::DataType samplerType,const string & funcCall,bool useNormalizedCoordInput,bool usePixCoord)1152 glu::FragmentSource TextureGatherCase::genFragmentShaderSource(bool requireGpuShader5, int numTexCoordComponents,
1153 glu::DataType samplerType, const string &funcCall,
1154 bool useNormalizedCoordInput, bool usePixCoord)
1155 {
1156 DE_ASSERT(glu::isDataTypeSampler(samplerType));
1157 DE_ASSERT(de::inRange(numTexCoordComponents, 2, 3));
1158 DE_ASSERT(!usePixCoord || useNormalizedCoordInput);
1159
1160 const string texCoordType = "vec" + de::toString(numTexCoordComponents);
1161
1162 std::string fragmentSource =
1163 "${GLSL_VERSION_DECL}\n" + string(requireGpuShader5 ? "${GPU_SHADER5_REQUIRE}\n" : "") +
1164 "\n"
1165 "layout (location = 0) out mediump " +
1166 glu::getDataTypeName(getSamplerGatherResultType(samplerType)) +
1167 " o_color;\n"
1168 "\n"
1169 "in highp " +
1170 texCoordType + " v_texCoord;\n" + (useNormalizedCoordInput ? "in highp vec2 v_normalizedCoord;\n" : "") +
1171 "\n"
1172 "uniform highp " +
1173 string(glu::getDataTypeName(samplerType)) + " u_sampler;\n" +
1174 (useNormalizedCoordInput ? "uniform highp vec2 u_viewportSize;\n" : "") +
1175 "\n"
1176 "void main(void)\n"
1177 "{\n" +
1178 (usePixCoord ? "\tivec2 pixCoord = ivec2(v_normalizedCoord*u_viewportSize);\n" : "") +
1179 " o_color = " + funcCall +
1180 ";\n"
1181 "}\n";
1182
1183 return glu::FragmentSource(specializeShader(m_context, fragmentSource.c_str()));
1184 }
1185
genGatherFuncCall(GatherType gatherType,const tcu::TextureFormat & textureFormat,const GatherArgs & gatherArgs,const string & refZExpr,const IVec2 & offsetRange,int indentationDepth)1186 string TextureGatherCase::genGatherFuncCall(GatherType gatherType, const tcu::TextureFormat &textureFormat,
1187 const GatherArgs &gatherArgs, const string &refZExpr,
1188 const IVec2 &offsetRange, int indentationDepth)
1189 {
1190 string result;
1191
1192 switch (gatherType)
1193 {
1194 case GATHERTYPE_BASIC:
1195 result += "textureGather";
1196 break;
1197 case GATHERTYPE_OFFSET: // \note Fallthrough.
1198 case GATHERTYPE_OFFSET_DYNAMIC:
1199 result += "textureGatherOffset";
1200 break;
1201 case GATHERTYPE_OFFSETS:
1202 result += "textureGatherOffsets";
1203 break;
1204 default:
1205 DE_ASSERT(false);
1206 }
1207
1208 result += "(u_sampler, v_texCoord";
1209
1210 if (isDepthFormat(textureFormat))
1211 {
1212 DE_ASSERT(gatherArgs.componentNdx < 0);
1213 result += ", " + refZExpr;
1214 }
1215
1216 if (gatherType == GATHERTYPE_OFFSET || gatherType == GATHERTYPE_OFFSET_DYNAMIC || gatherType == GATHERTYPE_OFFSETS)
1217 {
1218 result += ", ";
1219 switch (gatherType)
1220 {
1221 case GATHERTYPE_OFFSET:
1222 result += "ivec2" + de::toString(gatherArgs.offsets[0]);
1223 break;
1224
1225 case GATHERTYPE_OFFSET_DYNAMIC:
1226 result += "pixCoord.yx % ivec2(" + de::toString(offsetRange.y() - offsetRange.x() + 1) + ") + " +
1227 de::toString(offsetRange.x());
1228 break;
1229
1230 case GATHERTYPE_OFFSETS:
1231 result += "ivec2[4](\n" + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[0]) +
1232 ",\n" + string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[1]) + ",\n" +
1233 string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[2]) + ",\n" +
1234 string(indentationDepth, '\t') + "\tivec2" + de::toString(gatherArgs.offsets[3]) + ")\n" +
1235 string(indentationDepth, '\t') + "\t";
1236 break;
1237
1238 default:
1239 DE_ASSERT(false);
1240 }
1241 }
1242
1243 if (gatherArgs.componentNdx >= 0)
1244 {
1245 DE_ASSERT(gatherArgs.componentNdx < 4);
1246 result += ", " + de::toString(gatherArgs.componentNdx);
1247 }
1248
1249 result += ")";
1250
1251 return result;
1252 }
1253
1254 // \note If componentNdx for genProgramSources() is -1, component index is not specified.
genProgramSources(GatherType gatherType,TextureType textureType,const tcu::TextureFormat & textureFormat,const GatherArgs & gatherArgs,const string & refZExpr,const IVec2 & offsetRange)1255 glu::ProgramSources TextureGatherCase::genProgramSources(GatherType gatherType, TextureType textureType,
1256 const tcu::TextureFormat &textureFormat,
1257 const GatherArgs &gatherArgs, const string &refZExpr,
1258 const IVec2 &offsetRange)
1259 {
1260 const bool usePixCoord = gatherType == GATHERTYPE_OFFSET_DYNAMIC;
1261 const bool useNormalizedCoord = usePixCoord || isDepthFormat(textureFormat);
1262 const bool isDynamicOffset = gatherType == GATHERTYPE_OFFSET_DYNAMIC;
1263 const bool isShadow = isDepthFormat(textureFormat);
1264 const glu::DataType samplerType = getSamplerType(textureType, textureFormat);
1265 const int numDims = getNumTextureSamplingDimensions(textureType);
1266 const string funcCall = genGatherFuncCall(gatherType, textureFormat, gatherArgs, refZExpr, offsetRange, 1);
1267
1268 return glu::ProgramSources() << genVertexShaderSource(requireGpuShader5(gatherType), numDims,
1269 isDynamicOffset || isShadow)
1270 << genFragmentShaderSource(requireGpuShader5(gatherType), numDims, samplerType,
1271 funcCall, useNormalizedCoord, usePixCoord);
1272 }
1273
init(void)1274 void TextureGatherCase::init(void)
1275 {
1276 TestLog &log = m_testCtx.getLog();
1277 const glu::RenderContext &renderCtx = m_context.getRenderContext();
1278 const glw::Functions &gl = renderCtx.getFunctions();
1279 const uint32_t texTypeGL = getGLTextureType(m_textureType);
1280 const bool supportsES32orGL45 =
1281 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
1282 glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
1283
1284 // Check prerequisites.
1285 if (requireGpuShader5(m_gatherType) && !supportsES32orGL45 &&
1286 !m_context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5"))
1287 throw tcu::NotSupportedError("GL_EXT_gpu_shader5 required");
1288
1289 // Log and check implementation offset limits, if appropriate.
1290 if (m_offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM)
1291 {
1292 const IVec2 offsetRange = getOffsetRange();
1293 log << TestLog::Integer("ImplementationMinTextureGatherOffset",
1294 "Implementation's value for GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET", "", QP_KEY_TAG_NONE,
1295 offsetRange[0])
1296 << TestLog::Integer("ImplementationMaxTextureGatherOffset",
1297 "Implementation's value for GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET", "", QP_KEY_TAG_NONE,
1298 offsetRange[1]);
1299 TCU_CHECK_MSG(
1300 offsetRange[0] <= SPEC_MAX_MIN_OFFSET,
1301 ("GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET must be at most " + de::toString((int)SPEC_MAX_MIN_OFFSET)).c_str());
1302 TCU_CHECK_MSG(offsetRange[1] >= SPEC_MIN_MAX_OFFSET, ("GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET must be at least " +
1303 de::toString((int)SPEC_MIN_MAX_OFFSET))
1304 .c_str());
1305 }
1306
1307 if (!isContextTypeES(m_context.getRenderContext().getType()))
1308 gl.enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
1309
1310 // Create rbo and fbo.
1311
1312 m_colorBuffer = MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
1313 gl.bindRenderbuffer(GL_RENDERBUFFER, **m_colorBuffer);
1314 gl.renderbufferStorage(GL_RENDERBUFFER, glu::getInternalFormat(m_colorBufferFormat), RENDER_SIZE.x(),
1315 RENDER_SIZE.y());
1316 GLU_EXPECT_NO_ERROR(gl.getError(), "Create and setup renderbuffer object");
1317
1318 m_fbo = MovePtr<glu::Framebuffer>(new glu::Framebuffer(renderCtx));
1319 gl.bindFramebuffer(GL_FRAMEBUFFER, **m_fbo);
1320 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **m_colorBuffer);
1321 GLU_EXPECT_NO_ERROR(gl.getError(), "Create and setup framebuffer object");
1322
1323 log << TestLog::Message << "Using a framebuffer object with renderbuffer with format "
1324 << glu::getTextureFormatName(glu::getInternalFormat(m_colorBufferFormat)) << " and size " << RENDER_SIZE
1325 << TestLog::EndMessage;
1326
1327 // Generate subclass-specific iterations.
1328
1329 generateIterations();
1330 m_currentIteration = 0;
1331
1332 // Initialize texture.
1333
1334 createAndUploadTexture();
1335 gl.texParameteri(texTypeGL, GL_TEXTURE_WRAP_S, glu::getGLWrapMode(m_wrapS));
1336 gl.texParameteri(texTypeGL, GL_TEXTURE_WRAP_T, glu::getGLWrapMode(m_wrapT));
1337 gl.texParameteri(texTypeGL, GL_TEXTURE_MIN_FILTER, glu::getGLFilterMode(m_minFilter));
1338 gl.texParameteri(texTypeGL, GL_TEXTURE_MAG_FILTER, glu::getGLFilterMode(m_magFilter));
1339
1340 if (m_baseLevel != 0)
1341 gl.texParameteri(texTypeGL, GL_TEXTURE_BASE_LEVEL, m_baseLevel);
1342
1343 if (isDepthFormat(m_textureFormat))
1344 {
1345 gl.texParameteri(texTypeGL, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
1346 gl.texParameteri(texTypeGL, GL_TEXTURE_COMPARE_FUNC, glu::getGLCompareFunc(m_shadowCompareMode));
1347 }
1348
1349 if (m_textureSwizzle.isSome())
1350 {
1351 const uint32_t swizzleNamesGL[4] = {GL_TEXTURE_SWIZZLE_R, GL_TEXTURE_SWIZZLE_G, GL_TEXTURE_SWIZZLE_B,
1352 GL_TEXTURE_SWIZZLE_A};
1353
1354 for (int i = 0; i < 4; i++)
1355 {
1356 const uint32_t curGLSwizzle = getGLTextureSwizzleComponent(m_textureSwizzle.getSwizzle()[i]);
1357 gl.texParameteri(texTypeGL, swizzleNamesGL[i], curGLSwizzle);
1358 }
1359 }
1360
1361 GLU_EXPECT_NO_ERROR(gl.getError(), "Set texture parameters");
1362
1363 log << TestLog::Message << "Texture base level is " << m_baseLevel << TestLog::EndMessage << TestLog::Message
1364 << "s and t wrap modes are " << glu::getTextureWrapModeName(glu::getGLWrapMode(m_wrapS)) << " and "
1365 << glu::getTextureWrapModeName(glu::getGLWrapMode(m_wrapT)) << ", respectively" << TestLog::EndMessage
1366 << TestLog::Message << "Minification and magnification filter modes are "
1367 << glu::getTextureFilterName(glu::getGLFilterMode(m_minFilter)) << " and "
1368 << glu::getTextureFilterName(glu::getGLFilterMode(m_magFilter)) << ", respectively "
1369 << ((m_flags & GATHERCASE_MIPMAP_INCOMPLETE) ? "(note that they cause the texture to be incomplete)" :
1370 "(note that they should have no effect on gather result)")
1371 << TestLog::EndMessage << TestLog::Message << "Using texture swizzle " << m_textureSwizzle
1372 << TestLog::EndMessage;
1373
1374 if (m_shadowCompareMode != tcu::Sampler::COMPAREMODE_NONE)
1375 log << TestLog::Message << "Using texture compare func "
1376 << glu::getCompareFuncName(glu::getGLCompareFunc(m_shadowCompareMode)) << TestLog::EndMessage;
1377 }
1378
deinit(void)1379 void TextureGatherCase::deinit(void)
1380 {
1381 if (!isContextTypeES(m_context.getRenderContext().getType()))
1382 m_context.getRenderContext().getFunctions().disable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
1383
1384 m_program = MovePtr<ShaderProgram>(DE_NULL);
1385 m_fbo = MovePtr<glu::Framebuffer>(DE_NULL);
1386 m_colorBuffer = MovePtr<glu::Renderbuffer>(DE_NULL);
1387 }
1388
iterate(void)1389 TextureGatherCase::IterateResult TextureGatherCase::iterate(void)
1390 {
1391 TestLog &log = m_testCtx.getLog();
1392 const tcu::ScopedLogSection iterationSection(log, "Iteration" + de::toString(m_currentIteration),
1393 "Iteration " + de::toString(m_currentIteration));
1394 const glu::RenderContext &renderCtx = m_context.getRenderContext();
1395 const glw::Functions &gl = renderCtx.getFunctions();
1396 const GatherArgs &gatherArgs = getGatherArgs(m_currentIteration);
1397 const string refZExpr = "v_normalizedCoord.x";
1398 const bool needPixelCoordInShader = m_gatherType == GATHERTYPE_OFFSET_DYNAMIC;
1399 const bool needNormalizedCoordInShader = needPixelCoordInShader || isDepthFormat(m_textureFormat);
1400
1401 // Generate a program appropriate for this iteration.
1402
1403 m_program = MovePtr<ShaderProgram>(
1404 new ShaderProgram(renderCtx, genProgramSources(m_gatherType, m_textureType, m_textureFormat, gatherArgs,
1405 refZExpr, getOffsetRange())));
1406 if (m_currentIteration == 0)
1407 m_testCtx.getLog() << *m_program;
1408 else
1409 m_testCtx.getLog()
1410 << TestLog::Message
1411 << "Using a program similar to the previous one, except with a gather function call as follows:\n"
1412 << genGatherFuncCall(m_gatherType, m_textureFormat, gatherArgs, refZExpr, getOffsetRange(), 0)
1413 << TestLog::EndMessage;
1414 if (!m_program->isOk())
1415 {
1416 if (m_currentIteration != 0)
1417 m_testCtx.getLog() << *m_program;
1418 TCU_FAIL("Failed to build program");
1419 }
1420
1421 // Render.
1422
1423 gl.viewport(0, 0, RENDER_SIZE.x(), RENDER_SIZE.y());
1424 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1425 gl.clear(GL_COLOR_BUFFER_BIT);
1426
1427 {
1428 const float position[4 * 2] = {
1429 -1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f,
1430 };
1431
1432 const float normalizedCoord[4 * 2] = {
1433 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1434 };
1435
1436 const vector<float> texCoord = computeQuadTexCoord(m_currentIteration);
1437
1438 vector<glu::VertexArrayBinding> attrBindings;
1439 attrBindings.push_back(glu::va::Float("a_position", 2, 4, 0, &position[0]));
1440 attrBindings.push_back(glu::va::Float("a_texCoord", (int)texCoord.size() / 4, 4, 0, &texCoord[0]));
1441 if (needNormalizedCoordInShader)
1442 attrBindings.push_back(glu::va::Float("a_normalizedCoord", 2, 4, 0, &normalizedCoord[0]));
1443
1444 const uint16_t indices[6] = {0, 1, 2, 2, 1, 3};
1445
1446 gl.useProgram(m_program->getProgram());
1447
1448 {
1449 const int samplerUniformLocation = gl.getUniformLocation(m_program->getProgram(), "u_sampler");
1450 TCU_CHECK(samplerUniformLocation >= 0);
1451 gl.uniform1i(samplerUniformLocation, 0);
1452 }
1453
1454 if (needPixelCoordInShader)
1455 {
1456 const int viewportSizeUniformLocation = gl.getUniformLocation(m_program->getProgram(), "u_viewportSize");
1457 TCU_CHECK(viewportSizeUniformLocation >= 0);
1458 gl.uniform2f(viewportSizeUniformLocation, (float)RENDER_SIZE.x(), (float)RENDER_SIZE.y());
1459 }
1460
1461 if (texCoord.size() == 2 * 4)
1462 {
1463 Vec2 texCoordVec[4];
1464 computeTexCoordVecs(texCoord, texCoordVec);
1465 log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3]
1466 << TestLog::EndMessage;
1467 }
1468 else if (texCoord.size() == 3 * 4)
1469 {
1470 Vec3 texCoordVec[4];
1471 computeTexCoordVecs(texCoord, texCoordVec);
1472 log << TestLog::Message << "Texture coordinates run from " << texCoordVec[0] << " to " << texCoordVec[3]
1473 << TestLog::EndMessage;
1474 }
1475 else
1476 DE_ASSERT(false);
1477
1478 glu::draw(renderCtx, m_program->getProgram(), (int)attrBindings.size(), &attrBindings[0],
1479 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
1480 }
1481
1482 // Verify result.
1483
1484 {
1485 const tcu::TextureLevel rendered = getPixels(renderCtx, RENDER_SIZE, m_colorBufferFormat);
1486
1487 if (!verify(m_currentIteration, rendered.getAccess()))
1488 {
1489 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result verification failed");
1490 return STOP;
1491 }
1492 }
1493
1494 m_currentIteration++;
1495 if (m_currentIteration == (int)getNumIterations())
1496 {
1497 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1498 return STOP;
1499 }
1500 else
1501 return CONTINUE;
1502 }
1503
1504 template <typename TexViewT, typename TexCoordT>
verify(const ConstPixelBufferAccess & rendered,const TexViewT & texture,const TexCoordT (& texCoords)[4],const GatherArgs & gatherArgs) const1505 bool TextureGatherCase::verify(const ConstPixelBufferAccess &rendered, const TexViewT &texture,
1506 const TexCoordT (&texCoords)[4], const GatherArgs &gatherArgs) const
1507 {
1508 TestLog &log = m_testCtx.getLog();
1509
1510 if (m_flags & GATHERCASE_MIPMAP_INCOMPLETE)
1511 {
1512 const int componentNdx = de::max(0, gatherArgs.componentNdx);
1513 const Vec4 incompleteColor(0.0f, 0.0f, 0.0f, 1.0f);
1514 const Vec4 refColor(incompleteColor[componentNdx]);
1515 const bool isOk = verifySingleColored(log, rendered, refColor);
1516
1517 if (!isOk)
1518 log << TestLog::Message << "Note: expected color " << refColor << " for all pixels; "
1519 << incompleteColor[componentNdx] << " is component at index " << componentNdx << " in the color "
1520 << incompleteColor << ", which is used for incomplete textures" << TestLog::EndMessage;
1521
1522 return isOk;
1523 }
1524 else
1525 {
1526 DE_ASSERT(m_colorBufferFormat.order == tcu::TextureFormat::RGBA);
1527 DE_ASSERT(m_colorBufferFormat.type == tcu::TextureFormat::UNORM_INT8 ||
1528 m_colorBufferFormat.type == tcu::TextureFormat::UNSIGNED_INT8 ||
1529 m_colorBufferFormat.type == tcu::TextureFormat::SIGNED_INT8);
1530
1531 const MovePtr<PixelOffsets> pixelOffsets = makePixelOffsetsFunctor(m_gatherType, gatherArgs, getOffsetRange());
1532 const tcu::PixelFormat pixelFormat = tcu::PixelFormat(8, 8, 8, 8);
1533 const IVec4 colorBits = tcu::max(glu::TextureTestUtil::getBitsVec(pixelFormat) - 1, tcu::IVec4(0));
1534 const IVec3 coordBits = m_textureType == TEXTURETYPE_2D ? IVec3(20, 20, 0) :
1535 m_textureType == TEXTURETYPE_CUBE ? IVec3(10, 10, 10) :
1536 m_textureType == TEXTURETYPE_2D_ARRAY ? IVec3(20, 20, 20) :
1537 IVec3(-1);
1538 const IVec3 uvwBits = m_textureType == TEXTURETYPE_2D ? IVec3(7, 7, 0) :
1539 m_textureType == TEXTURETYPE_CUBE ? IVec3(6, 6, 0) :
1540 m_textureType == TEXTURETYPE_2D_ARRAY ? IVec3(7, 7, 7) :
1541 IVec3(-1);
1542 tcu::Sampler sampler;
1543 sampler.wrapS = m_wrapS;
1544 sampler.wrapT = m_wrapT;
1545 sampler.compare = m_shadowCompareMode;
1546
1547 if (isDepthFormat(m_textureFormat))
1548 {
1549 tcu::TexComparePrecision comparePrec;
1550 comparePrec.coordBits = coordBits;
1551 comparePrec.uvwBits = uvwBits;
1552 comparePrec.referenceBits = 16;
1553 comparePrec.resultBits = pixelFormat.redBits - 1;
1554
1555 return verifyGatherOffsetsCompare(log, rendered, texture, texCoords, sampler, comparePrec,
1556 PixelCompareRefZDefault(RENDER_SIZE), *pixelOffsets);
1557 }
1558 else
1559 {
1560 const int componentNdx = de::max(0, gatherArgs.componentNdx);
1561
1562 if (isUnormFormatType(m_textureFormat.type))
1563 {
1564 tcu::LookupPrecision lookupPrec;
1565 lookupPrec.colorThreshold = tcu::computeFixedPointThreshold(colorBits);
1566 lookupPrec.coordBits = coordBits;
1567 lookupPrec.uvwBits = uvwBits;
1568 lookupPrec.colorMask = glu::TextureTestUtil::getCompareMask(pixelFormat);
1569 return verifyGatherOffsets<float>(log, rendered, texture, texCoords, sampler, lookupPrec, componentNdx,
1570 *pixelOffsets);
1571 }
1572 else if (isUIntFormatType(m_textureFormat.type) || isSIntFormatType(m_textureFormat.type))
1573 {
1574 tcu::IntLookupPrecision lookupPrec;
1575 lookupPrec.colorThreshold = UVec4(0);
1576 lookupPrec.coordBits = coordBits;
1577 lookupPrec.uvwBits = uvwBits;
1578 lookupPrec.colorMask = glu::TextureTestUtil::getCompareMask(pixelFormat);
1579
1580 if (isUIntFormatType(m_textureFormat.type))
1581 return verifyGatherOffsets<uint32_t>(log, rendered, texture, texCoords, sampler, lookupPrec,
1582 componentNdx, *pixelOffsets);
1583 else if (isSIntFormatType(m_textureFormat.type))
1584 return verifyGatherOffsets<int32_t>(log, rendered, texture, texCoords, sampler, lookupPrec,
1585 componentNdx, *pixelOffsets);
1586 else
1587 {
1588 DE_ASSERT(false);
1589 return false;
1590 }
1591 }
1592 else
1593 {
1594 DE_ASSERT(false);
1595 return false;
1596 }
1597 }
1598 }
1599 }
1600
generateBasic2DCaseIterations(GatherType gatherType,const tcu::TextureFormat & textureFormat,const IVec2 & offsetRange)1601 vector<GatherArgs> generateBasic2DCaseIterations(GatherType gatherType, const tcu::TextureFormat &textureFormat,
1602 const IVec2 &offsetRange)
1603 {
1604 const int numComponentCases =
1605 isDepthFormat(textureFormat) ?
1606 1 :
1607 4 + 1; // \note For non-depth textures, test explicit components 0 to 3 and implicit component 0.
1608 vector<GatherArgs> result;
1609
1610 for (int componentCaseNdx = 0; componentCaseNdx < numComponentCases; componentCaseNdx++)
1611 {
1612 const int componentNdx = componentCaseNdx - 1;
1613
1614 switch (gatherType)
1615 {
1616 case GATHERTYPE_BASIC:
1617 result.push_back(GatherArgs(componentNdx));
1618 break;
1619
1620 case GATHERTYPE_OFFSET:
1621 {
1622 const int min = offsetRange.x();
1623 const int max = offsetRange.y();
1624 const int hmin = divRoundToZero(min, 2);
1625 const int hmax = divRoundToZero(max, 2);
1626
1627 result.push_back(GatherArgs(componentNdx, IVec2(min, max)));
1628
1629 if (componentCaseNdx ==
1630 0) // Don't test all offsets variants for all color components (they should be pretty orthogonal).
1631 {
1632 result.push_back(GatherArgs(componentNdx, IVec2(min, min)));
1633 result.push_back(GatherArgs(componentNdx, IVec2(max, min)));
1634 result.push_back(GatherArgs(componentNdx, IVec2(max, max)));
1635
1636 result.push_back(GatherArgs(componentNdx, IVec2(0, hmax)));
1637 result.push_back(GatherArgs(componentNdx, IVec2(hmin, 0)));
1638 result.push_back(GatherArgs(componentNdx, IVec2(0, 0)));
1639 }
1640
1641 break;
1642 }
1643
1644 case GATHERTYPE_OFFSET_DYNAMIC:
1645 result.push_back(GatherArgs(componentNdx));
1646 break;
1647
1648 case GATHERTYPE_OFFSETS:
1649 {
1650 const int min = offsetRange.x();
1651 const int max = offsetRange.y();
1652 const int hmin = divRoundToZero(min, 2);
1653 const int hmax = divRoundToZero(max, 2);
1654
1655 result.push_back(
1656 GatherArgs(componentNdx, IVec2(min, min), IVec2(min, max), IVec2(max, min), IVec2(max, max)));
1657
1658 if (componentCaseNdx ==
1659 0) // Don't test all offsets variants for all color components (they should be pretty orthogonal).
1660 result.push_back(
1661 GatherArgs(componentNdx, IVec2(min, hmax), IVec2(hmin, max), IVec2(0, hmax), IVec2(hmax, 0)));
1662 break;
1663 }
1664
1665 default:
1666 DE_ASSERT(false);
1667 }
1668 }
1669
1670 return result;
1671 }
1672
1673 class TextureGather2DCase : public TextureGatherCase
1674 {
1675 public:
TextureGather2DCase(Context & context,const char * name,const char * description,GatherType gatherType,OffsetSize offsetSize,tcu::TextureFormat textureFormat,tcu::Sampler::CompareMode shadowCompareMode,tcu::Sampler::WrapMode wrapS,tcu::Sampler::WrapMode wrapT,const MaybeTextureSwizzle & texSwizzle,tcu::Sampler::FilterMode minFilter,tcu::Sampler::FilterMode magFilter,int baseLevel,uint32_t flags,const IVec2 & textureSize)1676 TextureGather2DCase(Context &context, const char *name, const char *description, GatherType gatherType,
1677 OffsetSize offsetSize, tcu::TextureFormat textureFormat,
1678 tcu::Sampler::CompareMode shadowCompareMode, tcu::Sampler::WrapMode wrapS,
1679 tcu::Sampler::WrapMode wrapT, const MaybeTextureSwizzle &texSwizzle,
1680 tcu::Sampler::FilterMode minFilter, tcu::Sampler::FilterMode magFilter, int baseLevel,
1681 uint32_t flags, const IVec2 &textureSize)
1682 : TextureGatherCase(context, name, description, TEXTURETYPE_2D, gatherType, offsetSize, textureFormat,
1683 shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags)
1684 , m_textureSize(textureSize)
1685 , m_swizzledTexture(tcu::TextureFormat(), 1, 1)
1686 {
1687 }
1688
1689 protected:
1690 void generateIterations(void);
1691 void createAndUploadTexture(void);
getNumIterations(void) const1692 int getNumIterations(void) const
1693 {
1694 DE_ASSERT(!m_iterations.empty());
1695 return (int)m_iterations.size();
1696 }
getGatherArgs(int iterationNdx) const1697 GatherArgs getGatherArgs(int iterationNdx) const
1698 {
1699 return m_iterations[iterationNdx];
1700 }
1701 vector<float> computeQuadTexCoord(int iterationNdx) const;
1702 bool verify(int iterationNdx, const ConstPixelBufferAccess &rendered) const;
1703
1704 private:
1705 const IVec2 m_textureSize;
1706
1707 MovePtr<glu::Texture2D> m_texture;
1708 tcu::Texture2D m_swizzledTexture;
1709 vector<GatherArgs> m_iterations;
1710 };
1711
computeQuadTexCoord(int) const1712 vector<float> TextureGather2DCase::computeQuadTexCoord(int /* iterationNdx */) const
1713 {
1714 vector<float> res;
1715 glu::TextureTestUtil::computeQuadTexCoord2D(res, Vec2(-0.3f, -0.4f), Vec2(1.5f, 1.6f));
1716 return res;
1717 }
1718
generateIterations(void)1719 void TextureGather2DCase::generateIterations(void)
1720 {
1721 DE_ASSERT(m_iterations.empty());
1722 m_iterations = generateBasic2DCaseIterations(m_gatherType, m_textureFormat, getOffsetRange());
1723 }
1724
createAndUploadTexture(void)1725 void TextureGather2DCase::createAndUploadTexture(void)
1726 {
1727 const glu::RenderContext &renderCtx = m_context.getRenderContext();
1728 const glw::Functions &gl = renderCtx.getFunctions();
1729 const tcu::TextureFormatInfo texFmtInfo = tcu::getTextureFormatInfo(m_textureFormat);
1730
1731 m_texture = MovePtr<glu::Texture2D>(
1732 new glu::Texture2D(renderCtx, glu::getInternalFormat(m_textureFormat), m_textureSize.x(), m_textureSize.y()));
1733
1734 {
1735 tcu::Texture2D &refTexture = m_texture->getRefTexture();
1736 const int levelBegin = m_baseLevel;
1737 const int levelEnd = isMipmapFilter(m_minFilter) && !(m_flags & GATHERCASE_MIPMAP_INCOMPLETE) ?
1738 refTexture.getNumLevels() :
1739 m_baseLevel + 1;
1740 DE_ASSERT(m_baseLevel < refTexture.getNumLevels());
1741
1742 for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
1743 {
1744 refTexture.allocLevel(levelNdx);
1745 const PixelBufferAccess &level = refTexture.getLevel(levelNdx);
1746 fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax,
1747 (uint32_t)m_testCtx.getCommandLine().getBaseSeed());
1748 m_testCtx.getLog() << TestLog::Image("InputTextureLevel" + de::toString(levelNdx),
1749 "Input texture, level " + de::toString(levelNdx), level)
1750 << TestLog::Message << "Note: texture level's size is "
1751 << IVec2(level.getWidth(), level.getHeight()) << TestLog::EndMessage;
1752 }
1753
1754 swizzleTexture(m_swizzledTexture, refTexture, m_textureSwizzle);
1755 }
1756
1757 gl.activeTexture(GL_TEXTURE0);
1758 m_texture->upload();
1759 }
1760
verify(int iterationNdx,const ConstPixelBufferAccess & rendered) const1761 bool TextureGather2DCase::verify(int iterationNdx, const ConstPixelBufferAccess &rendered) const
1762 {
1763 Vec2 texCoords[4];
1764 computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
1765 return TextureGatherCase::verify(rendered, getOneLevelSubView(tcu::Texture2DView(m_swizzledTexture), m_baseLevel),
1766 texCoords, m_iterations[iterationNdx]);
1767 }
1768
1769 class TextureGather2DArrayCase : public TextureGatherCase
1770 {
1771 public:
TextureGather2DArrayCase(Context & context,const char * name,const char * description,GatherType gatherType,OffsetSize offsetSize,tcu::TextureFormat textureFormat,tcu::Sampler::CompareMode shadowCompareMode,tcu::Sampler::WrapMode wrapS,tcu::Sampler::WrapMode wrapT,const MaybeTextureSwizzle & texSwizzle,tcu::Sampler::FilterMode minFilter,tcu::Sampler::FilterMode magFilter,int baseLevel,uint32_t flags,const IVec3 & textureSize)1772 TextureGather2DArrayCase(Context &context, const char *name, const char *description, GatherType gatherType,
1773 OffsetSize offsetSize, tcu::TextureFormat textureFormat,
1774 tcu::Sampler::CompareMode shadowCompareMode, tcu::Sampler::WrapMode wrapS,
1775 tcu::Sampler::WrapMode wrapT, const MaybeTextureSwizzle &texSwizzle,
1776 tcu::Sampler::FilterMode minFilter, tcu::Sampler::FilterMode magFilter, int baseLevel,
1777 uint32_t flags, const IVec3 &textureSize)
1778 : TextureGatherCase(context, name, description, TEXTURETYPE_2D_ARRAY, gatherType, offsetSize, textureFormat,
1779 shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel, flags)
1780 , m_textureSize(textureSize)
1781 , m_swizzledTexture(tcu::TextureFormat(), 1, 1, 1)
1782 {
1783 }
1784
1785 protected:
1786 void generateIterations(void);
1787 void createAndUploadTexture(void);
getNumIterations(void) const1788 int getNumIterations(void) const
1789 {
1790 DE_ASSERT(!m_iterations.empty());
1791 return (int)m_iterations.size();
1792 }
getGatherArgs(int iterationNdx) const1793 GatherArgs getGatherArgs(int iterationNdx) const
1794 {
1795 return m_iterations[iterationNdx].gatherArgs;
1796 }
1797 vector<float> computeQuadTexCoord(int iterationNdx) const;
1798 bool verify(int iterationNdx, const ConstPixelBufferAccess &rendered) const;
1799
1800 private:
1801 struct Iteration
1802 {
1803 GatherArgs gatherArgs;
1804 int layerNdx;
1805 };
1806
1807 const IVec3 m_textureSize;
1808
1809 MovePtr<glu::Texture2DArray> m_texture;
1810 tcu::Texture2DArray m_swizzledTexture;
1811 vector<Iteration> m_iterations;
1812 };
1813
computeQuadTexCoord(int iterationNdx) const1814 vector<float> TextureGather2DArrayCase::computeQuadTexCoord(int iterationNdx) const
1815 {
1816 vector<float> res;
1817 glu::TextureTestUtil::computeQuadTexCoord2DArray(res, m_iterations[iterationNdx].layerNdx, Vec2(-0.3f, -0.4f),
1818 Vec2(1.5f, 1.6f));
1819 return res;
1820 }
1821
generateIterations(void)1822 void TextureGather2DArrayCase::generateIterations(void)
1823 {
1824 DE_ASSERT(m_iterations.empty());
1825
1826 const vector<GatherArgs> basicIterations =
1827 generateBasic2DCaseIterations(m_gatherType, m_textureFormat, getOffsetRange());
1828
1829 // \note Out-of-bounds layer indices are tested too.
1830 for (int layerNdx = -1; layerNdx < m_textureSize.z() + 1; layerNdx++)
1831 {
1832 // Don't duplicate all cases for all layers.
1833 if (layerNdx == 0)
1834 {
1835 for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
1836 {
1837 m_iterations.push_back(Iteration());
1838 m_iterations.back().gatherArgs = basicIterations[basicNdx];
1839 m_iterations.back().layerNdx = layerNdx;
1840 }
1841 }
1842 else
1843 {
1844 // For other layers than 0, only test one component and one set of offsets per layer.
1845 for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
1846 {
1847 if (isDepthFormat(m_textureFormat) || basicIterations[basicNdx].componentNdx == (layerNdx + 2) % 4)
1848 {
1849 m_iterations.push_back(Iteration());
1850 m_iterations.back().gatherArgs = basicIterations[basicNdx];
1851 m_iterations.back().layerNdx = layerNdx;
1852 break;
1853 }
1854 }
1855 }
1856 }
1857 }
1858
createAndUploadTexture(void)1859 void TextureGather2DArrayCase::createAndUploadTexture(void)
1860 {
1861 TestLog &log = m_testCtx.getLog();
1862 const glu::RenderContext &renderCtx = m_context.getRenderContext();
1863 const glw::Functions &gl = renderCtx.getFunctions();
1864 const tcu::TextureFormatInfo texFmtInfo = tcu::getTextureFormatInfo(m_textureFormat);
1865
1866 m_texture = MovePtr<glu::Texture2DArray>(new glu::Texture2DArray(
1867 renderCtx, glu::getInternalFormat(m_textureFormat), m_textureSize.x(), m_textureSize.y(), m_textureSize.z()));
1868
1869 {
1870 tcu::Texture2DArray &refTexture = m_texture->getRefTexture();
1871 const int levelBegin = m_baseLevel;
1872 const int levelEnd = isMipmapFilter(m_minFilter) && !(m_flags & GATHERCASE_MIPMAP_INCOMPLETE) ?
1873 refTexture.getNumLevels() :
1874 m_baseLevel + 1;
1875 DE_ASSERT(m_baseLevel < refTexture.getNumLevels());
1876
1877 for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
1878 {
1879 refTexture.allocLevel(levelNdx);
1880 const PixelBufferAccess &level = refTexture.getLevel(levelNdx);
1881 fillWithRandomColorTiles(level, texFmtInfo.valueMin, texFmtInfo.valueMax,
1882 (uint32_t)m_testCtx.getCommandLine().getBaseSeed());
1883
1884 log << TestLog::ImageSet("InputTextureLevel", "Input texture, level " + de::toString(levelNdx));
1885 for (int layerNdx = 0; layerNdx < m_textureSize.z(); layerNdx++)
1886 log << TestLog::Image("InputTextureLevel" + de::toString(layerNdx) + "Layer" + de::toString(layerNdx),
1887 "Layer " + de::toString(layerNdx),
1888 tcu::getSubregion(level, 0, 0, layerNdx, level.getWidth(), level.getHeight(), 1));
1889 log << TestLog::EndImageSet << TestLog::Message << "Note: texture level's size is "
1890 << IVec3(level.getWidth(), level.getHeight(), level.getDepth()) << TestLog::EndMessage;
1891 }
1892
1893 swizzleTexture(m_swizzledTexture, refTexture, m_textureSwizzle);
1894 }
1895
1896 gl.activeTexture(GL_TEXTURE0);
1897 m_texture->upload();
1898 }
1899
verify(int iterationNdx,const ConstPixelBufferAccess & rendered) const1900 bool TextureGather2DArrayCase::verify(int iterationNdx, const ConstPixelBufferAccess &rendered) const
1901 {
1902 Vec3 texCoords[4];
1903 computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
1904 return TextureGatherCase::verify(rendered,
1905 getOneLevelSubView(tcu::Texture2DArrayView(m_swizzledTexture), m_baseLevel),
1906 texCoords, m_iterations[iterationNdx].gatherArgs);
1907 }
1908
1909 // \note Cube case always uses just basic textureGather(); offset versions are not defined for cube maps.
1910 class TextureGatherCubeCase : public TextureGatherCase
1911 {
1912 public:
TextureGatherCubeCase(Context & context,const char * name,const char * description,tcu::TextureFormat textureFormat,tcu::Sampler::CompareMode shadowCompareMode,tcu::Sampler::WrapMode wrapS,tcu::Sampler::WrapMode wrapT,const MaybeTextureSwizzle & texSwizzle,tcu::Sampler::FilterMode minFilter,tcu::Sampler::FilterMode magFilter,int baseLevel,uint32_t flags,int textureSize)1913 TextureGatherCubeCase(Context &context, const char *name, const char *description, tcu::TextureFormat textureFormat,
1914 tcu::Sampler::CompareMode shadowCompareMode, tcu::Sampler::WrapMode wrapS,
1915 tcu::Sampler::WrapMode wrapT, const MaybeTextureSwizzle &texSwizzle,
1916 tcu::Sampler::FilterMode minFilter, tcu::Sampler::FilterMode magFilter, int baseLevel,
1917 uint32_t flags, int textureSize)
1918 : TextureGatherCase(context, name, description, TEXTURETYPE_CUBE, GATHERTYPE_BASIC, OFFSETSIZE_NONE,
1919 textureFormat, shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel,
1920 flags)
1921 , m_textureSize(textureSize)
1922 , m_swizzledTexture(tcu::TextureFormat(), 1)
1923 {
1924 }
1925
1926 protected:
1927 void generateIterations(void);
1928 void createAndUploadTexture(void);
getNumIterations(void) const1929 int getNumIterations(void) const
1930 {
1931 DE_ASSERT(!m_iterations.empty());
1932 return (int)m_iterations.size();
1933 }
getGatherArgs(int iterationNdx) const1934 GatherArgs getGatherArgs(int iterationNdx) const
1935 {
1936 return m_iterations[iterationNdx].gatherArgs;
1937 }
1938 vector<float> computeQuadTexCoord(int iterationNdx) const;
1939 bool verify(int iterationNdx, const ConstPixelBufferAccess &rendered) const;
1940
1941 private:
1942 struct Iteration
1943 {
1944 GatherArgs gatherArgs;
1945 tcu::CubeFace face;
1946 };
1947
1948 const int m_textureSize;
1949
1950 MovePtr<glu::TextureCube> m_texture;
1951 tcu::TextureCube m_swizzledTexture;
1952 vector<Iteration> m_iterations;
1953 };
1954
computeQuadTexCoord(int iterationNdx) const1955 vector<float> TextureGatherCubeCase::computeQuadTexCoord(int iterationNdx) const
1956 {
1957 const bool corners = (m_flags & GATHERCASE_DONT_SAMPLE_CUBE_CORNERS) == 0;
1958 const Vec2 minC = corners ? Vec2(-1.2f) : Vec2(-0.6f, -1.2f);
1959 const Vec2 maxC = corners ? Vec2(1.2f) : Vec2(0.6f, 1.2f);
1960 vector<float> res;
1961 glu::TextureTestUtil::computeQuadTexCoordCube(res, m_iterations[iterationNdx].face, minC, maxC);
1962 return res;
1963 }
1964
generateIterations(void)1965 void TextureGatherCubeCase::generateIterations(void)
1966 {
1967 DE_ASSERT(m_iterations.empty());
1968
1969 const vector<GatherArgs> basicIterations =
1970 generateBasic2DCaseIterations(m_gatherType, m_textureFormat, getOffsetRange());
1971
1972 for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
1973 {
1974 const tcu::CubeFace cubeFace = (tcu::CubeFace)cubeFaceI;
1975
1976 // Don't duplicate all cases for all faces.
1977 if (cubeFaceI == 0)
1978 {
1979 for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
1980 {
1981 m_iterations.push_back(Iteration());
1982 m_iterations.back().gatherArgs = basicIterations[basicNdx];
1983 m_iterations.back().face = cubeFace;
1984 }
1985 }
1986 else
1987 {
1988 // For other faces than first, only test one component per face.
1989 for (int basicNdx = 0; basicNdx < (int)basicIterations.size(); basicNdx++)
1990 {
1991 if (isDepthFormat(m_textureFormat) || basicIterations[basicNdx].componentNdx == cubeFaceI % 4)
1992 {
1993 m_iterations.push_back(Iteration());
1994 m_iterations.back().gatherArgs = basicIterations[basicNdx];
1995 m_iterations.back().face = cubeFace;
1996 break;
1997 }
1998 }
1999 }
2000 }
2001 }
2002
createAndUploadTexture(void)2003 void TextureGatherCubeCase::createAndUploadTexture(void)
2004 {
2005 TestLog &log = m_testCtx.getLog();
2006 const glu::RenderContext &renderCtx = m_context.getRenderContext();
2007 const glw::Functions &gl = renderCtx.getFunctions();
2008 const tcu::TextureFormatInfo texFmtInfo = tcu::getTextureFormatInfo(m_textureFormat);
2009
2010 m_texture = MovePtr<glu::TextureCube>(
2011 new glu::TextureCube(renderCtx, glu::getInternalFormat(m_textureFormat), m_textureSize));
2012
2013 {
2014 tcu::TextureCube &refTexture = m_texture->getRefTexture();
2015 const int levelBegin = m_baseLevel;
2016 const int levelEnd = isMipmapFilter(m_minFilter) && !(m_flags & GATHERCASE_MIPMAP_INCOMPLETE) ?
2017 refTexture.getNumLevels() :
2018 m_baseLevel + 1;
2019 DE_ASSERT(m_baseLevel < refTexture.getNumLevels());
2020
2021 for (int levelNdx = levelBegin; levelNdx < levelEnd; levelNdx++)
2022 {
2023 log << TestLog::ImageSet("InputTextureLevel" + de::toString(levelNdx),
2024 "Input texture, level " + de::toString(levelNdx));
2025
2026 for (int cubeFaceI = 0; cubeFaceI < tcu::CUBEFACE_LAST; cubeFaceI++)
2027 {
2028 const tcu::CubeFace cubeFace = (tcu::CubeFace)cubeFaceI;
2029 refTexture.allocLevel(cubeFace, levelNdx);
2030 const PixelBufferAccess &levelFace = refTexture.getLevelFace(levelNdx, cubeFace);
2031 fillWithRandomColorTiles(levelFace, texFmtInfo.valueMin, texFmtInfo.valueMax,
2032 (uint32_t)m_testCtx.getCommandLine().getBaseSeed() ^ (uint32_t)cubeFaceI);
2033
2034 m_testCtx.getLog() << TestLog::Image("InputTextureLevel" + de::toString(levelNdx) + "Face" +
2035 de::toString((int)cubeFace),
2036 de::toString(cubeFace), levelFace);
2037 }
2038
2039 log << TestLog::EndImageSet << TestLog::Message << "Note: texture level's size is "
2040 << refTexture.getLevelFace(levelNdx, tcu::CUBEFACE_NEGATIVE_X).getWidth() << TestLog::EndMessage;
2041 }
2042
2043 swizzleTexture(m_swizzledTexture, refTexture, m_textureSwizzle);
2044 }
2045
2046 gl.activeTexture(GL_TEXTURE0);
2047 m_texture->upload();
2048 }
2049
verify(int iterationNdx,const ConstPixelBufferAccess & rendered) const2050 bool TextureGatherCubeCase::verify(int iterationNdx, const ConstPixelBufferAccess &rendered) const
2051 {
2052 Vec3 texCoords[4];
2053 computeTexCoordVecs(computeQuadTexCoord(iterationNdx), texCoords);
2054 return TextureGatherCase::verify(rendered, getOneLevelSubView(tcu::TextureCubeView(m_swizzledTexture), m_baseLevel),
2055 texCoords, m_iterations[iterationNdx].gatherArgs);
2056 }
2057
makeTextureGatherCase(TextureType textureType,Context & context,const char * name,const char * description,GatherType gatherType,OffsetSize offsetSize,tcu::TextureFormat textureFormat,tcu::Sampler::CompareMode shadowCompareMode,tcu::Sampler::WrapMode wrapS,tcu::Sampler::WrapMode wrapT,const MaybeTextureSwizzle & texSwizzle,tcu::Sampler::FilterMode minFilter,tcu::Sampler::FilterMode magFilter,int baseLevel,const IVec3 & textureSize,uint32_t flags=0)2058 static inline TextureGatherCase *makeTextureGatherCase(
2059 TextureType textureType, Context &context, const char *name, const char *description, GatherType gatherType,
2060 OffsetSize offsetSize, tcu::TextureFormat textureFormat, tcu::Sampler::CompareMode shadowCompareMode,
2061 tcu::Sampler::WrapMode wrapS, tcu::Sampler::WrapMode wrapT, const MaybeTextureSwizzle &texSwizzle,
2062 tcu::Sampler::FilterMode minFilter, tcu::Sampler::FilterMode magFilter, int baseLevel, const IVec3 &textureSize,
2063 uint32_t flags = 0)
2064 {
2065 switch (textureType)
2066 {
2067 case TEXTURETYPE_2D:
2068 return new TextureGather2DCase(context, name, description, gatherType, offsetSize, textureFormat,
2069 shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter, baseLevel,
2070 flags, textureSize.swizzle(0, 1));
2071
2072 case TEXTURETYPE_2D_ARRAY:
2073 return new TextureGather2DArrayCase(context, name, description, gatherType, offsetSize, textureFormat,
2074 shadowCompareMode, wrapS, wrapT, texSwizzle, minFilter, magFilter,
2075 baseLevel, flags, textureSize);
2076
2077 case TEXTURETYPE_CUBE:
2078 DE_ASSERT(gatherType == GATHERTYPE_BASIC);
2079 DE_ASSERT(offsetSize == OFFSETSIZE_NONE);
2080 return new TextureGatherCubeCase(context, name, description, textureFormat, shadowCompareMode, wrapS, wrapT,
2081 texSwizzle, minFilter, magFilter, baseLevel, flags, textureSize.x());
2082
2083 default:
2084 DE_ASSERT(false);
2085 return DE_NULL;
2086 }
2087 }
2088
2089 } // namespace
2090
TextureGatherTests(Context & context)2091 TextureGatherTests::TextureGatherTests(Context &context) : TestCaseGroup(context, "gather", "textureGather* tests")
2092 {
2093 }
2094
compareModeName(tcu::Sampler::CompareMode mode)2095 static inline const char *compareModeName(tcu::Sampler::CompareMode mode)
2096 {
2097 switch (mode)
2098 {
2099 case tcu::Sampler::COMPAREMODE_LESS:
2100 return "less";
2101 case tcu::Sampler::COMPAREMODE_LESS_OR_EQUAL:
2102 return "less_or_equal";
2103 case tcu::Sampler::COMPAREMODE_GREATER:
2104 return "greater";
2105 case tcu::Sampler::COMPAREMODE_GREATER_OR_EQUAL:
2106 return "greater_or_equal";
2107 case tcu::Sampler::COMPAREMODE_EQUAL:
2108 return "equal";
2109 case tcu::Sampler::COMPAREMODE_NOT_EQUAL:
2110 return "not_equal";
2111 case tcu::Sampler::COMPAREMODE_ALWAYS:
2112 return "always";
2113 case tcu::Sampler::COMPAREMODE_NEVER:
2114 return "never";
2115 default:
2116 DE_ASSERT(false);
2117 return DE_NULL;
2118 }
2119 }
2120
init(void)2121 void TextureGatherTests::init(void)
2122 {
2123 const struct
2124 {
2125 const char *name;
2126 TextureType type;
2127 } textureTypes[] = {{"2d", TEXTURETYPE_2D}, {"2d_array", TEXTURETYPE_2D_ARRAY}, {"cube", TEXTURETYPE_CUBE}};
2128
2129 const struct
2130 {
2131 const char *name;
2132 tcu::TextureFormat format;
2133 } formats[] = {{"rgba8", tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)},
2134 {"rgba8ui", tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8)},
2135 {"rgba8i", tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8)},
2136 {"depth32f", tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT)}};
2137
2138 const struct
2139 {
2140 const char *name;
2141 IVec3 size;
2142 } textureSizes[] = {{"size_pot", IVec3(64, 64, 3)}, {"size_npot", IVec3(17, 23, 3)}};
2143
2144 const struct
2145 {
2146 const char *name;
2147 tcu::Sampler::WrapMode mode;
2148 } wrapModes[] = {{"clamp_to_edge", tcu::Sampler::CLAMP_TO_EDGE},
2149 {"repeat", tcu::Sampler::REPEAT_GL},
2150 {"mirrored_repeat", tcu::Sampler::MIRRORED_REPEAT_GL}};
2151
2152 for (int gatherTypeI = 0; gatherTypeI < GATHERTYPE_LAST; gatherTypeI++)
2153 {
2154 const GatherType gatherType = (GatherType)gatherTypeI;
2155 TestCaseGroup *const gatherTypeGroup =
2156 new TestCaseGroup(m_context, gatherTypeName(gatherType), gatherTypeDescription(gatherType));
2157 addChild(gatherTypeGroup);
2158
2159 for (int offsetSizeI = 0; offsetSizeI < OFFSETSIZE_LAST; offsetSizeI++)
2160 {
2161 const OffsetSize offsetSize = (OffsetSize)offsetSizeI;
2162 if ((gatherType == GATHERTYPE_BASIC) != (offsetSize == OFFSETSIZE_NONE))
2163 continue;
2164
2165 TestCaseGroup *const offsetSizeGroup =
2166 offsetSize == OFFSETSIZE_NONE ?
2167 gatherTypeGroup :
2168 new TestCaseGroup(m_context,
2169 offsetSize == OFFSETSIZE_MINIMUM_REQUIRED ? "min_required_offset" :
2170 offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM ? "implementation_offset" :
2171 DE_NULL,
2172 offsetSize == OFFSETSIZE_MINIMUM_REQUIRED ?
2173 "Use offsets within GL minimum required range" :
2174 offsetSize == OFFSETSIZE_IMPLEMENTATION_MAXIMUM ?
2175 "Use offsets within the implementation range" :
2176 DE_NULL);
2177 if (offsetSizeGroup != gatherTypeGroup)
2178 gatherTypeGroup->addChild(offsetSizeGroup);
2179
2180 for (int textureTypeNdx = 0; textureTypeNdx < DE_LENGTH_OF_ARRAY(textureTypes); textureTypeNdx++)
2181 {
2182 const TextureType textureType = textureTypes[textureTypeNdx].type;
2183
2184 if (textureType == TEXTURETYPE_CUBE && gatherType != GATHERTYPE_BASIC)
2185 continue;
2186
2187 TestCaseGroup *const textureTypeGroup =
2188 new TestCaseGroup(m_context, textureTypes[textureTypeNdx].name, "");
2189 offsetSizeGroup->addChild(textureTypeGroup);
2190
2191 for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
2192 {
2193 const tcu::TextureFormat &format = formats[formatNdx].format;
2194 TestCaseGroup *const formatGroup = new TestCaseGroup(m_context, formats[formatNdx].name, "");
2195 textureTypeGroup->addChild(formatGroup);
2196
2197 for (int noCornersI = 0; noCornersI <= ((textureType == TEXTURETYPE_CUBE) ? 1 : 0); noCornersI++)
2198 {
2199 const bool noCorners = noCornersI != 0;
2200 TestCaseGroup *const cornersGroup =
2201 noCorners ?
2202 new TestCaseGroup(m_context, "no_corners",
2203 "Test case variants that don't sample around cube map corners") :
2204 formatGroup;
2205
2206 if (formatGroup != cornersGroup)
2207 formatGroup->addChild(cornersGroup);
2208
2209 for (int textureSizeNdx = 0; textureSizeNdx < DE_LENGTH_OF_ARRAY(textureSizes);
2210 textureSizeNdx++)
2211 {
2212 const IVec3 &textureSize = textureSizes[textureSizeNdx].size;
2213 TestCaseGroup *const textureSizeGroup =
2214 new TestCaseGroup(m_context, textureSizes[textureSizeNdx].name, "");
2215 cornersGroup->addChild(textureSizeGroup);
2216
2217 for (int compareModeI = 0; compareModeI < tcu::Sampler::COMPAREMODE_LAST; compareModeI++)
2218 {
2219 const tcu::Sampler::CompareMode compareMode = (tcu::Sampler::CompareMode)compareModeI;
2220
2221 if ((compareMode != tcu::Sampler::COMPAREMODE_NONE) != isDepthFormat(format))
2222 continue;
2223
2224 if (compareMode != tcu::Sampler::COMPAREMODE_NONE &&
2225 compareMode != tcu::Sampler::COMPAREMODE_LESS &&
2226 compareMode != tcu::Sampler::COMPAREMODE_GREATER)
2227 continue;
2228
2229 TestCaseGroup *const compareModeGroup =
2230 compareMode == tcu::Sampler::COMPAREMODE_NONE ?
2231 textureSizeGroup :
2232 new TestCaseGroup(
2233 m_context, (string() + "compare_" + compareModeName(compareMode)).c_str(),
2234 "");
2235 if (compareModeGroup != textureSizeGroup)
2236 textureSizeGroup->addChild(compareModeGroup);
2237
2238 for (int wrapCaseNdx = 0; wrapCaseNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapCaseNdx++)
2239 {
2240 const int wrapSNdx = wrapCaseNdx;
2241 const int wrapTNdx = (wrapCaseNdx + 1) % DE_LENGTH_OF_ARRAY(wrapModes);
2242 const tcu::Sampler::WrapMode wrapS = wrapModes[wrapSNdx].mode;
2243 const tcu::Sampler::WrapMode wrapT = wrapModes[wrapTNdx].mode;
2244
2245 const string caseName =
2246 string() + wrapModes[wrapSNdx].name + "_" + wrapModes[wrapTNdx].name;
2247
2248 compareModeGroup->addChild(makeTextureGatherCase(
2249 textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format,
2250 compareMode, wrapS, wrapT, MaybeTextureSwizzle::createNoneTextureSwizzle(),
2251 tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, textureSize,
2252 noCorners ? GATHERCASE_DONT_SAMPLE_CUBE_CORNERS : 0));
2253 }
2254 }
2255 }
2256 }
2257
2258 if (offsetSize !=
2259 OFFSETSIZE_MINIMUM_REQUIRED) // Don't test all features for both offset size types, as they should be rather orthogonal.
2260 {
2261 if (!isDepthFormat(format))
2262 {
2263 TestCaseGroup *const swizzleGroup = new TestCaseGroup(m_context, "texture_swizzle", "");
2264 formatGroup->addChild(swizzleGroup);
2265
2266 DE_STATIC_ASSERT(TEXTURESWIZZLECOMPONENT_R == 0);
2267 for (int swizzleCaseNdx = 0; swizzleCaseNdx < TEXTURESWIZZLECOMPONENT_LAST;
2268 swizzleCaseNdx++)
2269 {
2270 MaybeTextureSwizzle swizzle = MaybeTextureSwizzle::createSomeTextureSwizzle();
2271 string caseName;
2272
2273 for (int i = 0; i < 4; i++)
2274 {
2275 swizzle.getSwizzle()[i] =
2276 (TextureSwizzleComponent)((swizzleCaseNdx + i) %
2277 (int)TEXTURESWIZZLECOMPONENT_LAST);
2278 caseName += (i > 0 ? "_" : "") + de::toLower(de::toString(swizzle.getSwizzle()[i]));
2279 }
2280
2281 swizzleGroup->addChild(makeTextureGatherCase(
2282 textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format,
2283 tcu::Sampler::COMPAREMODE_NONE, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
2284 swizzle, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST, 0, IVec3(64, 64, 3)));
2285 }
2286 }
2287
2288 {
2289 TestCaseGroup *const filterModeGroup =
2290 new TestCaseGroup(m_context, "filter_mode", "Test that filter modes have no effect");
2291 formatGroup->addChild(filterModeGroup);
2292
2293 const struct
2294 {
2295 const char *name;
2296 tcu::Sampler::FilterMode filter;
2297 } magFilters[] = {{"linear", tcu::Sampler::LINEAR}, {"nearest", tcu::Sampler::NEAREST}};
2298
2299 const struct
2300 {
2301 const char *name;
2302 tcu::Sampler::FilterMode filter;
2303 } minFilters[] = {
2304 // \note Don't test NEAREST here, as it's covered by other cases.
2305 {"linear", tcu::Sampler::LINEAR},
2306 {"nearest_mipmap_nearest", tcu::Sampler::NEAREST_MIPMAP_NEAREST},
2307 {"nearest_mipmap_linear", tcu::Sampler::NEAREST_MIPMAP_LINEAR},
2308 {"linear_mipmap_nearest", tcu::Sampler::LINEAR_MIPMAP_NEAREST},
2309 {"linear_mipmap_linear", tcu::Sampler::LINEAR_MIPMAP_LINEAR},
2310 };
2311
2312 for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilters); minFilterNdx++)
2313 for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilters);
2314 magFilterNdx++)
2315 {
2316 const tcu::Sampler::FilterMode minFilter = minFilters[minFilterNdx].filter;
2317 const tcu::Sampler::FilterMode magFilter = magFilters[magFilterNdx].filter;
2318 const tcu::Sampler::CompareMode compareMode = isDepthFormat(format) ?
2319 tcu::Sampler::COMPAREMODE_LESS :
2320 tcu::Sampler::COMPAREMODE_NONE;
2321
2322 if ((isUnormFormatType(format.type) || isDepthFormat(format)) &&
2323 magFilter == tcu::Sampler::NEAREST)
2324 continue; // Covered by other cases.
2325 if ((isUIntFormatType(format.type) || isSIntFormatType(format.type)) &&
2326 (magFilter != tcu::Sampler::NEAREST ||
2327 minFilter != tcu::Sampler::NEAREST_MIPMAP_NEAREST))
2328 continue;
2329
2330 const string caseName = string() + "min_" + minFilters[minFilterNdx].name +
2331 "_mag_" + magFilters[magFilterNdx].name;
2332
2333 filterModeGroup->addChild(makeTextureGatherCase(
2334 textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format,
2335 compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
2336 MaybeTextureSwizzle::createNoneTextureSwizzle(), minFilter, magFilter, 0,
2337 IVec3(64, 64, 3)));
2338 }
2339 }
2340
2341 {
2342 TestCaseGroup *const baseLevelGroup = new TestCaseGroup(m_context, "base_level", "");
2343 formatGroup->addChild(baseLevelGroup);
2344
2345 for (int baseLevel = 1; baseLevel <= 2; baseLevel++)
2346 {
2347 const string caseName = "level_" + de::toString(baseLevel);
2348 const tcu::Sampler::CompareMode compareMode = isDepthFormat(format) ?
2349 tcu::Sampler::COMPAREMODE_LESS :
2350 tcu::Sampler::COMPAREMODE_NONE;
2351 baseLevelGroup->addChild(makeTextureGatherCase(
2352 textureType, m_context, caseName.c_str(), "", gatherType, offsetSize, format,
2353 compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
2354 MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST,
2355 tcu::Sampler::NEAREST, baseLevel, IVec3(64, 64, 3)));
2356 }
2357 }
2358
2359 // What shadow textures should return for incomplete textures is unclear.
2360 // Integer and unsigned integer lookups from incomplete textures return undefined values.
2361 if (!isDepthFormat(format) && !isSIntFormatType(format.type) && !isUIntFormatType(format.type))
2362 {
2363 TestCaseGroup *const incompleteGroup = new TestCaseGroup(
2364 m_context, "incomplete",
2365 "Test that textureGather* takes components from (0,0,0,1) for incomplete textures");
2366 formatGroup->addChild(incompleteGroup);
2367
2368 const tcu::Sampler::CompareMode compareMode =
2369 isDepthFormat(format) ? tcu::Sampler::COMPAREMODE_LESS : tcu::Sampler::COMPAREMODE_NONE;
2370 incompleteGroup->addChild(makeTextureGatherCase(
2371 textureType, m_context, "mipmap_incomplete", "", gatherType, offsetSize, format,
2372 compareMode, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL,
2373 MaybeTextureSwizzle::createNoneTextureSwizzle(), tcu::Sampler::NEAREST_MIPMAP_NEAREST,
2374 tcu::Sampler::NEAREST, 0, IVec3(64, 64, 3), GATHERCASE_MIPMAP_INCOMPLETE));
2375 }
2376 }
2377 }
2378 }
2379 }
2380 }
2381 }
2382
2383 } // namespace Functional
2384 } // namespace gles31
2385 } // namespace deqp
2386