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 the smallest value (among A, B, C, ...) bigger than the one just before it.
2330 // The only valid sequence being: A B C D E F G H
2331
2332 {
2333 int failingNdx = -1;
2334
2335 {
2336 int currentAtomicValueNdx = 0;
2337 for (int i = 0; i < m_numInvocationsPerPixel; i++)
2338 {
2339 if (returnValues[i] == compareArgs[currentAtomicValueNdx])
2340 continue;
2341 if (i > 0 && returnValues[i] == compareArgs[currentAtomicValueNdx+1])
2342 {
2343 currentAtomicValueNdx++;
2344 continue;
2345 }
2346 failingNdx = i;
2347 break;
2348 }
2349 }
2350
2351 if (failingNdx >= 0)
2352 {
2353 log << TestLog::Message << "// Failure: intermediate return values at pixels " << arrayStr(pixelCoords) << " of current layer are "
2354 << arrayStr(returnValues) << TestLog::EndMessage
2355 << TestLog::Message << "// Note: relevant shader invocation global IDs are " << arrayStr(invocationGlobalIDs) << TestLog::EndMessage
2356 << TestLog::Message << "// Note: 'compare' argument values for the IDs are " << arrayStr(compareArgs) << TestLog::EndMessage
2357 << TestLog::Message << "// Note: expected the return value sequence to fulfill the following conditions:\n"
2358 << "// - first value is " << compareArgs[0] << "\n"
2359 << "// - each value other than the first is either the same as the one just before it, or the smallest value (in the sequence "
2360 << arrayStr(compareArgs) << ") bigger than the one just before it" << TestLog::EndMessage;
2361 if (failingNdx == 0)
2362 log << TestLog::Message << "// Note: the first return value (" << returnValues[0] << ") isn't " << compareArgs[0] << TestLog::EndMessage;
2363 else
2364 log << TestLog::Message << "// Note: the return value at index " << failingNdx << " (value " << returnValues[failingNdx] << ") "
2365 << "is neither " << returnValues[failingNdx-1] << " (the one just before it) "
2366 << "nor " << compareArgs[arrayIndexOf(compareArgs, returnValues[failingNdx-1])+1] << " (the smallest value bigger than the one just before it)"
2367 << TestLog::EndMessage;
2368
2369 return false;
2370 }
2371 }
2372 }
2373
2374 return true;
2375 }
2376
2377 private:
2378 const TextureType m_imageType;
2379 const int m_endResultImageWidth;
2380 const int m_numInvocationsPerPixel;
2381 };
2382
iterate(void)2383 AtomicCompSwapCase::IterateResult AtomicCompSwapCase::iterate (void)
2384 {
2385 const RenderContext& renderCtx = m_context.getRenderContext();
2386 TestLog& log (m_testCtx.getLog());
2387 glu::CallLogWrapper glLog (renderCtx.getFunctions(), log);
2388 const deUint32 internalFormatGL = glu::getInternalFormat(m_format);
2389 const deUint32 textureTargetGL = getGLTextureTarget(m_imageType);
2390 const IVec3& imageSize = defaultImageSize(m_imageType);
2391 const int numSlicesOrFaces = m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
2392 const bool isUintFormat = isFormatTypeUnsignedInteger(m_format.type);
2393 const bool isIntFormat = isFormatTypeSignedInteger(m_format.type);
2394 const glu::Buffer endResultTextureBuf (renderCtx);
2395 const glu::Buffer returnValueTextureBuf (renderCtx);
2396 const glu::Texture endResultTexture (renderCtx); //!< Texture for the final result; i.e. the texture on which the atomic operations are done. Size imageSize.
2397 const glu::Texture returnValueTexture (renderCtx); //!< Texture into which the return values are stored if m_caseType == CASETYPE_RETURN_VALUES.
2398 // Size imageSize*IVec3(N, 1, 1) or, for cube maps, imageSize*IVec3(N, N, 1) where N is NUM_INVOCATIONS_PER_PIXEL.
2399
2400 DE_ASSERT(isUintFormat || isIntFormat);
2401
2402 glLog.enableLogging(true);
2403
2404 // Adjust result image size for result image
2405 if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2406 {
2407 int maxWidth = getGLTextureMaxSize(glLog, m_imageType);
2408
2409 while (maxWidth < m_numInvocationsPerPixel * imageSize.x())
2410 {
2411 int* numInvocationsPerPixel = const_cast<int*>(&m_numInvocationsPerPixel);
2412 (*numInvocationsPerPixel) -= 1;
2413 }
2414 }
2415
2416 // Setup textures.
2417
2418 log << TestLog::Message << "// Created a texture (name " << *endResultTexture << ") to act as the target of atomic operations" << TestLog::EndMessage;
2419 if (m_imageType == TEXTURETYPE_BUFFER)
2420 log << TestLog::Message << "// Created a buffer for the texture (name " << *endResultTextureBuf << ")" << TestLog::EndMessage;
2421
2422 if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2423 {
2424 log << TestLog::Message << "// Created a texture (name " << *returnValueTexture << ") to which the intermediate return values of the atomic operation are stored" << TestLog::EndMessage;
2425 if (m_imageType == TEXTURETYPE_BUFFER)
2426 log << TestLog::Message << "// Created a buffer for the texture (name " << *returnValueTextureBuf << ")" << TestLog::EndMessage;
2427 }
2428
2429 // Fill endResultTexture with initial pattern.
2430
2431 {
2432 const LayeredImage imageData(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2433
2434 {
2435 for (int z = 0; z < numSlicesOrFaces; z++)
2436 for (int y = 0; y < imageSize.y(); y++)
2437 for (int x = 0; x < imageSize.x(); x++)
2438 imageData.setPixel(x, y, z, IVec4(getCompareArg(IVec3(x, y, z), imageSize.x())));
2439 }
2440
2441 // Upload initial pattern to endResultTexture and bind to image.
2442
2443 glLog.glActiveTexture(GL_TEXTURE0);
2444 glLog.glBindTexture(textureTargetGL, *endResultTexture);
2445 setTexParameteri(glLog, textureTargetGL);
2446
2447 log << TestLog::Message << "// Filling end-result texture with initial pattern" << TestLog::EndMessage;
2448
2449 uploadTexture(glLog, imageData, *endResultTextureBuf);
2450 }
2451
2452 glLog.glBindImageTexture(0, *endResultTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2453 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2454
2455 if (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES)
2456 {
2457 // Set storage for returnValueTexture and bind to image.
2458
2459 glLog.glActiveTexture(GL_TEXTURE1);
2460 glLog.glBindTexture(textureTargetGL, *returnValueTexture);
2461 setTexParameteri(glLog, textureTargetGL);
2462
2463 log << TestLog::Message << "// Setting storage of return-value texture" << TestLog::EndMessage;
2464 setTextureStorage(glLog, m_imageType, internalFormatGL, imageSize * (m_imageType == TEXTURETYPE_CUBE ? IVec3(m_numInvocationsPerPixel, m_numInvocationsPerPixel, 1)
2465 : IVec3(m_numInvocationsPerPixel, 1, 1)),
2466 *returnValueTextureBuf);
2467
2468 glLog.glBindImageTexture(1, *returnValueTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, internalFormatGL);
2469 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2470 }
2471
2472 // Perform atomics in compute shader.
2473
2474 {
2475 // Generate compute shader.
2476
2477 const string colorScalarTypeName = isUintFormat ? "uint" : isIntFormat ? "int" : DE_NULL;
2478 const string colorVecTypeName = string(isUintFormat ? "u" : isIntFormat ? "i" : DE_NULL) + "vec4";
2479 const string atomicCoord = m_imageType == TEXTURETYPE_BUFFER ? "gx % " + toString(imageSize.x())
2480 : m_imageType == TEXTURETYPE_2D ? "ivec2(gx % " + toString(imageSize.x()) + ", gy)"
2481 : "ivec3(gx % " + toString(imageSize.x()) + ", gy, gz)";
2482 const string invocationCoord = m_imageType == TEXTURETYPE_BUFFER ? "gx"
2483 : m_imageType == TEXTURETYPE_2D ? "ivec2(gx, gy)"
2484 : "ivec3(gx, gy, gz)";
2485 const string shaderImageFormatStr = getShaderImageFormatQualifier(m_format);
2486 const string shaderImageTypeStr = getShaderImageType(m_format.type, m_imageType);
2487 const string glslVersionDeclaration = glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2488
2489 const glu::ShaderProgram program(renderCtx,
2490 glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
2491 + imageAtomicExtensionShaderRequires(renderCtx)
2492 + textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2493 "\n"
2494 "precision highp " + shaderImageTypeStr + ";\n"
2495 "\n"
2496 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2497 "layout (" + shaderImageFormatStr + ", binding=0) coherent uniform " + shaderImageTypeStr + " u_results;\n"
2498 + (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2499 "layout (" + shaderImageFormatStr + ", binding=1) writeonly uniform " + shaderImageTypeStr + " u_returnValues;\n"
2500 : "") +
2501 "uniform int offset;"
2502 "\n"
2503 "void main (void)\n"
2504 "{\n"
2505 " int gx = int(gl_GlobalInvocationID.x) + offset * " + std::to_string(imageSize.x()) + ";\n"
2506 " int gy = int(gl_GlobalInvocationID.y);\n"
2507 " int gz = int(gl_GlobalInvocationID.z);\n"
2508 " " + colorScalarTypeName + " compare = " + colorScalarTypeName + getCompareArgShaderStr("gx", "gy", "gz", imageSize.x()) + ";\n"
2509 " " + colorScalarTypeName + " data = " + colorScalarTypeName + getAssignArgShaderStr("gx", "gy", "gz", imageSize.x()) + ";\n"
2510 " " + colorScalarTypeName + " status = " + colorScalarTypeName + "(-1);\n"
2511 " status = imageAtomicCompSwap(u_results, " + atomicCoord + ", compare, data);\n"
2512 + (m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ?
2513 // Ensure there's an ordered ascending pattern to correctly check values are being stored in order
2514 " if(compare == status) 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 {
2533 deUint32 offsetLocation = glLog.glGetUniformLocation(program.getProgram(), "offset");
2534 int dispatchCount = m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? m_numInvocationsPerPixel : 1u;
2535
2536 for (int i = 0; i < dispatchCount; ++i)
2537 {
2538 // Ensure we get correct values for output
2539 glLog.glUniform1i(offsetLocation, i);
2540 glLog.glDispatchCompute((dispatchCount - i)*imageSize.x(), imageSize.y(), numSlicesOrFaces);
2541 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2542 }
2543 }
2544 }
2545
2546 // Create reference, read texture and compare.
2547
2548 {
2549 const deUint32 textureToCheckGL = m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? *endResultTexture
2550 : m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? *returnValueTexture
2551 : (deUint32)-1;
2552
2553 const deUint32 textureToCheckBufGL = m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? *endResultTextureBuf
2554 : m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? *returnValueTextureBuf
2555 : (deUint32)-1;
2556
2557 // The relevant region of the texture being checked (potentially
2558 // different from actual texture size for cube maps, because cube maps
2559 // may have unused pixels due to square size restriction).
2560 const IVec3 relevantRegion = imageSize * (m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? IVec3(1, 1, 1)
2561 : IVec3(m_numInvocationsPerPixel, 1, 1));
2562
2563 const UniquePtr<const ImageLayerVerifier> verifier (m_caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? new EndResultVerifier(m_imageType, imageSize.x(), m_numInvocationsPerPixel)
2564 : m_caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? new ReturnValueVerifier(m_imageType, imageSize.x(), m_numInvocationsPerPixel)
2565 : (ImageLayerVerifier*)DE_NULL);
2566
2567 if (readTextureAndVerify(renderCtx, glLog, textureToCheckGL, textureToCheckBufGL, m_imageType, m_format, relevantRegion, *verifier))
2568 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2569 else
2570 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2571
2572 return STOP;
2573 }
2574 }
2575
2576 //! Case testing the "coherent" or "volatile" qualifier, along with memoryBarrier() and barrier().
2577 class CoherenceCase : public TestCase
2578 {
2579 public:
2580 enum Qualifier
2581 {
2582 QUALIFIER_COHERENT = 0,
2583 QUALIFIER_VOLATILE,
2584
2585 QUALIFIER_LAST
2586 };
2587
CoherenceCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType imageType,Qualifier qualifier)2588 CoherenceCase (Context& context, const char* name, const char* description, const TextureFormat& format, TextureType imageType, Qualifier qualifier)
2589 : TestCase (context, name, description)
2590 , m_format (format)
2591 , m_imageType (imageType)
2592 , m_qualifier (qualifier)
2593 {
2594 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_Y) == DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_X) &&
2595 DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_Z) == DE_LENGTH_OF_ARRAY(CoherenceCase::SHADER_READ_OFFSETS_X));
2596
2597 DE_ASSERT(qualifier == QUALIFIER_COHERENT || qualifier == QUALIFIER_VOLATILE);
2598
2599 DE_ASSERT(m_format == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32) ||
2600 m_format == TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32) ||
2601 m_format == TextureFormat(TextureFormat::R, TextureFormat::FLOAT));
2602 }
2603
init(void)2604 void init (void) { checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, m_context.getRenderContext()); }
2605 IterateResult iterate (void);
2606
2607 private:
2608 static const int SHADER_READ_OFFSETS_X[4];
2609 static const int SHADER_READ_OFFSETS_Y[4];
2610 static const int SHADER_READ_OFFSETS_Z[4];
2611 static const char* const SHADER_READ_OFFSETS_X_STR;
2612 static const char* const SHADER_READ_OFFSETS_Y_STR;
2613 static const char* const SHADER_READ_OFFSETS_Z_STR;
2614
2615 const TextureFormat m_format;
2616 const TextureType m_imageType;
2617 const Qualifier m_qualifier;
2618 };
2619
2620 const int CoherenceCase::SHADER_READ_OFFSETS_X[4] = { 1, 4, 7, 10 };
2621 const int CoherenceCase::SHADER_READ_OFFSETS_Y[4] = { 2, 5, 8, 11 };
2622 const int CoherenceCase::SHADER_READ_OFFSETS_Z[4] = { 3, 6, 9, 12 };
2623 const char* const CoherenceCase::SHADER_READ_OFFSETS_X_STR = "int[]( 1, 4, 7, 10 )";
2624 const char* const CoherenceCase::SHADER_READ_OFFSETS_Y_STR = "int[]( 2, 5, 8, 11 )";
2625 const char* const CoherenceCase::SHADER_READ_OFFSETS_Z_STR = "int[]( 3, 6, 9, 12 )";
2626
iterate(void)2627 CoherenceCase::IterateResult CoherenceCase::iterate (void)
2628 {
2629 const RenderContext& renderCtx = m_context.getRenderContext();
2630 TestLog& log (m_testCtx.getLog());
2631 glu::CallLogWrapper glLog (renderCtx.getFunctions(), log);
2632 const deUint32 internalFormatGL = glu::getInternalFormat(m_format);
2633 const deUint32 textureTargetGL = getGLTextureTarget(m_imageType);
2634 const IVec3& imageSize = defaultImageSize(m_imageType);
2635 const int numSlicesOrFaces = m_imageType == TEXTURETYPE_CUBE ? 6 : imageSize.z();
2636 const bool isUintFormat = isFormatTypeUnsignedInteger(m_format.type);
2637 const bool isIntFormat = isFormatTypeSignedInteger(m_format.type);
2638 const char* const qualifierName = m_qualifier == QUALIFIER_COHERENT ? "coherent"
2639 : m_qualifier == QUALIFIER_VOLATILE ? "volatile"
2640 : DE_NULL;
2641 const glu::Buffer textureBuf (renderCtx);
2642 const glu::Texture texture (renderCtx);
2643 const IVec3 numGroups = IVec3(16, de::min(16, imageSize.y()), de::min(2, numSlicesOrFaces));
2644 const IVec3 workItemSize = IVec3(imageSize.x(), imageSize.y(), numSlicesOrFaces);
2645 const IVec3 localSize = workItemSize / numGroups;
2646 const IVec3 minReqMaxLocalSize = IVec3(128, 128, 64);
2647 const int minReqMaxLocalInvocations = 128;
2648
2649 DE_ASSERT(workItemSize == localSize*numGroups);
2650 DE_ASSERT(tcu::boolAll(tcu::lessThanEqual(localSize, minReqMaxLocalSize)));
2651 DE_ASSERT(localSize.x()*localSize.y()*localSize.z() <= minReqMaxLocalInvocations);
2652 DE_UNREF(minReqMaxLocalSize);
2653 DE_UNREF(minReqMaxLocalInvocations);
2654
2655 glLog.enableLogging(true);
2656
2657 // Setup texture.
2658
2659 log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
2660 if (m_imageType == TEXTURETYPE_BUFFER)
2661 log << TestLog::Message << "// Created a buffer for the texture (name " << *textureBuf << ")" << TestLog::EndMessage;
2662
2663 glLog.glActiveTexture(GL_TEXTURE0);
2664 glLog.glBindTexture(textureTargetGL, *texture);
2665 setTexParameteri(glLog, textureTargetGL);
2666 setTextureStorage(glLog, m_imageType, internalFormatGL, imageSize, *textureBuf);
2667 glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2668 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2669
2670 // Perform computations in compute shader.
2671
2672 {
2673 // Generate compute shader.
2674
2675 const string colorVecTypeName = string(isUintFormat ? "u" : isIntFormat ? "i" : "") + "vec4";
2676 const char* const colorScalarTypeName = isUintFormat ? "uint" : isIntFormat ? "int" : "float";
2677 const string invocationCoord = m_imageType == TEXTURETYPE_BUFFER ? "gx"
2678 : m_imageType == TEXTURETYPE_2D ? "ivec2(gx, gy)"
2679 : "ivec3(gx, gy, gz)";
2680 const string shaderImageFormatStr = getShaderImageFormatQualifier(m_format);
2681 const string shaderImageTypeStr = getShaderImageType(m_format.type, m_imageType);
2682 const string localSizeX = de::toString(localSize.x());
2683 const string localSizeY = de::toString(localSize.y());
2684 const string localSizeZ = de::toString(localSize.z());
2685 const std::string glslVersionDeclaration = glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2686
2687
2688 const glu::ShaderProgram program(renderCtx,
2689 glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
2690 + textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2691 "\n"
2692 "precision highp " + shaderImageTypeStr + ";\n"
2693 "\n"
2694 "layout (local_size_x = " + localSizeX
2695 + ", local_size_y = " + localSizeY
2696 + ", local_size_z = " + localSizeZ
2697 + ") in;\n"
2698 "layout (" + shaderImageFormatStr + ", binding=0) " + qualifierName + " uniform " + shaderImageTypeStr + " u_image;\n"
2699 "void main (void)\n"
2700 "{\n"
2701 " int gx = int(gl_GlobalInvocationID.x);\n"
2702 " int gy = int(gl_GlobalInvocationID.y);\n"
2703 " int gz = int(gl_GlobalInvocationID.z);\n"
2704 " imageStore(u_image, " + invocationCoord + ", " + colorVecTypeName + "(gx^gy^gz));\n"
2705 "\n"
2706 " memoryBarrier();\n"
2707 " barrier();\n"
2708 "\n"
2709 " " + colorScalarTypeName + " sum = " + colorScalarTypeName + "(0);\n"
2710 " int groupBaseX = gx/" + localSizeX + "*" + localSizeX + ";\n"
2711 " int groupBaseY = gy/" + localSizeY + "*" + localSizeY + ";\n"
2712 " int groupBaseZ = gz/" + localSizeZ + "*" + localSizeZ + ";\n"
2713 " int xOffsets[] = " + SHADER_READ_OFFSETS_X_STR + ";\n"
2714 " int yOffsets[] = " + SHADER_READ_OFFSETS_Y_STR + ";\n"
2715 " int zOffsets[] = " + SHADER_READ_OFFSETS_Z_STR + ";\n"
2716 " for (int i = 0; i < " + toString(DE_LENGTH_OF_ARRAY(SHADER_READ_OFFSETS_X)) + "; i++)\n"
2717 " {\n"
2718 " int readX = groupBaseX + (gx + xOffsets[i]) % " + localSizeX + ";\n"
2719 " int readY = groupBaseY + (gy + yOffsets[i]) % " + localSizeY + ";\n"
2720 " int readZ = groupBaseZ + (gz + zOffsets[i]) % " + localSizeZ + ";\n"
2721 " sum += imageLoad(u_image, " + (m_imageType == TEXTURETYPE_BUFFER ? "readX"
2722 : m_imageType == TEXTURETYPE_2D ? "ivec2(readX, readY)"
2723 : "ivec3(readX, readY, readZ)") + ").x;\n"
2724 " }\n"
2725 "\n"
2726 " memoryBarrier();\n"
2727 " barrier();\n"
2728 "\n"
2729 " imageStore(u_image, " + invocationCoord + ", " + colorVecTypeName + "(sum));\n"
2730 "}\n"));
2731
2732 UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2733
2734 log << program;
2735
2736 if (!program.isOk())
2737 {
2738 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2739 return STOP;
2740 }
2741
2742 // Setup and dispatch.
2743
2744 glLog.glUseProgram(program.getProgram());
2745
2746 glLog.glDispatchCompute(numGroups.x(), numGroups.y(), numGroups.z());
2747 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2748 }
2749
2750 // Create reference, read texture and compare.
2751
2752 {
2753 LayeredImage reference(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2754
2755 {
2756 LayeredImage base(m_imageType, m_format, imageSize.x(), imageSize.y(), imageSize.z());
2757 for (int z = 0; z < numSlicesOrFaces; z++)
2758 for (int y = 0; y < imageSize.y(); y++)
2759 for (int x = 0; x < imageSize.x(); x++)
2760 base.setPixel(x, y, z, IVec4(x^y^z));
2761
2762 for (int z = 0; z < numSlicesOrFaces; z++)
2763 for (int y = 0; y < imageSize.y(); y++)
2764 for (int x = 0; x < imageSize.x(); x++)
2765 {
2766 const int groupBaseX = x / localSize.x() * localSize.x();
2767 const int groupBaseY = y / localSize.y() * localSize.y();
2768 const int groupBaseZ = z / localSize.z() * localSize.z();
2769 int sum = 0;
2770 for (int i = 0; i < DE_LENGTH_OF_ARRAY(SHADER_READ_OFFSETS_X); i++)
2771 sum += base.getPixelInt(groupBaseX + (x + SHADER_READ_OFFSETS_X[i]) % localSize.x(),
2772 groupBaseY + (y + SHADER_READ_OFFSETS_Y[i]) % localSize.y(),
2773 groupBaseZ + (z + SHADER_READ_OFFSETS_Z[i]) % localSize.z()).x();
2774
2775 reference.setPixel(x, y, z, IVec4(sum));
2776 }
2777 }
2778
2779 if (readTextureAndVerify(renderCtx, glLog, *texture, *textureBuf, m_imageType, m_format, imageSize, ImageLayerComparer(reference)))
2780 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2781 else
2782 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
2783
2784 return STOP;
2785 }
2786 }
2787
2788 class R32UIImageSingleValueVerifier : public ImageLayerVerifier
2789 {
2790 public:
R32UIImageSingleValueVerifier(const deUint32 value)2791 R32UIImageSingleValueVerifier (const deUint32 value) : m_min(value), m_max(value) {}
R32UIImageSingleValueVerifier(const deUint32 min,const deUint32 max)2792 R32UIImageSingleValueVerifier (const deUint32 min, const deUint32 max) : m_min(min), m_max(max) {}
2793
operator ()(TestLog & log,const ConstPixelBufferAccess & resultSlice,int) const2794 bool operator() (TestLog& log, const ConstPixelBufferAccess& resultSlice, int) const
2795 {
2796 DE_ASSERT(resultSlice.getWidth() == 1 && resultSlice.getHeight() == 1 && resultSlice.getDepth() == 1);
2797 DE_ASSERT(resultSlice.getFormat() == TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32));
2798
2799 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;
2800
2801 const deUint32 resultValue = resultSlice.getPixelUint(0, 0).x();
2802 if (!de::inRange(resultValue, m_min, m_max))
2803 {
2804 log << TestLog::Message << "// Failure: got value " << resultValue << TestLog::EndMessage;
2805 return false;
2806 }
2807 else
2808 {
2809 log << TestLog::Message << "// Success: got value " << resultValue << TestLog::EndMessage;
2810 return true;
2811 }
2812 }
2813
2814 private:
2815 const deUint32 m_min;
2816 const deUint32 m_max;
2817 };
2818
2819 //! 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
2820 // can thus be qualifier readonly, writeonly, or both.
2821 class ImageSizeCase : public TestCase
2822 {
2823 public:
2824 enum ImageAccess
2825 {
2826 IMAGEACCESS_READ_ONLY = 0,
2827 IMAGEACCESS_WRITE_ONLY,
2828 IMAGEACCESS_READ_ONLY_WRITE_ONLY,
2829
2830 IMAGEACCESS_LAST
2831 };
2832
ImageSizeCase(Context & context,const char * name,const char * description,const TextureFormat & format,TextureType imageType,const IVec3 & size,ImageAccess imageAccess)2833 ImageSizeCase (Context& context, const char* name, const char* description, const TextureFormat& format, TextureType imageType, const IVec3& size, ImageAccess imageAccess)
2834 : TestCase (context, name, description)
2835 , m_format (format)
2836 , m_imageType (imageType)
2837 , m_imageSize (size)
2838 , m_imageAccess (imageAccess)
2839 {
2840 }
2841
init(void)2842 void init (void) { checkTextureTypeExtensions(m_context.getContextInfo(), m_imageType, m_context.getRenderContext()); }
2843 IterateResult iterate (void);
2844
2845 private:
2846 const TextureFormat m_format;
2847 const TextureType m_imageType;
2848 const IVec3 m_imageSize;
2849 const ImageAccess m_imageAccess;
2850 };
2851
iterate(void)2852 ImageSizeCase::IterateResult ImageSizeCase::iterate (void)
2853 {
2854 const RenderContext& renderCtx = m_context.getRenderContext();
2855 TestLog& log (m_testCtx.getLog());
2856 glu::CallLogWrapper glLog (renderCtx.getFunctions(), log);
2857 const deUint32 internalFormatGL = glu::getInternalFormat(m_format);
2858 const deUint32 textureTargetGL = getGLTextureTarget(m_imageType);
2859 const glu::Buffer mainTextureBuf (renderCtx);
2860 const glu::Texture mainTexture (renderCtx);
2861 const glu::Texture shaderOutResultTexture (renderCtx);
2862
2863 glLog.enableLogging(true);
2864
2865 // Setup textures.
2866
2867 log << TestLog::Message << "// Created a texture (name " << *mainTexture << ")" << TestLog::EndMessage;
2868 if (m_imageType == TEXTURETYPE_BUFFER)
2869 log << TestLog::Message << "// Created a buffer for the texture (name " << *mainTextureBuf << ")" << TestLog::EndMessage;
2870 log << TestLog::Message << "// Created a texture (name " << *shaderOutResultTexture << ") for storing the shader output" << TestLog::EndMessage;
2871
2872 glLog.glActiveTexture(GL_TEXTURE0);
2873 glLog.glBindTexture(textureTargetGL, *mainTexture);
2874 setTexParameteri(glLog, textureTargetGL);
2875 setTextureStorage(glLog, m_imageType, internalFormatGL, m_imageSize, *mainTextureBuf);
2876 glLog.glBindImageTexture(0, *mainTexture, 0, GL_TRUE, 0, GL_READ_WRITE, internalFormatGL);
2877 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2878
2879 glLog.glActiveTexture(GL_TEXTURE1);
2880 glLog.glBindTexture(GL_TEXTURE_2D, *shaderOutResultTexture);
2881 setTexParameteri(glLog, GL_TEXTURE_2D);
2882 setTextureStorage(glLog, TEXTURETYPE_2D, GL_R32UI, IVec3(1, 1, 1), 0 /* always 2d texture, no buffer needed */);
2883 glLog.glBindImageTexture(1, *shaderOutResultTexture, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_R32UI);
2884 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
2885
2886 // Read texture size in compute shader.
2887
2888 {
2889 // Generate compute shader.
2890
2891 const char* const shaderImageAccessStr = m_imageAccess == IMAGEACCESS_READ_ONLY ? "readonly"
2892 : m_imageAccess == IMAGEACCESS_WRITE_ONLY ? "writeonly"
2893 : m_imageAccess == IMAGEACCESS_READ_ONLY_WRITE_ONLY ? "readonly writeonly"
2894 : DE_NULL;
2895 const string shaderImageFormatStr = getShaderImageFormatQualifier(m_format);
2896 const string shaderImageTypeStr = getShaderImageType(m_format.type, m_imageType);
2897 const string glslVersionDeclaration = glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
2898
2899 const glu::ShaderProgram program(renderCtx,
2900 glu::ProgramSources() << glu::ComputeSource(glslVersionDeclaration + "\n"
2901 + textureTypeExtensionShaderRequires(m_imageType, renderCtx) +
2902 "\n"
2903 "precision highp " + shaderImageTypeStr + ";\n"
2904 "precision highp uimage2D;\n"
2905 "\n"
2906 "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
2907 "layout (" + shaderImageFormatStr + ", binding=0) " + shaderImageAccessStr + " uniform " + shaderImageTypeStr + " u_image;\n"
2908 "layout (r32ui, binding=1) writeonly uniform uimage2D u_result;\n"
2909 "void main (void)\n"
2910 "{\n"
2911 + (m_imageType == TEXTURETYPE_BUFFER ?
2912 " int result = imageSize(u_image);\n"
2913 : m_imageType == TEXTURETYPE_2D || m_imageType == TEXTURETYPE_CUBE ?
2914 " ivec2 size = imageSize(u_image);\n"
2915 " int result = size.y*1000 + size.x;\n"
2916 : m_imageType == TEXTURETYPE_3D || m_imageType == TEXTURETYPE_2D_ARRAY ?
2917 " ivec3 size = imageSize(u_image);\n"
2918 " int result = size.z*1000000 + size.y*1000 + size.x;\n"
2919 : DE_NULL) +
2920 " imageStore(u_result, ivec2(0, 0), uvec4(result));\n"
2921 "}\n"));
2922
2923 UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
2924
2925 log << program;
2926
2927 if (!program.isOk())
2928 {
2929 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
2930 return STOP;
2931 }
2932
2933 // Setup and dispatch.
2934
2935 glLog.glUseProgram(program.getProgram());
2936
2937 glLog.glDispatchCompute(1, 1, 1);
2938 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glDispatchCompute");
2939 }
2940
2941 // Read texture and compare to reference.
2942
2943 {
2944 const deUint32 referenceOutput = m_imageType == TEXTURETYPE_BUFFER ? (deUint32)( m_imageSize.x())
2945 : m_imageType == TEXTURETYPE_2D || m_imageType == TEXTURETYPE_CUBE ? (deUint32)( m_imageSize.y()*1000 + m_imageSize.x())
2946 : m_imageType == TEXTURETYPE_3D || m_imageType == TEXTURETYPE_2D_ARRAY ? (deUint32)(m_imageSize.z()*1000000 + m_imageSize.y()*1000 + m_imageSize.x())
2947 : (deUint32)-1;
2948
2949 if (readIntegerTextureViaFBOAndVerify(renderCtx, glLog, *shaderOutResultTexture, TEXTURETYPE_2D, TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
2950 IVec3(1, 1, 1), R32UIImageSingleValueVerifier(referenceOutput)))
2951 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2952 else
2953 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong value");
2954
2955 return STOP;
2956 }
2957 }
2958
2959 //! Case testing the control over early/late fragment tests.
2960 class EarlyFragmentTestsCase : public TestCase
2961 {
2962 public:
2963 enum TestType
2964 {
2965 TESTTYPE_DEPTH = 0,
2966 TESTTYPE_STENCIL,
2967
2968 TESTTYPE_LAST
2969 };
2970
2971 enum RenderTargetType
2972 {
2973 RENDERTARGET_DEFAULT = 0,
2974 RENDERTARGET_FBO,
2975 RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT,
2976
2977 RENDERTARGET_LAST
2978 };
2979
2980
EarlyFragmentTestsCase(Context & context,const char * name,const char * description,TestType type,bool useEarlyTests,RenderTargetType renderTarget)2981 EarlyFragmentTestsCase (Context& context, const char* name, const char* description, TestType type, bool useEarlyTests, RenderTargetType renderTarget)
2982 : TestCase (context, name, description)
2983 , m_type (type)
2984 , m_useEarlyTests (useEarlyTests)
2985 , m_renderTarget (renderTarget)
2986 {
2987 }
2988
init(void)2989 void init (void)
2990 {
2991 if (m_context.getContextInfo().getInt(GL_MAX_FRAGMENT_IMAGE_UNIFORMS) == 0)
2992 throw tcu::NotSupportedError("GL_MAX_FRAGMENT_IMAGE_UNIFORMS is zero");
2993
2994 if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_image_atomic") && !supportsES32orGL45(m_context.getRenderContext()))
2995 throw tcu::NotSupportedError("Test requires OES_shader_image_atomic extension");
2996
2997 if (m_type == TESTTYPE_DEPTH &&
2998 m_renderTarget == RENDERTARGET_DEFAULT &&
2999 m_context.getRenderTarget().getDepthBits() == 0)
3000 {
3001 throw tcu::NotSupportedError("Test requires depth buffer");
3002 }
3003
3004 if (m_type == TESTTYPE_STENCIL &&
3005 m_renderTarget == RENDERTARGET_DEFAULT &&
3006 m_context.getRenderTarget().getStencilBits() == 0)
3007 {
3008 throw tcu::NotSupportedError("Test requires stencil buffer");
3009 }
3010
3011 if (m_renderTarget == RENDERTARGET_DEFAULT &&
3012 (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE))
3013 throw tcu::NotSupportedError("Render target must have at least " + toString(RENDER_SIZE) + " width and height");
3014 }
3015
3016 IterateResult iterate (void);
3017
3018 private:
3019 static const int RENDER_SIZE;
3020
3021 const TestType m_type;
3022 const bool m_useEarlyTests;
3023 const RenderTargetType m_renderTarget;
3024 };
3025
3026 const int EarlyFragmentTestsCase::RENDER_SIZE = 32;
3027
iterate(void)3028 EarlyFragmentTestsCase::IterateResult EarlyFragmentTestsCase::iterate (void)
3029 {
3030 const RenderContext& renderCtx = m_context.getRenderContext();
3031 TestLog& log (m_testCtx.getLog());
3032 glu::CallLogWrapper glLog (renderCtx.getFunctions(), log);
3033 de::Random rnd (deStringHash(getName()));
3034 const bool expectPartialResult = m_useEarlyTests && m_renderTarget != RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT;
3035 const int viewportWidth = RENDER_SIZE;
3036 const int viewportHeight = RENDER_SIZE;
3037 const int viewportX = (m_renderTarget == RENDERTARGET_DEFAULT) ? (rnd.getInt(0, renderCtx.getRenderTarget().getWidth() - viewportWidth)) : (0);
3038 const int viewportY = (m_renderTarget == RENDERTARGET_DEFAULT) ? (rnd.getInt(0, renderCtx.getRenderTarget().getHeight() - viewportHeight)) : (0);
3039 const glu::Texture texture (renderCtx);
3040 de::MovePtr<glu::Framebuffer> fbo;
3041 de::MovePtr<glu::Renderbuffer> colorAttachment;
3042 de::MovePtr<glu::Renderbuffer> testAttachment;
3043
3044 glLog.enableLogging(true);
3045
3046 // Setup texture.
3047
3048 log << TestLog::Message << "// Created a texture (name " << *texture << ")" << TestLog::EndMessage;
3049
3050 glLog.glActiveTexture(GL_TEXTURE0);
3051 glLog.glBindTexture(GL_TEXTURE_2D, *texture);
3052 setTexParameteri(glLog, GL_TEXTURE_2D);
3053 {
3054 LayeredImage src(TEXTURETYPE_2D, TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32), 1, 1, 1);
3055 src.setPixel(0, 0, 0, IVec4(0));
3056 uploadTexture(glLog, src, 0 /* always 2d texture, no buffer needed */);
3057 }
3058 glLog.glBindImageTexture(0, *texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32UI);
3059 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "glBindImageTexture");
3060
3061 // Set up framebuffer
3062 if (m_renderTarget == RENDERTARGET_FBO ||
3063 m_renderTarget == RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT)
3064 {
3065 fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(renderCtx));
3066 colorAttachment = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
3067 testAttachment = de::MovePtr<glu::Renderbuffer>(new glu::Renderbuffer(renderCtx));
3068
3069 glLog.glBindRenderbuffer(GL_RENDERBUFFER, **colorAttachment);
3070 glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDER_SIZE, RENDER_SIZE);
3071 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen color attachment rb");
3072
3073 glLog.glBindFramebuffer(GL_FRAMEBUFFER, **fbo);
3074 glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **colorAttachment);
3075 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo color attachment");
3076
3077 if (m_renderTarget == RENDERTARGET_FBO && m_type == TESTTYPE_DEPTH)
3078 {
3079 glLog.glBindRenderbuffer(GL_RENDERBUFFER, **testAttachment);
3080 glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, RENDER_SIZE, RENDER_SIZE);
3081 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen depth attachment rb");
3082
3083 glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, **testAttachment);
3084 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo depth attachment");
3085 }
3086 else if (m_renderTarget == RENDERTARGET_FBO && m_type == TESTTYPE_STENCIL)
3087 {
3088 glLog.glBindRenderbuffer(GL_RENDERBUFFER, **testAttachment);
3089 glLog.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, RENDER_SIZE, RENDER_SIZE);
3090 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "gen stencil attachment rb");
3091
3092 glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, **testAttachment);
3093 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "set fbo stencil attachment");
3094 }
3095
3096 glLog.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, **colorAttachment);
3097 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "setup fbo");
3098 TCU_CHECK(glLog.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
3099 }
3100
3101 // Set up appropriate conditions for the test.
3102
3103 glLog.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
3104 glLog.glClear(GL_COLOR_BUFFER_BIT);
3105
3106 if (m_type == TESTTYPE_DEPTH)
3107 {
3108 glLog.glClearDepthf(0.5f);
3109 glLog.glClear(GL_DEPTH_BUFFER_BIT);
3110 glLog.glEnable(GL_DEPTH_TEST);
3111 }
3112 else if (m_type == TESTTYPE_STENCIL)
3113 {
3114 glLog.glClearStencil(0);
3115 glLog.glClear(GL_STENCIL_BUFFER_BIT);
3116 glLog.glScissor(viewportX, viewportY, viewportWidth/2, viewportHeight);
3117 glLog.glEnable(GL_SCISSOR_TEST);
3118 glLog.glClearStencil(1);
3119 glLog.glClear(GL_STENCIL_BUFFER_BIT);
3120 glLog.glDisable(GL_SCISSOR_TEST);
3121 glLog.glStencilFunc(GL_EQUAL, 1, 1);
3122 glLog.glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
3123 glLog.glEnable(GL_STENCIL_TEST);
3124 }
3125 else
3126 DE_ASSERT(false);
3127
3128 // Perform image stores in fragment shader.
3129
3130 {
3131 const std::string glslVersionDeclaration = glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(renderCtx.getType()));
3132
3133 // Generate fragment shader.
3134
3135 const glu::ShaderProgram program(renderCtx,
3136 glu::ProgramSources() << glu::VertexSource( glslVersionDeclaration + "\n"
3137 "\n"
3138 "highp in vec3 a_position;\n"
3139 "\n"
3140 "void main (void)\n"
3141 "{\n"
3142 " gl_Position = vec4(a_position, 1.0);\n"
3143 "}\n")
3144
3145 << glu::FragmentSource( glslVersionDeclaration + "\n"
3146 + imageAtomicExtensionShaderRequires(renderCtx) +
3147 "\n"
3148 + string(m_useEarlyTests ? "layout (early_fragment_tests) in;\n\n" : "") +
3149 "layout (location = 0) out highp vec4 o_color;\n"
3150 "\n"
3151 "precision highp uimage2D;\n"
3152 "\n"
3153 "layout (r32ui, binding=0) coherent uniform uimage2D u_image;\n"
3154 "\n"
3155 "void main (void)\n"
3156 "{\n"
3157 " imageAtomicAdd(u_image, ivec2(0, 0), uint(1));\n"
3158 " o_color = vec4(1.0);\n"
3159 "}\n"));
3160
3161 UniformAccessLogger uniforms(renderCtx.getFunctions(), log, program.getProgram());
3162
3163 log << program;
3164
3165 if (!program.isOk())
3166 {
3167 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Program compilation failed");
3168 return STOP;
3169 }
3170
3171 // Setup and draw full-viewport quad.
3172
3173 glLog.glUseProgram(program.getProgram());
3174
3175 {
3176 static const float vertexPositions[4*3] =
3177 {
3178 -1.0, -1.0, -1.0f,
3179 1.0, -1.0, 0.0f,
3180 -1.0, 1.0, 0.0f,
3181 1.0, 1.0, 1.0f,
3182 };
3183
3184 static const deUint16 indices[6] = { 0, 1, 2, 2, 1, 3 };
3185
3186 const glu::VertexArrayBinding attrBindings[] =
3187 {
3188 glu::va::Float("a_position", 3, 4, 0, &vertexPositions[0])
3189 };
3190
3191 glLog.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
3192
3193 glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3194 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
3195 GLU_EXPECT_NO_ERROR(renderCtx.getFunctions().getError(), "Draw failed");
3196 }
3197 }
3198
3199 // Log rendered result for convenience.
3200 {
3201 tcu::Surface rendered(viewportWidth, viewportHeight);
3202 glu::readPixels(renderCtx, viewportX, viewportY, rendered.getAccess());
3203 log << TestLog::Image("Rendered", "Rendered image", rendered);
3204 }
3205
3206 // Read counter value and check.
3207 {
3208 const int numSamples = de::max(1, renderCtx.getRenderTarget().getNumSamples());
3209 const int expectedCounter = expectPartialResult ? viewportWidth*viewportHeight/2 : viewportWidth*viewportHeight;
3210 const int tolerance = expectPartialResult ? de::max(viewportWidth, viewportHeight)*3 : 0;
3211 const int expectedMin = de::max(0, expectedCounter - tolerance);
3212 const int expectedMax = (expectedCounter + tolerance) * numSamples;
3213
3214 if (readIntegerTextureViaFBOAndVerify(renderCtx, glLog, *texture, TEXTURETYPE_2D, TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
3215 IVec3(1, 1, 1), R32UIImageSingleValueVerifier(expectedMin, expectedMax)))
3216 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3217 else
3218 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got wrong value");
3219
3220 return STOP;
3221 }
3222 }
3223
3224 } // anonymous
3225
ShaderImageLoadStoreTests(Context & context)3226 ShaderImageLoadStoreTests::ShaderImageLoadStoreTests (Context& context)
3227 : TestCaseGroup(context, "image_load_store", "Shader Image Load & Store Tests")
3228 {
3229 }
3230
~ShaderImageLoadStoreTests(void)3231 ShaderImageLoadStoreTests::~ShaderImageLoadStoreTests (void)
3232 {
3233 }
3234
init(void)3235 void ShaderImageLoadStoreTests::init (void)
3236 {
3237 // Per-image-type tests.
3238
3239 {
3240 static const TextureType imageTypes[] =
3241 {
3242 TEXTURETYPE_2D,
3243 TEXTURETYPE_CUBE,
3244 TEXTURETYPE_3D,
3245 TEXTURETYPE_2D_ARRAY,
3246 TEXTURETYPE_BUFFER
3247 };
3248
3249 static const TextureFormat formats[] =
3250 {
3251 TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT),
3252 TextureFormat(TextureFormat::RGBA, TextureFormat::HALF_FLOAT),
3253 TextureFormat(TextureFormat::R, TextureFormat::FLOAT),
3254
3255 TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32),
3256 TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT16),
3257 TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT8),
3258 TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT32),
3259
3260 TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT32),
3261 TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT16),
3262 TextureFormat(TextureFormat::RGBA, TextureFormat::SIGNED_INT8),
3263 TextureFormat(TextureFormat::R, TextureFormat::SIGNED_INT32),
3264
3265 TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8),
3266
3267 TextureFormat(TextureFormat::RGBA, TextureFormat::SNORM_INT8)
3268 };
3269
3270 for (int imageTypeNdx = 0; imageTypeNdx < DE_LENGTH_OF_ARRAY(imageTypes); imageTypeNdx++)
3271 {
3272 const TextureType imageType = imageTypes[imageTypeNdx];
3273 TestCaseGroup* const imageTypeGroup = new TestCaseGroup(m_context, getTextureTypeName(imageType), "");
3274 addChild(imageTypeGroup);
3275
3276 TestCaseGroup* const storeGroup = new TestCaseGroup(m_context, "store", "Plain imageStore() cases");
3277 TestCaseGroup* const loadStoreGroup = new TestCaseGroup(m_context, "load_store", "Cases with imageLoad() followed by imageStore()");
3278 TestCaseGroup* const atomicGroup = new TestCaseGroup(m_context, "atomic", "Atomic image operation cases");
3279 TestCaseGroup* const qualifierGroup = new TestCaseGroup(m_context, "qualifiers", "Coherent, volatile and restrict");
3280 TestCaseGroup* const reinterpretGroup = new TestCaseGroup(m_context, "format_reinterpret", "Cases with differing texture and image formats");
3281 TestCaseGroup* const imageSizeGroup = new TestCaseGroup(m_context, "image_size", "imageSize() cases");
3282 imageTypeGroup->addChild(storeGroup);
3283 imageTypeGroup->addChild(loadStoreGroup);
3284 imageTypeGroup->addChild(atomicGroup);
3285 imageTypeGroup->addChild(qualifierGroup);
3286 imageTypeGroup->addChild(reinterpretGroup);
3287 imageTypeGroup->addChild(imageSizeGroup);
3288
3289 for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
3290 {
3291 const TextureFormat& format = formats[formatNdx];
3292 const string formatName = getShaderImageFormatQualifier(formats[formatNdx]);
3293
3294 if (imageType == TEXTURETYPE_BUFFER && !isFormatSupportedForTextureBuffer(format))
3295 continue;
3296
3297 // Store cases.
3298
3299 storeGroup->addChild(new ImageStoreCase(m_context, formatName.c_str(), "", format, imageType));
3300 if (textureLayerType(imageType) != imageType)
3301 storeGroup->addChild(new ImageStoreCase(m_context, (formatName + "_single_layer").c_str(), "", format, imageType, ImageStoreCase::CASEFLAG_SINGLE_LAYER_BIND));
3302
3303 // Load & store.
3304
3305 loadStoreGroup->addChild(new ImageLoadAndStoreCase(m_context, formatName.c_str(), "", format, imageType));
3306 if (textureLayerType(imageType) != imageType)
3307 loadStoreGroup->addChild(new ImageLoadAndStoreCase(m_context, (formatName + "_single_layer").c_str(), "", format, imageType, ImageLoadAndStoreCase::CASEFLAG_SINGLE_LAYER_BIND));
3308
3309 if (format.order == TextureFormat::R)
3310 {
3311 // Atomic operations.
3312
3313 for (int operationI = 0; operationI < ATOMIC_OPERATION_LAST; operationI++)
3314 {
3315 for (int atomicCaseTypeI = 0; atomicCaseTypeI < ATOMIC_OPERATION_CASE_TYPE_LAST; atomicCaseTypeI++)
3316 {
3317 const AtomicOperation operation = (AtomicOperation)operationI;
3318
3319 if (format.type == TextureFormat::FLOAT && operation != ATOMIC_OPERATION_EXCHANGE)
3320 continue;
3321
3322 const AtomicOperationCaseType caseType = (AtomicOperationCaseType)atomicCaseTypeI;
3323 const string caseTypeName = caseType == ATOMIC_OPERATION_CASE_TYPE_END_RESULT ? "result"
3324 : caseType == ATOMIC_OPERATION_CASE_TYPE_RETURN_VALUES ? "return_value"
3325 : DE_NULL;
3326 const string caseName = string() + getAtomicOperationCaseName(operation) + "_" + formatName + "_" + caseTypeName;
3327
3328 if (operation == ATOMIC_OPERATION_COMP_SWAP)
3329 atomicGroup->addChild(new AtomicCompSwapCase(m_context, caseName.c_str(), "", format, imageType, caseType));
3330 else
3331 atomicGroup->addChild(new BinaryAtomicOperationCase(m_context, caseName.c_str(), "", format, imageType, operation, caseType));
3332 }
3333 }
3334
3335 // Coherence.
3336
3337 for (int coherenceQualifierI = 0; coherenceQualifierI < CoherenceCase::QUALIFIER_LAST; coherenceQualifierI++)
3338 {
3339 const CoherenceCase::Qualifier coherenceQualifier = (CoherenceCase::Qualifier)coherenceQualifierI;
3340 const char* const coherenceQualifierName = coherenceQualifier == CoherenceCase::QUALIFIER_COHERENT ? "coherent"
3341 : coherenceQualifier == CoherenceCase::QUALIFIER_VOLATILE ? "volatile"
3342 : DE_NULL;
3343 const string caseName = string() + coherenceQualifierName + "_" + formatName;
3344
3345 qualifierGroup->addChild(new CoherenceCase(m_context, caseName.c_str(), "", format, imageType, coherenceQualifier));
3346 }
3347 }
3348 }
3349
3350 // Restrict.
3351 qualifierGroup->addChild(new ImageLoadAndStoreCase(m_context, "restrict", "", TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), imageType, ImageLoadAndStoreCase::CASEFLAG_RESTRICT_IMAGES));
3352
3353 // Format re-interpretation.
3354
3355 for (int texFmtNdx = 0; texFmtNdx < DE_LENGTH_OF_ARRAY(formats); texFmtNdx++)
3356 for (int imgFmtNdx = 0; imgFmtNdx < DE_LENGTH_OF_ARRAY(formats); imgFmtNdx++)
3357 {
3358 const TextureFormat& texFmt = formats[texFmtNdx];
3359 const TextureFormat& imgFmt = formats[imgFmtNdx];
3360
3361 if (imageType == TEXTURETYPE_BUFFER && !isFormatSupportedForTextureBuffer(texFmt))
3362 continue;
3363
3364 if (texFmt != imgFmt && texFmt.getPixelSize() == imgFmt.getPixelSize())
3365 reinterpretGroup->addChild(new ImageLoadAndStoreCase(m_context,
3366 (getShaderImageFormatQualifier(texFmt) + "_" + getShaderImageFormatQualifier(imgFmt)).c_str(), "",
3367 texFmt, imgFmt, imageType));
3368 }
3369
3370 // imageSize().
3371
3372 {
3373 static const IVec3 baseImageSizes[] =
3374 {
3375 IVec3(32, 32, 32),
3376 IVec3(12, 34, 56),
3377 IVec3(1, 1, 1),
3378 IVec3(7, 1, 1)
3379 };
3380
3381 for (int imageAccessI = 0; imageAccessI < ImageSizeCase::IMAGEACCESS_LAST; imageAccessI++)
3382 {
3383 const ImageSizeCase::ImageAccess imageAccess = (ImageSizeCase::ImageAccess)imageAccessI;
3384 const char* const imageAccessStr = imageAccess == ImageSizeCase::IMAGEACCESS_READ_ONLY ? "readonly"
3385 : imageAccess == ImageSizeCase::IMAGEACCESS_WRITE_ONLY ? "writeonly"
3386 : imageAccess == ImageSizeCase::IMAGEACCESS_READ_ONLY_WRITE_ONLY ? "readonly_writeonly"
3387 : deFatalStr("Invalid ImageAccess");
3388
3389 for (int imageSizeNdx = 0; imageSizeNdx < DE_LENGTH_OF_ARRAY(baseImageSizes); imageSizeNdx++)
3390 {
3391 const IVec3& baseSize = baseImageSizes[imageSizeNdx];
3392 const IVec3 imageSize = imageType == TEXTURETYPE_BUFFER ? IVec3(baseSize.x(), 1, 1)
3393 : imageType == TEXTURETYPE_2D ? IVec3(baseSize.x(), baseSize.y(), 1)
3394 : imageType == TEXTURETYPE_CUBE ? IVec3(baseSize.x(), baseSize.x(), 1)
3395 : imageType == TEXTURETYPE_3D ? baseSize
3396 : imageType == TEXTURETYPE_2D_ARRAY ? baseSize
3397 : IVec3(-1, -1, -1);
3398
3399 const string sizeStr = imageType == TEXTURETYPE_BUFFER ? toString(imageSize.x())
3400 : imageType == TEXTURETYPE_2D ? toString(imageSize.x()) + "x" + toString(imageSize.y())
3401 : imageType == TEXTURETYPE_CUBE ? toString(imageSize.x()) + "x" + toString(imageSize.y())
3402 : imageType == TEXTURETYPE_3D ? toString(imageSize.x()) + "x" + toString(imageSize.y()) + "x" + toString(imageSize.z())
3403 : imageType == TEXTURETYPE_2D_ARRAY ? toString(imageSize.x()) + "x" + toString(imageSize.y()) + "x" + toString(imageSize.z())
3404 : deFatalStr("Invalid TextureType");
3405
3406 const string caseName = string() + imageAccessStr + "_" + sizeStr;
3407
3408 imageSizeGroup->addChild(new ImageSizeCase(m_context, caseName.c_str(), "", TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT), imageType, imageSize, imageAccess));
3409 }
3410 }
3411 }
3412 }
3413 }
3414
3415 // early_fragment_tests cases.
3416
3417 {
3418 TestCaseGroup* const earlyTestsGroup = new TestCaseGroup(m_context, "early_fragment_tests", "");
3419 addChild(earlyTestsGroup);
3420
3421 for (int testRenderTargetI = 0; testRenderTargetI < EarlyFragmentTestsCase::RENDERTARGET_LAST; testRenderTargetI++)
3422 for (int useEarlyTestsI = 0; useEarlyTestsI <= 1; useEarlyTestsI++)
3423 for (int testTypeI = 0; testTypeI < EarlyFragmentTestsCase::TESTTYPE_LAST; testTypeI++)
3424 {
3425 const EarlyFragmentTestsCase::RenderTargetType targetType = (EarlyFragmentTestsCase::RenderTargetType)testRenderTargetI;
3426 const bool useEarlyTests = useEarlyTestsI != 0;
3427 const EarlyFragmentTestsCase::TestType testType = (EarlyFragmentTestsCase::TestType)testTypeI;
3428
3429 const string testTypeName = testType == EarlyFragmentTestsCase::TESTTYPE_DEPTH ? "depth"
3430 : testType == EarlyFragmentTestsCase::TESTTYPE_STENCIL ? "stencil"
3431 : DE_NULL;
3432
3433 const string targetName = targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO ? (std::string("_fbo"))
3434 : targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT ? (std::string("_fbo_with_no_") + testTypeName)
3435 : std::string("");
3436
3437 const string caseName = string(useEarlyTests ? "" : "no_") + "early_fragment_tests_" + testTypeName + targetName;
3438
3439 const string caseDesc = string(useEarlyTests ? "Specify" : "Don't specify")
3440 + " early_fragment_tests, use the " + testTypeName + " test"
3441 + ((targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO) ? (", render to fbo")
3442 : (targetType == EarlyFragmentTestsCase::RENDERTARGET_FBO_WITHOUT_TEST_ATTACHMENT) ? (", render to fbo without relevant buffer")
3443 : (""));
3444
3445 earlyTestsGroup->addChild(new EarlyFragmentTestsCase(m_context, caseName.c_str(), caseDesc.c_str(), testType, useEarlyTests, targetType));
3446 }
3447 }
3448 }
3449
3450 } // Functional
3451 } // gles31
3452 } // deqp
3453