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