/*-------------------------------------------------------------------------
 * 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(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(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(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<std::string, std::string> 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 <int Type>
void loadFuncWrapper(NegativeTestContext &ctx)
{
    image_load(ctx, (glu::TextureTestUtil::TextureType)Type);
}

template <int Type>
void storeFuncWrapper(NegativeTestContext &ctx)
{
    image_store(ctx, (glu::TextureTestUtil::TextureType)Type);
}

template <int Type>
void atomicFuncWrapper(NegativeTestContext &ctx)
{
    image_atomic(ctx, (glu::TextureTestUtil::TextureType)Type);
}

template <int Type>
void atomicExchangeFuncWrapper(NegativeTestContext &ctx)
{
    image_atomic_exchange(ctx, (glu::TextureTestUtil::TextureType)Type);
}

} // namespace

// Set of texture types to create tests for.
#define CREATE_TEST_FUNC_PER_TEXTURE_TYPE(NAME, FUNC)                                                       \
    const FunctionContainer NAME[] = {                                                                      \
        {FUNC<glu::TextureTestUtil::TEXTURETYPE_2D>, "texture_2d", "Texture2D negative tests."},            \
        {FUNC<glu::TextureTestUtil::TEXTURETYPE_3D>, "texture_3d", "Texture3D negative tests."},            \
        {FUNC<glu::TextureTestUtil::TEXTURETYPE_CUBE>, "cube", "Cube texture negative tests."},             \
        {FUNC<glu::TextureTestUtil::TEXTURETYPE_2D_ARRAY>, "2d_array", "2D array texture negative tests."}, \
        {FUNC<glu::TextureTestUtil::TEXTURETYPE_BUFFER>, "buffer", "Buffer negative tests."},               \
        {FUNC<glu::TextureTestUtil::TEXTURETYPE_CUBE_ARRAY>, "cube_array", "Cube array texture negative tests."}}

std::vector<FunctionContainer> getNegativeShaderImageLoadTestFunctions(void)
{
    CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, loadFuncWrapper);
    return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs));
}

std::vector<FunctionContainer> getNegativeShaderImageStoreTestFunctions(void)
{
    CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, storeFuncWrapper);
    return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs));
}

std::vector<FunctionContainer> getNegativeShaderImageAtomicTestFunctions(void)
{
    CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, atomicFuncWrapper);
    return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs));
}

std::vector<FunctionContainer> getNegativeShaderImageAtomicExchangeTestFunctions(void)
{
    CREATE_TEST_FUNC_PER_TEXTURE_TYPE(funcs, atomicExchangeFuncWrapper);
    return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs));
}

} // namespace NegativeTestShared
} // namespace Functional
} // namespace gles31
} // namespace deqp
