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