xref: /aosp_15_r20/external/deqp/modules/gles31/functional/es31fShaderImageLoadStoreTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 Shader Image Load & Store Tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fShaderImageLoadStoreTests.hpp"
25 #include "glsTextureTestUtil.hpp"
26 #include "gluContextInfo.hpp"
27 #include "gluRenderContext.hpp"
28 #include "gluShaderProgram.hpp"
29 #include "gluObjectWrapper.hpp"
30 #include "gluPixelTransfer.hpp"
31 #include "gluTextureUtil.hpp"
32 #include "gluStrUtil.hpp"
33 #include "gluCallLogWrapper.hpp"
34 #include "gluProgramInterfaceQuery.hpp"
35 #include "gluDrawUtil.hpp"
36 #include "tcuTestLog.hpp"
37 #include "tcuTextureUtil.hpp"
38 #include "tcuVector.hpp"
39 #include "tcuImageCompare.hpp"
40 #include "tcuFloat.hpp"
41 #include "tcuVectorUtil.hpp"
42 #include "deStringUtil.hpp"
43 #include "deSharedPtr.hpp"
44 #include "deUniquePtr.hpp"
45 #include "deRandom.hpp"
46 #include "deMemory.h"
47 #include "glwFunctions.hpp"
48 #include "glwDefs.hpp"
49 #include "glwEnums.hpp"
50 
51 #include <vector>
52 #include <string>
53 #include <algorithm>
54 #include <map>
55 
56 using de::SharedPtr;
57 using de::toString;
58 using de::UniquePtr;
59 using glu::RenderContext;
60 using tcu::ConstPixelBufferAccess;
61 using tcu::IVec2;
62 using tcu::IVec3;
63 using tcu::IVec4;
64 using tcu::PixelBufferAccess;
65 using tcu::TestLog;
66 using tcu::TextureFormat;
67 using tcu::UVec2;
68 using tcu::UVec3;
69 using tcu::UVec4;
70 using tcu::Vec2;
71 using tcu::Vec3;
72 using tcu::Vec4;
73 
74 using std::string;
75 using std::vector;
76 
77 namespace deqp
78 {
79 
80 using namespace gls::TextureTestUtil;
81 using namespace glu::TextureTestUtil;
82 
83 namespace gles31
84 {
85 namespace Functional
86 {
87 
88 //! Default image sizes used in most test cases.
defaultImageSize(TextureType type)89 static inline IVec3 defaultImageSize(TextureType type)
90 {
91     switch (type)
92     {
93     case TEXTURETYPE_BUFFER:
94         return IVec3(64, 1, 1);
95     case TEXTURETYPE_2D:
96         return IVec3(64, 64, 1);
97     case TEXTURETYPE_CUBE:
98         return IVec3(64, 64, 1);
99     case TEXTURETYPE_3D:
100         return IVec3(64, 64, 8);
101     case TEXTURETYPE_2D_ARRAY:
102         return IVec3(64, 64, 8);
103     default:
104         DE_ASSERT(false);
105         return IVec3(-1);
106     }
107 }
108 
109 template <typename T>
arrayStr(const std::vector<T> (& arr))110 static string arrayStr(const std::vector<T>(&arr))
111 {
112     string result = "{ ";
113     for (size_t i = 0; i < arr.size(); i++)
114         result += (i > 0 ? ", " : "") + toString(arr[i]);
115     result += " }";
116     return result;
117 }
118 
119 template <typename T>
arrayIndexOf(const std::vector<T> (& arr),const T & e)120 static int arrayIndexOf(const std::vector<T>(&arr), const T &e)
121 {
122     return (int)(std::find(arr.begin(), arr.end(), e) - arr.begin());
123 }
124 
getTextureTypeName(TextureType type)125 static const char *getTextureTypeName(TextureType type)
126 {
127     switch (type)
128     {
129     case TEXTURETYPE_BUFFER:
130         return "buffer";
131     case TEXTURETYPE_2D:
132         return "2d";
133     case TEXTURETYPE_CUBE:
134         return "cube";
135     case TEXTURETYPE_3D:
136         return "3d";
137     case TEXTURETYPE_2D_ARRAY:
138         return "2d_array";
139     default:
140         DE_ASSERT(false);
141         return DE_NULL;
142     }
143 }
144 
isFormatTypeUnsignedInteger(TextureFormat::ChannelType type)145 static inline bool isFormatTypeUnsignedInteger(TextureFormat::ChannelType type)
146 {
147     return type == TextureFormat::UNSIGNED_INT8 || type == TextureFormat::UNSIGNED_INT16 ||
148            type == TextureFormat::UNSIGNED_INT32;
149 }
150 
isFormatTypeSignedInteger(TextureFormat::ChannelType type)151 static inline bool isFormatTypeSignedInteger(TextureFormat::ChannelType type)
152 {
153     return type == TextureFormat::SIGNED_INT8 || type == TextureFormat::SIGNED_INT16 ||
154            type == TextureFormat::SIGNED_INT32;
155 }
156 
isFormatTypeInteger(TextureFormat::ChannelType type)157 static inline bool isFormatTypeInteger(TextureFormat::ChannelType type)
158 {
159     return isFormatTypeUnsignedInteger(type) || isFormatTypeSignedInteger(type);
160 }
161 
isFormatTypeUnorm(TextureFormat::ChannelType type)162 static inline bool isFormatTypeUnorm(TextureFormat::ChannelType type)
163 {
164     return type == TextureFormat::UNORM_INT8 || type == TextureFormat::UNORM_INT16 ||
165            type == TextureFormat::UNORM_INT32;
166 }
167 
isFormatTypeSnorm(TextureFormat::ChannelType type)168 static inline bool isFormatTypeSnorm(TextureFormat::ChannelType type)
169 {
170     return type == TextureFormat::SNORM_INT8 || type == TextureFormat::SNORM_INT16 ||
171            type == TextureFormat::SNORM_INT32;
172 }
173 
isFormatSupportedForTextureBuffer(const TextureFormat & format)174 static inline bool isFormatSupportedForTextureBuffer(const TextureFormat &format)
175 {
176     switch (format.order)
177     {
178     case TextureFormat::RGB:
179         return format.type == TextureFormat::FLOAT || format.type == TextureFormat::SIGNED_INT32 ||
180                format.type == TextureFormat::UNSIGNED_INT32;
181 
182     // \note Fallthroughs.
183     case TextureFormat::R:
184     case TextureFormat::RG:
185     case TextureFormat::RGBA:
186         return format.type == TextureFormat::UNORM_INT8 || format.type == TextureFormat::HALF_FLOAT ||
187                format.type == TextureFormat::FLOAT || format.type == TextureFormat::SIGNED_INT8 ||
188                format.type == TextureFormat::SIGNED_INT16 || format.type == TextureFormat::SIGNED_INT32 ||
189                format.type == TextureFormat::UNSIGNED_INT8 || format.type == TextureFormat::UNSIGNED_INT16 ||
190                format.type == TextureFormat::UNSIGNED_INT32;
191 
192     default:
193         return false;
194     }
195 }
196 
getShaderImageFormatQualifier(const TextureFormat & format)197 static inline string getShaderImageFormatQualifier(const TextureFormat &format)
198 {
199     const char *orderPart;
200     const char *typePart;
201 
202     switch (format.order)
203     {
204     case TextureFormat::R:
205         orderPart = "r";
206         break;
207     case TextureFormat::RGBA:
208         orderPart = "rgba";
209         break;
210     default:
211         DE_ASSERT(false);
212         orderPart = DE_NULL;
213     }
214 
215     switch (format.type)
216     {
217     case TextureFormat::FLOAT:
218         typePart = "32f";
219         break;
220     case TextureFormat::HALF_FLOAT:
221         typePart = "16f";
222         break;
223 
224     case TextureFormat::UNSIGNED_INT32:
225         typePart = "32ui";
226         break;
227     case TextureFormat::UNSIGNED_INT16:
228         typePart = "16ui";
229         break;
230     case TextureFormat::UNSIGNED_INT8:
231         typePart = "8ui";
232         break;
233 
234     case TextureFormat::SIGNED_INT32:
235         typePart = "32i";
236         break;
237     case TextureFormat::SIGNED_INT16:
238         typePart = "16i";
239         break;
240     case TextureFormat::SIGNED_INT8:
241         typePart = "8i";
242         break;
243 
244     case TextureFormat::UNORM_INT16:
245         typePart = "16";
246         break;
247     case TextureFormat::UNORM_INT8:
248         typePart = "8";
249         break;
250 
251     case TextureFormat::SNORM_INT16:
252         typePart = "16_snorm";
253         break;
254     case TextureFormat::SNORM_INT8:
255         typePart = "8_snorm";
256         break;
257 
258     default:
259         DE_ASSERT(false);
260         typePart = DE_NULL;
261     }
262 
263     return string() + orderPart + typePart;
264 }
265 
getShaderSamplerOrImageType(TextureFormat::ChannelType formatType,TextureType textureType,bool isSampler)266 static inline string getShaderSamplerOrImageType(TextureFormat::ChannelType formatType, TextureType textureType,
267                                                  bool isSampler)
268 {
269     const char *const formatPart = isFormatTypeUnsignedInteger(formatType) ? "u" :
270                                    isFormatTypeSignedInteger(formatType)   ? "i" :
271                                                                              "";
272 
273     const char *const imageTypePart = textureType == TEXTURETYPE_BUFFER   ? "Buffer" :
274                                       textureType == TEXTURETYPE_2D       ? "2D" :
275                                       textureType == TEXTURETYPE_3D       ? "3D" :
276                                       textureType == TEXTURETYPE_CUBE     ? "Cube" :
277                                       textureType == TEXTURETYPE_2D_ARRAY ? "2DArray" :
278                                                                             DE_NULL;
279 
280     return string() + formatPart + (isSampler ? "sampler" : "image") + imageTypePart;
281 }
282 
getShaderImageType(TextureFormat::ChannelType formatType,TextureType imageType)283 static inline string getShaderImageType(TextureFormat::ChannelType formatType, TextureType imageType)
284 {
285     return getShaderSamplerOrImageType(formatType, imageType, false);
286 }
287 
getShaderSamplerType(TextureFormat::ChannelType formatType,TextureType imageType)288 static inline string getShaderSamplerType(TextureFormat::ChannelType formatType, TextureType imageType)
289 {
290     return getShaderSamplerOrImageType(formatType, imageType, true);
291 }
292 
getGLTextureTarget(TextureType texType)293 static inline uint32_t getGLTextureTarget(TextureType texType)
294 {
295     switch (texType)
296     {
297     case TEXTURETYPE_BUFFER:
298         return GL_TEXTURE_BUFFER;
299     case TEXTURETYPE_2D:
300         return GL_TEXTURE_2D;
301     case TEXTURETYPE_3D:
302         return GL_TEXTURE_3D;
303     case TEXTURETYPE_CUBE:
304         return GL_TEXTURE_CUBE_MAP;
305     case TEXTURETYPE_2D_ARRAY:
306         return GL_TEXTURE_2D_ARRAY;
307     default:
308         DE_ASSERT(false);
309         return (uint32_t)-1;
310     }
311 }
312 
getGLTextureMaxSize(glu::CallLogWrapper glLog,TextureType texType)313 static inline int getGLTextureMaxSize(glu::CallLogWrapper glLog, TextureType texType)
314 {
315     int maxSize;
316     uint32_t macroValue;
317     switch (texType)
318     {
319     case TEXTURETYPE_BUFFER:
320         macroValue = GL_MAX_TEXTURE_BUFFER_SIZE;
321         break;
322     case TEXTURETYPE_3D:
323     case TEXTURETYPE_2D_ARRAY:
324         macroValue = GL_MAX_3D_TEXTURE_SIZE;
325         break;
326     case TEXTURETYPE_2D:
327     case TEXTURETYPE_CUBE:
328         macroValue = GL_MAX_TEXTURE_SIZE;
329         break;
330     default:
331         DE_ASSERT(false);
332         return (uint32_t)-1;
333     }
334 
335     glLog.glGetIntegerv(macroValue, &maxSize);
336 
337     return maxSize;
338 }
339 
cubeFaceToGLFace(tcu::CubeFace face)340 static uint32_t cubeFaceToGLFace(tcu::CubeFace face)
341 {
342     switch (face)
343     {
344     case tcu::CUBEFACE_NEGATIVE_X:
345         return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
346     case tcu::CUBEFACE_POSITIVE_X:
347         return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
348     case tcu::CUBEFACE_NEGATIVE_Y:
349         return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
350     case tcu::CUBEFACE_POSITIVE_Y:
351         return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
352     case tcu::CUBEFACE_NEGATIVE_Z:
353         return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
354     case tcu::CUBEFACE_POSITIVE_Z:
355         return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
356     default:
357         DE_ASSERT(false);
358         return GL_NONE;
359     }
360 }
361 
newOneLevelTexture1D(const tcu::TextureFormat & format,int w)362 static inline tcu::Texture1D *newOneLevelTexture1D(const tcu::TextureFormat &format, int w)
363 {
364     tcu::Texture1D *const res = new tcu::Texture1D(format, w);
365     res->allocLevel(0);
366     return res;
367 }
368 
newOneLevelTexture2D(const tcu::TextureFormat & format,int w,int h)369 static inline tcu::Texture2D *newOneLevelTexture2D(const tcu::TextureFormat &format, int w, int h)
370 {
371     tcu::Texture2D *const res = new tcu::Texture2D(format, w, h);
372     res->allocLevel(0);
373     return res;
374 }
375 
newOneLevelTextureCube(const tcu::TextureFormat & format,int size)376 static inline tcu::TextureCube *newOneLevelTextureCube(const tcu::TextureFormat &format, int size)
377 {
378     tcu::TextureCube *const res = new tcu::TextureCube(format, size);
379     for (int i = 0; i < tcu::CUBEFACE_LAST; i++)
380         res->allocLevel((tcu::CubeFace)i, 0);
381     return res;
382 }
383 
newOneLevelTexture3D(const tcu::TextureFormat & format,int w,int h,int d)384 static inline tcu::Texture3D *newOneLevelTexture3D(const tcu::TextureFormat &format, int w, int h, int d)
385 {
386     tcu::Texture3D *const res = new tcu::Texture3D(format, w, h, d);
387     res->allocLevel(0);
388     return res;
389 }
390 
newOneLevelTexture2DArray(const tcu::TextureFormat & format,int w,int h,int d)391 static inline tcu::Texture2DArray *newOneLevelTexture2DArray(const tcu::TextureFormat &format, int w, int h, int d)
392 {
393     tcu::Texture2DArray *const res = new tcu::Texture2DArray(format, w, h, d);
394     res->allocLevel(0);
395     return res;
396 }
397 
textureLayerType(TextureType entireTextureType)398 static inline TextureType textureLayerType(TextureType entireTextureType)
399 {
400     switch (entireTextureType)
401     {
402     // Single-layer types.
403     // \note Fallthrough.
404     case TEXTURETYPE_BUFFER:
405     case TEXTURETYPE_2D:
406         return entireTextureType;
407 
408     // Multi-layer types with 2d layers.
409     case TEXTURETYPE_3D:
410     case TEXTURETYPE_CUBE:
411     case TEXTURETYPE_2D_ARRAY:
412         return TEXTURETYPE_2D;
413 
414     default:
415         DE_ASSERT(false);
416         return TEXTURETYPE_LAST;
417     }
418 }
419 
420 static const char *const s_texBufExtString = "GL_EXT_texture_buffer";
421 
supportsES32orGL45(const RenderContext & renderContext)422 static bool supportsES32orGL45(const RenderContext &renderContext)
423 {
424     glu::ContextType contextType = renderContext.getType();
425     return glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
426            glu::contextSupports(contextType, glu::ApiType::core(4, 5));
427 }
428 
checkTextureTypeExtensions(const glu::ContextInfo & contextInfo,TextureType type,const RenderContext & renderContext)429 static inline void checkTextureTypeExtensions(const glu::ContextInfo &contextInfo, TextureType type,
430                                               const RenderContext &renderContext)
431 {
432     if (type == TEXTURETYPE_BUFFER && !contextInfo.isExtensionSupported(s_texBufExtString) &&
433         !supportsES32orGL45(renderContext))
434         throw tcu::NotSupportedError("Test requires " + string(s_texBufExtString) + " extension");
435 }
436 
textureTypeExtensionShaderRequires(TextureType type,const RenderContext & renderContext)437 static inline string textureTypeExtensionShaderRequires(TextureType type, const RenderContext &renderContext)
438 {
439     if (!supportsES32orGL45(renderContext) && (type == TEXTURETYPE_BUFFER))
440         return "#extension " + string(s_texBufExtString) + " : require\n";
441     else
442         return "";
443 }
444 
445 static const char *const s_imageAtomicExtString = "GL_OES_shader_image_atomic";
446 
imageAtomicExtensionShaderRequires(const RenderContext & renderContext)447 static inline string imageAtomicExtensionShaderRequires(const RenderContext &renderContext)
448 {
449     if (!supportsES32orGL45(renderContext))
450         return "#extension " + string(s_imageAtomicExtString) + " : require\n";
451     else
452         return "";
453 }
454 
455 namespace
456 {
457 
458 enum AtomicOperation
459 {
460     ATOMIC_OPERATION_ADD = 0,
461     ATOMIC_OPERATION_MIN,
462     ATOMIC_OPERATION_MAX,
463     ATOMIC_OPERATION_AND,
464     ATOMIC_OPERATION_OR,
465     ATOMIC_OPERATION_XOR,
466     ATOMIC_OPERATION_EXCHANGE,
467     ATOMIC_OPERATION_COMP_SWAP,
468 
469     ATOMIC_OPERATION_LAST
470 };
471 
472 //! An order-independent operation is one for which the end result doesn't depend on the order in which the operations are carried (i.e. is both commutative and associative).
isOrderIndependentAtomicOperation(AtomicOperation op)473 static bool isOrderIndependentAtomicOperation(AtomicOperation op)
474 {
475     return op == ATOMIC_OPERATION_ADD || op == ATOMIC_OPERATION_MIN || op == ATOMIC_OPERATION_MAX ||
476            op == ATOMIC_OPERATION_AND || op == ATOMIC_OPERATION_OR || op == ATOMIC_OPERATION_XOR;
477 }
478 
479 //! Computes the result of an atomic operation where "a" is the data operated on and "b" is the parameter to the atomic function.
computeBinaryAtomicOperationResult(AtomicOperation op,int a,int b)480 int computeBinaryAtomicOperationResult(AtomicOperation op, int a, int b)
481 {
482     switch (op)
483     {
484     case ATOMIC_OPERATION_ADD:
485         return a + b;
486     case ATOMIC_OPERATION_MIN:
487         return de::min(a, b);
488     case ATOMIC_OPERATION_MAX:
489         return de::max(a, b);
490     case ATOMIC_OPERATION_AND:
491         return a & b;
492     case ATOMIC_OPERATION_OR:
493         return a | b;
494     case ATOMIC_OPERATION_XOR:
495         return a ^ b;
496     case ATOMIC_OPERATION_EXCHANGE:
497         return b;
498     default:
499         DE_ASSERT(false);
500         return -1;
501     }
502 }
503 
504 //! \note For floats, only the exchange operation is supported.
computeBinaryAtomicOperationResult(AtomicOperation op,float,float b)505 float computeBinaryAtomicOperationResult(AtomicOperation op, float /*a*/, float b)
506 {
507     switch (op)
508     {
509     case ATOMIC_OPERATION_EXCHANGE:
510         return b;
511     default:
512         DE_ASSERT(false);
513         return -1;
514     }
515 }
516 
getAtomicOperationCaseName(AtomicOperation op)517 static const char *getAtomicOperationCaseName(AtomicOperation op)
518 {
519     switch (op)
520     {
521     case ATOMIC_OPERATION_ADD:
522         return "add";
523     case ATOMIC_OPERATION_MIN:
524         return "min";
525     case ATOMIC_OPERATION_MAX:
526         return "max";
527     case ATOMIC_OPERATION_AND:
528         return "and";
529     case ATOMIC_OPERATION_OR:
530         return "or";
531     case ATOMIC_OPERATION_XOR:
532         return "xor";
533     case ATOMIC_OPERATION_EXCHANGE:
534         return "exchange";
535     case ATOMIC_OPERATION_COMP_SWAP:
536         return "comp_swap";
537     default:
538         DE_ASSERT(false);
539         return DE_NULL;
540     }
541 }
542 
getAtomicOperationShaderFuncName(AtomicOperation op)543 static const char *getAtomicOperationShaderFuncName(AtomicOperation op)
544 {
545     switch (op)
546     {
547     case ATOMIC_OPERATION_ADD:
548         return "imageAtomicAdd";
549     case ATOMIC_OPERATION_MIN:
550         return "imageAtomicMin";
551     case ATOMIC_OPERATION_MAX:
552         return "imageAtomicMax";
553     case ATOMIC_OPERATION_AND:
554         return "imageAtomicAnd";
555     case ATOMIC_OPERATION_OR:
556         return "imageAtomicOr";
557     case ATOMIC_OPERATION_XOR:
558         return "imageAtomicXor";
559     case ATOMIC_OPERATION_EXCHANGE:
560         return "imageAtomicExchange";
561     case ATOMIC_OPERATION_COMP_SWAP:
562         return "imageAtomicCompSwap";
563     default:
564         DE_ASSERT(false);
565         return DE_NULL;
566     }
567 }
568 
569 //! In GLSL, when accessing cube images, the z coordinate is mapped to a cube face.
570 //! \note This is _not_ the same as casting the z to a tcu::CubeFace.
glslImageFuncZToCubeFace(int z)571 static inline tcu::CubeFace glslImageFuncZToCubeFace(int z)
572 {
573     static const tcu::CubeFace faces[6] = {tcu::CUBEFACE_POSITIVE_X, tcu::CUBEFACE_NEGATIVE_X,
574                                            tcu::CUBEFACE_POSITIVE_Y, tcu::CUBEFACE_NEGATIVE_Y,
575                                            tcu::CUBEFACE_POSITIVE_Z, tcu::CUBEFACE_NEGATIVE_Z};
576 
577     DE_ASSERT(de::inBounds(z, 0, DE_LENGTH_OF_ARRAY(faces)));
578     return faces[z];
579 }
580 
581 class BufferMemMap
582 {
583 public:
BufferMemMap(const glw::Functions & gl,uint32_t target,int offset,int size,uint32_t access)584     BufferMemMap(const glw::Functions &gl, uint32_t target, int offset, int size, uint32_t access)
585         : m_gl(gl)
586         , m_target(target)
587         , m_ptr(DE_NULL)
588     {
589         m_ptr = gl.mapBufferRange(target, offset, size, access);
590         GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange()");
591         TCU_CHECK(m_ptr);
592     }
593 
~BufferMemMap(void)594     ~BufferMemMap(void)
595     {
596         m_gl.unmapBuffer(m_target);
597     }
598 
getPtr(void) const599     void *getPtr(void) const
600     {
601         return m_ptr;
602     }
operator *(void) const603     void *operator*(void) const
604     {
605         return m_ptr;
606     }
607 
608 private:
609     BufferMemMap(const BufferMemMap &other);
610     BufferMemMap &operator=(const BufferMemMap &other);
611 
612     const glw::Functions &m_gl;
613     const uint32_t m_target;
614     void *m_ptr;
615 };
616 
617 //! Utility for more readable uniform assignment logging; logs the name of the uniform when assigning. Handles the locations, querying them the first time they're assigned
618 //  \note Assumes that the appropriate program is in use when assigning uniforms.
619 class UniformAccessLogger
620 {
621 public:
UniformAccessLogger(const glw::Functions & gl,TestLog & log,uint32_t programGL)622     UniformAccessLogger(const glw::Functions &gl, TestLog &log, uint32_t programGL)
623         : m_gl(gl)
624         , m_log(log)
625         , m_programGL(programGL)
626     {
627     }
628 
629     void assign1i(const string &name, int x);
630     void assign3f(const string &name, float x, float y, float z);
631 
632 private:
633     int getLocation(const string &name);
634 
635     const glw::Functions &m_gl;
636     TestLog &m_log;
637     const uint32_t m_programGL;
638 
639     std::map<string, int> m_uniformLocations;
640 };
641 
getLocation(const string & name)642 int UniformAccessLogger::getLocation(const string &name)
643 {
644     if (m_uniformLocations.find(name) == m_uniformLocations.end())
645     {
646         const int loc = m_gl.getUniformLocation(m_programGL, name.c_str());
647         TCU_CHECK(loc != -1);
648         m_uniformLocations[name] = loc;
649     }
650     return m_uniformLocations[name];
651 }
652 
assign1i(const string & name,int x)653 void UniformAccessLogger::assign1i(const string &name, int x)
654 {
655     const int loc = getLocation(name);
656     m_log << TestLog::Message << "// Assigning to uniform " << name << ": " << x << TestLog::EndMessage;
657     m_gl.uniform1i(loc, x);
658 }
659 
assign3f(const string & name,float x,float y,float z)660 void UniformAccessLogger::assign3f(const string &name, float x, float y, float z)
661 {
662     const int loc = getLocation(name);
663     m_log << TestLog::Message << "// Assigning to uniform " << name << ": " << Vec3(x, y, z) << TestLog::EndMessage;
664     m_gl.uniform3f(loc, x, y, z);
665 }
666 
667 //! Class containing a (single-level) texture of a given type. Supports accessing pixels with coordinate convention similar to that in imageStore() and imageLoad() in shaders; useful especially for cube maps.
668 class LayeredImage
669 {
670 public:
671     LayeredImage(TextureType type, const TextureFormat &format, int w, int h, int d);
672 
getImageType(void) const673     TextureType getImageType(void) const
674     {
675         return m_type;
676     }
getSize(void) const677     const IVec3 &getSize(void) const
678     {
679         return m_size;
680     }
getFormat(void) const681     const TextureFormat &getFormat(void) const
682     {
683         return m_format;
684     }
685 
686     // \note For cube maps, set/getPixel's z parameter specifies the cube face in the same manner as in imageStore/imageLoad in GL shaders (see glslImageFuncZToCubeFace), instead of directly as a tcu::CubeFace.
687 
688     template <typename ColorT>
689     void setPixel(int x, int y, int z, const ColorT &color) const;
690 
691     Vec4 getPixel(int x, int y, int z) const;
692     IVec4 getPixelInt(int x, int y, int z) const;
getPixelUint(int x,int y,int z) const693     UVec4 getPixelUint(int x, int y, int z) const
694     {
695         return getPixelInt(x, y, z).asUint();
696     }
697 
getAccess(void)698     PixelBufferAccess getAccess(void)
699     {
700         return getAccessInternal();
701     }
getSliceAccess(int slice)702     PixelBufferAccess getSliceAccess(int slice)
703     {
704         return getSliceAccessInternal(slice);
705     }
getCubeFaceAccess(tcu::CubeFace face)706     PixelBufferAccess getCubeFaceAccess(tcu::CubeFace face)
707     {
708         return getCubeFaceAccessInternal(face);
709     }
710 
getAccess(void) const711     ConstPixelBufferAccess getAccess(void) const
712     {
713         return getAccessInternal();
714     }
getSliceAccess(int slice) const715     ConstPixelBufferAccess getSliceAccess(int slice) const
716     {
717         return getSliceAccessInternal(slice);
718     }
getCubeFaceAccess(tcu::CubeFace face) const719     ConstPixelBufferAccess getCubeFaceAccess(tcu::CubeFace face) const
720     {
721         return getCubeFaceAccessInternal(face);
722     }
723 
724 private:
725     LayeredImage(const LayeredImage &);
726     LayeredImage &operator=(const LayeredImage &);
727 
728     // Some helpers to reduce code duplication between const/non-const versions of getAccess and others.
729     PixelBufferAccess getAccessInternal(void) const;
730     PixelBufferAccess getSliceAccessInternal(int slice) const;
731     PixelBufferAccess getCubeFaceAccessInternal(tcu::CubeFace face) const;
732 
733     const TextureType m_type;
734     const IVec3 m_size;
735     const TextureFormat m_format;
736 
737     // \note Depending on m_type, exactly one of the following will contain non-null.
738     const SharedPtr<tcu::Texture1D> m_texBuffer;
739     const SharedPtr<tcu::Texture2D> m_tex2D;
740     const SharedPtr<tcu::TextureCube> m_texCube;
741     const SharedPtr<tcu::Texture3D> m_tex3D;
742     const SharedPtr<tcu::Texture2DArray> m_tex2DArray;
743 };
744 
LayeredImage(TextureType type,const TextureFormat & format,int w,int h,int d)745 LayeredImage::LayeredImage(TextureType type, const TextureFormat &format, int w, int h, int d)
746     : m_type(type)
747     , m_size(w, h, d)
748     , m_format(format)
749     , m_texBuffer(type == TEXTURETYPE_BUFFER ? SharedPtr<tcu::Texture1D>(newOneLevelTexture1D(format, w)) :
750                                                SharedPtr<tcu::Texture1D>())
751     , m_tex2D(type == TEXTURETYPE_2D ? SharedPtr<tcu::Texture2D>(newOneLevelTexture2D(format, w, h)) :
752                                        SharedPtr<tcu::Texture2D>())
753     , m_texCube(type == TEXTURETYPE_CUBE ? SharedPtr<tcu::TextureCube>(newOneLevelTextureCube(format, w)) :
754                                            SharedPtr<tcu::TextureCube>())
755     , m_tex3D(type == TEXTURETYPE_3D ? SharedPtr<tcu::Texture3D>(newOneLevelTexture3D(format, w, h, d)) :
756                                        SharedPtr<tcu::Texture3D>())
757     , m_tex2DArray(type == TEXTURETYPE_2D_ARRAY ?
758                        SharedPtr<tcu::Texture2DArray>(newOneLevelTexture2DArray(format, w, h, d)) :
759                        SharedPtr<tcu::Texture2DArray>())
760 {
761     DE_ASSERT(m_size.z() == 1 || m_type == TEXTURETYPE_3D || m_type == TEXTURETYPE_2D_ARRAY);
762 
763     DE_ASSERT(m_size.y() == 1 || m_type == TEXTURETYPE_2D || m_type == TEXTURETYPE_CUBE || m_type == TEXTURETYPE_3D ||
764               m_type == TEXTURETYPE_2D_ARRAY);
765 
766     DE_ASSERT(w == h || type != TEXTURETYPE_CUBE);
767 
768     DE_ASSERT(m_texBuffer != DE_NULL || m_tex2D != DE_NULL || m_texCube != DE_NULL || m_tex3D != DE_NULL ||
769               m_tex2DArray != DE_NULL);
770 }
771 
772 template <typename ColorT>
setPixel(int x,int y,int z,const ColorT & color) const773 void LayeredImage::setPixel(int x, int y, int z, const ColorT &color) const
774 {
775     const PixelBufferAccess access =
776         m_type == TEXTURETYPE_BUFFER   ? m_texBuffer->getLevel(0) :
777         m_type == TEXTURETYPE_2D       ? m_tex2D->getLevel(0) :
778         m_type == TEXTURETYPE_CUBE     ? m_texCube->getLevelFace(0, glslImageFuncZToCubeFace(z)) :
779         m_type == TEXTURETYPE_3D       ? m_tex3D->getLevel(0) :
780         m_type == TEXTURETYPE_2D_ARRAY ? m_tex2DArray->getLevel(0) :
781                                          PixelBufferAccess();
782 
783     access.setPixel(color, x, y, m_type == TEXTURETYPE_CUBE ? 0 : z);
784 }
785 
getPixel(int x,int y,int z) const786 Vec4 LayeredImage::getPixel(int x, int y, int z) const
787 {
788     const ConstPixelBufferAccess access =
789         m_type == TEXTURETYPE_CUBE ? getCubeFaceAccess(glslImageFuncZToCubeFace(z)) : getAccess();
790     return access.getPixel(x, y, m_type == TEXTURETYPE_CUBE ? 0 : z);
791 }
792 
getPixelInt(int x,int y,int z) const793 IVec4 LayeredImage::getPixelInt(int x, int y, int z) const
794 {
795     const ConstPixelBufferAccess access =
796         m_type == TEXTURETYPE_CUBE ? getCubeFaceAccess(glslImageFuncZToCubeFace(z)) : getAccess();
797     return access.getPixelInt(x, y, m_type == TEXTURETYPE_CUBE ? 0 : z);
798 }
799 
getAccessInternal(void) const800 PixelBufferAccess LayeredImage::getAccessInternal(void) const
801 {
802     DE_ASSERT(m_type == TEXTURETYPE_BUFFER || m_type == TEXTURETYPE_2D || m_type == TEXTURETYPE_3D ||
803               m_type == TEXTURETYPE_2D_ARRAY);
804 
805     return m_type == TEXTURETYPE_BUFFER   ? m_texBuffer->getLevel(0) :
806            m_type == TEXTURETYPE_2D       ? m_tex2D->getLevel(0) :
807            m_type == TEXTURETYPE_3D       ? m_tex3D->getLevel(0) :
808            m_type == TEXTURETYPE_2D_ARRAY ? m_tex2DArray->getLevel(0) :
809                                             PixelBufferAccess();
810 }
811 
getSliceAccessInternal(int slice) const812 PixelBufferAccess LayeredImage::getSliceAccessInternal(int slice) const
813 {
814     const PixelBufferAccess srcAccess = getAccessInternal();
815     return tcu::getSubregion(srcAccess, 0, 0, slice, srcAccess.getWidth(), srcAccess.getHeight(), 1);
816 }
817 
getCubeFaceAccessInternal(tcu::CubeFace face) const818 PixelBufferAccess LayeredImage::getCubeFaceAccessInternal(tcu::CubeFace face) const
819 {
820     DE_ASSERT(m_type == TEXTURETYPE_CUBE);
821     return m_texCube->getLevelFace(0, face);
822 }
823 
824 //! Set texture storage or, if using buffer texture, setup buffer and attach to texture.
setTextureStorage(glu::CallLogWrapper & glLog,TextureType imageType,uint32_t internalFormat,const IVec3 & imageSize,uint32_t textureBufGL)825 static void setTextureStorage(glu::CallLogWrapper &glLog, TextureType imageType, uint32_t internalFormat,
826                               const IVec3 &imageSize, uint32_t textureBufGL)
827 {
828     const uint32_t textureTarget = getGLTextureTarget(imageType);
829 
830     switch (imageType)
831     {
832     case TEXTURETYPE_BUFFER:
833     {
834         const TextureFormat format = glu::mapGLInternalFormat(internalFormat);
835         const int numBytes         = format.getPixelSize() * imageSize.x();
836         DE_ASSERT(isFormatSupportedForTextureBuffer(format));
837         glLog.glBindBuffer(GL_TEXTURE_BUFFER, textureBufGL);
838         glLog.glBufferData(GL_TEXTURE_BUFFER, numBytes, DE_NULL, GL_STATIC_DRAW);
839         glLog.glTexBuffer(GL_TEXTURE_BUFFER, internalFormat, textureBufGL);
840         DE_ASSERT(imageSize.y() == 1 && imageSize.z() == 1);
841         break;
842     }
843 
844         // \note Fall-throughs.
845 
846     case TEXTURETYPE_2D:
847     case TEXTURETYPE_CUBE:
848         glLog.glTexStorage2D(textureTarget, 1, internalFormat, imageSize.x(), imageSize.y());
849         DE_ASSERT(imageSize.z() == 1);
850         break;
851 
852     case TEXTURETYPE_3D:
853     case TEXTURETYPE_2D_ARRAY:
854         glLog.glTexStorage3D(textureTarget, 1, internalFormat, imageSize.x(), imageSize.y(), imageSize.z());
855         break;
856 
857     default:
858         DE_ASSERT(false);
859     }
860 }
861 
uploadTexture(glu::CallLogWrapper & glLog,const LayeredImage & src,uint32_t textureBufGL)862 static void uploadTexture(glu::CallLogWrapper &glLog, const LayeredImage &src, uint32_t textureBufGL)
863 {
864     const uint32_t internalFormat            = glu::getInternalFormat(src.getFormat());
865     const glu::TransferFormat transferFormat = glu::getTransferFormat(src.getFormat());
866     const IVec3 &imageSize                   = src.getSize();
867 
868     setTextureStorage(glLog, src.getImageType(), internalFormat, imageSize, textureBufGL);
869 
870     {
871         const int pixelSize = src.getFormat().getPixelSize();
872         int unpackAlignment;
873 
874         if (deIsPowerOfTwo32(pixelSize))
875             unpackAlignment = 8;
876         else
877             unpackAlignment = 1;
878 
879         glLog.glPixelStorei(GL_UNPACK_ALIGNMENT, unpackAlignment);
880     }
881 
882     if (src.getImageType() == TEXTURETYPE_BUFFER)
883     {
884         glLog.glBindBuffer(GL_TEXTURE_BUFFER, textureBufGL);
885         glLog.glBufferData(GL_TEXTURE_BUFFER, src.getFormat().getPixelSize() * imageSize.x(),
886                            src.getAccess().getDataPtr(), GL_STATIC_DRAW);
887     }
888     else if (src.getImageType() == TEXTURETYPE_2D)
889         glLog.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imageSize.x(), imageSize.y(), transferFormat.format,
890                               transferFormat.dataType, src.getAccess().getDataPtr());
891     else if (src.getImageType() == TEXTURETYPE_CUBE)
892     {
893         for (int faceI = 0; faceI < tcu::CUBEFACE_LAST; faceI++)
894         {
895             const tcu::CubeFace face = (tcu::CubeFace)faceI;
896             glLog.glTexSubImage2D(cubeFaceToGLFace(face), 0, 0, 0, imageSize.x(), imageSize.y(), transferFormat.format,
897                                   transferFormat.dataType, src.getCubeFaceAccess(face).getDataPtr());
898         }
899     }
900     else
901     {
902         DE_ASSERT(src.getImageType() == TEXTURETYPE_3D || src.getImageType() == TEXTURETYPE_2D_ARRAY);
903         const uint32_t textureTarget = getGLTextureTarget(src.getImageType());
904         glLog.glTexSubImage3D(textureTarget, 0, 0, 0, 0, imageSize.x(), imageSize.y(), imageSize.z(),
905                               transferFormat.format, transferFormat.dataType, src.getAccess().getDataPtr());
906     }
907 }
908 
readPixelsRGBAInteger32(const PixelBufferAccess & dst,int originX,int originY,glu::CallLogWrapper & glLog)909 static void readPixelsRGBAInteger32(const PixelBufferAccess &dst, int originX, int originY, glu::CallLogWrapper &glLog)
910 {
911     DE_ASSERT(dst.getDepth() == 1);
912 
913     if (isFormatTypeUnsignedInteger(dst.getFormat().type))
914     {
915         vector<UVec4> data(dst.getWidth() * dst.getHeight());
916 
917         glLog.glReadPixels(originX, originY, dst.getWidth(), dst.getHeight(), GL_RGBA_INTEGER, GL_UNSIGNED_INT,
918                            &data[0]);
919 
920         for (int y = 0; y < dst.getHeight(); y++)
921             for (int x = 0; x < dst.getWidth(); x++)
922                 dst.setPixel(data[y * dst.getWidth() + x], x, y);
923     }
924     else if (isFormatTypeSignedInteger(dst.getFormat().type))
925     {
926         vector<IVec4> data(dst.getWidth() * dst.getHeight());
927 
928         glLog.glReadPixels(originX, originY, dst.getWidth(), dst.getHeight(), GL_RGBA_INTEGER, GL_INT, &data[0]);
929 
930         for (int y = 0; y < dst.getHeight(); y++)
931             for (int x = 0; x < dst.getWidth(); x++)
932                 dst.setPixel(data[y * dst.getWidth() + x], x, y);
933     }
934     else
935         DE_ASSERT(false);
936 }
937 
938 //! Base for a functor for verifying and logging a 2d texture layer (2d image, cube face, 3d slice, 2d layer).
939 class ImageLayerVerifier
940 {
941 public:
942     virtual bool operator()(TestLog &, const ConstPixelBufferAccess &, int sliceOrFaceNdx) const = 0;
~ImageLayerVerifier(void)943     virtual ~ImageLayerVerifier(void)
944     {
945     }
946 };
947 
setTexParameteri(glu::CallLogWrapper & glLog,uint32_t target)948 static void setTexParameteri(glu::CallLogWrapper &glLog, uint32_t target)
949 {
950     if (target != GL_TEXTURE_BUFFER)
951     {
952         glLog.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
953         glLog.glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
954     }
955 }
956 
957 //! Binds texture (one layer at a time) to color attachment of FBO and does glReadPixels(). Calls the verifier for each layer.
958 //! \note Not for buffer textures.
readIntegerTextureViaFBOAndVerify(const RenderContext & renderCtx,glu::CallLogWrapper & glLog,uint32_t textureGL,TextureType textureType,const TextureFormat & textureFormat,const IVec3 & textureSize,const ImageLayerVerifier & verifyLayer)959 static bool readIntegerTextureViaFBOAndVerify(const RenderContext &renderCtx, glu::CallLogWrapper &glLog,
960                                               uint32_t textureGL, TextureType textureType,
961                                               const TextureFormat &textureFormat, const IVec3 &textureSize,
962                                               const ImageLayerVerifier &verifyLayer)
963 {
964     DE_ASSERT(isFormatTypeInteger(textureFormat.type));
965     DE_ASSERT(textureType != TEXTURETYPE_BUFFER);
966 
967     TestLog &log = glLog.getLog();
968 
969     const tcu::ScopedLogSection section(
970         log, "Verification", "Result verification (bind texture layer-by-layer to FBO, read with glReadPixels())");
971 
972     const int numSlicesOrFaces     = textureType == TEXTURETYPE_CUBE ? 6 : textureSize.z();
973     const uint32_t textureTargetGL = getGLTextureTarget(textureType);
974     glu::Framebuffer fbo(renderCtx);
975     tcu::TextureLevel resultSlice(textureFormat, textureSize.x(), textureSize.y());
976 
977     glLog.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
978     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "Bind FBO");
979 
980     glLog.glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
981     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glMemoryBarrier");
982 
983     glLog.glActiveTexture(GL_TEXTURE0);
984     glLog.glBindTexture(textureTargetGL, textureGL);
985     setTexParameteri(glLog, textureTargetGL);
986 
987     for (int sliceOrFaceNdx = 0; sliceOrFaceNdx < numSlicesOrFaces; sliceOrFaceNdx++)
988     {
989         if (textureType == TEXTURETYPE_CUBE)
990             glLog.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
991                                          cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx)), textureGL, 0);
992         else if (textureType == TEXTURETYPE_2D)
993             glLog.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureGL, 0);
994         else if (textureType == TEXTURETYPE_3D || textureType == TEXTURETYPE_2D_ARRAY)
995             glLog.glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureGL, 0, sliceOrFaceNdx);
996         else
997             DE_ASSERT(false);
998 
999         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "Bind texture to framebuffer color attachment 0");
1000 
1001         TCU_CHECK(glLog.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
1002 
1003         readPixelsRGBAInteger32(resultSlice.getAccess(), 0, 0, glLog);
1004         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glReadPixels");
1005 
1006         if (!verifyLayer(log, resultSlice, sliceOrFaceNdx))
1007             return false;
1008     }
1009 
1010     return true;
1011 }
1012 
1013 //! Reads texture with texture() in compute shader, one layer at a time, putting values into a SSBO and reading with a mapping. Calls the verifier for each layer.
1014 //! \note Not for buffer textures.
readFloatOrNormTextureWithLookupsAndVerify(const RenderContext & renderCtx,glu::CallLogWrapper & glLog,uint32_t textureGL,TextureType textureType,const TextureFormat & textureFormat,const IVec3 & textureSize,const ImageLayerVerifier & verifyLayer)1015 static bool readFloatOrNormTextureWithLookupsAndVerify(const RenderContext &renderCtx, glu::CallLogWrapper &glLog,
1016                                                        uint32_t textureGL, TextureType textureType,
1017                                                        const TextureFormat &textureFormat, const IVec3 &textureSize,
1018                                                        const ImageLayerVerifier &verifyLayer)
1019 {
1020     DE_ASSERT(!isFormatTypeInteger(textureFormat.type));
1021     DE_ASSERT(textureType != TEXTURETYPE_BUFFER);
1022 
1023     TestLog &log = glLog.getLog();
1024 
1025     const tcu::ScopedLogSection section(
1026         log, "Verification",
1027         "Result verification (read texture layer-by-layer in compute shader with texture() into SSBO)");
1028     const std::string glslVersionDeclaration =
1029         getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
1030 
1031     const glu::ShaderProgram program(renderCtx,
1032                                      glu::ProgramSources() << glu::ComputeSource(
1033                                          glslVersionDeclaration +
1034                                          "\n"
1035                                          "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
1036                                          "layout (binding = 0) buffer Output\n"
1037                                          "{\n"
1038                                          "    vec4 color[" +
1039                                          toString(textureSize.x() * textureSize.y()) +
1040                                          "];\n"
1041                                          "} sb_out;\n"
1042                                          "\n"
1043                                          "precision highp " +
1044                                          getShaderSamplerType(textureFormat.type, textureType) +
1045                                          ";\n"
1046                                          "\n"
1047                                          "uniform highp " +
1048                                          getShaderSamplerType(textureFormat.type, textureType) +
1049                                          " u_texture;\n"
1050                                          "uniform highp vec3 u_texCoordLD;\n"
1051                                          "uniform highp vec3 u_texCoordRD;\n"
1052                                          "uniform highp vec3 u_texCoordLU;\n"
1053                                          "uniform highp vec3 u_texCoordRU;\n"
1054                                          "\n"
1055                                          "void main (void)\n"
1056                                          "{\n"
1057                                          "    int gx = int(gl_GlobalInvocationID.x);\n"
1058                                          "    int gy = int(gl_GlobalInvocationID.y);\n"
1059                                          "    highp float s = (float(gx) + 0.5) / float(" +
1060                                          toString(textureSize.x()) +
1061                                          ");\n"
1062                                          "    highp float t = (float(gy) + 0.5) / float(" +
1063                                          toString(textureType == TEXTURETYPE_CUBE ? textureSize.x() : textureSize.y()) +
1064                                          ");\n"
1065                                          "    highp vec3 texCoord = u_texCoordLD*(1.0-s)*(1.0-t)\n"
1066                                          "                        + u_texCoordRD*(    s)*(1.0-t)\n"
1067                                          "                        + u_texCoordLU*(1.0-s)*(    t)\n"
1068                                          "                        + u_texCoordRU*(    s)*(    t);\n"
1069                                          "    int ndx = gy*" +
1070                                          toString(textureSize.x()) +
1071                                          " + gx;\n"
1072                                          "    sb_out.color[ndx] = texture(u_texture, texCoord" +
1073                                          (textureType == TEXTURETYPE_2D ? ".xy" : "") +
1074                                          ");\n"
1075                                          "}\n"));
1076 
1077     glLog.glUseProgram(program.getProgram());
1078 
1079     log << program;
1080 
1081     if (!program.isOk())
1082     {
1083         log << TestLog::Message << "// Failure: failed to compile program" << TestLog::EndMessage;
1084         TCU_FAIL("Program compilation failed");
1085     }
1086 
1087     {
1088         const uint32_t textureTargetGL = getGLTextureTarget(textureType);
1089         const glu::Buffer outputBuffer(renderCtx);
1090         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
1091 
1092         // Setup texture.
1093 
1094         glLog.glActiveTexture(GL_TEXTURE0);
1095         glLog.glBindTexture(textureTargetGL, textureGL);
1096         setTexParameteri(glLog, textureTargetGL);
1097 
1098         uniforms.assign1i("u_texture", 0);
1099 
1100         // Setup output buffer.
1101         {
1102             const uint32_t blockIndex =
1103                 glLog.glGetProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
1104             const int blockSize = glu::getProgramResourceInt(renderCtx.getFunctions(), program.getProgram(),
1105                                                              GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
1106 
1107             log << TestLog::Message << "// Got buffer data size = " << blockSize << TestLog::EndMessage;
1108             TCU_CHECK(blockSize > 0);
1109 
1110             glLog.glBindBuffer(GL_SHADER_STORAGE_BUFFER, *outputBuffer);
1111             glLog.glBufferData(GL_SHADER_STORAGE_BUFFER, blockSize, DE_NULL, GL_STREAM_READ);
1112             glLog.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, *outputBuffer);
1113             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "SSB setup failed");
1114         }
1115 
1116         // Dispatch one layer at a time, read back and verify.
1117         {
1118             const int numSlicesOrFaces = textureType == TEXTURETYPE_CUBE ? 6 : textureSize.z();
1119             tcu::TextureLevel resultSlice(textureFormat, textureSize.x(), textureSize.y());
1120             const PixelBufferAccess resultSliceAccess = resultSlice.getAccess();
1121             const uint32_t blockIndex =
1122                 glLog.glGetProgramResourceIndex(program.getProgram(), GL_SHADER_STORAGE_BLOCK, "Output");
1123             const int blockSize = glu::getProgramResourceInt(renderCtx.getFunctions(), program.getProgram(),
1124                                                              GL_SHADER_STORAGE_BLOCK, blockIndex, GL_BUFFER_DATA_SIZE);
1125             const uint32_t valueIndex =
1126                 glLog.glGetProgramResourceIndex(program.getProgram(), GL_BUFFER_VARIABLE, "Output.color");
1127             const glu::InterfaceVariableInfo valueInfo = glu::getProgramInterfaceVariableInfo(
1128                 renderCtx.getFunctions(), program.getProgram(), GL_BUFFER_VARIABLE, valueIndex);
1129 
1130             TCU_CHECK(valueInfo.arraySize == (uint32_t)(textureSize.x() * textureSize.y()));
1131 
1132             glLog.glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
1133 
1134             for (int sliceOrFaceNdx = 0; sliceOrFaceNdx < numSlicesOrFaces; sliceOrFaceNdx++)
1135             {
1136                 if (textureType == TEXTURETYPE_CUBE)
1137                 {
1138                     vector<float> coords;
1139                     computeQuadTexCoordCube(coords, glslImageFuncZToCubeFace(sliceOrFaceNdx));
1140                     uniforms.assign3f("u_texCoordLD", coords[3 * 0 + 0], coords[3 * 0 + 1], coords[3 * 0 + 2]);
1141                     uniforms.assign3f("u_texCoordRD", coords[3 * 2 + 0], coords[3 * 2 + 1], coords[3 * 2 + 2]);
1142                     uniforms.assign3f("u_texCoordLU", coords[3 * 1 + 0], coords[3 * 1 + 1], coords[3 * 1 + 2]);
1143                     uniforms.assign3f("u_texCoordRU", coords[3 * 3 + 0], coords[3 * 3 + 1], coords[3 * 3 + 2]);
1144                 }
1145                 else
1146                 {
1147                     const float z = textureType == TEXTURETYPE_3D ?
1148                                         ((float)sliceOrFaceNdx + 0.5f) / (float)numSlicesOrFaces :
1149                                         (float)sliceOrFaceNdx;
1150                     uniforms.assign3f("u_texCoordLD", 0.0f, 0.0f, z);
1151                     uniforms.assign3f("u_texCoordRD", 1.0f, 0.0f, z);
1152                     uniforms.assign3f("u_texCoordLU", 0.0f, 1.0f, z);
1153                     uniforms.assign3f("u_texCoordRU", 1.0f, 1.0f, z);
1154                 }
1155 
1156                 glLog.glDispatchCompute(textureSize.x(), textureSize.y(), 1);
1157 
1158                 {
1159                     log << TestLog::Message << "// Note: mapping buffer and reading color values written"
1160                         << TestLog::EndMessage;
1161 
1162                     const BufferMemMap bufMap(renderCtx.getFunctions(), GL_SHADER_STORAGE_BUFFER, 0, blockSize,
1163                                               GL_MAP_READ_BIT);
1164 
1165                     for (int y = 0; y < textureSize.y(); y++)
1166                         for (int x = 0; x < textureSize.x(); x++)
1167                         {
1168                             const int ndx = y * textureSize.x() + x;
1169                             const float *const clrData =
1170                                 (const float *)((const uint8_t *)bufMap.getPtr() + valueInfo.offset +
1171                                                 valueInfo.arrayStride * ndx);
1172 
1173                             switch (textureFormat.order)
1174                             {
1175                             case TextureFormat::R:
1176                                 resultSliceAccess.setPixel(Vec4(clrData[0]), x, y);
1177                                 break;
1178                             case TextureFormat::RGBA:
1179                                 resultSliceAccess.setPixel(Vec4(clrData[0], clrData[1], clrData[2], clrData[3]), x, y);
1180                                 break;
1181                             default:
1182                                 DE_ASSERT(false);
1183                             }
1184                         }
1185                 }
1186 
1187                 if (!verifyLayer(log, resultSliceAccess, sliceOrFaceNdx))
1188                     return false;
1189             }
1190         }
1191 
1192         return true;
1193     }
1194 }
1195 
1196 //! Read buffer texture by reading the corresponding buffer with a mapping.
readBufferTextureWithMappingAndVerify(const RenderContext & renderCtx,glu::CallLogWrapper & glLog,uint32_t bufferGL,const TextureFormat & textureFormat,int imageSize,const ImageLayerVerifier & verifyLayer)1197 static bool readBufferTextureWithMappingAndVerify(const RenderContext &renderCtx, glu::CallLogWrapper &glLog,
1198                                                   uint32_t bufferGL, const TextureFormat &textureFormat, int imageSize,
1199                                                   const ImageLayerVerifier &verifyLayer)
1200 {
1201     tcu::TextureLevel result(textureFormat, imageSize, 1);
1202     const PixelBufferAccess resultAccess = result.getAccess();
1203     const int dataSize                   = imageSize * textureFormat.getPixelSize();
1204 
1205     const tcu::ScopedLogSection section(glLog.getLog(), "Verification",
1206                                         "Result verification (read texture's buffer with a mapping)");
1207     glLog.glBindBuffer(GL_TEXTURE_BUFFER, bufferGL);
1208 
1209     {
1210         const BufferMemMap bufMap(renderCtx.getFunctions(), GL_TEXTURE_BUFFER, 0, dataSize, GL_MAP_READ_BIT);
1211         deMemcpy(resultAccess.getDataPtr(), bufMap.getPtr(), dataSize);
1212     }
1213 
1214     return verifyLayer(glLog.getLog(), resultAccess, 0);
1215 }
1216 
1217 //! Calls the appropriate texture verification function depending on texture format or type.
readTextureAndVerify(const RenderContext & renderCtx,glu::CallLogWrapper & glLog,uint32_t textureGL,uint32_t bufferGL,TextureType textureType,const TextureFormat & textureFormat,const IVec3 & imageSize,const ImageLayerVerifier & verifyLayer)1218 static bool readTextureAndVerify(const RenderContext &renderCtx, glu::CallLogWrapper &glLog, uint32_t textureGL,
1219                                  uint32_t bufferGL, TextureType textureType, const TextureFormat &textureFormat,
1220                                  const IVec3 &imageSize, const ImageLayerVerifier &verifyLayer)
1221 {
1222     if (textureType == TEXTURETYPE_BUFFER)
1223         return readBufferTextureWithMappingAndVerify(renderCtx, glLog, bufferGL, textureFormat, imageSize.x(),
1224                                                      verifyLayer);
1225     else
1226         return isFormatTypeInteger(textureFormat.type) ?
1227                    readIntegerTextureViaFBOAndVerify(renderCtx, glLog, textureGL, textureType, textureFormat, imageSize,
1228                                                      verifyLayer) :
1229                    readFloatOrNormTextureWithLookupsAndVerify(renderCtx, glLog, textureGL, textureType, textureFormat,
1230                                                               imageSize, verifyLayer);
1231 }
1232 
1233 //! An ImageLayerVerifier that simply compares the result slice to a slice in a reference image.
1234 //! \note Holds the reference image as a reference (no pun intended) instead of a copy; caller must be aware of lifetime issues.
1235 class ImageLayerComparer : public ImageLayerVerifier
1236 {
1237 public:
ImageLayerComparer(const LayeredImage & reference,const IVec2 & relevantRegion=IVec2 (0))1238     ImageLayerComparer(const LayeredImage &reference,
1239                        const IVec2 &relevantRegion = IVec2(0) /* If given, only check this region of each slice. */)
1240         : m_reference(reference)
1241         , m_relevantRegion(relevantRegion.x() > 0 && relevantRegion.y() > 0 ? relevantRegion :
1242                                                                               reference.getSize().swizzle(0, 1))
1243     {
1244     }
1245 
operator ()(TestLog & log,const tcu::ConstPixelBufferAccess & resultSlice,int sliceOrFaceNdx) const1246     bool operator()(TestLog &log, const tcu::ConstPixelBufferAccess &resultSlice, int sliceOrFaceNdx) const
1247     {
1248         const bool isCube = m_reference.getImageType() == TEXTURETYPE_CUBE;
1249         const ConstPixelBufferAccess referenceSlice =
1250             tcu::getSubregion(isCube ? m_reference.getCubeFaceAccess(glslImageFuncZToCubeFace(sliceOrFaceNdx)) :
1251                                        m_reference.getSliceAccess(sliceOrFaceNdx),
1252                               0, 0, m_relevantRegion.x(), m_relevantRegion.y());
1253 
1254         const string comparisonName = "Comparison" + toString(sliceOrFaceNdx);
1255         const string comparisonDesc =
1256             "Image Comparison, " +
1257             (isCube ?
1258                  "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx)))) :
1259                  "slice " + toString(sliceOrFaceNdx));
1260 
1261         if (isFormatTypeInteger(m_reference.getFormat().type))
1262             return tcu::intThresholdCompare(log, comparisonName.c_str(), comparisonDesc.c_str(), referenceSlice,
1263                                             resultSlice, UVec4(0), tcu::COMPARE_LOG_RESULT);
1264         else
1265             return tcu::floatThresholdCompare(log, comparisonName.c_str(), comparisonDesc.c_str(), referenceSlice,
1266                                               resultSlice, Vec4(0.01f), tcu::COMPARE_LOG_RESULT);
1267     }
1268 
1269 private:
1270     const LayeredImage &m_reference;
1271     const IVec2 m_relevantRegion;
1272 };
1273 
1274 //! Case that just stores some computation results into an image.
1275 class ImageStoreCase : public TestCase
1276 {
1277 public:
1278     enum CaseFlag
1279     {
1280         CASEFLAG_SINGLE_LAYER_BIND =
1281             1
1282             << 0 //!< If given, glBindImageTexture() is called with GL_FALSE <layered> argument, and for each layer the compute shader is separately dispatched.
1283     };
1284 
ImageStoreCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType textureType,uint32_t caseFlags=0)1285     ImageStoreCase(Context &context, const char *name, const char *description, const TextureFormat &format,
1286                    TextureType textureType, uint32_t caseFlags = 0)
1287         : TestCase(context, name, description)
1288         , m_format(format)
1289         , m_textureType(textureType)
1290         , m_singleLayerBind((caseFlags & CASEFLAG_SINGLE_LAYER_BIND) != 0)
1291     {
1292     }
1293 
init(void)1294     void init(void)
1295     {
1296         checkTextureTypeExtensions(m_context.getContextInfo(), m_textureType, m_context.getRenderContext());
1297     }
1298     IterateResult iterate(void);
1299 
1300 private:
1301     const TextureFormat m_format;
1302     const TextureType m_textureType;
1303     const bool m_singleLayerBind;
1304 };
1305 
iterate(void)1306 ImageStoreCase::IterateResult ImageStoreCase::iterate(void)
1307 {
1308     const RenderContext &renderCtx = m_context.getRenderContext();
1309     TestLog &log(m_testCtx.getLog());
1310     glu::CallLogWrapper glLog(renderCtx.getFunctions(), log);
1311     const uint32_t internalFormatGL = glu::getInternalFormat(m_format);
1312     const uint32_t textureTargetGL  = getGLTextureTarget(m_textureType);
1313     const IVec3 &imageSize          = defaultImageSize(m_textureType);
1314     const int numSlicesOrFaces      = m_textureType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
1315     const int maxImageDimension     = de::max(imageSize.x(), de::max(imageSize.y(), imageSize.z()));
1316     const float storeColorScale     = isFormatTypeUnorm(m_format.type) ? 1.0f / (float)(maxImageDimension - 1) :
1317                                       isFormatTypeSnorm(m_format.type) ? 2.0f / (float)(maxImageDimension - 1) :
1318                                                                          1.0f;
1319     const float storeColorBias      = isFormatTypeSnorm(m_format.type) ? -1.0f : 0.0f;
1320     const glu::Buffer textureBuf(renderCtx); // \note Only really used if using buffer texture.
1321     const glu::Texture texture(renderCtx);
1322 
1323     glLog.enableLogging(true);
1324 
1325     // Setup texture.
1326 
1327     log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
1328     if (m_textureType == TEXTURETYPE_BUFFER)
1329         log << TestLog::Message << "// Created a buffer for the texture (name " << *textureBuf << ")"
1330             << TestLog::EndMessage;
1331 
1332     glLog.glActiveTexture(GL_TEXTURE0);
1333     glLog.glBindTexture(textureTargetGL, *texture);
1334     setTexParameteri(glLog, textureTargetGL);
1335     setTextureStorage(glLog, m_textureType, internalFormatGL, imageSize, *textureBuf);
1336 
1337     // Perform image stores in compute shader.
1338 
1339     {
1340         // Generate compute shader.
1341 
1342         const string shaderImageFormatStr = getShaderImageFormatQualifier(m_format);
1343         const TextureType shaderImageType = m_singleLayerBind ? textureLayerType(m_textureType) : m_textureType;
1344         const string shaderImageTypeStr   = getShaderImageType(m_format.type, shaderImageType);
1345         const bool isUintFormat           = isFormatTypeUnsignedInteger(m_format.type);
1346         const bool isIntFormat            = isFormatTypeSignedInteger(m_format.type);
1347         const string colorBaseExpr        = string(isUintFormat ? "u" :
1348                                                    isIntFormat  ? "i" :
1349                                                                   "") +
1350                                      "vec4(gx^gy^gz, "
1351                                      "(" +
1352                                      toString(imageSize.x() - 1) +
1353                                      "-gx)^gy^gz, "
1354                                      "gx^(" +
1355                                      toString(imageSize.y() - 1) +
1356                                      "-gy)^gz, "
1357                                      "(" +
1358                                      toString(imageSize.x() - 1) + "-gx)^(" + toString(imageSize.y() - 1) + "-gy)^gz)";
1359         const string colorExpr = colorBaseExpr + (storeColorScale == 1.0f ? "" : "*" + toString(storeColorScale)) +
1360                                  (storeColorBias == 0.0f ? "" : " + float(" + toString(storeColorBias) + ")");
1361         const std::string glslVersionDeclaration =
1362             glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
1363 
1364         const glu::ShaderProgram program(
1365             renderCtx,
1366             glu::ProgramSources() << glu::ComputeSource(
1367                 glslVersionDeclaration + "\n" + textureTypeExtensionShaderRequires(shaderImageType, renderCtx) +
1368                 "\n"
1369                 "precision highp " +
1370                 shaderImageTypeStr +
1371                 ";\n"
1372                 "\n"
1373                 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
1374                 "layout (" +
1375                 shaderImageFormatStr + ", binding=0) writeonly uniform " + shaderImageTypeStr + " u_image;\n" +
1376                 (m_singleLayerBind ? "uniform int u_layerNdx;\n" : "") +
1377                 "\n"
1378                 "void main (void)\n"
1379                 "{\n"
1380                 "    int gx = int(gl_GlobalInvocationID.x);\n"
1381                 "    int gy = int(gl_GlobalInvocationID.y);\n"
1382                 "    int gz = " +
1383                 (m_singleLayerBind ? "u_layerNdx" : "int(gl_GlobalInvocationID.z)") + ";\n" +
1384                 (shaderImageType == TEXTURETYPE_BUFFER ? "    imageStore(u_image, gx, " + colorExpr + ");\n" :
1385                  shaderImageType == TEXTURETYPE_2D ? "    imageStore(u_image, ivec2(gx, gy), " + colorExpr + ");\n" :
1386                  shaderImageType == TEXTURETYPE_3D || shaderImageType == TEXTURETYPE_CUBE ||
1387                          shaderImageType == TEXTURETYPE_2D_ARRAY ?
1388                                                      "    imageStore(u_image, ivec3(gx, gy, gz), " + colorExpr +
1389                                                          ");\n" :
1390                                                      deFatalStr("Invalid TextureType")) +
1391                 "}\n"));
1392 
1393         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
1394 
1395         log << program;
1396 
1397         if (!program.isOk())
1398         {
1399             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
1400             return STOP;
1401         }
1402 
1403         // Setup and dispatch.
1404 
1405         glLog.glUseProgram(program.getProgram());
1406 
1407         if (m_singleLayerBind)
1408         {
1409             for (int layerNdx = 0; layerNdx < numSlicesOrFaces; layerNdx++)
1410             {
1411                 if (layerNdx > 0)
1412                     glLog.glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
1413 
1414                 uniforms.assign1i("u_layerNdx", layerNdx);
1415 
1416                 glLog.glBindImageTexture(0, *texture, 0, GL_FALSE, layerNdx, GL_WRITE_ONLY, internalFormatGL);
1417                 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1418 
1419                 glLog.glDispatchCompute(imageSize.x(), imageSize.y(), 1);
1420                 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1421             }
1422         }
1423         else
1424         {
1425             glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_WRITE_ONLY, internalFormatGL);
1426             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1427 
1428             glLog.glDispatchCompute(imageSize.x(), imageSize.y(), numSlicesOrFaces);
1429             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1430         }
1431     }
1432 
1433     // Create reference, read texture and compare to reference.
1434     {
1435         const int isIntegerFormat = isFormatTypeInteger(m_format.type);
1436         LayeredImage reference(m_textureType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
1437 
1438         DE_ASSERT(!isIntegerFormat || (storeColorScale == 1.0f && storeColorBias == 0.0f));
1439 
1440         for (int z = 0; z < numSlicesOrFaces; z++)
1441             for (int y = 0; y < imageSize.y(); y++)
1442                 for (int x = 0; x < imageSize.x(); x++)
1443                 {
1444                     const IVec4 color(x ^ y ^ z, (imageSize.x() - 1 - x) ^ y ^ z, x ^ (imageSize.y() - 1 - y) ^ z,
1445                                       (imageSize.x() - 1 - x) ^ (imageSize.y() - 1 - y) ^ z);
1446 
1447                     if (isIntegerFormat)
1448                         reference.setPixel(x, y, z, color);
1449                     else
1450                         reference.setPixel(x, y, z, color.asFloat() * storeColorScale + storeColorBias);
1451                 }
1452 
1453         const bool compareOk = readTextureAndVerify(renderCtx, glLog, *texture, *textureBuf, m_textureType, m_format,
1454                                                     imageSize, ImageLayerComparer(reference));
1455 
1456         m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
1457                                 compareOk ? "Pass" : "Image comparison failed");
1458         return STOP;
1459     }
1460 }
1461 
1462 //! Case that copies an image to another, using imageLoad() and imageStore(). Texture formats don't necessarily match image formats.
1463 class ImageLoadAndStoreCase : public TestCase
1464 {
1465 public:
1466     enum CaseFlag
1467     {
1468         CASEFLAG_SINGLE_LAYER_BIND =
1469             1
1470             << 0, //!< If given, glBindImageTexture() is called with GL_FALSE <layered> argument, and for each layer the compute shader is separately dispatched.
1471         CASEFLAG_RESTRICT_IMAGES = 1 << 1 //!< If given, images in shader will be qualified with "restrict".
1472     };
1473 
ImageLoadAndStoreCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType textureType,uint32_t caseFlags=0)1474     ImageLoadAndStoreCase(Context &context, const char *name, const char *description, const TextureFormat &format,
1475                           TextureType textureType, uint32_t caseFlags = 0)
1476         : TestCase(context, name, description)
1477         , m_textureFormat(format)
1478         , m_imageFormat(format)
1479         , m_textureType(textureType)
1480         , m_restrictImages((caseFlags & CASEFLAG_RESTRICT_IMAGES) != 0)
1481         , m_singleLayerBind((caseFlags & CASEFLAG_SINGLE_LAYER_BIND) != 0)
1482     {
1483     }
1484 
ImageLoadAndStoreCase(Context & context,const char * name,const char * description,const TextureFormat & textureFormat,const TextureFormat & imageFormat,TextureType textureType,uint32_t caseFlags=0)1485     ImageLoadAndStoreCase(Context &context, const char *name, const char *description,
1486                           const TextureFormat &textureFormat, const TextureFormat &imageFormat, TextureType textureType,
1487                           uint32_t caseFlags = 0)
1488         : TestCase(context, name, description)
1489         , m_textureFormat(textureFormat)
1490         , m_imageFormat(imageFormat)
1491         , m_textureType(textureType)
1492         , m_restrictImages((caseFlags & CASEFLAG_RESTRICT_IMAGES) != 0)
1493         , m_singleLayerBind((caseFlags & CASEFLAG_SINGLE_LAYER_BIND) != 0)
1494     {
1495         DE_ASSERT(textureFormat.getPixelSize() == imageFormat.getPixelSize());
1496     }
1497 
init(void)1498     void init(void)
1499     {
1500         checkTextureTypeExtensions(m_context.getContextInfo(), m_textureType, m_context.getRenderContext());
1501     }
1502     IterateResult iterate(void);
1503 
1504 private:
1505     template <TextureFormat::ChannelType ImageFormatType, typename TcuFloatType, typename TcuFloatStorageType>
1506     static void replaceBadFloatReinterpretValues(LayeredImage &image, const TextureFormat &imageFormat);
1507 
1508     const TextureFormat m_textureFormat;
1509     const TextureFormat m_imageFormat;
1510     const TextureType m_textureType;
1511     const bool m_restrictImages;
1512     const bool m_singleLayerBind;
1513 };
1514 
1515 template <TextureFormat::ChannelType ImageFormatType, typename TcuFloatType, typename TcuFloatTypeStorageType>
replaceBadFloatReinterpretValues(LayeredImage & image,const TextureFormat & imageFormat)1516 void ImageLoadAndStoreCase::replaceBadFloatReinterpretValues(LayeredImage &image, const TextureFormat &imageFormat)
1517 {
1518     // Find potential bad values, such as nan or inf, and replace with something else.
1519     const int pixelSize        = imageFormat.getPixelSize();
1520     const int imageNumChannels = imageFormat.order == tcu::TextureFormat::R    ? 1 :
1521                                  imageFormat.order == tcu::TextureFormat::RGBA ? 4 :
1522                                                                                  0;
1523     const IVec3 imageSize      = image.getSize();
1524     const int numSlicesOrFaces = image.getImageType() == TEXTURETYPE_CUBE ? 6 : imageSize.z();
1525 
1526     DE_ASSERT(pixelSize % imageNumChannels == 0);
1527 
1528     for (int z = 0; z < numSlicesOrFaces; z++)
1529     {
1530         const PixelBufferAccess sliceAccess = image.getImageType() == TEXTURETYPE_CUBE ?
1531                                                   image.getCubeFaceAccess((tcu::CubeFace)z) :
1532                                                   image.getSliceAccess(z);
1533         const int rowPitch                  = sliceAccess.getRowPitch();
1534         void *const data                    = sliceAccess.getDataPtr();
1535 
1536         for (int y = 0; y < imageSize.y(); y++)
1537             for (int x = 0; x < imageSize.x(); x++)
1538             {
1539                 void *const pixelData = (uint8_t *)data + y * rowPitch + x * pixelSize;
1540 
1541                 for (int c = 0; c < imageNumChannels; c++)
1542                 {
1543                     void *const channelData = (uint8_t *)pixelData + c * pixelSize / imageNumChannels;
1544                     const TcuFloatType f(*(TcuFloatTypeStorageType *)channelData);
1545 
1546                     if (f.isDenorm() || f.isInf() || f.isNaN())
1547                         *(TcuFloatTypeStorageType *)channelData = TcuFloatType(0.0f).bits();
1548                 }
1549             }
1550     }
1551 }
1552 
iterate(void)1553 ImageLoadAndStoreCase::IterateResult ImageLoadAndStoreCase::iterate(void)
1554 {
1555     const RenderContext &renderCtx = m_context.getRenderContext();
1556     TestLog &log(m_testCtx.getLog());
1557     glu::CallLogWrapper glLog(renderCtx.getFunctions(), log);
1558     const uint32_t textureInternalFormatGL = glu::getInternalFormat(m_textureFormat);
1559     const uint32_t imageInternalFormatGL   = glu::getInternalFormat(m_imageFormat);
1560     const uint32_t textureTargetGL         = getGLTextureTarget(m_textureType);
1561     const IVec3 &imageSize                 = defaultImageSize(m_textureType);
1562     const int maxImageDimension            = de::max(imageSize.x(), de::max(imageSize.y(), imageSize.z()));
1563     const float storeColorScale = isFormatTypeUnorm(m_textureFormat.type) ? 1.0f / (float)(maxImageDimension - 1) :
1564                                   isFormatTypeSnorm(m_textureFormat.type) ? 2.0f / (float)(maxImageDimension - 1) :
1565                                                                             1.0f;
1566     const float storeColorBias  = isFormatTypeSnorm(m_textureFormat.type) ? -1.0f : 0.0f;
1567     const int numSlicesOrFaces  = m_textureType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
1568     const bool isIntegerTextureFormat = isFormatTypeInteger(m_textureFormat.type);
1569     const glu::Buffer texture0Buf(renderCtx);
1570     const glu::Buffer texture1Buf(renderCtx);
1571     const glu::Texture texture0(renderCtx);
1572     const glu::Texture texture1(renderCtx);
1573     LayeredImage reference(m_textureType, m_textureFormat, imageSize.x(), imageSize.y(), imageSize.z());
1574 
1575     glLog.enableLogging(true);
1576 
1577     // Setup textures.
1578 
1579     log << TestLog::Message << "// Created 2 textures (names " << *texture0 << " and " << *texture1 << ")"
1580         << TestLog::EndMessage;
1581     if (m_textureType == TEXTURETYPE_BUFFER)
1582         log << TestLog::Message << "// Created buffers for the textures (names " << *texture0Buf << " and "
1583             << *texture1Buf << ")" << TestLog::EndMessage;
1584 
1585     // First, fill reference with (a fairly arbitrary) initial pattern. This will be used as texture upload source data as well as for actual reference computation later on.
1586 
1587     DE_ASSERT(!isIntegerTextureFormat || (storeColorScale == 1.0f && storeColorBias == 0.0f));
1588 
1589     for (int z = 0; z < numSlicesOrFaces; z++)
1590         for (int y = 0; y < imageSize.y(); y++)
1591             for (int x = 0; x < imageSize.x(); x++)
1592             {
1593                 const IVec4 color(x ^ y ^ z, (imageSize.x() - 1 - x) ^ y ^ z, x ^ (imageSize.y() - 1 - y) ^ z,
1594                                   (imageSize.x() - 1 - x) ^ (imageSize.y() - 1 - y) ^ z);
1595 
1596                 if (isIntegerTextureFormat)
1597                     reference.setPixel(x, y, z, color);
1598                 else
1599                     reference.setPixel(x, y, z, color.asFloat() * storeColorScale + storeColorBias);
1600             }
1601 
1602     // If re-interpreting the texture contents as floating point values, need to get rid of inf, nan etc.
1603     if (m_imageFormat.type == TextureFormat::HALF_FLOAT && m_textureFormat.type != TextureFormat::HALF_FLOAT)
1604         replaceBadFloatReinterpretValues<TextureFormat::HALF_FLOAT, tcu::Float16, uint16_t>(reference, m_imageFormat);
1605     else if (m_imageFormat.type == TextureFormat::FLOAT && m_textureFormat.type != TextureFormat::FLOAT)
1606         replaceBadFloatReinterpretValues<TextureFormat::FLOAT, tcu::Float32, uint32_t>(reference, m_imageFormat);
1607 
1608     // Upload initial pattern to texture 0.
1609 
1610     glLog.glActiveTexture(GL_TEXTURE0);
1611     glLog.glBindTexture(textureTargetGL, *texture0);
1612     setTexParameteri(glLog, textureTargetGL);
1613 
1614     log << TestLog::Message << "// Filling texture " << *texture0 << " with xor pattern" << TestLog::EndMessage;
1615 
1616     uploadTexture(glLog, reference, *texture0Buf);
1617 
1618     // Set storage for texture 1.
1619 
1620     glLog.glActiveTexture(GL_TEXTURE1);
1621     glLog.glBindTexture(textureTargetGL, *texture1);
1622     setTexParameteri(glLog, textureTargetGL);
1623     setTextureStorage(glLog, m_textureType, textureInternalFormatGL, imageSize, *texture1Buf);
1624 
1625     // Perform image loads and stores in compute shader and finalize reference computation.
1626 
1627     {
1628         // Generate compute shader.
1629 
1630         const char *const maybeRestrict   = m_restrictImages ? "restrict" : "";
1631         const string shaderImageFormatStr = getShaderImageFormatQualifier(m_imageFormat);
1632         const TextureType shaderImageType = m_singleLayerBind ? textureLayerType(m_textureType) : m_textureType;
1633         const string shaderImageTypeStr   = getShaderImageType(m_imageFormat.type, shaderImageType);
1634         const std::string glslVersionDeclaration =
1635             glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
1636 
1637         const glu::ShaderProgram program(
1638             renderCtx,
1639             glu::ProgramSources() << glu::ComputeSource(
1640                 glslVersionDeclaration + "\n" + textureTypeExtensionShaderRequires(shaderImageType, renderCtx) +
1641                 "\n"
1642                 "precision highp " +
1643                 shaderImageTypeStr +
1644                 ";\n"
1645                 "\n"
1646                 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
1647                 "layout (" +
1648                 shaderImageFormatStr + ", binding=0) " + maybeRestrict + " readonly uniform " + shaderImageTypeStr +
1649                 " u_image0;\n"
1650                 "layout (" +
1651                 shaderImageFormatStr + ", binding=1) " + maybeRestrict + " writeonly uniform " + shaderImageTypeStr +
1652                 " u_image1;\n"
1653                 "\n"
1654                 "void main (void)\n"
1655                 "{\n" +
1656                 (shaderImageType == TEXTURETYPE_BUFFER ? "    int pos = int(gl_GlobalInvocationID.x);\n"
1657                                                          "    imageStore(u_image1, pos, imageLoad(u_image0, " +
1658                                                              toString(imageSize.x() - 1) + "-pos));\n" :
1659                  shaderImageType == TEXTURETYPE_2D ? "    ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n"
1660                                                      "    imageStore(u_image1, pos, imageLoad(u_image0, ivec2(" +
1661                                                          toString(imageSize.x() - 1) + "-pos.x, pos.y)));\n" :
1662                  shaderImageType == TEXTURETYPE_3D || shaderImageType == TEXTURETYPE_CUBE ||
1663                          shaderImageType == TEXTURETYPE_2D_ARRAY ?
1664                                                      "    ivec3 pos = ivec3(gl_GlobalInvocationID);\n"
1665                                                      "    imageStore(u_image1, pos, imageLoad(u_image0, ivec3(" +
1666                                                          toString(imageSize.x() - 1) + "-pos.x, pos.y, pos.z)));\n" :
1667                                                      deFatalStr("Invalid TextureType")) +
1668                 "}\n"));
1669 
1670         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
1671 
1672         log << program;
1673 
1674         if (!program.isOk())
1675         {
1676             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
1677             return STOP;
1678         }
1679 
1680         // Setup and dispatch.
1681 
1682         glLog.glUseProgram(program.getProgram());
1683 
1684         if (m_singleLayerBind)
1685         {
1686             for (int layerNdx = 0; layerNdx < numSlicesOrFaces; layerNdx++)
1687             {
1688                 if (layerNdx > 0)
1689                     glLog.glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
1690 
1691                 glLog.glBindImageTexture(0, *texture0, 0, GL_FALSE, layerNdx, GL_READ_ONLY, imageInternalFormatGL);
1692                 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1693 
1694                 glLog.glBindImageTexture(1, *texture1, 0, GL_FALSE, layerNdx, GL_WRITE_ONLY, imageInternalFormatGL);
1695                 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1696 
1697                 glLog.glDispatchCompute(imageSize.x(), imageSize.y(), 1);
1698                 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1699             }
1700         }
1701         else
1702         {
1703             glLog.glBindImageTexture(0, *texture0, 0, GL_TRUE, 0, GL_READ_ONLY, imageInternalFormatGL);
1704             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1705 
1706             glLog.glBindImageTexture(1, *texture1, 0, GL_TRUE, 0, GL_WRITE_ONLY, imageInternalFormatGL);
1707             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
1708 
1709             glLog.glDispatchCompute(imageSize.x(), imageSize.y(), numSlicesOrFaces);
1710             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
1711         }
1712 
1713         // Finalize reference.
1714 
1715         if (m_textureFormat != m_imageFormat)
1716         {
1717             // Format re-interpretation case. Read data with image format and write back, with the same image format.
1718             // We do this because the data may change a little during lookups (e.g. unorm8 -> float; not all unorms can be exactly represented as floats).
1719 
1720             const int pixelSize = m_imageFormat.getPixelSize();
1721             tcu::TextureLevel scratch(m_imageFormat, 1, 1);
1722             const PixelBufferAccess scratchAccess = scratch.getAccess();
1723 
1724             for (int z = 0; z < numSlicesOrFaces; z++)
1725             {
1726                 const PixelBufferAccess sliceAccess = m_textureType == TEXTURETYPE_CUBE ?
1727                                                           reference.getCubeFaceAccess((tcu::CubeFace)z) :
1728                                                           reference.getSliceAccess(z);
1729                 const int rowPitch                  = sliceAccess.getRowPitch();
1730                 void *const data                    = sliceAccess.getDataPtr();
1731 
1732                 for (int y = 0; y < imageSize.y(); y++)
1733                     for (int x = 0; x < imageSize.x(); x++)
1734                     {
1735                         void *const pixelData = (uint8_t *)data + y * rowPitch + x * pixelSize;
1736 
1737                         deMemcpy(scratchAccess.getDataPtr(), pixelData, pixelSize);
1738 
1739                         if (isFormatTypeInteger(m_imageFormat.type))
1740                             scratchAccess.setPixel(scratchAccess.getPixelUint(0, 0), 0, 0);
1741                         else
1742                             scratchAccess.setPixel(scratchAccess.getPixel(0, 0), 0, 0);
1743 
1744                         deMemcpy(pixelData, scratchAccess.getDataPtr(), pixelSize);
1745                     }
1746             }
1747         }
1748 
1749         for (int z = 0; z < numSlicesOrFaces; z++)
1750             for (int y = 0; y < imageSize.y(); y++)
1751                 for (int x = 0; x < imageSize.x() / 2; x++)
1752                 {
1753                     if (isIntegerTextureFormat)
1754                     {
1755                         const UVec4 temp = reference.getPixelUint(imageSize.x() - 1 - x, y, z);
1756                         reference.setPixel(imageSize.x() - 1 - x, y, z, reference.getPixelUint(x, y, z));
1757                         reference.setPixel(x, y, z, temp);
1758                     }
1759                     else
1760                     {
1761                         const Vec4 temp = reference.getPixel(imageSize.x() - 1 - x, y, z);
1762                         reference.setPixel(imageSize.x() - 1 - x, y, z, reference.getPixel(x, y, z));
1763                         reference.setPixel(x, y, z, temp);
1764                     }
1765                 }
1766     }
1767 
1768     // Read texture 1 and compare to reference.
1769 
1770     const bool compareOk = readTextureAndVerify(renderCtx, glLog, *texture1, *texture1Buf, m_textureType,
1771                                                 m_textureFormat, imageSize, ImageLayerComparer(reference));
1772 
1773     m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
1774                             compareOk ? "Pass" : "Image comparison failed");
1775     return STOP;
1776 }
1777 
1778 enum AtomicOperationCaseType
1779 {
1780     ATOMIC_OPERATION_CASE_TYPE_END_RESULT =
1781         0, //!< Atomic case checks the end result of the operations, and not the return values.
1782     ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES, //!< Atomic case checks the return values of the atomic function, and not the end result.
1783 
1784     ATOMIC_OPERATION_CASE_TYPE_LAST
1785 };
1786 
1787 /*--------------------------------------------------------------------*//*!
1788  * \brief Binary atomic operation case.
1789  *
1790  * Case that performs binary atomic operations (i.e. any but compSwap) and
1791  * verifies according to the given AtomicOperationCaseType.
1792  *
1793  * For the "end result" case type, a single texture (and image) is created,
1794  * upon which the atomic operations operate. A compute shader is dispatched
1795  * with dimensions equal to the image size, except with a bigger X size
1796  * so that every pixel is operated on by multiple invocations. The end
1797  * results are verified in BinaryAtomicOperationCase::EndResultVerifier.
1798  * The return values of the atomic function calls are ignored.
1799  *
1800  * For the "return value" case type, the case does much the same operations
1801  * as in the "end result" case, but also creates an additional texture,
1802  * of size equal to the dispatch size, into which the return values of the
1803  * atomic functions are stored (with imageStore()). The return values are
1804  * verified in BinaryAtomicOperationCase::ReturnValueVerifier.
1805  * The end result values are not checked.
1806  *
1807  * The compute shader invocations contributing to a pixel (X, Y, Z) in the
1808  * end result image are the invocations with global IDs
1809  * (X, Y, Z), (X+W, Y, Z), (X+2*W, Y, Z), ..., (X+(N-1)*W, Y, W), where W
1810  * is the width of the end result image and N is m_numInvocationsPerPixel.
1811  *//*--------------------------------------------------------------------*/
1812 class BinaryAtomicOperationCase : public TestCase
1813 {
1814 public:
BinaryAtomicOperationCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType imageType,AtomicOperation operation,AtomicOperationCaseType caseType)1815     BinaryAtomicOperationCase(Context &context, const char *name, const char *description, const TextureFormat &format,
1816                               TextureType imageType, AtomicOperation operation, AtomicOperationCaseType caseType)
1817         : TestCase(context, name, description)
1818         , m_format(format)
1819         , m_imageType(imageType)
1820         , m_operation(operation)
1821         , m_caseType(caseType)
1822     {
1823         DE_ASSERT(m_format == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32) ||
1824                   m_format == TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32) ||
1825                   (m_format == TextureFormat(TextureFormat::R, TextureFormat::FLOAT) &&
1826                    m_operation == ATOMIC_OPERATION_EXCHANGE));
1827 
1828         DE_ASSERT(m_operation != ATOMIC_OPERATION_COMP_SWAP);
1829     }
1830 
1831     void init(void);
1832     IterateResult iterate(void);
1833 
1834 private:
1835     class EndResultVerifier;
1836     class ReturnValueVerifier;
1837 
1838     static int getOperationInitialValue(
1839         AtomicOperation op); //!< Appropriate value with which to initialize the texture.
1840     //! Compute the argument given to the atomic function at the given invocation ID, when the entire dispatch has the given width and height.
1841     static int getAtomicFuncArgument(AtomicOperation op, const IVec3 &invocationID, const IVec2 &dispatchSizeXY);
1842     //! Generate the shader expression for the argument given to the atomic function. x, y and z are the identifiers for the invocation ID components.
1843     static string getAtomicFuncArgumentShaderStr(AtomicOperation op, const string &x, const string &y, const string &z,
1844                                                  const IVec2 &dispatchSizeXY);
1845 
1846     const int m_numInvocationsPerPixel = 5;
1847     const TextureFormat m_format;
1848     const TextureType m_imageType;
1849     const AtomicOperation m_operation;
1850     const AtomicOperationCaseType m_caseType;
1851 };
1852 
getOperationInitialValue(AtomicOperation op)1853 int BinaryAtomicOperationCase::getOperationInitialValue(AtomicOperation op)
1854 {
1855     switch (op)
1856     {
1857     // \note 18 is just an arbitrary small nonzero value.
1858     case ATOMIC_OPERATION_ADD:
1859         return 18;
1860     case ATOMIC_OPERATION_MIN:
1861         return (1 << 15) - 1;
1862     case ATOMIC_OPERATION_MAX:
1863         return 18;
1864     case ATOMIC_OPERATION_AND:
1865         return (1 << 15) - 1;
1866     case ATOMIC_OPERATION_OR:
1867         return 18;
1868     case ATOMIC_OPERATION_XOR:
1869         return 18;
1870     case ATOMIC_OPERATION_EXCHANGE:
1871         return 18;
1872     default:
1873         DE_ASSERT(false);
1874         return -1;
1875     }
1876 }
1877 
getAtomicFuncArgument(AtomicOperation op,const IVec3 & invocationID,const IVec2 & dispatchSizeXY)1878 int BinaryAtomicOperationCase::getAtomicFuncArgument(AtomicOperation op, const IVec3 &invocationID,
1879                                                      const IVec2 &dispatchSizeXY)
1880 {
1881     const int x   = invocationID.x();
1882     const int y   = invocationID.y();
1883     const int z   = invocationID.z();
1884     const int wid = dispatchSizeXY.x();
1885     const int hei = dispatchSizeXY.y();
1886 
1887     switch (op)
1888     {
1889     // \note Fall-throughs.
1890     case ATOMIC_OPERATION_ADD:
1891     case ATOMIC_OPERATION_MIN:
1892     case ATOMIC_OPERATION_MAX:
1893     case ATOMIC_OPERATION_AND:
1894     case ATOMIC_OPERATION_OR:
1895     case ATOMIC_OPERATION_XOR:
1896         return x * x + y * y + z * z;
1897 
1898     case ATOMIC_OPERATION_EXCHANGE:
1899         return (z * wid + x) * hei + y;
1900 
1901     default:
1902         DE_ASSERT(false);
1903         return -1;
1904     }
1905 }
1906 
getAtomicFuncArgumentShaderStr(AtomicOperation op,const string & x,const string & y,const string & z,const IVec2 & dispatchSizeXY)1907 string BinaryAtomicOperationCase::getAtomicFuncArgumentShaderStr(AtomicOperation op, const string &x, const string &y,
1908                                                                  const string &z, const IVec2 &dispatchSizeXY)
1909 {
1910     switch (op)
1911     {
1912     // \note Fall-throughs.
1913     case ATOMIC_OPERATION_ADD:
1914     case ATOMIC_OPERATION_MIN:
1915     case ATOMIC_OPERATION_MAX:
1916     case ATOMIC_OPERATION_AND:
1917     case ATOMIC_OPERATION_OR:
1918     case ATOMIC_OPERATION_XOR:
1919         return "(" + x + "*" + x + " + " + y + "*" + y + " + " + z + "*" + z + ")";
1920 
1921     case ATOMIC_OPERATION_EXCHANGE:
1922         return "((" + z + "*" + toString(dispatchSizeXY.x()) + " + " + x + ")*" + toString(dispatchSizeXY.y()) + " + " +
1923                y + ")";
1924 
1925     default:
1926         DE_ASSERT(false);
1927         return "";
1928     }
1929 }
1930 
1931 class BinaryAtomicOperationCase::EndResultVerifier : public ImageLayerVerifier
1932 {
1933 public:
EndResultVerifier(AtomicOperation operation,TextureType imageType,int numInvocationsPerPixel)1934     EndResultVerifier(AtomicOperation operation, TextureType imageType, int numInvocationsPerPixel)
1935         : m_operation(operation)
1936         , m_imageType(imageType)
1937         , m_numInvocationsPerPixel(numInvocationsPerPixel)
1938     {
1939     }
1940 
operator ()(TestLog & log,const ConstPixelBufferAccess & resultSlice,int sliceOrFaceNdx) const1941     bool operator()(TestLog &log, const ConstPixelBufferAccess &resultSlice, int sliceOrFaceNdx) const
1942     {
1943         const bool isIntegerFormat = isFormatTypeInteger(resultSlice.getFormat().type);
1944         const IVec2 dispatchSizeXY(m_numInvocationsPerPixel * resultSlice.getWidth(), resultSlice.getHeight());
1945 
1946         log << TestLog::Image("EndResults" + toString(sliceOrFaceNdx),
1947                               "Result Values, " + (m_imageType == TEXTURETYPE_CUBE ?
1948                                                        "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(
1949                                                                      glslImageFuncZToCubeFace(sliceOrFaceNdx)))) :
1950                                                        "slice " + toString(sliceOrFaceNdx)),
1951                               resultSlice);
1952 
1953         for (int y = 0; y < resultSlice.getHeight(); y++)
1954             for (int x = 0; x < resultSlice.getWidth(); x++)
1955             {
1956                 union
1957                 {
1958                     int i;
1959                     float f;
1960                 } result;
1961 
1962                 if (isIntegerFormat)
1963                     result.i = resultSlice.getPixelInt(x, y).x();
1964                 else
1965                     result.f = resultSlice.getPixel(x, y).x();
1966 
1967                 // Compute the arguments that were given to the atomic function in the invocations that contribute to this pixel.
1968 
1969                 std::vector<IVec3> invocationGlobalIDs(m_numInvocationsPerPixel);
1970                 std::vector<int> atomicArgs(m_numInvocationsPerPixel);
1971 
1972                 for (int i = 0; i < m_numInvocationsPerPixel; i++)
1973                 {
1974                     const IVec3 gid(x + i * resultSlice.getWidth(), y, sliceOrFaceNdx);
1975 
1976                     invocationGlobalIDs[i] = gid;
1977                     atomicArgs[i]          = getAtomicFuncArgument(m_operation, gid, dispatchSizeXY);
1978                 }
1979 
1980                 if (isOrderIndependentAtomicOperation(m_operation))
1981                 {
1982                     // Just accumulate the atomic args (and the initial value) according to the operation, and compare.
1983 
1984                     DE_ASSERT(isIntegerFormat);
1985 
1986                     int reference = getOperationInitialValue(m_operation);
1987 
1988                     for (int i = 0; i < m_numInvocationsPerPixel; i++)
1989                         reference = computeBinaryAtomicOperationResult(m_operation, reference, atomicArgs[i]);
1990 
1991                     if (result.i != reference)
1992                     {
1993                         log << TestLog::Message << "// Failure: end result at pixel " << IVec2(x, y)
1994                             << " of current layer is " << result.i << TestLog::EndMessage << TestLog::Message
1995                             << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs)
1996                             << TestLog::EndMessage << TestLog::Message
1997                             << "// Note: data expression values for the IDs are " << arrayStr(atomicArgs)
1998                             << TestLog::EndMessage << TestLog::Message << "// Note: reference value is " << reference
1999                             << TestLog::EndMessage;
2000                         return false;
2001                     }
2002                 }
2003                 else if (m_operation == ATOMIC_OPERATION_EXCHANGE)
2004                 {
2005                     // Check that the end result equals one of the atomic args.
2006 
2007                     bool matchFound = false;
2008 
2009                     for (int i = 0; i < m_numInvocationsPerPixel && !matchFound; i++)
2010                         matchFound = isIntegerFormat ? result.i == atomicArgs[i] :
2011                                                        de::abs(result.f - (float)atomicArgs[i]) <= 0.01f;
2012 
2013                     if (!matchFound)
2014                     {
2015                         log << TestLog::Message << "// Failure: invalid value at pixel " << IVec2(x, y) << ": got "
2016                             << (isIntegerFormat ? toString(result.i) : toString(result.f)) << TestLog::EndMessage
2017                             << TestLog::Message << "// Note: expected one of " << arrayStr(atomicArgs)
2018                             << TestLog::EndMessage;
2019 
2020                         return false;
2021                     }
2022                 }
2023                 else
2024                     DE_ASSERT(false);
2025             }
2026 
2027         return true;
2028     }
2029 
2030 private:
2031     const AtomicOperation m_operation;
2032     const TextureType m_imageType;
2033     const int m_numInvocationsPerPixel;
2034 };
2035 
2036 template <typename T>
getPixelValueX(const ConstPixelBufferAccess & resultSlice,int x,int y)2037 T getPixelValueX(const ConstPixelBufferAccess &resultSlice, int x, int y)
2038 {
2039     return resultSlice.getPixelInt(x, y).x();
2040 }
2041 
2042 template <>
getPixelValueX(const ConstPixelBufferAccess & resultSlice,int x,int y)2043 float getPixelValueX<float>(const ConstPixelBufferAccess &resultSlice, int x, int y)
2044 {
2045     return resultSlice.getPixel(x, y).x();
2046 }
2047 
2048 class BinaryAtomicOperationCase::ReturnValueVerifier : public ImageLayerVerifier
2049 {
2050 public:
2051     //! \note endResultImageLayerSize is (width, height) of the image operated on by the atomic ops, and not the size of the image where the return values are stored.
ReturnValueVerifier(AtomicOperation operation,TextureType imageType,const IVec2 & endResultImageLayerSize,int numInvocationsPerPixel)2052     ReturnValueVerifier(AtomicOperation operation, TextureType imageType, const IVec2 &endResultImageLayerSize,
2053                         int numInvocationsPerPixel)
2054         : m_operation(operation)
2055         , m_imageType(imageType)
2056         , m_endResultImageLayerSize(endResultImageLayerSize)
2057         , m_numInvocationsPerPixel(numInvocationsPerPixel)
2058     {
2059     }
2060 
operator ()(TestLog & log,const ConstPixelBufferAccess & resultSlice,int sliceOrFaceNdx) const2061     bool operator()(TestLog &log, const ConstPixelBufferAccess &resultSlice, int sliceOrFaceNdx) const
2062     {
2063         const bool isIntegerFormat(isFormatTypeInteger(resultSlice.getFormat().type));
2064         const IVec2 dispatchSizeXY(resultSlice.getWidth(), resultSlice.getHeight());
2065 
2066         DE_ASSERT(resultSlice.getWidth() == m_numInvocationsPerPixel * m_endResultImageLayerSize.x() &&
2067                   resultSlice.getHeight() == m_endResultImageLayerSize.y() && resultSlice.getDepth() == 1);
2068 
2069         log << TestLog::Image("ReturnValues" + toString(sliceOrFaceNdx),
2070                               "Per-Invocation Return Values, " +
2071                                   (m_imageType == TEXTURETYPE_CUBE ?
2072                                        "face " + string(glu::getCubeMapFaceName(
2073                                                      cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx)))) :
2074                                        "slice " + toString(sliceOrFaceNdx)),
2075                               resultSlice);
2076 
2077         for (int y = 0; y < m_endResultImageLayerSize.y(); y++)
2078             for (int x = 0; x < m_endResultImageLayerSize.x(); x++)
2079             {
2080                 if (isIntegerFormat)
2081                 {
2082                     if (!checkPixel<int>(log, resultSlice, x, y, sliceOrFaceNdx, dispatchSizeXY))
2083                         return false;
2084                 }
2085                 else
2086                 {
2087                     if (!checkPixel<float>(log, resultSlice, x, y, sliceOrFaceNdx, dispatchSizeXY))
2088                         return false;
2089                 }
2090             }
2091 
2092         return true;
2093     }
2094 
2095 private:
2096     const AtomicOperation m_operation;
2097     const TextureType m_imageType;
2098     const IVec2 m_endResultImageLayerSize;
2099     const int m_numInvocationsPerPixel;
2100 
2101     template <typename T>
checkPixel(TestLog & log,const ConstPixelBufferAccess & resultSlice,int x,int y,int sliceOrFaceNdx,const IVec2 & dispatchSizeXY) const2102     bool checkPixel(TestLog &log, const ConstPixelBufferAccess &resultSlice, int x, int y, int sliceOrFaceNdx,
2103                     const IVec2 &dispatchSizeXY) const
2104     {
2105         // Get the atomic function args and return values for all the invocations that contribute to the pixel (x, y) in the current end result slice.
2106 
2107         std::vector<T> returnValues(m_numInvocationsPerPixel);
2108         std::vector<T> atomicArgs(m_numInvocationsPerPixel);
2109         std::vector<IVec3> invocationGlobalIDs(m_numInvocationsPerPixel);
2110         std::vector<IVec2> pixelCoords(m_numInvocationsPerPixel);
2111 
2112         for (int i = 0; i < m_numInvocationsPerPixel; i++)
2113         {
2114             const IVec2 pixCoord(x + i * m_endResultImageLayerSize.x(), y);
2115             const IVec3 gid(pixCoord.x(), pixCoord.y(), sliceOrFaceNdx);
2116 
2117             invocationGlobalIDs[i] = gid;
2118             pixelCoords[i]         = pixCoord;
2119 
2120             returnValues[i] = getPixelValueX<T>(resultSlice, gid.x(), y);
2121             atomicArgs[i]   = static_cast<T>(getAtomicFuncArgument(m_operation, gid, dispatchSizeXY));
2122         }
2123 
2124         // Verify that the return values form a valid sequence.
2125         {
2126             const bool success = verifyOperationAccumulationIntermediateValues(
2127                 m_operation, static_cast<T>(getOperationInitialValue(m_operation)), atomicArgs, returnValues);
2128 
2129             if (!success)
2130             {
2131                 log << TestLog::Message << "// Failure: intermediate return values at pixels " << arrayStr(pixelCoords)
2132                     << " of current layer are " << arrayStr(returnValues) << TestLog::EndMessage << TestLog::Message
2133                     << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs)
2134                     << TestLog::EndMessage << TestLog::Message << "// Note: data expression values for the IDs are "
2135                     << arrayStr(atomicArgs) << "; return values are not a valid result for any order of operations"
2136                     << TestLog::EndMessage;
2137                 return false;
2138             }
2139         }
2140 
2141         return true;
2142     }
2143 
2144     //! Check whether there exists an ordering of args such that { init*A", init*A*B, ..., init*A*B*...*LAST } is the "returnValues" sequence, where { A, B, ..., LAST } is args, and * denotes the operation.
2145     //    That is, whether "returnValues" is a valid sequence of intermediate return values when "operation" has been accumulated on "args" (and "init") in some arbitrary order.
2146     template <typename T>
verifyOperationAccumulationIntermediateValues(AtomicOperation operation,T init,const std::vector<T> (& args),const std::vector<T> (& returnValues)) const2147     bool verifyOperationAccumulationIntermediateValues(AtomicOperation operation, T init, const std::vector<T>(&args),
2148                                                        const std::vector<T>(&returnValues)) const
2149     {
2150         std::vector<bool> argsUsed(m_numInvocationsPerPixel, false);
2151 
2152         return verifyRecursive(operation, 0, init, argsUsed, args, returnValues);
2153     }
2154 
compare(int a,int b)2155     static bool compare(int a, int b)
2156     {
2157         return a == b;
2158     }
compare(float a,float b)2159     static bool compare(float a, float b)
2160     {
2161         return de::abs(a - b) <= 0.01f;
2162     }
2163 
2164     //! Depth-first search for verifying the return value sequence.
2165     template <typename T>
verifyRecursive(AtomicOperation operation,int index,T valueSoFar,std::vector<bool> (& argsUsed),const std::vector<T> (& args),const std::vector<T> (& returnValues)) const2166     bool verifyRecursive(AtomicOperation operation, int index, T valueSoFar, std::vector<bool>(&argsUsed),
2167                          const std::vector<T>(&args), const std::vector<T>(&returnValues)) const
2168     {
2169         if (index < m_numInvocationsPerPixel)
2170         {
2171             for (int i = 0; i < m_numInvocationsPerPixel; i++)
2172             {
2173                 if (!argsUsed[i] && compare(returnValues[i], valueSoFar))
2174                 {
2175                     argsUsed[i] = true;
2176                     if (verifyRecursive(operation, index + 1,
2177                                         computeBinaryAtomicOperationResult(operation, valueSoFar, args[i]), argsUsed,
2178                                         args, returnValues))
2179                         return true;
2180                     argsUsed[i] = false;
2181                 }
2182             }
2183 
2184             return false;
2185         }
2186         else
2187             return true;
2188     }
2189 };
2190 
init(void)2191 void BinaryAtomicOperationCase::init(void)
2192 {
2193     const glu::RenderContext &renderContext = m_context.getRenderContext();
2194     if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic") &&
2195         !supportsES32orGL45(renderContext))
2196         throw tcu::NotSupportedError("Test requires OES_shader_image_atomic extension");
2197 
2198     checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, renderContext);
2199 }
2200 
iterate(void)2201 BinaryAtomicOperationCase::IterateResult BinaryAtomicOperationCase::iterate(void)
2202 {
2203     const RenderContext &renderCtx = m_context.getRenderContext();
2204     TestLog &log(m_testCtx.getLog());
2205     glu::CallLogWrapper glLog(renderCtx.getFunctions(), log);
2206     const uint32_t internalFormatGL = glu::getInternalFormat(m_format);
2207     const uint32_t textureTargetGL  = getGLTextureTarget(m_imageType);
2208     const IVec3 &imageSize          = defaultImageSize(m_imageType);
2209     const int numSlicesOrFaces      = m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
2210     const bool isUintFormat         = isFormatTypeUnsignedInteger(m_format.type);
2211     const bool isIntFormat          = isFormatTypeSignedInteger(m_format.type);
2212     const glu::Buffer endResultTextureBuf(renderCtx);
2213     const glu::Buffer returnValueTextureBuf(renderCtx);
2214     const glu::Texture endResultTexture(
2215         renderCtx); //!< Texture for the final result; i.e. the texture on which the atomic operations are done. Size imageSize.
2216     const glu::Texture returnValueTexture(
2217         renderCtx); //!< Texture into which the return values are stored if m_caseType == CASETYPE_RETURN_VALUES.
2218     //      Size imageSize*IVec3(N, 1, 1) or, for cube maps, imageSize*IVec3(N, N, 1) where N is m_numInvocationsPerPixel.
2219 
2220     glLog.enableLogging(true);
2221 
2222     // Adjust result image size for result image
2223     if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2224     {
2225         int maxWidth = getGLTextureMaxSize(glLog, m_imageType);
2226 
2227         while (maxWidth < m_numInvocationsPerPixel * imageSize.x())
2228         {
2229             int *numInvocationsPerPixel = const_cast<int *>(&m_numInvocationsPerPixel);
2230             (*numInvocationsPerPixel) -= 1;
2231         }
2232     }
2233 
2234     // Setup textures.
2235 
2236     log << TestLog::Message << "// Created a texture (name " << *endResultTexture
2237         << ") to act as the target of atomic operations" << TestLog::EndMessage;
2238     if (m_imageType == TEXTURETYPE_BUFFER)
2239         log << TestLog::Message << "// Created a buffer for the texture (name " << *endResultTextureBuf << ")"
2240             << TestLog::EndMessage;
2241 
2242     if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2243     {
2244         log << TestLog::Message << "// Created a texture (name " << *returnValueTexture
2245             << ") to which the intermediate return values of the atomic operation are stored" << TestLog::EndMessage;
2246         if (m_imageType == TEXTURETYPE_BUFFER)
2247             log << TestLog::Message << "// Created a buffer for the texture (name " << *returnValueTextureBuf << ")"
2248                 << TestLog::EndMessage;
2249     }
2250 
2251     // Fill endResultTexture with initial pattern.
2252 
2253     {
2254         const LayeredImage imageData(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2255 
2256         {
2257             const IVec4 initial(getOperationInitialValue(m_operation));
2258 
2259             for (int z = 0; z < numSlicesOrFaces; z++)
2260                 for (int y = 0; y < imageSize.y(); y++)
2261                     for (int x = 0; x < imageSize.x(); x++)
2262                         imageData.setPixel(x, y, z, initial);
2263         }
2264 
2265         // Upload initial pattern to endResultTexture and bind to image.
2266 
2267         glLog.glActiveTexture(GL_TEXTURE0);
2268         glLog.glBindTexture(textureTargetGL, *endResultTexture);
2269         setTexParameteri(glLog, textureTargetGL);
2270 
2271         log << TestLog::Message << "// Filling end-result texture with initial pattern (initial value "
2272             << getOperationInitialValue(m_operation) << ")" << TestLog::EndMessage;
2273 
2274         uploadTexture(glLog, imageData, *endResultTextureBuf);
2275     }
2276 
2277     glLog.glBindImageTexture(0, *endResultTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2278     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2279 
2280     if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2281     {
2282         // Set storage for returnValueTexture and bind to image.
2283 
2284         glLog.glActiveTexture(GL_TEXTURE1);
2285         glLog.glBindTexture(textureTargetGL, *returnValueTexture);
2286         setTexParameteri(glLog, textureTargetGL);
2287 
2288         log << TestLog::Message << "// Setting storage of return-value texture" << TestLog::EndMessage;
2289         setTextureStorage(glLog, m_imageType, internalFormatGL,
2290                           imageSize * (m_imageType == TEXTURETYPE_CUBE ?
2291                                            IVec3(m_numInvocationsPerPixel, m_numInvocationsPerPixel, 1) :
2292                                            IVec3(m_numInvocationsPerPixel, 1, 1)),
2293                           *returnValueTextureBuf);
2294 
2295         glLog.glBindImageTexture(1, *returnValueTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, internalFormatGL);
2296         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2297     }
2298 
2299     // Perform image stores in compute shader and finalize reference computation.
2300 
2301     {
2302         // Generate compute shader.
2303 
2304         const string colorVecTypeName = string(isUintFormat ? "u" : isIntFormat ? "i" : "") + "vec4";
2305         const string atomicCoord =
2306             m_imageType == TEXTURETYPE_BUFFER ? "gx % " + toString(imageSize.x()) :
2307             m_imageType == TEXTURETYPE_2D     ? "ivec2(gx % " + toString(imageSize.x()) + ", gy)" :
2308                                                 "ivec3(gx % " + toString(imageSize.x()) + ", gy, gz)";
2309         const string invocationCoord = m_imageType == TEXTURETYPE_BUFFER ? "gx" :
2310                                        m_imageType == TEXTURETYPE_2D     ? "ivec2(gx, gy)" :
2311                                                                            "ivec3(gx, gy, gz)";
2312         const string atomicArgExpr =
2313             (isUintFormat ? "uint" :
2314              isIntFormat  ? "" :
2315                             "float") +
2316             getAtomicFuncArgumentShaderStr(m_operation, "gx", "gy", "gz",
2317                                            IVec2(m_numInvocationsPerPixel * imageSize.x(), imageSize.y()));
2318         const string atomicInvocation = string() + getAtomicOperationShaderFuncName(m_operation) + "(u_results, " +
2319                                         atomicCoord + ", " + atomicArgExpr + ")";
2320         const string shaderImageFormatStr = getShaderImageFormatQualifier(m_format);
2321         const string shaderImageTypeStr   = getShaderImageType(m_format.type, m_imageType);
2322         const std::string glslVersionDeclaration =
2323             glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2324 
2325         const glu::ShaderProgram program(
2326             renderCtx,
2327             glu::ProgramSources() << glu::ComputeSource(
2328                 glslVersionDeclaration + "\n" + imageAtomicExtensionShaderRequires(renderCtx) +
2329                 textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2330                 "\n"
2331                 "precision highp " +
2332                 shaderImageTypeStr +
2333                 ";\n"
2334                 "\n"
2335                 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2336                 "layout (" +
2337                 shaderImageFormatStr + ", binding=0) coherent uniform " + shaderImageTypeStr + " u_results;\n" +
2338                 (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2339                      "layout (" + shaderImageFormatStr + ", binding=1) writeonly uniform " + shaderImageTypeStr +
2340                          " u_returnValues;\n" :
2341                      "") +
2342                 "\n"
2343                 "void main (void)\n"
2344                 "{\n"
2345                 "    int gx = int(gl_GlobalInvocationID.x);\n"
2346                 "    int gy = int(gl_GlobalInvocationID.y);\n"
2347                 "    int gz = int(gl_GlobalInvocationID.z);\n" +
2348                 (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2349                      "    imageStore(u_returnValues, " + invocationCoord + ", " + colorVecTypeName + "(" +
2350                          atomicInvocation + "));\n" :
2351                  m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? "    " + atomicInvocation + ";\n" :
2352                                                                        deFatalStr("Invalid AtomicOperationCaseType")) +
2353                 "}\n"));
2354 
2355         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2356 
2357         log << program;
2358 
2359         if (!program.isOk())
2360         {
2361             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2362             return STOP;
2363         }
2364 
2365         // Setup and dispatch.
2366 
2367         glLog.glUseProgram(program.getProgram());
2368 
2369         glLog.glDispatchCompute(m_numInvocationsPerPixel * imageSize.x(), imageSize.y(), numSlicesOrFaces);
2370         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2371     }
2372 
2373     // Read texture and check.
2374 
2375     {
2376         const uint32_t textureToCheckGL = m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT    ? *endResultTexture :
2377                                           m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? *returnValueTexture :
2378                                                                                                    (uint32_t)-1;
2379         const uint32_t textureToCheckBufGL =
2380             m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT    ? *endResultTextureBuf :
2381             m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? *returnValueTextureBuf :
2382                                                                      (uint32_t)-1;
2383 
2384         const IVec3 textureToCheckSize =
2385             imageSize * IVec3(m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? 1 : m_numInvocationsPerPixel, 1, 1);
2386         const UniquePtr<const ImageLayerVerifier> verifier(
2387             m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ?
2388                 new EndResultVerifier(m_operation, m_imageType, m_numInvocationsPerPixel) :
2389             m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2390                 new ReturnValueVerifier(m_operation, m_imageType, imageSize.swizzle(0, 1), m_numInvocationsPerPixel) :
2391                 (ImageLayerVerifier *)DE_NULL);
2392 
2393         if (readTextureAndVerify(renderCtx, glLog, textureToCheckGL, textureToCheckBufGL, m_imageType, m_format,
2394                                  textureToCheckSize, *verifier))
2395             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2396         else
2397             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2398 
2399         return STOP;
2400     }
2401 }
2402 
2403 /*--------------------------------------------------------------------*//*!
2404  * \brief Atomic compSwap operation case.
2405  *
2406  * Similar in principle to BinaryAtomicOperationCase, but separated for
2407  * convenience, since the atomic function is somewhat different. Like
2408  * BinaryAtomicOperationCase, this has separate cases for checking end
2409  * result and return values.
2410  *//*--------------------------------------------------------------------*/
2411 class AtomicCompSwapCase : public TestCase
2412 {
2413 public:
AtomicCompSwapCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType imageType,AtomicOperationCaseType caseType)2414     AtomicCompSwapCase(Context &context, const char *name, const char *description, const TextureFormat &format,
2415                        TextureType imageType, AtomicOperationCaseType caseType)
2416         : TestCase(context, name, description)
2417         , m_format(format)
2418         , m_imageType(imageType)
2419         , m_caseType(caseType)
2420     {
2421         DE_ASSERT(m_format == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32) ||
2422                   m_format == TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32));
2423     }
2424 
2425     void init(void);
2426     IterateResult iterate(void);
2427 
2428 private:
2429     class EndResultVerifier;
2430     class ReturnValueVerifier;
2431 
2432     static int getCompareArg(const IVec3 &invocationID, int imageWidth);
2433     static int getAssignArg(const IVec3 &invocationID, int imageWidth);
2434     static string getCompareArgShaderStr(const string &x, const string &y, const string &z, int imageWidth);
2435     static string getAssignArgShaderStr(const string &x, const string &y, const string &z, int imageWidth);
2436 
2437     const int m_numInvocationsPerPixel = 5;
2438     const TextureFormat m_format;
2439     const TextureType m_imageType;
2440     const AtomicOperationCaseType m_caseType;
2441 };
2442 
getCompareArg(const IVec3 & invocationID,int imageWidth)2443 int AtomicCompSwapCase::getCompareArg(const IVec3 &invocationID, int imageWidth)
2444 {
2445     const int x                     = invocationID.x();
2446     const int y                     = invocationID.y();
2447     const int z                     = invocationID.z();
2448     const int wrapX                 = x % imageWidth;
2449     const int curPixelInvocationNdx = x / imageWidth;
2450 
2451     return wrapX * wrapX + y * y + z * z + curPixelInvocationNdx * 42;
2452 }
2453 
getAssignArg(const IVec3 & invocationID,int imageWidth)2454 int AtomicCompSwapCase::getAssignArg(const IVec3 &invocationID, int imageWidth)
2455 {
2456     return getCompareArg(IVec3(invocationID.x() + imageWidth, invocationID.y(), invocationID.z()), imageWidth);
2457 }
2458 
getCompareArgShaderStr(const string & x,const string & y,const string & z,int imageWidth)2459 string AtomicCompSwapCase::getCompareArgShaderStr(const string &x, const string &y, const string &z, int imageWidth)
2460 {
2461     const string wrapX                 = "(" + x + "%" + toString(imageWidth) + ")";
2462     const string curPixelInvocationNdx = "(" + x + "/" + toString(imageWidth) + ")";
2463 
2464     return "(" + wrapX + "*" + wrapX + " + " + y + "*" + y + " + " + z + "*" + z + " + " + curPixelInvocationNdx +
2465            "*42)";
2466 }
2467 
getAssignArgShaderStr(const string & x,const string & y,const string & z,int imageWidth)2468 string AtomicCompSwapCase::getAssignArgShaderStr(const string &x, const string &y, const string &z, int imageWidth)
2469 {
2470     const string wrapX                 = "(" + x + "%" + toString(imageWidth) + ")";
2471     const string curPixelInvocationNdx = "(" + x + "/" + toString(imageWidth) + " + 1)";
2472 
2473     return "(" + wrapX + "*" + wrapX + " + " + y + "*" + y + " + " + z + "*" + z + " + " + curPixelInvocationNdx +
2474            "*42)";
2475 }
2476 
init(void)2477 void AtomicCompSwapCase::init(void)
2478 {
2479     const glu::RenderContext &renderContext = m_context.getRenderContext();
2480     if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic") &&
2481         !supportsES32orGL45(renderContext))
2482         throw tcu::NotSupportedError("Test requires OES_shader_image_atomic extension");
2483 
2484     checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, renderContext);
2485 }
2486 
2487 class AtomicCompSwapCase::EndResultVerifier : public ImageLayerVerifier
2488 {
2489 public:
EndResultVerifier(TextureType imageType,int imageWidth,int numInvocationsPerPixel)2490     EndResultVerifier(TextureType imageType, int imageWidth, int numInvocationsPerPixel)
2491         : m_imageType(imageType)
2492         , m_imageWidth(imageWidth)
2493         , m_numInvocationsPerPixel(numInvocationsPerPixel)
2494     {
2495     }
2496 
operator ()(TestLog & log,const ConstPixelBufferAccess & resultSlice,int sliceOrFaceNdx) const2497     bool operator()(TestLog &log, const ConstPixelBufferAccess &resultSlice, int sliceOrFaceNdx) const
2498     {
2499         DE_ASSERT(isFormatTypeInteger(resultSlice.getFormat().type));
2500         DE_ASSERT(resultSlice.getWidth() == m_imageWidth);
2501 
2502         log << TestLog::Image("EndResults" + toString(sliceOrFaceNdx),
2503                               "Result Values, " + (m_imageType == TEXTURETYPE_CUBE ?
2504                                                        "face " + string(glu::getCubeMapFaceName(cubeFaceToGLFace(
2505                                                                      glslImageFuncZToCubeFace(sliceOrFaceNdx)))) :
2506                                                        "slice " + toString(sliceOrFaceNdx)),
2507                               resultSlice);
2508 
2509         for (int y = 0; y < resultSlice.getHeight(); y++)
2510             for (int x = 0; x < resultSlice.getWidth(); x++)
2511             {
2512                 // Compute the value-to-assign arguments that were given to the atomic function in the invocations that contribute to this pixel.
2513                 // One of those should be the result.
2514 
2515                 const int result = resultSlice.getPixelInt(x, y).x();
2516                 std::vector<IVec3> invocationGlobalIDs(m_numInvocationsPerPixel);
2517                 std::vector<int> assignArgs(m_numInvocationsPerPixel);
2518 
2519                 for (int i = 0; i < m_numInvocationsPerPixel; i++)
2520                 {
2521                     const IVec3 gid(x + i * resultSlice.getWidth(), y, sliceOrFaceNdx);
2522 
2523                     invocationGlobalIDs[i] = gid;
2524                     assignArgs[i]          = getAssignArg(gid, m_imageWidth);
2525                 }
2526 
2527                 {
2528                     bool matchFound = false;
2529                     for (int i = 0; i < m_numInvocationsPerPixel && !matchFound; i++)
2530                         matchFound = result == assignArgs[i];
2531 
2532                     if (!matchFound)
2533                     {
2534                         log << TestLog::Message << "// Failure: invalid value at pixel " << IVec2(x, y) << ": got "
2535                             << result << TestLog::EndMessage << TestLog::Message
2536                             << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs)
2537                             << TestLog::EndMessage << TestLog::Message << "// Note: expected one of "
2538                             << arrayStr(assignArgs)
2539                             << " (those are the values given as the 'data' argument in the invocations that contribute "
2540                                "to this pixel)"
2541                             << TestLog::EndMessage;
2542                         return false;
2543                     }
2544                 }
2545             }
2546 
2547         return true;
2548     }
2549 
2550 private:
2551     const TextureType m_imageType;
2552     const int m_imageWidth;
2553     const int m_numInvocationsPerPixel;
2554 };
2555 
2556 class AtomicCompSwapCase::ReturnValueVerifier : public ImageLayerVerifier
2557 {
2558 public:
2559     //! \note endResultImageLayerSize is (width, height) of the image operated on by the atomic ops, and not the size of the image where the return values are stored.
ReturnValueVerifier(TextureType imageType,int endResultImageWidth,int numInvocationsPerPixel)2560     ReturnValueVerifier(TextureType imageType, int endResultImageWidth, int numInvocationsPerPixel)
2561         : m_imageType(imageType)
2562         , m_endResultImageWidth(endResultImageWidth)
2563         , m_numInvocationsPerPixel(numInvocationsPerPixel)
2564     {
2565     }
2566 
operator ()(TestLog & log,const ConstPixelBufferAccess & resultSlice,int sliceOrFaceNdx) const2567     bool operator()(TestLog &log, const ConstPixelBufferAccess &resultSlice, int sliceOrFaceNdx) const
2568     {
2569         DE_ASSERT(isFormatTypeInteger(resultSlice.getFormat().type));
2570         DE_ASSERT(resultSlice.getWidth() == m_numInvocationsPerPixel * m_endResultImageWidth);
2571 
2572         log << TestLog::Image("ReturnValues" + toString(sliceOrFaceNdx),
2573                               "Per-Invocation Return Values, " +
2574                                   (m_imageType == TEXTURETYPE_CUBE ?
2575                                        "face " + string(glu::getCubeMapFaceName(
2576                                                      cubeFaceToGLFace(glslImageFuncZToCubeFace(sliceOrFaceNdx)))) :
2577                                        "slice " + toString(sliceOrFaceNdx)),
2578                               resultSlice);
2579 
2580         for (int y = 0; y < resultSlice.getHeight(); y++)
2581             for (int x = 0; x < m_endResultImageWidth; x++)
2582             {
2583                 // Get the atomic function args and return values for all the invocations that contribute to the pixel (x, y) in the current end result slice.
2584 
2585                 std::vector<int> returnValues(m_numInvocationsPerPixel);
2586                 std::vector<int> compareArgs(m_numInvocationsPerPixel);
2587                 std::vector<IVec3> invocationGlobalIDs(m_numInvocationsPerPixel);
2588                 std::vector<IVec2> pixelCoords(m_numInvocationsPerPixel);
2589 
2590                 for (int i = 0; i < m_numInvocationsPerPixel; i++)
2591                 {
2592                     const IVec2 pixCoord(x + i * m_endResultImageWidth, y);
2593                     const IVec3 gid(pixCoord.x(), pixCoord.y(), sliceOrFaceNdx);
2594 
2595                     pixelCoords[i]         = pixCoord;
2596                     invocationGlobalIDs[i] = gid;
2597                     returnValues[i]        = resultSlice.getPixelInt(gid.x(), y).x();
2598                     compareArgs[i]         = getCompareArg(gid, m_endResultImageWidth);
2599                 }
2600 
2601                 // Verify that the return values form a valid sequence.
2602                 // Due to the way the compare and assign arguments to the atomic calls are organized
2603                 // among the different invocations contributing to the same pixel -- i.e. one invocation
2604                 // compares to A and assigns B, another compares to B and assigns C, and so on, where
2605                 // A<B<C etc -- the first value in the return value sequence must be A, and each following
2606                 // value must be the smallest value (among A, B, C, ...) bigger than the one just before it.
2607                 // The only valid sequence being: A B C D E F G H
2608 
2609                 {
2610                     int failingNdx = -1;
2611 
2612                     {
2613                         int currentAtomicValueNdx = 0;
2614                         for (int i = 0; i < m_numInvocationsPerPixel; i++)
2615                         {
2616                             if (returnValues[i] == compareArgs[currentAtomicValueNdx])
2617                                 continue;
2618                             if (i > 0 && returnValues[i] == compareArgs[currentAtomicValueNdx + 1])
2619                             {
2620                                 currentAtomicValueNdx++;
2621                                 continue;
2622                             }
2623                             failingNdx = i;
2624                             break;
2625                         }
2626                     }
2627 
2628                     if (failingNdx >= 0)
2629                     {
2630                         log << TestLog::Message << "// Failure: intermediate return values at pixels "
2631                             << arrayStr(pixelCoords) << " of current layer are " << arrayStr(returnValues)
2632                             << TestLog::EndMessage << TestLog::Message
2633                             << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs)
2634                             << TestLog::EndMessage << TestLog::Message
2635                             << "// Note: 'compare' argument values for the IDs are " << arrayStr(compareArgs)
2636                             << TestLog::EndMessage << TestLog::Message
2637                             << "// Note: expected the return value sequence to fulfill the following conditions:\n"
2638                             << "// - first value is " << compareArgs[0] << "\n"
2639                             << "// - each value other than the first is either the same as the one just before it, or "
2640                                "the smallest value (in the sequence "
2641                             << arrayStr(compareArgs) << ") bigger than the one just before it" << TestLog::EndMessage;
2642                         if (failingNdx == 0)
2643                             log << TestLog::Message << "// Note: the first return value (" << returnValues[0]
2644                                 << ") isn't " << compareArgs[0] << TestLog::EndMessage;
2645                         else
2646                             log << TestLog::Message << "// Note: the return value at index " << failingNdx << " (value "
2647                                 << returnValues[failingNdx] << ") "
2648                                 << "is neither " << returnValues[failingNdx - 1] << " (the one just before it) "
2649                                 << "nor " << compareArgs[arrayIndexOf(compareArgs, returnValues[failingNdx - 1]) + 1]
2650                                 << " (the smallest value bigger than the one just before it)" << TestLog::EndMessage;
2651 
2652                         return false;
2653                     }
2654                 }
2655             }
2656 
2657         return true;
2658     }
2659 
2660 private:
2661     const TextureType m_imageType;
2662     const int m_endResultImageWidth;
2663     const int m_numInvocationsPerPixel;
2664 };
2665 
iterate(void)2666 AtomicCompSwapCase::IterateResult AtomicCompSwapCase::iterate(void)
2667 {
2668     const RenderContext &renderCtx = m_context.getRenderContext();
2669     TestLog &log(m_testCtx.getLog());
2670     glu::CallLogWrapper glLog(renderCtx.getFunctions(), log);
2671     const uint32_t internalFormatGL = glu::getInternalFormat(m_format);
2672     const uint32_t textureTargetGL  = getGLTextureTarget(m_imageType);
2673     const IVec3 &imageSize          = defaultImageSize(m_imageType);
2674     const int numSlicesOrFaces      = m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
2675     const bool isUintFormat         = isFormatTypeUnsignedInteger(m_format.type);
2676     const bool isIntFormat          = isFormatTypeSignedInteger(m_format.type);
2677     const glu::Buffer endResultTextureBuf(renderCtx);
2678     const glu::Buffer returnValueTextureBuf(renderCtx);
2679     const glu::Texture endResultTexture(
2680         renderCtx); //!< Texture for the final result; i.e. the texture on which the atomic operations are done. Size imageSize.
2681     const glu::Texture returnValueTexture(
2682         renderCtx); //!< Texture into which the return values are stored if m_caseType == CASETYPE_RETURN_VALUES.
2683     //      Size imageSize*IVec3(N, 1, 1) or, for cube maps, imageSize*IVec3(N, N, 1) where N is NUM_INVOCATIONS_PER_PIXEL.
2684 
2685     DE_ASSERT(isUintFormat || isIntFormat);
2686 
2687     glLog.enableLogging(true);
2688 
2689     // Adjust result image size for result image
2690     if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2691     {
2692         int maxWidth = getGLTextureMaxSize(glLog, m_imageType);
2693 
2694         while (maxWidth < m_numInvocationsPerPixel * imageSize.x())
2695         {
2696             int *numInvocationsPerPixel = const_cast<int *>(&m_numInvocationsPerPixel);
2697             (*numInvocationsPerPixel) -= 1;
2698         }
2699     }
2700 
2701     // Setup textures.
2702 
2703     log << TestLog::Message << "// Created a texture (name " << *endResultTexture
2704         << ") to act as the target of atomic operations" << TestLog::EndMessage;
2705     if (m_imageType == TEXTURETYPE_BUFFER)
2706         log << TestLog::Message << "// Created a buffer for the texture (name " << *endResultTextureBuf << ")"
2707             << TestLog::EndMessage;
2708 
2709     if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2710     {
2711         log << TestLog::Message << "// Created a texture (name " << *returnValueTexture
2712             << ") to which the intermediate return values of the atomic operation are stored" << TestLog::EndMessage;
2713         if (m_imageType == TEXTURETYPE_BUFFER)
2714             log << TestLog::Message << "// Created a buffer for the texture (name " << *returnValueTextureBuf << ")"
2715                 << TestLog::EndMessage;
2716     }
2717 
2718     // Fill endResultTexture with initial pattern.
2719 
2720     {
2721         const LayeredImage imageData(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2722 
2723         {
2724             for (int z = 0; z < numSlicesOrFaces; z++)
2725                 for (int y = 0; y < imageSize.y(); y++)
2726                     for (int x = 0; x < imageSize.x(); x++)
2727                         imageData.setPixel(x, y, z, IVec4(getCompareArg(IVec3(x, y, z), imageSize.x())));
2728         }
2729 
2730         // Upload initial pattern to endResultTexture and bind to image.
2731 
2732         glLog.glActiveTexture(GL_TEXTURE0);
2733         glLog.glBindTexture(textureTargetGL, *endResultTexture);
2734         setTexParameteri(glLog, textureTargetGL);
2735 
2736         log << TestLog::Message << "// Filling end-result texture with initial pattern" << TestLog::EndMessage;
2737 
2738         uploadTexture(glLog, imageData, *endResultTextureBuf);
2739     }
2740 
2741     glLog.glBindImageTexture(0, *endResultTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2742     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2743 
2744     if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2745     {
2746         // Set storage for returnValueTexture and bind to image.
2747 
2748         glLog.glActiveTexture(GL_TEXTURE1);
2749         glLog.glBindTexture(textureTargetGL, *returnValueTexture);
2750         setTexParameteri(glLog, textureTargetGL);
2751 
2752         log << TestLog::Message << "// Setting storage of return-value texture" << TestLog::EndMessage;
2753         setTextureStorage(glLog, m_imageType, internalFormatGL,
2754                           imageSize * (m_imageType == TEXTURETYPE_CUBE ?
2755                                            IVec3(m_numInvocationsPerPixel, m_numInvocationsPerPixel, 1) :
2756                                            IVec3(m_numInvocationsPerPixel, 1, 1)),
2757                           *returnValueTextureBuf);
2758 
2759         glLog.glBindImageTexture(1, *returnValueTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, internalFormatGL);
2760         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2761     }
2762 
2763     // Perform atomics in compute shader.
2764 
2765     {
2766         // Generate compute shader.
2767 
2768         const string colorScalarTypeName = isUintFormat ? "uint" : isIntFormat ? "int" : DE_NULL;
2769         const string colorVecTypeName    = string(isUintFormat ? "u" : isIntFormat ? "i" : DE_NULL) + "vec4";
2770         const string atomicCoord =
2771             m_imageType == TEXTURETYPE_BUFFER ? "gx % " + toString(imageSize.x()) :
2772             m_imageType == TEXTURETYPE_2D     ? "ivec2(gx % " + toString(imageSize.x()) + ", gy)" :
2773                                                 "ivec3(gx % " + toString(imageSize.x()) + ", gy, gz)";
2774         const string invocationCoord      = m_imageType == TEXTURETYPE_BUFFER ? "gx" :
2775                                             m_imageType == TEXTURETYPE_2D     ? "ivec2(gx, gy)" :
2776                                                                                 "ivec3(gx, gy, gz)";
2777         const string shaderImageFormatStr = getShaderImageFormatQualifier(m_format);
2778         const string shaderImageTypeStr   = getShaderImageType(m_format.type, m_imageType);
2779         const string glslVersionDeclaration =
2780             glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2781 
2782         const glu::ShaderProgram program(
2783             renderCtx,
2784             glu::ProgramSources() << glu::ComputeSource(
2785                 glslVersionDeclaration + "\n" + imageAtomicExtensionShaderRequires(renderCtx) +
2786                 textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2787                 "\n"
2788                 "precision highp " +
2789                 shaderImageTypeStr +
2790                 ";\n"
2791                 "\n"
2792                 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2793                 "layout (" +
2794                 shaderImageFormatStr + ", binding=0) coherent uniform " + shaderImageTypeStr + " u_results;\n" +
2795                 (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2796                      "layout (" + shaderImageFormatStr + ", binding=1) writeonly uniform " + shaderImageTypeStr +
2797                          " u_returnValues;\n" :
2798                      "") +
2799                 "uniform int offset;"
2800                 "\n"
2801                 "void main (void)\n"
2802                 "{\n"
2803                 "    int gx = int(gl_GlobalInvocationID.x) + offset * " +
2804                 std::to_string(imageSize.x()) +
2805                 ";\n"
2806                 "    int gy = int(gl_GlobalInvocationID.y);\n"
2807                 "    int gz = int(gl_GlobalInvocationID.z);\n"
2808                 "    " +
2809                 colorScalarTypeName + " compare = " + colorScalarTypeName +
2810                 getCompareArgShaderStr("gx", "gy", "gz", imageSize.x()) +
2811                 ";\n"
2812                 "    " +
2813                 colorScalarTypeName + " data    = " + colorScalarTypeName +
2814                 getAssignArgShaderStr("gx", "gy", "gz", imageSize.x()) +
2815                 ";\n"
2816                 "    " +
2817                 colorScalarTypeName + " status  = " + colorScalarTypeName +
2818                 "(-1);\n"
2819                 "    status = imageAtomicCompSwap(u_results, " +
2820                 atomicCoord + ", compare, data);\n" +
2821                 (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2822                      // Ensure there's an ordered ascending pattern to correctly check values are being stored in order
2823                      "    if(compare == status) imageStore(u_returnValues, " + invocationCoord + ", " +
2824                          colorVecTypeName + "(status));\n" :
2825                      "") +
2826                 "}\n"));
2827 
2828         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2829 
2830         log << program;
2831 
2832         if (!program.isOk())
2833         {
2834             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2835             return STOP;
2836         }
2837 
2838         // Setup and dispatch.
2839 
2840         glLog.glUseProgram(program.getProgram());
2841 
2842         {
2843             uint32_t offsetLocation = glLog.glGetUniformLocation(program.getProgram(), "offset");
2844             int dispatchCount = m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? m_numInvocationsPerPixel : 1u;
2845 
2846             for (int i = 0; i < dispatchCount; ++i)
2847             {
2848                 // Ensure we get correct values for output
2849                 glLog.glUniform1i(offsetLocation, i);
2850                 glLog.glDispatchCompute((dispatchCount - i) * imageSize.x(), imageSize.y(), numSlicesOrFaces);
2851                 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2852                 glLog.glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
2853                 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glMemoryBarrier");
2854             }
2855         }
2856     }
2857 
2858     // Create reference, read texture and compare.
2859 
2860     {
2861         const uint32_t textureToCheckGL = m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT    ? *endResultTexture :
2862                                           m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? *returnValueTexture :
2863                                                                                                    (uint32_t)-1;
2864 
2865         const uint32_t textureToCheckBufGL =
2866             m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT    ? *endResultTextureBuf :
2867             m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? *returnValueTextureBuf :
2868                                                                      (uint32_t)-1;
2869 
2870         // The relevant region of the texture being checked (potentially
2871         // different from actual texture size for cube maps, because cube maps
2872         // may have unused pixels due to square size restriction).
2873         const IVec3 relevantRegion =
2874             imageSize * (m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? IVec3(1, 1, 1) :
2875                                                                                IVec3(m_numInvocationsPerPixel, 1, 1));
2876 
2877         const UniquePtr<const ImageLayerVerifier> verifier(
2878             m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ?
2879                 new EndResultVerifier(m_imageType, imageSize.x(), m_numInvocationsPerPixel) :
2880             m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2881                 new ReturnValueVerifier(m_imageType, imageSize.x(), m_numInvocationsPerPixel) :
2882                 (ImageLayerVerifier *)DE_NULL);
2883 
2884         if (readTextureAndVerify(renderCtx, glLog, textureToCheckGL, textureToCheckBufGL, m_imageType, m_format,
2885                                  relevantRegion, *verifier))
2886             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2887         else
2888             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2889 
2890         return STOP;
2891     }
2892 }
2893 
2894 //! Case testing the "coherent" or "volatile" qualifier, along with memoryBarrier() and barrier().
2895 class CoherenceCase : public TestCase
2896 {
2897 public:
2898     enum Qualifier
2899     {
2900         QUALIFIER_COHERENT = 0,
2901         QUALIFIER_VOLATILE,
2902 
2903         QUALIFIER_LAST
2904     };
2905 
CoherenceCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType imageType,Qualifier qualifier)2906     CoherenceCase(Context &context, const char *name, const char *description, const TextureFormat &format,
2907                   TextureType imageType, Qualifier qualifier)
2908         : TestCase(context, name, description)
2909         , m_format(format)
2910         , m_imageType(imageType)
2911         , m_qualifier(qualifier)
2912     {
2913         DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_Y) ==
2914                              DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_X) &&
2915                          DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_Z) ==
2916                              DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_X));
2917 
2918         DE_ASSERT(qualifier == QUALIFIER_COHERENT || qualifier == QUALIFIER_VOLATILE);
2919 
2920         DE_ASSERT(m_format == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32) ||
2921                   m_format == TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32) ||
2922                   m_format == TextureFormat(TextureFormat::R, TextureFormat::FLOAT));
2923     }
2924 
init(void)2925     void init(void)
2926     {
2927         checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, m_context.getRenderContext());
2928     }
2929     IterateResult iterate(void);
2930 
2931 private:
2932     static const int SHADER_READ_OFFSETS_X[4];
2933     static const int SHADER_READ_OFFSETS_Y[4];
2934     static const int SHADER_READ_OFFSETS_Z[4];
2935     static const char *const SHADER_READ_OFFSETS_X_STR;
2936     static const char *const SHADER_READ_OFFSETS_Y_STR;
2937     static const char *const SHADER_READ_OFFSETS_Z_STR;
2938 
2939     const TextureFormat m_format;
2940     const TextureType m_imageType;
2941     const Qualifier m_qualifier;
2942 };
2943 
2944 const int CoherenceCase::SHADER_READ_OFFSETS_X[4]          = {1, 4, 7, 10};
2945 const int CoherenceCase::SHADER_READ_OFFSETS_Y[4]          = {2, 5, 8, 11};
2946 const int CoherenceCase::SHADER_READ_OFFSETS_Z[4]          = {3, 6, 9, 12};
2947 const char *const CoherenceCase::SHADER_READ_OFFSETS_X_STR = "int[]( 1, 4, 7, 10 )";
2948 const char *const CoherenceCase::SHADER_READ_OFFSETS_Y_STR = "int[]( 2, 5, 8, 11 )";
2949 const char *const CoherenceCase::SHADER_READ_OFFSETS_Z_STR = "int[]( 3, 6, 9, 12 )";
2950 
iterate(void)2951 CoherenceCase::IterateResult CoherenceCase::iterate(void)
2952 {
2953     const RenderContext &renderCtx = m_context.getRenderContext();
2954     TestLog &log(m_testCtx.getLog());
2955     glu::CallLogWrapper glLog(renderCtx.getFunctions(), log);
2956     const uint32_t internalFormatGL = glu::getInternalFormat(m_format);
2957     const uint32_t textureTargetGL  = getGLTextureTarget(m_imageType);
2958     const IVec3 &imageSize          = defaultImageSize(m_imageType);
2959     const int numSlicesOrFaces      = m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
2960     const bool isUintFormat         = isFormatTypeUnsignedInteger(m_format.type);
2961     const bool isIntFormat          = isFormatTypeSignedInteger(m_format.type);
2962     const char *const qualifierName = m_qualifier == QUALIFIER_COHERENT ? "coherent" :
2963                                       m_qualifier == QUALIFIER_VOLATILE ? "volatile" :
2964                                                                           DE_NULL;
2965     const glu::Buffer textureBuf(renderCtx);
2966     const glu::Texture texture(renderCtx);
2967     const IVec3 numGroups               = IVec3(16, de::min(16, imageSize.y()), de::min(2, numSlicesOrFaces));
2968     const IVec3 workItemSize            = IVec3(imageSize.x(), imageSize.y(), numSlicesOrFaces);
2969     const IVec3 localSize               = workItemSize / numGroups;
2970     const IVec3 minReqMaxLocalSize      = IVec3(128, 128, 64);
2971     const int minReqMaxLocalInvocations = 128;
2972 
2973     DE_ASSERT(workItemSize == localSize * numGroups);
2974     DE_ASSERT(tcu::boolAll(tcu::lessThanEqual(localSize, minReqMaxLocalSize)));
2975     DE_ASSERT(localSize.x() * localSize.y() * localSize.z() <= minReqMaxLocalInvocations);
2976     DE_UNREF(minReqMaxLocalSize);
2977     DE_UNREF(minReqMaxLocalInvocations);
2978 
2979     glLog.enableLogging(true);
2980 
2981     // Setup texture.
2982 
2983     log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
2984     if (m_imageType == TEXTURETYPE_BUFFER)
2985         log << TestLog::Message << "// Created a buffer for the texture (name " << *textureBuf << ")"
2986             << TestLog::EndMessage;
2987 
2988     glLog.glActiveTexture(GL_TEXTURE0);
2989     glLog.glBindTexture(textureTargetGL, *texture);
2990     setTexParameteri(glLog, textureTargetGL);
2991     setTextureStorage(glLog, m_imageType, internalFormatGL, imageSize, *textureBuf);
2992     glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2993     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2994 
2995     // Perform computations in compute shader.
2996 
2997     {
2998         // Generate compute shader.
2999 
3000         const string colorVecTypeName         = string(isUintFormat ? "u" : isIntFormat ? "i" : "") + "vec4";
3001         const char *const colorScalarTypeName = isUintFormat ? "uint" : isIntFormat ? "int" : "float";
3002         const string invocationCoord          = m_imageType == TEXTURETYPE_BUFFER ? "gx" :
3003                                                 m_imageType == TEXTURETYPE_2D     ? "ivec2(gx, gy)" :
3004                                                                                     "ivec3(gx, gy, gz)";
3005         const string shaderImageFormatStr     = getShaderImageFormatQualifier(m_format);
3006         const string shaderImageTypeStr       = getShaderImageType(m_format.type, m_imageType);
3007         const string localSizeX               = de::toString(localSize.x());
3008         const string localSizeY               = de::toString(localSize.y());
3009         const string localSizeZ               = de::toString(localSize.z());
3010         const std::string glslVersionDeclaration =
3011             glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
3012 
3013         const glu::ShaderProgram program(
3014             renderCtx, glu::ProgramSources() << glu::ComputeSource(
3015                            glslVersionDeclaration + "\n" + textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
3016                            "\n"
3017                            "precision highp " +
3018                            shaderImageTypeStr +
3019                            ";\n"
3020                            "\n"
3021                            "layout (local_size_x = " +
3022                            localSizeX + ", local_size_y = " + localSizeY + ", local_size_z = " + localSizeZ +
3023                            ") in;\n"
3024                            "layout (" +
3025                            shaderImageFormatStr + ", binding=0) " + qualifierName + " uniform " + shaderImageTypeStr +
3026                            " u_image;\n"
3027                            "void main (void)\n"
3028                            "{\n"
3029                            "    int gx = int(gl_GlobalInvocationID.x);\n"
3030                            "    int gy = int(gl_GlobalInvocationID.y);\n"
3031                            "    int gz = int(gl_GlobalInvocationID.z);\n"
3032                            "    imageStore(u_image, " +
3033                            invocationCoord + ", " + colorVecTypeName +
3034                            "(gx^gy^gz));\n"
3035                            "\n"
3036                            "    memoryBarrier();\n"
3037                            "    barrier();\n"
3038                            "\n"
3039                            "    " +
3040                            colorScalarTypeName + " sum = " + colorScalarTypeName +
3041                            "(0);\n"
3042                            "    int groupBaseX = gx/" +
3043                            localSizeX + "*" + localSizeX +
3044                            ";\n"
3045                            "    int groupBaseY = gy/" +
3046                            localSizeY + "*" + localSizeY +
3047                            ";\n"
3048                            "    int groupBaseZ = gz/" +
3049                            localSizeZ + "*" + localSizeZ +
3050                            ";\n"
3051                            "    int xOffsets[] = " +
3052                            SHADER_READ_OFFSETS_X_STR +
3053                            ";\n"
3054                            "    int yOffsets[] = " +
3055                            SHADER_READ_OFFSETS_Y_STR +
3056                            ";\n"
3057                            "    int zOffsets[] = " +
3058                            SHADER_READ_OFFSETS_Z_STR +
3059                            ";\n"
3060                            "    for (int i = 0; i < " +
3061                            toString(DE_LENGTH_OF_ARRAY(SHADER_READ_OFFSETS_X)) +
3062                            "; i++)\n"
3063                            "    {\n"
3064                            "        int readX = groupBaseX + (gx + xOffsets[i]) % " +
3065                            localSizeX +
3066                            ";\n"
3067                            "        int readY = groupBaseY + (gy + yOffsets[i]) % " +
3068                            localSizeY +
3069                            ";\n"
3070                            "        int readZ = groupBaseZ + (gz + zOffsets[i]) % " +
3071                            localSizeZ +
3072                            ";\n"
3073                            "        sum += imageLoad(u_image, " +
3074                            (m_imageType == TEXTURETYPE_BUFFER ? "readX" :
3075                             m_imageType == TEXTURETYPE_2D     ? "ivec2(readX, readY)" :
3076                                                                 "ivec3(readX, readY, readZ)") +
3077                            ").x;\n"
3078                            "    }\n"
3079                            "\n"
3080                            "    memoryBarrier();\n"
3081                            "    barrier();\n"
3082                            "\n"
3083                            "    imageStore(u_image, " +
3084                            invocationCoord + ", " + colorVecTypeName +
3085                            "(sum));\n"
3086                            "}\n"));
3087 
3088         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
3089 
3090         log << program;
3091 
3092         if (!program.isOk())
3093         {
3094             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
3095             return STOP;
3096         }
3097 
3098         // Setup and dispatch.
3099 
3100         glLog.glUseProgram(program.getProgram());
3101 
3102         glLog.glDispatchCompute(numGroups.x(), numGroups.y(), numGroups.z());
3103         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
3104     }
3105 
3106     // Create reference, read texture and compare.
3107 
3108     {
3109         LayeredImage reference(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
3110 
3111         {
3112             LayeredImage base(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
3113             for (int z = 0; z < numSlicesOrFaces; z++)
3114                 for (int y = 0; y < imageSize.y(); y++)
3115                     for (int x = 0; x < imageSize.x(); x++)
3116                         base.setPixel(x, y, z, IVec4(x ^ y ^ z));
3117 
3118             for (int z = 0; z < numSlicesOrFaces; z++)
3119                 for (int y = 0; y < imageSize.y(); y++)
3120                     for (int x = 0; x < imageSize.x(); x++)
3121                     {
3122                         const int groupBaseX = x / localSize.x() * localSize.x();
3123                         const int groupBaseY = y / localSize.y() * localSize.y();
3124                         const int groupBaseZ = z / localSize.z() * localSize.z();
3125                         int sum              = 0;
3126                         for (int i = 0; i < DE_LENGTH_OF_ARRAY(SHADER_READ_OFFSETS_X); i++)
3127                             sum += base.getPixelInt(groupBaseX + (x + SHADER_READ_OFFSETS_X[i]) % localSize.x(),
3128                                                     groupBaseY + (y + SHADER_READ_OFFSETS_Y[i]) % localSize.y(),
3129                                                     groupBaseZ + (z + SHADER_READ_OFFSETS_Z[i]) % localSize.z())
3130                                        .x();
3131 
3132                         reference.setPixel(x, y, z, IVec4(sum));
3133                     }
3134         }
3135 
3136         if (readTextureAndVerify(renderCtx, glLog, *texture, *textureBuf, m_imageType, m_format, imageSize,
3137                                  ImageLayerComparer(reference)))
3138             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3139         else
3140             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
3141 
3142         return STOP;
3143     }
3144 }
3145 
3146 class R32UIImageSingleValueVerifier : public ImageLayerVerifier
3147 {
3148 public:
R32UIImageSingleValueVerifier(const uint32_t value)3149     R32UIImageSingleValueVerifier(const uint32_t value) : m_min(value), m_max(value)
3150     {
3151     }
R32UIImageSingleValueVerifier(const uint32_t min,const uint32_t max)3152     R32UIImageSingleValueVerifier(const uint32_t min, const uint32_t max) : m_min(min), m_max(max)
3153     {
3154     }
3155 
operator ()(TestLog & log,const ConstPixelBufferAccess & resultSlice,int) const3156     bool operator()(TestLog &log, const ConstPixelBufferAccess &resultSlice, int) const
3157     {
3158         DE_ASSERT(resultSlice.getWidth() == 1 && resultSlice.getHeight() == 1 && resultSlice.getDepth() == 1);
3159         DE_ASSERT(resultSlice.getFormat() == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32));
3160 
3161         log << TestLog::Message << "// Note: expecting to get value "
3162             << (m_min == m_max ? toString(m_min) : "in range [" + toString(m_min) + ", " + toString(m_max) + "]")
3163             << TestLog::EndMessage;
3164 
3165         const uint32_t resultValue = resultSlice.getPixelUint(0, 0).x();
3166         if (!de::inRange(resultValue, m_min, m_max))
3167         {
3168             log << TestLog::Message << "// Failure: got value " << resultValue << TestLog::EndMessage;
3169             return false;
3170         }
3171         else
3172         {
3173             log << TestLog::Message << "// Success: got value " << resultValue << TestLog::EndMessage;
3174             return true;
3175         }
3176     }
3177 
3178 private:
3179     const uint32_t m_min;
3180     const uint32_t m_max;
3181 };
3182 
3183 //! Tests the imageSize() GLSL function. Stores result in a 1x1 R32UI image. The image with which imageSize() is called isn't read or written, and
3184 //  can thus be qualifier readonly, writeonly, or both.
3185 class ImageSizeCase : public TestCase
3186 {
3187 public:
3188     enum ImageAccess
3189     {
3190         IMAGEACCESS_READ_ONLY = 0,
3191         IMAGEACCESS_WRITE_ONLY,
3192         IMAGEACCESS_READ_ONLY_WRITE_ONLY,
3193 
3194         IMAGEACCESS_LAST
3195     };
3196 
ImageSizeCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType imageType,const IVec3 & size,ImageAccess imageAccess)3197     ImageSizeCase(Context &context, const char *name, const char *description, const TextureFormat &format,
3198                   TextureType imageType, const IVec3 &size, ImageAccess imageAccess)
3199         : TestCase(context, name, description)
3200         , m_format(format)
3201         , m_imageType(imageType)
3202         , m_imageSize(size)
3203         , m_imageAccess(imageAccess)
3204     {
3205     }
3206 
init(void)3207     void init(void)
3208     {
3209         checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, m_context.getRenderContext());
3210     }
3211     IterateResult iterate(void);
3212 
3213 private:
3214     const TextureFormat m_format;
3215     const TextureType m_imageType;
3216     const IVec3 m_imageSize;
3217     const ImageAccess m_imageAccess;
3218 };
3219 
iterate(void)3220 ImageSizeCase::IterateResult ImageSizeCase::iterate(void)
3221 {
3222     const RenderContext &renderCtx = m_context.getRenderContext();
3223     TestLog &log(m_testCtx.getLog());
3224     glu::CallLogWrapper glLog(renderCtx.getFunctions(), log);
3225     const uint32_t internalFormatGL = glu::getInternalFormat(m_format);
3226     const uint32_t textureTargetGL  = getGLTextureTarget(m_imageType);
3227     const glu::Buffer mainTextureBuf(renderCtx);
3228     const glu::Texture mainTexture(renderCtx);
3229     const glu::Texture shaderOutResultTexture(renderCtx);
3230 
3231     glLog.enableLogging(true);
3232 
3233     // Setup textures.
3234 
3235     log << TestLog::Message << "// Created a texture (name " << *mainTexture << ")" << TestLog::EndMessage;
3236     if (m_imageType == TEXTURETYPE_BUFFER)
3237         log << TestLog::Message << "// Created a buffer for the texture (name " << *mainTextureBuf << ")"
3238             << TestLog::EndMessage;
3239     log << TestLog::Message << "// Created a texture (name " << *shaderOutResultTexture
3240         << ") for storing the shader output" << TestLog::EndMessage;
3241 
3242     glLog.glActiveTexture(GL_TEXTURE0);
3243     glLog.glBindTexture(textureTargetGL, *mainTexture);
3244     setTexParameteri(glLog, textureTargetGL);
3245     setTextureStorage(glLog, m_imageType, internalFormatGL, m_imageSize, *mainTextureBuf);
3246     glLog.glBindImageTexture(0, *mainTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
3247     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
3248 
3249     glLog.glActiveTexture(GL_TEXTURE1);
3250     glLog.glBindTexture(GL_TEXTURE_2D, *shaderOutResultTexture);
3251     setTexParameteri(glLog, GL_TEXTURE_2D);
3252     setTextureStorage(glLog, TEXTURETYPE_2D, GL_R32UI, IVec3(1, 1, 1), 0 /* always 2d texture, no buffer needed */);
3253     glLog.glBindImageTexture(1, *shaderOutResultTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_R32UI);
3254     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
3255 
3256     // Read texture size in compute shader.
3257 
3258     {
3259         // Generate compute shader.
3260 
3261         const char *const shaderImageAccessStr = m_imageAccess == IMAGEACCESS_READ_ONLY  ? "readonly" :
3262                                                  m_imageAccess == IMAGEACCESS_WRITE_ONLY ? "writeonly" :
3263                                                  m_imageAccess == IMAGEACCESS_READ_ONLY_WRITE_ONLY ?
3264                                                                                            "readonly writeonly" :
3265                                                                                            DE_NULL;
3266         const string shaderImageFormatStr      = getShaderImageFormatQualifier(m_format);
3267         const string shaderImageTypeStr        = getShaderImageType(m_format.type, m_imageType);
3268         const string glslVersionDeclaration =
3269             glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
3270 
3271         const glu::ShaderProgram program(
3272             renderCtx,
3273             glu::ProgramSources() << glu::ComputeSource(
3274                 glslVersionDeclaration + "\n" + textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
3275                 "\n"
3276                 "precision highp " +
3277                 shaderImageTypeStr +
3278                 ";\n"
3279                 "precision highp uimage2D;\n"
3280                 "\n"
3281                 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
3282                 "layout (" +
3283                 shaderImageFormatStr + ", binding=0) " + shaderImageAccessStr + " uniform " + shaderImageTypeStr +
3284                 " u_image;\n"
3285                 "layout (r32ui, binding=1) writeonly uniform uimage2D u_result;\n"
3286                 "void main (void)\n"
3287                 "{\n" +
3288                 (m_imageType == TEXTURETYPE_BUFFER ? "    int result = imageSize(u_image);\n" :
3289                  m_imageType == TEXTURETYPE_2D || m_imageType == TEXTURETYPE_CUBE ?
3290                                                      "    ivec2 size = imageSize(u_image);\n"
3291                                                      "    int result = size.y*1000 + size.x;\n" :
3292                  m_imageType == TEXTURETYPE_3D || m_imageType == TEXTURETYPE_2D_ARRAY ?
3293                                                      "    ivec3 size = imageSize(u_image);\n"
3294                                                      "    int result = size.z*1000000 + size.y*1000 + size.x;\n" :
3295                                                      DE_NULL) +
3296                 "    imageStore(u_result, ivec2(0, 0), uvec4(result));\n"
3297                 "}\n"));
3298 
3299         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
3300 
3301         log << program;
3302 
3303         if (!program.isOk())
3304         {
3305             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
3306             return STOP;
3307         }
3308 
3309         // Setup and dispatch.
3310 
3311         glLog.glUseProgram(program.getProgram());
3312 
3313         glLog.glDispatchCompute(1, 1, 1);
3314         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
3315     }
3316 
3317     // Read texture and compare to reference.
3318 
3319     {
3320         const uint32_t referenceOutput =
3321             m_imageType == TEXTURETYPE_BUFFER ?
3322                 (uint32_t)(m_imageSize.x()) :
3323             m_imageType == TEXTURETYPE_2D || m_imageType == TEXTURETYPE_CUBE ?
3324                 (uint32_t)(m_imageSize.y() * 1000 + m_imageSize.x()) :
3325             m_imageType == TEXTURETYPE_3D || m_imageType == TEXTURETYPE_2D_ARRAY ?
3326                 (uint32_t)(m_imageSize.z() * 1000000 + m_imageSize.y() * 1000 + m_imageSize.x()) :
3327                 (uint32_t)-1;
3328 
3329         if (readIntegerTextureViaFBOAndVerify(renderCtx, glLog, *shaderOutResultTexture, TEXTURETYPE_2D,
3330                                               TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
3331                                               IVec3(1, 1, 1), R32UIImageSingleValueVerifier(referenceOutput)))
3332             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3333         else
3334             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong value");
3335 
3336         return STOP;
3337     }
3338 }
3339 
3340 //! Case testing the control over early/late fragment tests.
3341 class EarlyFragmentTestsCase : public TestCase
3342 {
3343 public:
3344     enum TestType
3345     {
3346         TESTTYPE_DEPTH = 0,
3347         TESTTYPE_STENCIL,
3348 
3349         TESTTYPE_LAST
3350     };
3351 
3352     enum RenderTargetType
3353     {
3354         RENDERTARGET_DEFAULT = 0,
3355         RENDERTARGET_FBO,
3356         RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT,
3357 
3358         RENDERTARGET_LAST
3359     };
3360 
EarlyFragmentTestsCase(Context & context,const char * name,const char * description,TestType type,bool useEarlyTests,RenderTargetType renderTarget)3361     EarlyFragmentTestsCase(Context &context, const char *name, const char *description, TestType type,
3362                            bool useEarlyTests, RenderTargetType renderTarget)
3363         : TestCase(context, name, description)
3364         , m_type(type)
3365         , m_useEarlyTests(useEarlyTests)
3366         , m_renderTarget(renderTarget)
3367     {
3368     }
3369 
init(void)3370     void init(void)
3371     {
3372         if (m_context.getContextInfo().getInt(GL_MAX_FRAGMENT_IMAGE_UNIFORMS) == 0)
3373             throw tcu::NotSupportedError("GL_MAX_FRAGMENT_IMAGE_UNIFORMS is zero");
3374 
3375         if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic") &&
3376             !supportsES32orGL45(m_context.getRenderContext()))
3377             throw tcu::NotSupportedError("Test requires OES_shader_image_atomic extension");
3378 
3379         if (m_type == TESTTYPE_DEPTH && m_renderTarget == RENDERTARGET_DEFAULT &&
3380             m_context.getRenderTarget().getDepthBits() == 0)
3381         {
3382             throw tcu::NotSupportedError("Test requires depth buffer");
3383         }
3384 
3385         if (m_type == TESTTYPE_STENCIL && m_renderTarget == RENDERTARGET_DEFAULT &&
3386             m_context.getRenderTarget().getStencilBits() == 0)
3387         {
3388             throw tcu::NotSupportedError("Test requires stencil buffer");
3389         }
3390 
3391         if (m_renderTarget == RENDERTARGET_DEFAULT && (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
3392                                                        m_context.getRenderTarget().getHeight() < RENDER_SIZE))
3393             throw tcu::NotSupportedError("Render target must have at least " + toString(RENDER_SIZE) +
3394                                          " width and height");
3395     }
3396 
3397     IterateResult iterate(void);
3398 
3399 private:
3400     static const int RENDER_SIZE;
3401 
3402     const TestType m_type;
3403     const bool m_useEarlyTests;
3404     const RenderTargetType m_renderTarget;
3405 };
3406 
3407 const int EarlyFragmentTestsCase::RENDER_SIZE = 32;
3408 
iterate(void)3409 EarlyFragmentTestsCase::IterateResult EarlyFragmentTestsCase::iterate(void)
3410 {
3411     const RenderContext &renderCtx = m_context.getRenderContext();
3412     TestLog &log(m_testCtx.getLog());
3413     glu::CallLogWrapper glLog(renderCtx.getFunctions(), log);
3414     de::Random rnd(deStringHash(getName()));
3415     const bool expectPartialResult = m_useEarlyTests && m_renderTarget != RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT;
3416     const int viewportWidth        = RENDER_SIZE;
3417     const int viewportHeight       = RENDER_SIZE;
3418     const int viewportX            = (m_renderTarget == RENDERTARGET_DEFAULT) ?
3419                                          (rnd.getInt(0, renderCtx.getRenderTarget().getWidth() - viewportWidth)) :
3420                                          (0);
3421     const int viewportY            = (m_renderTarget == RENDERTARGET_DEFAULT) ?
3422                                          (rnd.getInt(0, renderCtx.getRenderTarget().getHeight() - viewportHeight)) :
3423                                          (0);
3424     const glu::Texture texture(renderCtx);
3425     de::MovePtr<glu::Framebuffer> fbo;
3426     de::MovePtr<glu::Renderbuffer> colorAttachment;
3427     de::MovePtr<glu::Renderbuffer> testAttachment;
3428 
3429     glLog.enableLogging(true);
3430 
3431     // Setup texture.
3432 
3433     log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
3434 
3435     glLog.glActiveTexture(GL_TEXTURE0);
3436     glLog.glBindTexture(GL_TEXTURE_2D, *texture);
3437     setTexParameteri(glLog, GL_TEXTURE_2D);
3438     {
3439         LayeredImage src(TEXTURETYPE_2D, TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32), 1, 1, 1);
3440         src.setPixel(0, 0, 0, IVec4(0));
3441         uploadTexture(glLog, src, 0 /* always 2d texture, no buffer needed */);
3442     }
3443     glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32UI);
3444     GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
3445 
3446     // Set up framebuffer
3447     if (m_renderTarget == RENDERTARGET_FBO || m_renderTarget == RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT)
3448     {
3449         fbo             = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(renderCtx));
3450         colorAttachment = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
3451         testAttachment  = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
3452 
3453         glLog.glBindRenderbuffer(GL_RENDERBUFFER, **colorAttachment);
3454         glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDER_SIZE, RENDER_SIZE);
3455         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen color attachment rb");
3456 
3457         glLog.glBindFramebuffer(GL_FRAMEBUFFER, **fbo);
3458         glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **colorAttachment);
3459         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo color attachment");
3460 
3461         if (m_renderTarget == RENDERTARGET_FBO && m_type == TESTTYPE_DEPTH)
3462         {
3463             glLog.glBindRenderbuffer(GL_RENDERBUFFER, **testAttachment);
3464             glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, RENDER_SIZE, RENDER_SIZE);
3465             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen depth attachment rb");
3466 
3467             glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, **testAttachment);
3468             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo depth attachment");
3469         }
3470         else if (m_renderTarget == RENDERTARGET_FBO && m_type == TESTTYPE_STENCIL)
3471         {
3472             glLog.glBindRenderbuffer(GL_RENDERBUFFER, **testAttachment);
3473             glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, RENDER_SIZE, RENDER_SIZE);
3474             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen stencil attachment rb");
3475 
3476             glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, **testAttachment);
3477             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo stencil attachment");
3478         }
3479 
3480         glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **colorAttachment);
3481         GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "setup fbo");
3482         TCU_CHECK(glLog.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
3483     }
3484 
3485     // Set up appropriate conditions for the test.
3486 
3487     glLog.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
3488     glLog.glClear(GL_COLOR_BUFFER_BIT);
3489 
3490     if (m_type == TESTTYPE_DEPTH)
3491     {
3492         glLog.glClearDepthf(0.5f);
3493         glLog.glClear(GL_DEPTH_BUFFER_BIT);
3494         glLog.glEnable(GL_DEPTH_TEST);
3495     }
3496     else if (m_type == TESTTYPE_STENCIL)
3497     {
3498         glLog.glClearStencil(0);
3499         glLog.glClear(GL_STENCIL_BUFFER_BIT);
3500         glLog.glScissor(viewportX, viewportY, viewportWidth / 2, viewportHeight);
3501         glLog.glEnable(GL_SCISSOR_TEST);
3502         glLog.glClearStencil(1);
3503         glLog.glClear(GL_STENCIL_BUFFER_BIT);
3504         glLog.glDisable(GL_SCISSOR_TEST);
3505         glLog.glStencilFunc(GL_EQUAL, 1, 1);
3506         glLog.glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
3507         glLog.glEnable(GL_STENCIL_TEST);
3508     }
3509     else
3510         DE_ASSERT(false);
3511 
3512     // Perform image stores in fragment shader.
3513 
3514     {
3515         const std::string glslVersionDeclaration =
3516             glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
3517 
3518         // Generate fragment shader.
3519 
3520         const glu::ShaderProgram program(
3521             renderCtx, glu::ProgramSources()
3522                            << glu::VertexSource(glslVersionDeclaration + "\n"
3523                                                                          "\n"
3524                                                                          "highp in vec3 a_position;\n"
3525                                                                          "\n"
3526                                                                          "void main (void)\n"
3527                                                                          "{\n"
3528                                                                          "    gl_Position = vec4(a_position, 1.0);\n"
3529                                                                          "}\n")
3530 
3531                            << glu::FragmentSource(
3532                                   glslVersionDeclaration + "\n" + imageAtomicExtensionShaderRequires(renderCtx) + "\n" +
3533                                   string(m_useEarlyTests ? "layout (early_fragment_tests) in;\n\n" : "") +
3534                                   "layout (location = 0) out highp vec4 o_color;\n"
3535                                   "\n"
3536                                   "precision highp uimage2D;\n"
3537                                   "\n"
3538                                   "layout (r32ui, binding=0) coherent uniform uimage2D u_image;\n"
3539                                   "\n"
3540                                   "void main (void)\n"
3541                                   "{\n"
3542                                   "    imageAtomicAdd(u_image, ivec2(0, 0), uint(1));\n"
3543                                   "    o_color = vec4(1.0);\n"
3544                                   "}\n"));
3545 
3546         UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
3547 
3548         log << program;
3549 
3550         if (!program.isOk())
3551         {
3552             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
3553             return STOP;
3554         }
3555 
3556         // Setup and draw full-viewport quad.
3557 
3558         glLog.glUseProgram(program.getProgram());
3559 
3560         {
3561             static const float vertexPositions[4 * 3] = {
3562                 -1.0, -1.0, -1.0f, 1.0, -1.0, 0.0f, -1.0, 1.0, 0.0f, 1.0, 1.0, 1.0f,
3563             };
3564 
3565             static const uint16_t indices[6] = {0, 1, 2, 2, 1, 3};
3566 
3567             const glu::VertexArrayBinding attrBindings[] = {glu::va::Float("a_position", 3, 4, 0, &vertexPositions[0])};
3568 
3569             glLog.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
3570 
3571             glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3572                       glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
3573             GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "Draw failed");
3574         }
3575     }
3576 
3577     // Log rendered result for convenience.
3578     {
3579         tcu::Surface rendered(viewportWidth, viewportHeight);
3580         glu::readPixels(renderCtx, viewportX, viewportY, rendered.getAccess());
3581         log << TestLog::Image("Rendered", "Rendered image", rendered);
3582     }
3583 
3584     // Read counter value and check.
3585     {
3586         const int numSamples = de::max(1, renderCtx.getRenderTarget().getNumSamples());
3587         const int expectedCounter =
3588             expectPartialResult ? viewportWidth * viewportHeight / 2 : viewportWidth * viewportHeight;
3589         const int tolerance   = expectPartialResult ? de::max(viewportWidth, viewportHeight) * 3 : 0;
3590         const int expectedMin = de::max(0, expectedCounter - tolerance);
3591         const int expectedMax = (expectedCounter + tolerance) * numSamples;
3592 
3593         if (readIntegerTextureViaFBOAndVerify(renderCtx, glLog, *texture, TEXTURETYPE_2D,
3594                                               TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
3595                                               IVec3(1, 1, 1), R32UIImageSingleValueVerifier(expectedMin, expectedMax)))
3596             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3597         else
3598             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong value");
3599 
3600         return STOP;
3601     }
3602 }
3603 
3604 } // namespace
3605 
ShaderImageLoadStoreTests(Context & context)3606 ShaderImageLoadStoreTests::ShaderImageLoadStoreTests(Context &context)
3607     : TestCaseGroup(context, "image_load_store", "Shader Image Load & Store Tests")
3608 {
3609 }
3610 
~ShaderImageLoadStoreTests(void)3611 ShaderImageLoadStoreTests::~ShaderImageLoadStoreTests(void)
3612 {
3613 }
3614 
init(void)3615 void ShaderImageLoadStoreTests::init(void)
3616 {
3617     // Per-image-type tests.
3618 
3619     {
3620         static const TextureType imageTypes[] = {TEXTURETYPE_2D, TEXTURETYPE_CUBE, TEXTURETYPE_3D, TEXTURETYPE_2D_ARRAY,
3621                                                  TEXTURETYPE_BUFFER};
3622 
3623         static const TextureFormat formats[] = {TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT),
3624                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::HALF_FLOAT),
3625                                                 TextureFormat(TextureFormat::R, TextureFormat::FLOAT),
3626 
3627                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32),
3628                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT16),
3629                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT8),
3630                                                 TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
3631 
3632                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT32),
3633                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT16),
3634                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT8),
3635                                                 TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32),
3636 
3637                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8),
3638 
3639                                                 TextureFormat(TextureFormat::RGBA, TextureFormat::SNORM_INT8)};
3640 
3641         for (int imageTypeNdx = 0; imageTypeNdx < DE_LENGTH_OF_ARRAY(imageTypes); imageTypeNdx++)
3642         {
3643             const TextureType imageType         = imageTypes[imageTypeNdx];
3644             TestCaseGroup *const imageTypeGroup = new TestCaseGroup(m_context, getTextureTypeName(imageType), "");
3645             addChild(imageTypeGroup);
3646 
3647             TestCaseGroup *const storeGroup = new TestCaseGroup(m_context, "store", "Plain imageStore() cases");
3648             TestCaseGroup *const loadStoreGroup =
3649                 new TestCaseGroup(m_context, "load_store", "Cases with imageLoad() followed by imageStore()");
3650             TestCaseGroup *const atomicGroup = new TestCaseGroup(m_context, "atomic", "Atomic image operation cases");
3651             TestCaseGroup *const qualifierGroup =
3652                 new TestCaseGroup(m_context, "qualifiers", "Coherent, volatile and restrict");
3653             TestCaseGroup *const reinterpretGroup =
3654                 new TestCaseGroup(m_context, "format_reinterpret", "Cases with differing texture and image formats");
3655             TestCaseGroup *const imageSizeGroup = new TestCaseGroup(m_context, "image_size", "imageSize() cases");
3656             imageTypeGroup->addChild(storeGroup);
3657             imageTypeGroup->addChild(loadStoreGroup);
3658             imageTypeGroup->addChild(atomicGroup);
3659             imageTypeGroup->addChild(qualifierGroup);
3660             imageTypeGroup->addChild(reinterpretGroup);
3661             imageTypeGroup->addChild(imageSizeGroup);
3662 
3663             for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
3664             {
3665                 const TextureFormat &format = formats[formatNdx];
3666                 const string formatName     = getShaderImageFormatQualifier(formats[formatNdx]);
3667 
3668                 if (imageType == TEXTURETYPE_BUFFER && !isFormatSupportedForTextureBuffer(format))
3669                     continue;
3670 
3671                 // Store cases.
3672 
3673                 storeGroup->addChild(new ImageStoreCase(m_context, formatName.c_str(), "", format, imageType));
3674                 if (textureLayerType(imageType) != imageType)
3675                     storeGroup->addChild(new ImageStoreCase(m_context, (formatName + "_single_layer").c_str(), "",
3676                                                             format, imageType,
3677                                                             ImageStoreCase::CASEFLAG_SINGLE_LAYER_BIND));
3678 
3679                 // Load & store.
3680 
3681                 loadStoreGroup->addChild(
3682                     new ImageLoadAndStoreCase(m_context, formatName.c_str(), "", format, imageType));
3683                 if (textureLayerType(imageType) != imageType)
3684                     loadStoreGroup->addChild(
3685                         new ImageLoadAndStoreCase(m_context, (formatName + "_single_layer").c_str(), "", format,
3686                                                   imageType, ImageLoadAndStoreCase::CASEFLAG_SINGLE_LAYER_BIND));
3687 
3688                 if (format.order == TextureFormat::R)
3689                 {
3690                     // Atomic operations.
3691 
3692                     for (int operationI = 0; operationI < ATOMIC_OPERATION_LAST; operationI++)
3693                     {
3694                         for (int atomicCaseTypeI = 0; atomicCaseTypeI < ATOMIC_OPERATION_CASE_TYPE_LAST;
3695                              atomicCaseTypeI++)
3696                         {
3697                             const AtomicOperation operation = (AtomicOperation)operationI;
3698 
3699                             if (format.type == TextureFormat::FLOAT && operation != ATOMIC_OPERATION_EXCHANGE)
3700                                 continue;
3701 
3702                             const AtomicOperationCaseType caseType = (AtomicOperationCaseType)atomicCaseTypeI;
3703                             const string caseTypeName =
3704                                 caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT    ? "result" :
3705                                 caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? "return_value" :
3706                                                                                        DE_NULL;
3707                             const string caseName = string() + getAtomicOperationCaseName(operation) + "_" +
3708                                                     formatName + "_" + caseTypeName;
3709 
3710                             if (operation == ATOMIC_OPERATION_COMP_SWAP)
3711                                 atomicGroup->addChild(new AtomicCompSwapCase(m_context, caseName.c_str(), "", format,
3712                                                                              imageType, caseType));
3713                             else
3714                                 atomicGroup->addChild(new BinaryAtomicOperationCase(
3715                                     m_context, caseName.c_str(), "", format, imageType, operation, caseType));
3716                         }
3717                     }
3718 
3719                     // Coherence.
3720 
3721                     for (int coherenceQualifierI = 0; coherenceQualifierI < CoherenceCase::QUALIFIER_LAST;
3722                          coherenceQualifierI++)
3723                     {
3724                         const CoherenceCase::Qualifier coherenceQualifier =
3725                             (CoherenceCase::Qualifier)coherenceQualifierI;
3726                         const char *const coherenceQualifierName =
3727                             coherenceQualifier == CoherenceCase::QUALIFIER_COHERENT ? "coherent" :
3728                             coherenceQualifier == CoherenceCase::QUALIFIER_VOLATILE ? "volatile" :
3729                                                                                       DE_NULL;
3730                         const string caseName = string() + coherenceQualifierName + "_" + formatName;
3731 
3732                         qualifierGroup->addChild(
3733                             new CoherenceCase(m_context, caseName.c_str(), "", format, imageType, coherenceQualifier));
3734                     }
3735                 }
3736             }
3737 
3738             // Restrict.
3739             qualifierGroup->addChild(new ImageLoadAndStoreCase(
3740                 m_context, "restrict", "", TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), imageType,
3741                 ImageLoadAndStoreCase::CASEFLAG_RESTRICT_IMAGES));
3742 
3743             // Format re-interpretation.
3744 
3745             for (int texFmtNdx = 0; texFmtNdx < DE_LENGTH_OF_ARRAY(formats); texFmtNdx++)
3746                 for (int imgFmtNdx = 0; imgFmtNdx < DE_LENGTH_OF_ARRAY(formats); imgFmtNdx++)
3747                 {
3748                     const TextureFormat &texFmt = formats[texFmtNdx];
3749                     const TextureFormat &imgFmt = formats[imgFmtNdx];
3750 
3751                     if (imageType == TEXTURETYPE_BUFFER && !isFormatSupportedForTextureBuffer(texFmt))
3752                         continue;
3753 
3754                     if (texFmt != imgFmt && texFmt.getPixelSize() == imgFmt.getPixelSize())
3755                         reinterpretGroup->addChild(new ImageLoadAndStoreCase(
3756                             m_context,
3757                             (getShaderImageFormatQualifier(texFmt) + "_" + getShaderImageFormatQualifier(imgFmt))
3758                                 .c_str(),
3759                             "", texFmt, imgFmt, imageType));
3760                 }
3761 
3762             // imageSize().
3763 
3764             {
3765                 static const IVec3 baseImageSizes[] = {IVec3(32, 32, 32), IVec3(12, 34, 56), IVec3(1, 1, 1),
3766                                                        IVec3(7, 1, 1)};
3767 
3768                 for (int imageAccessI = 0; imageAccessI < ImageSizeCase::IMAGEACCESS_LAST; imageAccessI++)
3769                 {
3770                     const ImageSizeCase::ImageAccess imageAccess = (ImageSizeCase::ImageAccess)imageAccessI;
3771                     const char *const imageAccessStr =
3772                         imageAccess == ImageSizeCase::IMAGEACCESS_READ_ONLY  ? "readonly" :
3773                         imageAccess == ImageSizeCase::IMAGEACCESS_WRITE_ONLY ? "writeonly" :
3774                         imageAccess == ImageSizeCase::IMAGEACCESS_READ_ONLY_WRITE_ONLY ?
3775                                                                                "readonly_writeonly" :
3776                                                                                deFatalStr("Invalid ImageAccess");
3777 
3778                     for (int imageSizeNdx = 0; imageSizeNdx < DE_LENGTH_OF_ARRAY(baseImageSizes); imageSizeNdx++)
3779                     {
3780                         const IVec3 &baseSize = baseImageSizes[imageSizeNdx];
3781                         const IVec3 imageSize = imageType == TEXTURETYPE_BUFFER ? IVec3(baseSize.x(), 1, 1) :
3782                                                 imageType == TEXTURETYPE_2D     ? IVec3(baseSize.x(), baseSize.y(), 1) :
3783                                                 imageType == TEXTURETYPE_CUBE   ? IVec3(baseSize.x(), baseSize.x(), 1) :
3784                                                 imageType == TEXTURETYPE_3D     ? baseSize :
3785                                                 imageType == TEXTURETYPE_2D_ARRAY ? baseSize :
3786                                                                                     IVec3(-1, -1, -1);
3787 
3788                         const string sizeStr =
3789                             imageType == TEXTURETYPE_BUFFER ? toString(imageSize.x()) :
3790                             imageType == TEXTURETYPE_2D     ? toString(imageSize.x()) + "x" + toString(imageSize.y()) :
3791                             imageType == TEXTURETYPE_CUBE   ? toString(imageSize.x()) + "x" + toString(imageSize.y()) :
3792                             imageType == TEXTURETYPE_3D     ? toString(imageSize.x()) + "x" + toString(imageSize.y()) +
3793                                                               "x" + toString(imageSize.z()) :
3794                             imageType == TEXTURETYPE_2D_ARRAY ?
3795                                                           toString(imageSize.x()) + "x" + toString(imageSize.y()) +
3796                                                               "x" + toString(imageSize.z()) :
3797                                                           deFatalStr("Invalid TextureType");
3798 
3799                         const string caseName = string() + imageAccessStr + "_" + sizeStr;
3800 
3801                         imageSizeGroup->addChild(new ImageSizeCase(
3802                             m_context, caseName.c_str(), "", TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT),
3803                             imageType, imageSize, imageAccess));
3804                     }
3805                 }
3806             }
3807         }
3808     }
3809 
3810     // early_fragment_tests cases.
3811 
3812     {
3813         TestCaseGroup *const earlyTestsGroup = new TestCaseGroup(m_context, "early_fragment_tests", "");
3814         addChild(earlyTestsGroup);
3815 
3816         for (int testRenderTargetI = 0; testRenderTargetI < EarlyFragmentTestsCase::RENDERTARGET_LAST;
3817              testRenderTargetI++)
3818             for (int useEarlyTestsI = 0; useEarlyTestsI <= 1; useEarlyTestsI++)
3819                 for (int testTypeI = 0; testTypeI < EarlyFragmentTestsCase::TESTTYPE_LAST; testTypeI++)
3820                 {
3821                     const EarlyFragmentTestsCase::RenderTargetType targetType =
3822                         (EarlyFragmentTestsCase::RenderTargetType)testRenderTargetI;
3823                     const bool useEarlyTests                        = useEarlyTestsI != 0;
3824                     const EarlyFragmentTestsCase::TestType testType = (EarlyFragmentTestsCase::TestType)testTypeI;
3825 
3826                     const string testTypeName = testType == EarlyFragmentTestsCase::TESTTYPE_DEPTH   ? "depth" :
3827                                                 testType == EarlyFragmentTestsCase::TESTTYPE_STENCIL ? "stencil" :
3828                                                                                                        DE_NULL;
3829 
3830                     const string targetName =
3831                         targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO ?
3832                             (std::string("_fbo")) :
3833                         targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT ?
3834                             (std::string("_fbo_with_no_") + testTypeName) :
3835                             std::string("");
3836 
3837                     const string caseName =
3838                         string(useEarlyTests ? "" : "no_") + "early_fragment_tests_" + testTypeName + targetName;
3839 
3840                     const string caseDesc =
3841                         string(useEarlyTests ? "Specify" : "Don't specify") + " early_fragment_tests, use the " +
3842                         testTypeName + " test" +
3843                         ((targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO) ?
3844                              (", render to fbo") :
3845                          (targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT) ?
3846                              (", render to fbo without relevant buffer") :
3847                              (""));
3848 
3849                     earlyTestsGroup->addChild(new EarlyFragmentTestsCase(m_context, caseName.c_str(), caseDesc.c_str(),
3850                                                                          testType, useEarlyTests, targetType));
3851                 }
3852     }
3853 }
3854 
3855 } // namespace Functional
3856 } // namespace gles31
3857 } // namespace deqp
3858