/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 Module
 * -------------------------------------------------
 *
 * Copyright 2014 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 Shader indexing (arrays, vector, matrices) tests.
 *//*--------------------------------------------------------------------*/

#include "es3fShaderIndexingTests.hpp"
#include "glsShaderLibrary.hpp"
#include "glsShaderRenderCase.hpp"
#include "gluShaderUtil.hpp"
#include "tcuStringTemplate.hpp"

#include "deInt32.h"
#include "deMemory.h"

#include <map>

#include "glwEnums.hpp"
#include "glwFunctions.hpp"

using namespace std;
using namespace tcu;
using namespace glu;
using namespace deqp::gls;

namespace deqp
{
namespace gles3
{
namespace Functional
{

enum IndexAccessType
{
    INDEXACCESS_STATIC = 0,
    INDEXACCESS_DYNAMIC,
    INDEXACCESS_STATIC_LOOP,
    INDEXACCESS_DYNAMIC_LOOP,

    INDEXACCESS_LAST
};

static const char *getIndexAccessTypeName(IndexAccessType accessType)
{
    static const char *s_names[INDEXACCESS_LAST] = {"static", "dynamic", "static_loop", "dynamic_loop"};

    DE_ASSERT(deInBounds32((int)accessType, 0, INDEXACCESS_LAST));
    return s_names[(int)accessType];
}

enum VectorAccessType
{
    DIRECT = 0,
    COMPONENT,
    SUBSCRIPT_STATIC,
    SUBSCRIPT_DYNAMIC,
    SUBSCRIPT_STATIC_LOOP,
    SUBSCRIPT_DYNAMIC_LOOP,

    VECTORACCESS_LAST
};

static const char *getVectorAccessTypeName(VectorAccessType accessType)
{
    static const char *s_names[VECTORACCESS_LAST] = {"direct",
                                                     "component",
                                                     "static_subscript",
                                                     "dynamic_subscript",
                                                     "static_loop_subscript",
                                                     "dynamic_loop_subscript"};

    DE_ASSERT(deInBounds32((int)accessType, 0, VECTORACCESS_LAST));
    return s_names[(int)accessType];
}

void evalArrayCoordsFloat(ShaderEvalContext &c)
{
    c.color.x() = 1.875f * c.coords.x();
}
void evalArrayCoordsVec2(ShaderEvalContext &c)
{
    c.color.xy() = 1.875f * c.coords.swizzle(0, 1);
}
void evalArrayCoordsVec3(ShaderEvalContext &c)
{
    c.color.xyz() = 1.875f * c.coords.swizzle(0, 1, 2);
}
void evalArrayCoordsVec4(ShaderEvalContext &c)
{
    c.color = 1.875f * c.coords;
}

static ShaderEvalFunc getArrayCoordsEvalFunc(DataType dataType)
{
    if (dataType == TYPE_FLOAT)
        return evalArrayCoordsFloat;
    else if (dataType == TYPE_FLOAT_VEC2)
        return evalArrayCoordsVec2;
    else if (dataType == TYPE_FLOAT_VEC3)
        return evalArrayCoordsVec3;
    else if (dataType == TYPE_FLOAT_VEC4)
        return evalArrayCoordsVec4;

    DE_FATAL("Invalid data type.");
    return NULL;
}

void evalArrayUniformFloat(ShaderEvalContext &c)
{
    c.color.x() = 1.875f * c.constCoords.x();
}
void evalArrayUniformVec2(ShaderEvalContext &c)
{
    c.color.xy() = 1.875f * c.constCoords.swizzle(0, 1);
}
void evalArrayUniformVec3(ShaderEvalContext &c)
{
    c.color.xyz() = 1.875f * c.constCoords.swizzle(0, 1, 2);
}
void evalArrayUniformVec4(ShaderEvalContext &c)
{
    c.color = 1.875f * c.constCoords;
}

static ShaderEvalFunc getArrayUniformEvalFunc(DataType dataType)
{
    if (dataType == TYPE_FLOAT)
        return evalArrayUniformFloat;
    else if (dataType == TYPE_FLOAT_VEC2)
        return evalArrayUniformVec2;
    else if (dataType == TYPE_FLOAT_VEC3)
        return evalArrayUniformVec3;
    else if (dataType == TYPE_FLOAT_VEC4)
        return evalArrayUniformVec4;

    DE_FATAL("Invalid data type.");
    return NULL;
}

// ShaderIndexingCase

class ShaderIndexingCase : public ShaderRenderCase
{
public:
    ShaderIndexingCase(Context &context, const char *name, const char *description, bool isVertexCase, DataType varType,
                       ShaderEvalFunc evalFunc, const char *vertShaderSource, const char *fragShaderSource);
    virtual ~ShaderIndexingCase(void);

private:
    ShaderIndexingCase(const ShaderIndexingCase &);            // not allowed!
    ShaderIndexingCase &operator=(const ShaderIndexingCase &); // not allowed!

    virtual void setup(int programID);
    virtual void setupUniforms(int programID, const Vec4 &constCoords);

