/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Negative Shader Image Load Store Tests *//*--------------------------------------------------------------------*/ #include "es31fNegativeShaderImageLoadStoreTests.hpp" #include "deUniquePtr.hpp" #include "glwEnums.hpp" #include "gluShaderProgram.hpp" #include "glsTextureTestUtil.hpp" #include "tcuStringTemplate.hpp" #include "tcuTexture.hpp" #include "tcuTestLog.hpp" namespace deqp { namespace gles31 { namespace Functional { namespace NegativeTestShared { namespace { enum MemoryQualifier { MEMORY_NONE = 0, MEMORY_READONLY, MEMORY_WRITEONLY, MEMORY_BOTH, MEMORY_LAST }; enum ImageOperation { IMAGE_OPERATION_STORE = 0, IMAGE_OPERATION_LOAD, IMAGE_OPERATION_ATOMIC_ADD, IMAGE_OPERATION_ATOMIC_MIN, IMAGE_OPERATION_ATOMIC_MAX, IMAGE_OPERATION_ATOMIC_AND, IMAGE_OPERATION_ATOMIC_OR, IMAGE_OPERATION_ATOMIC_XOR, IMAGE_OPERATION_ATOMIC_EXCHANGE, IMAGE_OPERATION_ATOMIC_COMP_SWAP, IMAGE_OPERATION_LAST }; static const glu::ShaderType s_shaders[] = { glu::SHADERTYPE_VERTEX, glu::SHADERTYPE_FRAGMENT, glu::SHADERTYPE_GEOMETRY, glu::SHADERTYPE_TESSELLATION_CONTROL, glu::SHADERTYPE_TESSELLATION_EVALUATION, glu::SHADERTYPE_COMPUTE }; std::string getShaderImageLayoutQualifier (const tcu::TextureFormat& format) { std::ostringstream qualifier; switch (format.order) { case tcu::TextureFormat::RGBA: qualifier << "rgba"; break; case tcu::TextureFormat::R: qualifier << "r"; break; default: DE_ASSERT(false); return std::string(""); } switch (format.type) { case tcu::TextureFormat::FLOAT: qualifier << "32f"; break; case tcu::TextureFormat::HALF_FLOAT: qualifier << "16f"; break; case tcu::TextureFormat::UNORM_INT8: qualifier << "8"; break; case tcu::TextureFormat::SNORM_INT8: qualifier << "8_snorm"; break; case tcu::TextureFormat::SIGNED_INT32: qualifier << "32i"; break; case tcu::TextureFormat::SIGNED_INT16: qualifier << "16i"; break; case tcu::TextureFormat::SIGNED_INT8: qualifier << "8i"; break; case tcu::TextureFormat::UNSIGNED_INT32: qualifier << "32ui"; break; case tcu::TextureFormat::UNSIGNED_INT16: qualifier << "16ui"; break; case tcu::TextureFormat::UNSIGNED_INT8: qualifier << "8ui"; break; default: DE_ASSERT(false); return std::string(""); } return qualifier.str(); } std::string getShaderImageTypeDeclaration (const tcu::TextureFormat& format, glu::TextureTestUtil::TextureType imageType) { std::ostringstream declaration; switch (format.type) { case tcu::TextureFormat::FLOAT: case tcu::TextureFormat::HALF_FLOAT: case tcu::TextureFormat::UNORM_INT8: case tcu::TextureFormat::SNORM_INT8: declaration << ""; break; case tcu::TextureFormat::SIGNED_INT32: case tcu::TextureFormat::SIGNED_INT16: case tcu::TextureFormat::SIGNED_INT8: declaration << "i"; break; case tcu::TextureFormat::UNSIGNED_INT32: case tcu::TextureFormat::UNSIGNED_INT16: case tcu::TextureFormat::UNSIGNED_INT8: declaration << "u"; break; default: DE_ASSERT(false); return std::string(""); } declaration << "image"; switch(imageType) { case glu::TextureTestUtil::TEXTURETYPE_2D: declaration << "2D"; break; case glu::TextureTestUtil::TEXTURETYPE_3D: declaration << "3D"; break; case glu::TextureTestUtil::TEXTURETYPE_CUBE: declaration << "Cube"; break; case glu::TextureTestUtil::TEXTURETYPE_2D_ARRAY: declaration << "2DArray"; break; case glu::TextureTestUtil::TEXTURETYPE_BUFFER: declaration << "Buffer"; break; case glu::TextureTestUtil::TEXTURETYPE_CUBE_ARRAY: declaration << "CubeArray"; break; default: DE_ASSERT(false); return std::string(""); } return declaration.str(); } std::string getShaderImageTypeExtensionString (glu::TextureTestUtil::TextureType imageType) { std::string extension; switch(imageType) { case glu::TextureTestUtil::TEXTURETYPE_2D: case glu::TextureTestUtil::TEXTURETYPE_3D: case glu::TextureTestUtil::TEXTURETYPE_CUBE: case glu::TextureTestUtil::TEXTURETYPE_2D_ARRAY: extension = ""; break; case glu::TextureTestUtil::TEXTURETYPE_BUFFER: extension = "#extension GL_EXT_texture_buffer : enable"; break; case glu::TextureTestUtil::TEXTURETYPE_CUBE_ARRAY: extension = "#extension GL_EXT_texture_cube_map_array : enable"; break; default: DE_ASSERT(false); return std::string(""); } return extension; } std::string getShaderImageParamP (glu::TextureTestUtil::TextureType imageType) { switch(imageType) { case glu::TextureTestUtil::TEXTURETYPE_2D: return "ivec2(1, 1)"; case glu::TextureTestUtil::TEXTURETYPE_3D: case glu::TextureTestUtil::TEXTURETYPE_CUBE: case glu::TextureTestUtil::TEXTURETYPE_2D_ARRAY: case glu::TextureTestUtil::TEXTURETYPE_CUBE_ARRAY: return "ivec3(1, 1, 1)"; case glu::TextureTestUtil::TEXTURETYPE_BUFFER: return "1"; default: DE_ASSERT(false); return std::string(""); } } std::string getOtherFunctionArguments (const tcu::TextureFormat& format, ImageOperation function) { std::ostringstream data; data << ", "; bool isFloat = false; switch(format.type) { case tcu::TextureFormat::FLOAT: case tcu::TextureFormat::HALF_FLOAT: case tcu::TextureFormat::UNORM_INT8: case tcu::TextureFormat::SNORM_INT8: data << ""; isFloat = true; break; case tcu::TextureFormat::SIGNED_INT32: case tcu::TextureFormat::SIGNED_INT16: case tcu::TextureFormat::SIGNED_INT8: data << "i"; break; case tcu::TextureFormat::UNSIGNED_INT32: case tcu::TextureFormat::UNSIGNED_INT16: case tcu::TextureFormat::UNSIGNED_INT8: data << "u"; break; default: DE_ASSERT(false); return std::string(""); } switch (function) { case IMAGE_OPERATION_LOAD: return ""; case IMAGE_OPERATION_STORE: data << "vec4(1, 1, 1, 1)"; break; case IMAGE_OPERATION_ATOMIC_ADD: case IMAGE_OPERATION_ATOMIC_MIN: case IMAGE_OPERATION_ATOMIC_MAX: case IMAGE_OPERATION_ATOMIC_AND: case IMAGE_OPERATION_ATOMIC_OR: case IMAGE_OPERATION_ATOMIC_XOR: return ", 1"; case IMAGE_OPERATION_ATOMIC_EXCHANGE: return isFloat ? ", 1.0" : ", 1"; case IMAGE_OPERATION_ATOMIC_COMP_SWAP: return ", 1, 1"; default: DE_ASSERT(false); return std::string(""); } return data.str(); } std::string getMemoryQualifier (MemoryQualifier memory) { switch (memory) { case MEMORY_NONE: return std::string(""); case MEMORY_WRITEONLY: return std::string("writeonly"); case MEMORY_READONLY: return std::string("readonly"); case MEMORY_BOTH: return std::string("writeonly readonly"); default: DE_ASSERT(DE_FALSE); } return std::string(""); } std::string getShaderImageFunctionExtensionString (ImageOperation function) { switch (function) { case IMAGE_OPERATION_STORE: case IMAGE_OPERATION_LOAD: return std::string(""); case IMAGE_OPERATION_ATOMIC_ADD: case IMAGE_OPERATION_ATOMIC_MIN: case IMAGE_OPERATION_ATOMIC_MAX: case IMAGE_OPERATION_ATOMIC_AND: case IMAGE_OPERATION_ATOMIC_OR: case IMAGE_OPERATION_ATOMIC_XOR: case IMAGE_OPERATION_ATOMIC_EXCHANGE: case IMAGE_OPERATION_ATOMIC_COMP_SWAP: return std::string("#extension GL_OES_shader_image_atomic : enable"); default: DE_ASSERT(DE_FALSE); } return std::string(""); } std::string getFunctionName (ImageOperation function) { switch (function) { case IMAGE_OPERATION_STORE: return std::string("imageStore"); case IMAGE_OPERATION_LOAD: return std::string("imageLoad"); case IMAGE_OPERATION_ATOMIC_ADD: return std::string("imageAtomicAdd"); case IMAGE_OPERATION_ATOMIC_MIN: return std::string("imageAtomicMin"); case IMAGE_OPERATION_ATOMIC_MAX: return std::string("imageAtomicMax"); case IMAGE_OPERATION_ATOMIC_AND: return std::string("imageAtomicAnd"); case IMAGE_OPERATION_ATOMIC_OR: return std::string("imageAtomicOr"); case IMAGE_OPERATION_ATOMIC_XOR: return std::string("imageAtomicXor"); case IMAGE_OPERATION_ATOMIC_EXCHANGE: return std::string("imageAtomicExchange"); case IMAGE_OPERATION_ATOMIC_COMP_SWAP: return std::string("imageAtomicCompSwap"); default: DE_ASSERT(DE_FALSE); } return std::string(""); } std::string generateShaderSource (ImageOperation function, MemoryQualifier memory, glu::TextureTestUtil::TextureType imageType, const tcu::TextureFormat& format, glu::ShaderType shaderType) { const char* shaderTemplate = "${GLSL_VERSION_DECL}\n" "${GLSL_TYPE_EXTENSION}\n" "${GLSL_FUNCTION_EXTENSION}\n" "${GEOMETRY_SHADER_LAYOUT}\n" "layout(${LAYOUT_FORMAT}, binding = 0) highp uniform ${MEMORY_QUALIFIER} ${IMAGE_TYPE} u_img0;\n" "void main(void)\n" "{\n" " ${FUNCTION_NAME}(u_img0, ${IMAGE_PARAM_P}${FUNCTION_ARGUMENTS});\n" "}\n"; std::map params; params["GLSL_VERSION_DECL"] = getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES); params["GLSL_TYPE_EXTENSION"] = getShaderImageTypeExtensionString(imageType); params["GLSL_FUNCTION_EXTENSION"] = getShaderImageFunctionExtensionString(function); params["GEOMETRY_SHADER_LAYOUT"] = getGLShaderType(shaderType) == GL_GEOMETRY_SHADER ? "layout(max_vertices = 3) out;" : ""; params["LAYOUT_FORMAT"] = getShaderImageLayoutQualifier(format); params["MEMORY_QUALIFIER"] = getMemoryQualifier(memory); params["IMAGE_TYPE"] = getShaderImageTypeDeclaration(format, imageType); params["FUNCTION_NAME"] = getFunctionName(function); params["IMAGE_PARAM_P"] = getShaderImageParamP(imageType); params["FUNCTION_ARGUMENTS"] = getOtherFunctionArguments(format, function); return tcu::StringTemplate(shaderTemplate).specialize(params); } void testShader (NegativeTestContext& ctx, ImageOperation function, MemoryQualifier memory, glu::TextureTestUtil::TextureType imageType, const tcu::TextureFormat& format) { tcu::TestLog& log = ctx.getLog(); ctx.beginSection(getFunctionName(function) + " " + getMemoryQualifier(memory) + " " + getShaderImageLayoutQualifier(format)); for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_shaders); ndx++) { if (ctx.isShaderSupported(s_shaders[ndx])) { ctx.beginSection(std::string("Verify shader: ") + glu::getShaderTypeName(s_shaders[ndx])); std::string shaderSource(generateShaderSource(function, memory, imageType, format, s_shaders[ndx])); const glu::ShaderProgram program(ctx.getRenderContext(), glu::ProgramSources() << glu::ShaderSource(s_shaders[ndx], shaderSource)); if (program.getShaderInfo(s_shaders[ndx]).compileOk) { log << program; log << tcu::TestLog::Message << "Expected program to fail, but compilation passed." << tcu::TestLog::EndMessage; ctx.fail("Shader was not expected to compile."); } ctx.endSection(); } } ctx.endSection(); } void image_store (NegativeTestContext& ctx, glu::TextureTestUtil::TextureType imageType) { const tcu::TextureFormat formats[] = { tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::HALF_FLOAT), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SNORM_INT8), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT32) }; const MemoryQualifier memoryOptions[] = { MEMORY_READONLY, MEMORY_BOTH }; ctx.beginSection("It is an error to pass a readonly image to imageStore."); for (int memoryNdx = 0; memoryNdx < DE_LENGTH_OF_ARRAY(memoryOptions); ++memoryNdx) { for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(formats); ++fmtNdx) { testShader(ctx, IMAGE_OPERATION_STORE, memoryOptions[memoryNdx], imageType, formats[fmtNdx]); } } ctx.endSection(); } void image_load (NegativeTestContext& ctx, glu::TextureTestUtil::TextureType imageType) { const tcu::TextureFormat formats[] = { tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::HALF_FLOAT), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SNORM_INT8), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT32) }; const MemoryQualifier memoryOptions[] = { MEMORY_WRITEONLY, MEMORY_BOTH }; ctx.beginSection("It is an error to pass a writeonly image to imageLoad."); for (int memoryNdx = 0; memoryNdx < DE_LENGTH_OF_ARRAY(memoryOptions); ++memoryNdx) { for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(formats); ++fmtNdx) { testShader(ctx, IMAGE_OPERATION_LOAD, memoryOptions[memoryNdx], imageType, formats[fmtNdx]); } } ctx.endSection(); } void image_atomic (NegativeTestContext& ctx, glu::TextureTestUtil::TextureType imageType) { const tcu::TextureFormat formats[] = { tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT32) }; const MemoryQualifier memoryOptions[] = { MEMORY_READONLY, MEMORY_WRITEONLY, MEMORY_BOTH }; const ImageOperation imageOperations[] = { IMAGE_OPERATION_ATOMIC_ADD, IMAGE_OPERATION_ATOMIC_MIN, IMAGE_OPERATION_ATOMIC_MAX, IMAGE_OPERATION_ATOMIC_AND, IMAGE_OPERATION_ATOMIC_OR, IMAGE_OPERATION_ATOMIC_XOR, IMAGE_OPERATION_ATOMIC_COMP_SWAP }; ctx.beginSection("It is an error to pass a writeonly and/or readonly image to imageAtomic*."); for (int memoryNdx = 0; memoryNdx < DE_LENGTH_OF_ARRAY(memoryOptions); ++memoryNdx) { for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(formats); ++fmtNdx) { for (int functionNdx = 0; functionNdx < DE_LENGTH_OF_ARRAY(imageOperations); ++functionNdx) { testShader(ctx, imageOperations[functionNdx], memoryOptions[memoryNdx], imageType, formats[fmtNdx]); } } } ctx.endSection(); } void image_atomic_exchange (NegativeTestContext& ctx, glu::TextureTestUtil::TextureType imageType) { const tcu::TextureFormat formats[] = { tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::HALF_FLOAT), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::FLOAT), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SNORM_INT8), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::SIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::SIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT32), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT16), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNSIGNED_INT8), tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT32) }; const MemoryQualifier memoryOptions[] = { MEMORY_READONLY, MEMORY_WRITEONLY, MEMORY_BOTH }; ctx.beginSection("It is an error to pass a writeonly and/or readonly image to imageAtomic*."); for (int memoryNdx = 0; memoryNdx < DE_LENGTH_OF_ARRAY(memoryOptions); ++memoryNdx) { for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(formats); ++fmtNdx) { testShader(ctx, IMAGE_OPERATION_ATOMIC_EXCHANGE, memoryOptions[memoryNdx], imageType, formats[fmtNdx]); } } ctx.endSection(); } // Re-routing function template for generating the standard negative // test function signature with texture type added. template void loadFuncWrapper (NegativeTestContext& ctx) { image_load(ctx, (glu::TextureTestUtil::TextureType)Type); } template void storeFuncWrapper (NegativeTestContext& ctx) { image_store(ctx, (glu::TextureTestUtil::TextureType)Type); } template void atomicFuncWrapper (NegativeTestContext& ctx) { image_atomic(ctx, (glu::TextureTestUtil::TextureType)Type); } template void atomicExchangeFuncWrapper (NegativeTestContext& ctx) { image_atomic_exchange(ctx, (glu::TextureTestUtil::TextureType)Type); } } // anonymous // Set of texture types to create tests for. #define CREATE_TEST_FUNC_PER_TEXTURE_TYPE(NAME, FUNC) const FunctionContainer NAME[] = \ { \ {FUNC, "texture_2d", "Texture2D negative tests."}, \ {FUNC, "texture_3d", "Texture3D negative tests."}, \ {FUNC, "cube", "Cube texture negative tests."}, \ {FUNC, "2d_array", "2D array texture negative tests."}, \ {FUNC, "buffer", "Buffer negative tests."}, \ {FUNC, "cube_array", "Cube array texture negative tests."} \ } std::vector getNegativeShaderImageLoadTestFunctions (void) { CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, loadFuncWrapper); return std::vector(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs)); } std::vector getNegativeShaderImageStoreTestFunctions (void) { CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, storeFuncWrapper); return std::vector(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs)); } std::vector getNegativeShaderImageAtomicTestFunctions (void) { CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, atomicFuncWrapper); return std::vector(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs)); } std::vector getNegativeShaderImageAtomicExchangeTestFunctions (void) { CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, atomicExchangeFuncWrapper); return std::vector(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs)); } } // NegativeTestShared } // Functional } // gles31 } // deqp