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