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