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