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