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