    DataType m_varType;
};

ShaderIndexingCase::ShaderIndexingCase(Context &context, const char *name, const char *description, bool isVertexCase,
                                       DataType varType, ShaderEvalFunc evalFunc, const char *vertShaderSource,
                                       const char *fragShaderSource)
    : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name,
                       description, isVertexCase, evalFunc)
{
    m_varType          = varType;
    m_vertShaderSource = vertShaderSource;
    m_fragShaderSource = fragShaderSource;
}

ShaderIndexingCase::~ShaderIndexingCase(void)
{
}

void ShaderIndexingCase::setup(int programID)
{
    DE_UNREF(programID);
}

void ShaderIndexingCase::setupUniforms(int programID, const Vec4 &constCoords)
{
    const glw::Functions &gl = m_renderCtx.getFunctions();

    DE_UNREF(constCoords);

    int arrLoc = gl.getUniformLocation(programID, "u_arr");
    if (arrLoc != -1)
    {
        //int scalarSize = getDataTypeScalarSize(m_varType);
        if (m_varType == TYPE_FLOAT)
        {
            float arr[4];
            arr[0] = constCoords.x();
            arr[1] = constCoords.x() * 0.5f;
            arr[2] = constCoords.x() * 0.25f;
            arr[3] = constCoords.x() * 0.125f;
            gl.uniform1fv(arrLoc, 4, &arr[0]);
        }
        else if (m_varType == TYPE_FLOAT_VEC2)
        {
            Vec2 arr[4];
            arr[0] = constCoords.swizzle(0, 1);
            arr[1] = constCoords.swizzle(0, 1) * 0.5f;
            arr[2] = constCoords.swizzle(0, 1) * 0.25f;
            arr[3] = constCoords.swizzle(0, 1) * 0.125f;
            gl.uniform2fv(arrLoc, 4, arr[0].getPtr());
        }
        else if (m_varType == TYPE_FLOAT_VEC3)
        {
            Vec3 arr[4];
            arr[0] = constCoords.swizzle(0, 1, 2);
            arr[1] = constCoords.swizzle(0, 1, 2) * 0.5f;
            arr[2] = constCoords.swizzle(0, 1, 2) * 0.25f;
            arr[3] = constCoords.swizzle(0, 1, 2) * 0.125f;
            gl.uniform3fv(arrLoc, 4, arr[0].getPtr());
        }
        else if (m_varType == TYPE_FLOAT_VEC4)
        {
            Vec4 arr[4];
            arr[0] = constCoords.swizzle(0, 1, 2, 3);
            arr[1] = constCoords.swizzle(0, 1, 2, 3) * 0.5f;
            arr[2] = constCoords.swizzle(0, 1, 2, 3) * 0.25f;
            arr[3] = constCoords.swizzle(0, 1, 2, 3) * 0.125f;
            gl.uniform4fv(arrLoc, 4, arr[0].getPtr());
        }
        else
            throw tcu::TestError("u_arr should not have location assigned in this test case");
    }
}

// Helpers.

static ShaderIndexingCase *createVaryingArrayCase(Context &context, const char *caseName, const char *description,
                                                  DataType varType, IndexAccessType vertAccess,
                                                  IndexAccessType fragAccess)
{
    std::ostringstream vtx;
    vtx << "#version 300 es\n";
    vtx << "in highp vec4 a_position;\n";
    vtx << "in highp vec4 a_coords;\n";
    if (vertAccess == INDEXACCESS_DYNAMIC)
        vtx << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";
    else if (vertAccess == INDEXACCESS_DYNAMIC_LOOP)
        vtx << "uniform mediump int ui_four;\n";
    vtx << "out ${PRECISION} ${VAR_TYPE} var[${ARRAY_LEN}];\n";
    vtx << "\n";
    vtx << "void main()\n";
    vtx << "{\n";
    vtx << "    gl_Position = a_position;\n";
    if (vertAccess == INDEXACCESS_STATIC)
    {
        vtx << "    var[0] = ${VAR_TYPE}(a_coords);\n";
        vtx << "    var[1] = ${VAR_TYPE}(a_coords) * 0.5;\n";
        vtx << "    var[2] = ${VAR_TYPE}(a_coords) * 0.25;\n";
        vtx << "    var[3] = ${VAR_TYPE}(a_coords) * 0.125;\n";
    }
    else if (vertAccess == INDEXACCESS_DYNAMIC)
    {
        vtx << "    var[ui_zero]  = ${VAR_TYPE}(a_coords);\n";
        vtx << "    var[ui_one]   = ${VAR_TYPE}(a_coords) * 0.5;\n";
        vtx << "    var[ui_two]   = ${VAR_TYPE}(a_coords) * 0.25;\n";
        vtx << "    var[ui_three] = ${VAR_TYPE}(a_coords) * 0.125;\n";
    }
    else if (vertAccess == INDEXACCESS_STATIC_LOOP)
    {
        vtx << "    ${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
        vtx << "    for (int i = 0; i < 4; i++)\n";
        vtx << "    {\n";
        vtx << "        var[i] = ${VAR_TYPE}(coords);\n";
        vtx << "        coords = coords * 0.5;\n";
        vtx << "    }\n";
    }
    else
    {
        DE_ASSERT(vertAccess == INDEXACCESS_DYNAMIC_LOOP);
        vtx << "    ${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
        vtx << "    for (int i = 0; i < ui_four; i++)\n";
        vtx << "    {\n";
        vtx << "        var[i] = ${VAR_TYPE}(coords);\n";
        vtx << "        coords = coords * 0.5;\n";
        vtx << "    }\n";
    }
    vtx << "}\n";

    std::ostringstream frag;
    frag << "#version 300 es\n";
    frag << "precision mediump int;\n";
    frag << "layout(location = 0) out mediump vec4 o_color;\n";
    if (fragAccess == INDEXACCESS_DYNAMIC)
        frag << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";
    else if (fragAccess == INDEXACCESS_DYNAMIC_LOOP)
        frag << "uniform int ui_four;\n";
    frag << "in ${PRECISION} ${VAR_TYPE} var[${ARRAY_LEN}];\n";
    frag << "\n";
    frag << "void main()\n";
    frag << "{\n";
    frag << "    ${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n";
    if (fragAccess == INDEXACCESS_STATIC)
    {
        frag << "    res += var[0];\n";
        frag << "    res += var[1];\n";
        frag << "    res += var[2];\n";
        frag << "    res += var[3];\n";
    }
    else if (fragAccess == INDEXACCESS_DYNAMIC)
    {
        frag << "    res += var[ui_zero];\n";
        frag << "    res += var[ui_one];\n";
        frag << "    res += var[ui_two];\n";
        frag << "    res += var[ui_three];\n";
    }
    else if (fragAccess == INDEXACCESS_STATIC_LOOP)
    {
        frag << "    for (int i = 0; i < 4; i++)\n";
        frag << "        res += var[i];\n";
    }
    else
    {
        DE_ASSERT(fragAccess == INDEXACCESS_DYNAMIC_LOOP);
        frag << "    for (int i = 0; i < ui_four; i++)\n";
        frag << "        res += var[i];\n";
    }
    frag << "    o_color = vec4(res${PADDING});\n";
    frag << "}\n";

    // Fill in shader templates.
    map<string, string> params;
    params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
    params.insert(pair<string, string>("ARRAY_LEN", "4"));
    params.insert(pair<string, string>("PRECISION", "mediump"));

    if (varType == TYPE_FLOAT)
        params.insert(pair<string, string>("PADDING", ", 0.0, 0.0, 1.0"));
    else if (varType == TYPE_FLOAT_VEC2)
        params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
    else if (varType == TYPE_FLOAT_VEC3)
        params.insert(pair<string, string>("PADDING", ", 1.0"));
    else
        params.insert(pair<string, string>("PADDING", ""));

    StringTemplate vertTemplate(vtx.str().c_str());
    StringTemplate fragTemplate(frag.str().c_str());
    string vertexShaderSource   = vertTemplate.specialize(params);
    string fragmentShaderSource = fragTemplate.specialize(params);

    ShaderEvalFunc evalFunc = getArrayCoordsEvalFunc(varType);
    return new ShaderIndexingCase(context, caseName, description, true, varType, evalFunc, vertexShaderSource.c_str(),
                                  fragmentShaderSource.c_str());
}

static ShaderIndexingCase *createUniformArrayCase(Context &context, const char *caseName, const char *description,
                                                  bool isVertexCase, DataType varType, IndexAccessType readAccess)
{
    std::ostringstream vtx;
    std::ostringstream frag;
    std::ostringstream &op = isVertexCase ? vtx : frag;

    vtx << "#version 300 es\n";
    frag << "#version 300 es\n";

    vtx << "in highp vec4 a_position;\n";
    vtx << "in highp vec4 a_coords;\n";
    frag << "layout(location = 0) out mediump vec4 o_color;\n";

    if (isVertexCase)
    {
        vtx << "out mediump vec4 v_color;\n";
        frag << "in mediump vec4 v_color;\n";
    }
    else
    {
        vtx << "out mediump vec4 v_coords;\n";
        frag << "in mediump vec4 v_coords;\n";
    }

    if (readAccess == INDEXACCESS_DYNAMIC)
        op << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";
    else if (readAccess == INDEXACCESS_DYNAMIC_LOOP)
        op << "uniform mediump int ui_four;\n";

    op << "uniform ${PRECISION} ${VAR_TYPE} u_arr[${ARRAY_LEN}];\n";

    vtx << "\n";
    vtx << "void main()\n";
    vtx << "{\n";
    vtx << "    gl_Position = a_position;\n";

    frag << "\n";
    frag << "void main()\n";
    frag << "{\n";

    // Read array.
    op << "    ${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n";
    if (readAccess == INDEXACCESS_STATIC)
    {
        op << "    res += u_arr[0];\n";
        op << "    res += u_arr[1];\n";
        op << "    res += u_arr[2];\n";
        op << "    res += u_arr[3];\n";
    }
    else if (readAccess == INDEXACCESS_DYNAMIC)
    {
        op << "    res += u_arr[ui_zero];\n";
        op << "    res += u_arr[ui_one];\n";
        op << "    res += u_arr[ui_two];\n";
        op << "    res += u_arr[ui_three];\n";
    }
    else if (readAccess == INDEXACCESS_STATIC_LOOP)
    {
        op << "    for (int i = 0; i < 4; i++)\n";
        op << "        res += u_arr[i];\n";
    }
    else
    {
        DE_ASSERT(readAccess == INDEXACCESS_DYNAMIC_LOOP);
        op << "    for (int i = 0; i < ui_four; i++)\n";
        op << "        res += u_arr[i];\n";
    }

    if (isVertexCase)
    {
        vtx << "    v_color = vec4(res${PADDING});\n";
        frag << "    o_color = v_color;\n";
    }
    else
    {
        vtx << "    v_coords = a_coords;\n";
        frag << "    o_color = vec4(res${PADDING});\n";
    }

    vtx << "}\n";
    frag << "}\n";

    // Fill in shader templates.
    map<string, string> params;
    params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
    params.insert(pair<string, string>("ARRAY_LEN", "4"));
    params.insert(pair<string, string>("PRECISION", "mediump"));

    if (varType == TYPE_FLOAT)
        params.insert(pair<string, string>("PADDING", ", 0.0, 0.0, 1.0"));
    else if (varType == TYPE_FLOAT_VEC2)
        params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
    else if (varType == TYPE_FLOAT_VEC3)
        params.insert(pair<string, string>("PADDING", ", 1.0"));
    else
        params.insert(pair<string, string>("PADDING", ""));

    StringTemplate vertTemplate(vtx.str().c_str());
    StringTemplate fragTemplate(frag.str().c_str());
    string vertexShaderSource   = vertTemplate.specialize(params);
    string fragmentShaderSource = fragTemplate.specialize(params);

    ShaderEvalFunc evalFunc = getArrayUniformEvalFunc(varType);
    return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc,
                                  vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

static ShaderIndexingCase *createTmpArrayCase(Context &context, const char *caseName, const char *description,
                                              bool isVertexCase, DataType varType, IndexAccessType writeAccess,
                                              IndexAccessType readAccess)
{
    std::ostringstream vtx;
    std::ostringstream frag;
    std::ostringstream &op = isVertexCase ? vtx : frag;

    vtx << "#version 300 es\n";
    frag << "#version 300 es\n";

    vtx << "in highp vec4 a_position;\n";
    vtx << "in highp vec4 a_coords;\n";
    frag << "layout(location = 0) out mediump vec4 o_color;\n";

    if (isVertexCase)
    {
        vtx << "out mediump vec4 v_color;\n";
        frag << "in mediump vec4 v_color;\n";
    }
    else
    {
        vtx << "out mediump vec4 v_coords;\n";
        frag << "in mediump vec4 v_coords;\n";
    }

    if (writeAccess == INDEXACCESS_DYNAMIC || readAccess == INDEXACCESS_DYNAMIC)
        op << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";

    if (writeAccess == INDEXACCESS_DYNAMIC_LOOP || readAccess == INDEXACCESS_DYNAMIC_LOOP)
        op << "uniform mediump int ui_four;\n";

    vtx << "\n";
    vtx << "void main()\n";
    vtx << "{\n";
    vtx << "    gl_Position = a_position;\n";

    frag << "\n";
    frag << "void main()\n";
    frag << "{\n";

    // Write array.
    if (isVertexCase)
        op << "    ${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
    else
        op << "    ${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(v_coords);\n";

    op << "    ${PRECISION} ${VAR_TYPE} arr[${ARRAY_LEN}];\n";
    if (writeAccess == INDEXACCESS_STATIC)
    {
        op << "    arr[0] = ${VAR_TYPE}(coords);\n";
        op << "    arr[1] = ${VAR_TYPE}(coords) * 0.5;\n";
        op << "    arr[2] = ${VAR_TYPE}(coords) * 0.25;\n";
        op << "    arr[3] = ${VAR_TYPE}(coords) * 0.125;\n";
    }
    else if (writeAccess == INDEXACCESS_DYNAMIC)
    {
        op << "    arr[ui_zero]  = ${VAR_TYPE}(coords);\n";
        op << "    arr[ui_one]   = ${VAR_TYPE}(coords) * 0.5;\n";
        op << "    arr[ui_two]   = ${VAR_TYPE}(coords) * 0.25;\n";
        op << "    arr[ui_three] = ${VAR_TYPE}(coords) * 0.125;\n";
    }
    else if (writeAccess == INDEXACCESS_STATIC_LOOP)
    {
        op << "    for (int i = 0; i < 4; i++)\n";
        op << "    {\n";
        op << "        arr[i] = ${VAR_TYPE}(coords);\n";
        op << "        coords = coords * 0.5;\n";
        op << "    }\n";
    }
    else
    {
        DE_ASSERT(writeAccess == INDEXACCESS_DYNAMIC_LOOP);
        op << "    for (int i = 0; i < ui_four; i++)\n";
        op << "    {\n";
        op << "        arr[i] = ${VAR_TYPE}(coords);\n";
        op << "        coords = coords * 0.5;\n";
        op << "    }\n";
    }

    // Read array.
    op << "    ${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n";
    if (readAccess == INDEXACCESS_STATIC)
    {
        op << "    res += arr[0];\n";
        op << "    res += arr[1];\n";
        op << "    res += arr[2];\n";
        op << "    res += arr[3];\n";
    }
    else if (readAccess == INDEXACCESS_DYNAMIC)
    {
        op << "    res += arr[ui_zero];\n";
        op << "    res += arr[ui_one];\n";
        op << "    res += arr[ui_two];\n";
        op << "    res += arr[ui_three];\n";
    }
    else if (readAccess == INDEXACCESS_STATIC_LOOP)
    {
        op << "    for (int i = 0; i < 4; i++)\n";
        op << "        res += arr[i];\n";
    }
    else
    {
        DE_ASSERT(readAccess == INDEXACCESS_DYNAMIC_LOOP);
        op << "    for (int i = 0; i < ui_four; i++)\n";
        op << "        res += arr[i];\n";
    }

    if (isVertexCase)
    {
        vtx << "    v_color = vec4(res${PADDING});\n";
        frag << "    o_color = v_color;\n";
    }
    else
    {
        vtx << "    v_coords = a_coords;\n";
        frag << "    o_color = vec4(res${PADDING});\n";
    }

    vtx << "}\n";
    frag << "}\n";

    // Fill in shader templates.
    map<string, string> params;
    params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
    params.insert(pair<string, string>("ARRAY_LEN", "4"));
    params.insert(pair<string, string>("PRECISION", "mediump"));

    if (varType == TYPE_FLOAT)
        params.insert(pair<string, string>("PADDING", ", 0.0, 0.0, 1.0"));
    else if (varType == TYPE_FLOAT_VEC2)
        params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
    else if (varType == TYPE_FLOAT_VEC3)
        params.insert(pair<string, string>("PADDING", ", 1.0"));
    else
        params.insert(pair<string, string>("PADDING", ""));

    StringTemplate vertTemplate(vtx.str().c_str());
    StringTemplate fragTemplate(frag.str().c_str());
    string vertexShaderSource   = vertTemplate.specialize(params);
    string fragmentShaderSource = fragTemplate.specialize(params);

    ShaderEvalFunc evalFunc = getArrayCoordsEvalFunc(varType);
    return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc,
                                  vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

// VECTOR SUBSCRIPT.

void evalSubscriptVec2(ShaderEvalContext &c)
{
    c.color.xyz() = Vec3(c.coords.x() + 0.5f * c.coords.y());
}
void evalSubscriptVec3(ShaderEvalContext &c)
{
    c.color.xyz() = Vec3(c.coords.x() + 0.5f * c.coords.y() + 0.25f * c.coords.z());
}
void evalSubscriptVec4(ShaderEvalContext &c)
{
    c.color.xyz() = Vec3(c.coords.x() + 0.5f * c.coords.y() + 0.25f * c.coords.z() + 0.125f * c.coords.w());
}

static ShaderEvalFunc getVectorSubscriptEvalFunc(DataType dataType)
{
    if (dataType == TYPE_FLOAT_VEC2)
        return evalSubscriptVec2;
    else if (dataType == TYPE_FLOAT_VEC3)
        return evalSubscriptVec3;
    else if (dataType == TYPE_FLOAT_VEC4)
        return evalSubscriptVec4;

    DE_FATAL("Invalid data type.");
    return NULL;
}

static ShaderIndexingCase *createVectorSubscriptCase(Context &context, const char *caseName, const char *description,
                                                     bool isVertexCase, DataType varType, VectorAccessType writeAccess,
                                                     VectorAccessType readAccess)
{
    std::ostringstream vtx;
    std::ostringstream frag;
    std::ostringstream &op = isVertexCase ? vtx : frag;

    int vecLen             = getDataTypeScalarSize(varType);
    const char *vecLenName = getIntUniformName(vecLen);

    vtx << "#version 300 es\n";
    frag << "#version 300 es\n";

    vtx << "in highp vec4 a_position;\n";
    vtx << "in highp vec4 a_coords;\n";
    frag << "layout(location = 0) out mediump vec4 o_color;\n";

    if (isVertexCase)
    {
        vtx << "out mediump vec3 v_color;\n";
        frag << "in mediump vec3 v_color;\n";
    }
    else
    {
        vtx << "out mediump vec4 v_coords;\n";
        frag << "in mediump vec4 v_coords;\n";
    }

    if (writeAccess == SUBSCRIPT_DYNAMIC || readAccess == SUBSCRIPT_DYNAMIC)
    {
        op << "uniform mediump int ui_zero";
        if (vecLen >= 2)
            op << ", ui_one";
        if (vecLen >= 3)
            op << ", ui_two";
        if (vecLen >= 4)
            op << ", ui_three";
        op << ";\n";
    }

    if (writeAccess == SUBSCRIPT_DYNAMIC_LOOP || readAccess == SUBSCRIPT_DYNAMIC_LOOP)
        op << "uniform mediump int " << vecLenName << ";\n";

    vtx << "\n";
    vtx << "void main()\n";
    vtx << "{\n";
    vtx << "    gl_Position = a_position;\n";

    frag << "\n";
    frag << "void main()\n";
    frag << "{\n";

    // Write vector.
    if (isVertexCase)
        op << "    ${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
    else
        op << "    ${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(v_coords);\n";

    op << "    ${PRECISION} ${VAR_TYPE} tmp;\n";
    if (writeAccess == DIRECT)
        op << "    tmp = coords.${SWIZZLE} * vec4(1.0, 0.5, 0.25, 0.125).${SWIZZLE};\n";
    else if (writeAccess == COMPONENT)
    {
        op << "    tmp.x = coords.x;\n";
        if (vecLen >= 2)
            op << "    tmp.y = coords.y * 0.5;\n";
        if (vecLen >= 3)
            op << "    tmp.z = coords.z * 0.25;\n";
        if (vecLen >= 4)
            op << "    tmp.w = coords.w * 0.125;\n";
    }
    else if (writeAccess == SUBSCRIPT_STATIC)
    {
        op << "    tmp[0] = coords.x;\n";
        if (vecLen >= 2)
            op << "    tmp[1] = coords.y * 0.5;\n";
        if (vecLen >= 3)
            op << "    tmp[2] = coords.z * 0.25;\n";
        if (vecLen >= 4)
            op << "    tmp[3] = coords.w * 0.125;\n";
    }
    else if (writeAccess == SUBSCRIPT_DYNAMIC)
    {
        op << "    tmp[ui_zero]  = coords.x;\n";
        if (vecLen >= 2)
            op << "    tmp[ui_one]   = coords.y * 0.5;\n";
        if (vecLen >= 3)
            op << "    tmp[ui_two]   = coords.z * 0.25;\n";
        if (vecLen >= 4)
            op << "    tmp[ui_three] = coords.w * 0.125;\n";
    }
    else if (writeAccess == SUBSCRIPT_STATIC_LOOP)
    {
        op << "    for (int i = 0; i < " << vecLen << "; i++)\n";
        op << "    {\n";
        op << "        tmp[i] = coords.x;\n";
        op << "        coords = coords.${ROT_SWIZZLE} * 0.5;\n";
        op << "    }\n";
    }
    else
    {
        DE_ASSERT(writeAccess == SUBSCRIPT_DYNAMIC_LOOP);
        op << "    for (int i = 0; i < " << vecLenName << "; i++)\n";
        op << "    {\n";
        op << "        tmp[i] = coords.x;\n";
        op << "        coords = coords.${ROT_SWIZZLE} * 0.5;\n";
        op << "    }\n";
    }

    // Read vector.
    op << "    ${PRECISION} float res = 0.0;\n";
    if (readAccess == DIRECT)
        op << "    res = dot(tmp, ${VAR_TYPE}(1.0));\n";
    else if (readAccess == COMPONENT)
    {
        op << "    res += tmp.x;\n";
        if (vecLen >= 2)
            op << "    res += tmp.y;\n";
        if (vecLen >= 3)
            op << "    res += tmp.z;\n";
        if (vecLen >= 4)
            op << "    res += tmp.w;\n";
    }
    else if (readAccess == SUBSCRIPT_STATIC)
    {
        op << "    res += tmp[0];\n";
        if (vecLen >= 2)
            op << "    res += tmp[1];\n";
        if (vecLen >= 3)
            op << "    res += tmp[2];\n";
        if (vecLen >= 4)
            op << "    res += tmp[3];\n";
    }
    else if (readAccess == SUBSCRIPT_DYNAMIC)
    {
        op << "    res += tmp[ui_zero];\n";
        if (vecLen >= 2)
            op << "    res += tmp[ui_one];\n";
        if (vecLen >= 3)
            op << "    res += tmp[ui_two];\n";
        if (vecLen >= 4)
            op << "    res += tmp[ui_three];\n";
    }
    else if (readAccess == SUBSCRIPT_STATIC_LOOP)
    {
        op << "    for (int i = 0; i < " << vecLen << "; i++)\n";
        op << "        res += tmp[i];\n";
    }
    else
    {
        DE_ASSERT(readAccess == SUBSCRIPT_DYNAMIC_LOOP);
        op << "    for (int i = 0; i < " << vecLenName << "; i++)\n";
        op << "        res += tmp[i];\n";
    }

    if (isVertexCase)
    {
        vtx << "    v_color = vec3(res);\n";
        frag << "    o_color = vec4(v_color.rgb, 1.0);\n";
    }
    else
    {
        vtx << "    v_coords = a_coords;\n";
        frag << "    o_color = vec4(vec3(res), 1.0);\n";
    }

    vtx << "}\n";
    frag << "}\n";

    // Fill in shader templates.
    static const char *s_swizzles[5]    = {"", "x", "xy", "xyz", "xyzw"};
    static const char *s_rotSwizzles[5] = {"", "x", "yx", "yzx", "yzwx"};

    map<string, string> params;
    params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
    params.insert(pair<string, string>("PRECISION", "mediump"));
    params.insert(pair<string, string>("SWIZZLE", s_swizzles[vecLen]));
    params.insert(pair<string, string>("ROT_SWIZZLE", s_rotSwizzles[vecLen]));

    StringTemplate vertTemplate(vtx.str().c_str());
    StringTemplate fragTemplate(frag.str().c_str());
    string vertexShaderSource   = vertTemplate.specialize(params);
    string fragmentShaderSource = fragTemplate.specialize(params);

    ShaderEvalFunc evalFunc = getVectorSubscriptEvalFunc(varType);
    return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc,
                                  vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

// MATRIX SUBSCRIPT.

void evalSubscriptMat2(ShaderEvalContext &c)
{
    c.color.xy() = c.coords.swizzle(0, 1) + 0.5f * c.coords.swizzle(1, 2);
}
void evalSubscriptMat2x3(ShaderEvalContext &c)
{
    c.color.xyz() = c.coords.swizzle(0, 1, 2) + 0.5f * c.coords.swizzle(1, 2, 3);
}
void evalSubscriptMat2x4(ShaderEvalContext &c)
{
    c.color = c.coords.swizzle(0, 1, 2, 3) + 0.5f * c.coords.swizzle(1, 2, 3, 0);
}

void evalSubscriptMat3x2(ShaderEvalContext &c)
{
    c.color.xy() = c.coords.swizzle(0, 1) + 0.5f * c.coords.swizzle(1, 2) + 0.25f * c.coords.swizzle(2, 3);
}
void evalSubscriptMat3(ShaderEvalContext &c)
{
    c.color.xyz() = c.coords.swizzle(0, 1, 2) + 0.5f * c.coords.swizzle(1, 2, 3) + 0.25f * c.coords.swizzle(2, 3, 0);
}
void evalSubscriptMat3x4(ShaderEvalContext &c)
{
    c.color = c.coords.swizzle(0, 1, 2, 3) + 0.5f * c.coords.swizzle(1, 2, 3, 0) + 0.25f * c.coords.swizzle(2, 3, 0, 1);
}

void evalSubscriptMat4x2(ShaderEvalContext &c)
{
    c.color.xy() = c.coords.swizzle(0, 1) + 0.5f * c.coords.swizzle(1, 2) + 0.25f * c.coords.swizzle(2, 3) +
                   0.125f * c.coords.swizzle(3, 0);
}
void evalSubscriptMat4x3(ShaderEvalContext &c)
{
    c.color.xyz() = c.coords.swizzle(0, 1, 2) + 0.5f * c.coords.swizzle(1, 2, 3) + 0.25f * c.coords.swizzle(2, 3, 0) +
                    0.125f * c.coords.swizzle(3, 0, 1);
}
void evalSubscriptMat4(ShaderEvalContext &c)
{
    c.color = c.coords + 0.5f * c.coords.swizzle(1, 2, 3, 0) + 0.25f * c.coords.swizzle(2, 3, 0, 1) +
              0.125f * c.coords.swizzle(3, 0, 1, 2);
}

static ShaderEvalFunc getMatrixSubscriptEvalFunc(DataType dataType)
{
    switch (dataType)
    {
    case TYPE_FLOAT_MAT2:
        return evalSubscriptMat2;
    case TYPE_FLOAT_MAT2X3:
        return evalSubscriptMat2x3;
    case TYPE_FLOAT_MAT2X4:
        return evalSubscriptMat2x4;
    case TYPE_FLOAT_MAT3X2:
        return evalSubscriptMat3x2;
    case TYPE_FLOAT_MAT3:
        return evalSubscriptMat3;
    case TYPE_FLOAT_MAT3X4:
        return evalSubscriptMat3x4;
    case TYPE_FLOAT_MAT4X2:
        return evalSubscriptMat4x2;
    case TYPE_FLOAT_MAT4X3:
        return evalSubscriptMat4x3;
    case TYPE_FLOAT_MAT4:
        return evalSubscriptMat4;

    default:
        DE_FATAL("Invalid data type.");
        return DE_NULL;
    }
}

static ShaderIndexingCase *createMatrixSubscriptCase(Context &context, const char *caseName, const char *description,
                                                     bool isVertexCase, DataType varType, IndexAccessType writeAccess,
                                                     IndexAccessType readAccess)
{
    std::ostringstream vtx;
    std::ostringstream frag;
    std::ostringstream &op = isVertexCase ? vtx : frag;

    int numCols             = getDataTypeMatrixNumColumns(varType);
    int numRows             = getDataTypeMatrixNumRows(varType);
    const char *matSizeName = getIntUniformName(numCols);
    DataType vecType        = getDataTypeFloatVec(numRows);

    vtx << "#version 300 es\n";
    frag << "#version 300 es\n";

    vtx << "in highp vec4 a_position;\n";
    vtx << "in highp vec4 a_coords;\n";
    frag << "layout(location = 0) out mediump vec4 o_color;\n";

    if (isVertexCase)
    {
        vtx << "out mediump vec4 v_color;\n";
        frag << "in mediump vec4 v_color;\n";
    }
    else
    {
        vtx << "out mediump vec4 v_coords;\n";
        frag << "in mediump vec4 v_coords;\n";
    }

    if (writeAccess == INDEXACCESS_DYNAMIC || readAccess == INDEXACCESS_DYNAMIC)
    {
        op << "uniform mediump int ui_zero";
        if (numCols >= 2)
            op << ", ui_one";
        if (numCols >= 3)
            op << ", ui_two";
        if (numCols >= 4)
            op << ", ui_three";
        op << ";\n";
    }

    if (writeAccess == INDEXACCESS_DYNAMIC_LOOP || readAccess == INDEXACCESS_DYNAMIC_LOOP)
        op << "uniform mediump int " << matSizeName << ";\n";

    vtx << "\n";
    vtx << "void main()\n";
    vtx << "{\n";
    vtx << "    gl_Position = a_position;\n";

    frag << "\n";
    frag << "void main()\n";
    frag << "{\n";

    // Write matrix.
    if (isVertexCase)
        op << "    ${PRECISION} vec4 coords = a_coords;\n";
    else
        op << "    ${PRECISION} vec4 coords = v_coords;\n";

    op << "    ${PRECISION} ${MAT_TYPE} tmp;\n";
    if (writeAccess == INDEXACCESS_STATIC)
    {
        op << "    tmp[0] = ${VEC_TYPE}(coords);\n";
        if (numCols >= 2)
            op << "    tmp[1] = ${VEC_TYPE}(coords.yzwx) * 0.5;\n";
        if (numCols >= 3)
            op << "    tmp[2] = ${VEC_TYPE}(coords.zwxy) * 0.25;\n";
        if (numCols >= 4)
            op << "    tmp[3] = ${VEC_TYPE}(coords.wxyz) * 0.125;\n";
    }
    else if (writeAccess == INDEXACCESS_DYNAMIC)
    {
        op << "    tmp[ui_zero]  = ${VEC_TYPE}(coords);\n";
        if (numCols >= 2)
            op << "    tmp[ui_one]   = ${VEC_TYPE}(coords.yzwx) * 0.5;\n";
        if (numCols >= 3)
            op << "    tmp[ui_two]   = ${VEC_TYPE}(coords.zwxy) * 0.25;\n";
        if (numCols >= 4)
            op << "    tmp[ui_three] = ${VEC_TYPE}(coords.wxyz) * 0.125;\n";
    }
    else if (writeAccess == INDEXACCESS_STATIC_LOOP)
    {
        op << "    for (int i = 0; i < " << numCols << "; i++)\n";
        op << "    {\n";
        op << "        tmp[i] = ${VEC_TYPE}(coords);\n";
        op << "        coords = coords.yzwx * 0.5;\n";
        op << "    }\n";
    }
    else
    {
        DE_ASSERT(writeAccess == INDEXACCESS_DYNAMIC_LOOP);
        op << "    for (int i = 0; i < " << matSizeName << "; i++)\n";
        op << "    {\n";
        op << "        tmp[i] = ${VEC_TYPE}(coords);\n";
        op << "        coords = coords.yzwx * 0.5;\n";
        op << "    }\n";
    }

    // Read matrix.
    op << "    ${PRECISION} ${VEC_TYPE} res = ${VEC_TYPE}(0.0);\n";
    if (readAccess == INDEXACCESS_STATIC)
    {
        op << "    res += tmp[0];\n";
        if (numCols >= 2)
            op << "    res += tmp[1];\n";
        if (numCols >= 3)
            op << "    res += tmp[2];\n";
        if (numCols >= 4)
            op << "    res += tmp[3];\n";
    }
    else if (readAccess == INDEXACCESS_DYNAMIC)
    {
        op << "    res += tmp[ui_zero];\n";
        if (numCols >= 2)
            op << "    res += tmp[ui_one];\n";
        if (numCols >= 3)
            op << "    res += tmp[ui_two];\n";
        if (numCols >= 4)
            op << "    res += tmp[ui_three];\n";
    }
    else if (readAccess == INDEXACCESS_STATIC_LOOP)
    {
        op << "    for (int i = 0; i < " << numCols << "; i++)\n";
        op << "        res += tmp[i];\n";
    }
    else
    {
        DE_ASSERT(readAccess == INDEXACCESS_DYNAMIC_LOOP);
        op << "    for (int i = 0; i < " << matSizeName << "; i++)\n";
        op << "        res += tmp[i];\n";
    }

    if (isVertexCase)
    {
        vtx << "    v_color = vec4(res${PADDING});\n";
        frag << "    o_color = v_color;\n";
    }
    else
    {
        vtx << "    v_coords = a_coords;\n";
        frag << "    o_color = vec4(res${PADDING});\n";
    }

    vtx << "}\n";
    frag << "}\n";

    // Fill in shader templates.
    map<string, string> params;
    params.insert(pair<string, string>("MAT_TYPE", getDataTypeName(varType)));
    params.insert(pair<string, string>("VEC_TYPE", getDataTypeName(vecType)));
    params.insert(pair<string, string>("PRECISION", "mediump"));

    if (numRows == 2)
        params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
    else if (numRows == 3)
        params.insert(pair<string, string>("PADDING", ", 1.0"));
    else
        params.insert(pair<string, string>("PADDING", ""));

    StringTemplate vertTemplate(vtx.str().c_str());
    StringTemplate fragTemplate(frag.str().c_str());
    string vertexShaderSource   = vertTemplate.specialize(params);
    string fragmentShaderSource = fragTemplate.specialize(params);

    ShaderEvalFunc evalFunc = getMatrixSubscriptEvalFunc(varType);
    return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc,
                                  vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

// ShaderIndexingTests.

ShaderIndexingTests::ShaderIndexingTests(Context &context) : TestCaseGroup(context, "indexing", "Indexing Tests")
{
}

ShaderIndexingTests::~ShaderIndexingTests(void)
{
}

void ShaderIndexingTests::init(void)
{
    static const ShaderType s_shaderTypes[] = {SHADERTYPE_VERTEX, SHADERTYPE_FRAGMENT};

    static const DataType s_floatAndVecTypes[] = {TYPE_FLOAT, TYPE_FLOAT_VEC2, TYPE_FLOAT_VEC3, TYPE_FLOAT_VEC4};

    // Varying array access cases.
    {
        TestCaseGroup *varyingGroup = new TestCaseGroup(m_context, "varying_array", "Varying array access tests.");
        addChild(varyingGroup);

        for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_floatAndVecTypes); typeNdx++)
        {
            DataType varType = s_floatAndVecTypes[typeNdx];
            for (int vertAccess = 0; vertAccess < INDEXACCESS_LAST; vertAccess++)
            {
                for (int fragAccess = 0; fragAccess < INDEXACCESS_LAST; fragAccess++)
                {
                    const char *vertAccessName = getIndexAccessTypeName((IndexAccessType)vertAccess);
                    const char *fragAccessName = getIndexAccessTypeName((IndexAccessType)fragAccess);
                    string name =
                        string(getDataTypeName(varType)) + "_" + vertAccessName + "_write_" + fragAccessName + "_read";
                    string desc = string("Varying array with ") + vertAccessName + " write in vertex shader and " +
                                  fragAccessName + " read in fragment shader.";
                    varyingGroup->addChild(createVaryingArrayCase(m_context, name.c_str(), desc.c_str(), varType,
                                                                  (IndexAccessType)vertAccess,
                                                                  (IndexAccessType)fragAccess));
                }
            }
        }
    }

    // Uniform array access cases.
    {
        TestCaseGroup *uniformGroup = new TestCaseGroup(m_context, "uniform_array", "Uniform array access tests.");
        addChild(uniformGroup);

        for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_floatAndVecTypes); typeNdx++)
        {
            DataType varType = s_floatAndVecTypes[typeNdx];
            for (int readAccess = 0; readAccess < INDEXACCESS_LAST; readAccess++)
            {
                const char *readAccessName = getIndexAccessTypeName((IndexAccessType)readAccess);
                for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
                {
                    ShaderType shaderType      = s_shaderTypes[shaderTypeNdx];
                    const char *shaderTypeName = getShaderTypeName(shaderType);
                    string name = string(getDataTypeName(varType)) + "_" + readAccessName + "_read_" + shaderTypeName;
                    string desc =
                        string("Uniform array with ") + readAccessName + " read in " + shaderTypeName + " shader.";
                    bool isVertexCase = ((ShaderType)shaderType == SHADERTYPE_VERTEX);
                    uniformGroup->addChild(createUniformArrayCase(m_context, name.c_str(), desc.c_str(), isVertexCase,
                                                                  varType, (IndexAccessType)readAccess));
                }
            }
        }
    }

    // Temporary array access cases.
    {
        TestCaseGroup *tmpGroup = new TestCaseGroup(m_context, "tmp_array", "Temporary array access tests.");
        addChild(tmpGroup);

        for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_floatAndVecTypes); typeNdx++)
        {
            DataType varType = s_floatAndVecTypes[typeNdx];
            for (int writeAccess = 0; writeAccess < INDEXACCESS_LAST; writeAccess++)
            {
                for (int readAccess = 0; readAccess < INDEXACCESS_LAST; readAccess++)
                {
                    const char *writeAccessName = getIndexAccessTypeName((IndexAccessType)writeAccess);
                    const char *readAccessName  = getIndexAccessTypeName((IndexAccessType)readAccess);

                    for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
                    {
                        ShaderType shaderType      = s_shaderTypes[shaderTypeNdx];
                        const char *shaderTypeName = getShaderTypeName(shaderType);
                        string name = string(getDataTypeName(varType)) + "_" + writeAccessName + "_write_" +
                                      readAccessName + "_read_" + shaderTypeName;
                        string desc = string("Temporary array with ") + writeAccessName + " write and " +
                                      readAccessName + " read in " + shaderTypeName + " shader.";
                        bool isVertexCase = ((ShaderType)shaderType == SHADERTYPE_VERTEX);
                        tmpGroup->addChild(createTmpArrayCase(m_context, name.c_str(), desc.c_str(), isVertexCase,
                                                              varType, (IndexAccessType)writeAccess,
                                                              (IndexAccessType)readAccess));
                    }
                }
            }
        }
    }

    // Vector indexing with subscripts.
    {
        TestCaseGroup *vecGroup = new TestCaseGroup(m_context, "vector_subscript", "Vector subscript indexing.");
        addChild(vecGroup);

        static const DataType s_vectorTypes[] = {TYPE_FLOAT_VEC2, TYPE_FLOAT_VEC3, TYPE_FLOAT_VEC4};

        for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_vectorTypes); typeNdx++)
        {
            DataType varType = s_vectorTypes[typeNdx];
            for (int writeAccess = 0; writeAccess < VECTORACCESS_LAST; writeAccess++)
            {
                for (int readAccess = 0; readAccess < VECTORACCESS_LAST; readAccess++)
                {
                    const char *writeAccessName = getVectorAccessTypeName((VectorAccessType)writeAccess);
                    const char *readAccessName  = getVectorAccessTypeName((VectorAccessType)readAccess);

                    for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
                    {
                        ShaderType shaderType      = s_shaderTypes[shaderTypeNdx];
                        const char *shaderTypeName = getShaderTypeName(shaderType);
                        string name = string(getDataTypeName(varType)) + "_" + writeAccessName + "_write_" +
                                      readAccessName + "_read_" + shaderTypeName;
                        string desc = string("Vector subscript access with ") + writeAccessName + " write and " +
                                      readAccessName + " read in " + shaderTypeName + " shader.";
                        bool isVertexCase = ((ShaderType)shaderType == SHADERTYPE_VERTEX);
                        vecGroup->addChild(
                            createVectorSubscriptCase(m_context, name.c_str(), desc.c_str(), isVertexCase, varType,
                                                      (VectorAccessType)writeAccess, (VectorAccessType)readAccess));
                    }
                }
            }
        }
    }

    // Matrix indexing with subscripts.
    {
        TestCaseGroup *matGroup = new TestCaseGroup(m_context, "matrix_subscript", "Matrix subscript indexing.");
        addChild(matGroup);

        static const DataType s_matrixTypes[] = {TYPE_FLOAT_MAT2,   TYPE_FLOAT_MAT2X3, TYPE_FLOAT_MAT2X4,
                                                 TYPE_FLOAT_MAT3X2, TYPE_FLOAT_MAT3,   TYPE_FLOAT_MAT3X4,
                                                 TYPE_FLOAT_MAT4X2, TYPE_FLOAT_MAT4X3, TYPE_FLOAT_MAT4};

        for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_matrixTypes); typeNdx++)
        {
            DataType varType = s_matrixTypes[typeNdx];
            for (int writeAccess = 0; writeAccess < INDEXACCESS_LAST; writeAccess++)
            {
                for (int readAccess = 0; readAccess < INDEXACCESS_LAST; readAccess++)
                {
                    const char *writeAccessName = getIndexAccessTypeName((IndexAccessType)writeAccess);
                    const char *readAccessName  = getIndexAccessTypeName((IndexAccessType)readAccess);

                    for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
                    {
                        ShaderType shaderType      = s_shaderTypes[shaderTypeNdx];
                        const char *shaderTypeName = getShaderTypeName(shaderType);
                        string name = string(getDataTypeName(varType)) + "_" + writeAccessName + "_write_" +
                                      readAccessName + "_read_" + shaderTypeName;
                        string desc = string("Vector subscript access with ") + writeAccessName + " write and " +
                                      readAccessName + " read in " + shaderTypeName + " shader.";
                        bool isVertexCase = ((ShaderType)shaderType == SHADERTYPE_VERTEX);
                        matGroup->addChild(
                            createMatrixSubscriptCase(m_context, name.c_str(), desc.c_str(), isVertexCase, varType,
                                                      (IndexAccessType)writeAccess, (IndexAccessType)readAccess));
                    }
                }
            }
        }
    }

    {
        const std::vector<tcu::TestNode *> children =
            gls::ShaderLibrary(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo())
                .loadShaderFile("shaders/indexing.test");

        for (int i = 0; i < (int)children.size(); i++)
            addChild(children[i]);
    }
}

} // namespace Functional
} // namespace gles3
} // namespace deqp
