/*-------------------------------------------------------------------------
 * 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 Depth & stencil tests.
 *//*--------------------------------------------------------------------*/

#include "es3fDepthStencilTests.hpp"
#include "glsFragmentOpUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluStrUtil.hpp"
#include "tcuSurface.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include "tcuImageCompare.hpp"
#include "tcuCommandLine.hpp"
#include "tcuTextureUtil.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "deMemory.h"
#include "deString.h"
#include "rrFragmentOperations.hpp"
#include "sglrReferenceUtils.hpp"

#include <algorithm>
#include <sstream>

#include "glw.h"

namespace deqp
{
namespace gles3
{
namespace Functional
{

using std::ostringstream;
using std::vector;
using tcu::IVec2;
using tcu::TestLog;
using tcu::Vec2;
using tcu::Vec4;

enum
{
    VIEWPORT_WIDTH  = 4 * 3 * 4,
    VIEWPORT_HEIGHT = 4 * 12,

    NUM_RANDOM_CASES     = 25,
    NUM_RANDOM_SUB_CASES = 10
};

namespace DepthStencilCaseUtil
{

struct StencilParams
{
    uint32_t function;
    int reference;
    uint32_t compareMask;

    uint32_t stencilFailOp;
    uint32_t depthFailOp;
    uint32_t depthPassOp;

    uint32_t writeMask;

    StencilParams(void)
        : function(0)
        , reference(0)
        , compareMask(0)
        , stencilFailOp(0)
        , depthFailOp(0)
        , depthPassOp(0)
        , writeMask(0)
    {
    }
};

struct DepthStencilParams
{
    rr::FaceType visibleFace; //!< Quad visible face.

    bool stencilTestEnabled;
    StencilParams stencil[rr::FACETYPE_LAST];

    bool depthTestEnabled;
    uint32_t depthFunc;
    float depth;
    bool depthWriteMask;

    DepthStencilParams(void)
        : visibleFace(rr::FACETYPE_LAST)
        , stencilTestEnabled(false)
        , depthTestEnabled(false)
        , depthFunc(0)
        , depth(0.0f)
        , depthWriteMask(false)
    {
    }
};

tcu::TestLog &operator<<(tcu::TestLog &log, const StencilParams &params)
{
    log << TestLog::Message << "  func = " << glu::getCompareFuncStr(params.function) << "\n"
        << "  ref = " << params.reference << "\n"
        << "  compare mask = " << tcu::toHex(params.compareMask) << "\n"
        << "  stencil fail = " << glu::getStencilOpStr(params.stencilFailOp) << "\n"
        << "  depth fail = " << glu::getStencilOpStr(params.depthFailOp) << "\n"
        << "  depth pass = " << glu::getStencilOpStr(params.depthPassOp) << "\n"
        << "  write mask = " << tcu::toHex(params.writeMask) << "\n"
        << TestLog::EndMessage;
    return log;
}

tcu::TestLog &operator<<(tcu::TestLog &log, const DepthStencilParams &params)
{
    log << TestLog::Message << "Stencil test: " << (params.stencilTestEnabled ? "enabled" : "disabled")
        << TestLog::EndMessage;
    if (params.stencilTestEnabled)
    {
        log << TestLog::Message << "Front-face stencil state: " << TestLog::EndMessage;
        log << params.stencil[rr::FACETYPE_FRONT];

        log << TestLog::Message << "Back-face stencil state: " << TestLog::EndMessage;
        log << params.stencil[rr::FACETYPE_BACK];
    }

    log << TestLog::Message << "Depth test: " << (params.depthTestEnabled ? "enabled" : "disabled")
        << TestLog::EndMessage;
    if (params.depthTestEnabled)
    {
        log << TestLog::Message << "  func = " << glu::getCompareFuncStr(params.depthFunc)
            << "\n"
               "  depth value = "
            << params.depth
            << "\n"
               "  write mask = "
            << (params.depthWriteMask ? "true" : "false") << "\n"
            << TestLog::EndMessage;
    }

    log << TestLog::Message << "Triangles are " << (params.visibleFace == rr::FACETYPE_FRONT ? "front" : "back")
        << "-facing" << TestLog::EndMessage;

    return log;
}

struct ClearCommand
{
    rr::WindowRectangle rect;
    uint32_t buffers;
    tcu::Vec4 color;
    int stencil;
    // \note No depth here - don't use clears for setting depth values; use quad rendering instead. Cleared depths are in [0, 1] to begin with,
    //         whereas rendered depths are given in [-1, 1] and then mapped to [0, 1]; this discrepancy could cause precision issues in depth tests.

    ClearCommand(void) : rect(0, 0, 0, 0), buffers(0), stencil(0)
    {
    }

    ClearCommand(const rr::WindowRectangle &rect_, uint32_t buffers_, const tcu::Vec4 &color_, int stencil_)
        : rect(rect_)
        , buffers(buffers_)
        , color(color_)
        , stencil(stencil_)
    {
    }
};

struct RenderCommand
{
    DepthStencilParams params;
    rr::WindowRectangle rect;
    tcu::Vec4 color;
    tcu::BVec4 colorMask;

    RenderCommand(void) : rect(0, 0, 0, 0)
    {
    }
};

struct RefRenderCommand
{
    gls::FragmentOpUtil::IntegerQuad quad;
    rr::FragmentOperationState state;
};

struct TestRenderTarget
{
    int width;
    int height;
    int depthBits;
    int stencilBits;

    TestRenderTarget(int width_, int height_, int depthBits_, int stencilBits_)
        : width(width_)
        , height(height_)
        , depthBits(depthBits_)
        , stencilBits(stencilBits_)
    {
    }

    TestRenderTarget(void) : width(0), height(0), depthBits(0), stencilBits(0)
    {
    }
};

void getStencilTestValues(int stencilBits, int numValues, int *values)
{
    int numLowest  = numValues / 2;
    int numHighest = numValues - numLowest;
    int maxVal     = (1 << stencilBits) - 1;

    for (int ndx = 0; ndx < numLowest; ndx++)
        values[ndx] = ndx;

    for (int ndx = 0; ndx < numHighest; ndx++)
        values[numValues - ndx - 1] = maxVal - ndx;
}

void generateBaseClearAndDepthCommands(const TestRenderTarget &target, vector<ClearCommand> &clearCommands,
                                       vector<RenderCommand> &renderCommands)
{
    DE_ASSERT(clearCommands.empty());
    DE_ASSERT(renderCommands.empty());

    const int numL0CellsX = 4;
    const int numL0CellsY = 4;
    const int numL1CellsX = 3;
    const int numL1CellsY = 1;
    int cellL0Width       = target.width / numL0CellsX;
    int cellL0Height      = target.height / numL0CellsY;
    int cellL1Width       = cellL0Width / numL1CellsX;
    int cellL1Height      = cellL0Height / numL1CellsY;

    int stencilValues[numL0CellsX * numL0CellsY];
    float depthValues[numL1CellsX * numL1CellsY];

    if (cellL0Width <= 0 || cellL1Width <= 0 || cellL0Height <= 0 || cellL1Height <= 0)
        throw tcu::NotSupportedError("Too small render target");

    // Fullscreen clear to black.
    clearCommands.push_back(ClearCommand(rr::WindowRectangle(0, 0, target.width, target.height), GL_COLOR_BUFFER_BIT,
                                         Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0));

    // Compute stencil values: numL0CellsX*numL0CellsY combinations of lowest and highest bits.
    getStencilTestValues(target.stencilBits, numL0CellsX * numL0CellsY, &stencilValues[0]);

    // Compute depth values
    {
        int numValues   = DE_LENGTH_OF_ARRAY(depthValues);
        float depthStep = 2.0f / (float)(numValues - 1);

        for (int ndx = 0; ndx < numValues; ndx++)
            depthValues[ndx] = -1.0f + depthStep * (float)ndx;
    }

    for (int y0 = 0; y0 < numL0CellsY; y0++)
    {
        for (int x0 = 0; x0 < numL0CellsX; x0++)
        {
            int stencilValue = stencilValues[y0 * numL0CellsX + x0];

            for (int y1 = 0; y1 < numL1CellsY; y1++)
            {
                for (int x1 = 0; x1 < numL1CellsX; x1++)
                {
                    int x = x0 * cellL0Width + x1 * cellL1Width;
                    int y = y0 * cellL0Height + y1 * cellL1Height;
                    rr::WindowRectangle cellL1Rect(x, y, cellL1Width, cellL1Height);

                    clearCommands.push_back(ClearCommand(cellL1Rect, GL_STENCIL_BUFFER_BIT, Vec4(0), stencilValue));

                    RenderCommand renderCmd;
                    renderCmd.params.visibleFace      = rr::FACETYPE_FRONT;
                    renderCmd.params.depth            = depthValues[y1 * numL1CellsX + x1];
                    renderCmd.params.depthTestEnabled = true;
                    renderCmd.params.depthFunc        = GL_ALWAYS;
                    renderCmd.params.depthWriteMask   = true;
                    renderCmd.colorMask               = tcu::BVec4(false);
                    renderCmd.rect                    = cellL1Rect;

                    renderCommands.push_back(renderCmd);
                }
            }
        }
    }
}

void generateDepthVisualizeCommands(const TestRenderTarget &target, vector<RenderCommand> &commands)
{
    const float epsilon             = -0.05f;
    static const float depthSteps[] = {-1.0f, -0.5f, 0.0f, 0.5f, 1.0f};
    int numSteps                    = DE_LENGTH_OF_ARRAY(depthSteps);
    const float colorStep           = 1.0f / (float)(numSteps - 1);

    for (int ndx = 0; ndx < numSteps; ndx++)
    {
        RenderCommand cmd;

        cmd.params.visibleFace      = rr::FACETYPE_FRONT;
        cmd.rect                    = rr::WindowRectangle(0, 0, target.width, target.height);
        cmd.color                   = Vec4(0.0f, 0.0f, colorStep * (float)ndx, 0.0f);
        cmd.colorMask               = tcu::BVec4(false, false, true, false);
        cmd.params.depth            = depthSteps[ndx] + epsilon;
        cmd.params.depthTestEnabled = true;
        cmd.params.depthFunc        = GL_LESS;
        cmd.params.depthWriteMask   = false;

        commands.push_back(cmd);
    }
}

void generateStencilVisualizeCommands(const TestRenderTarget &target, vector<RenderCommand> &commands)
{
    const int numValues = 4 * 4;
    float colorStep     = 1.0f / numValues; // 0 is reserved for non-matching.
    int stencilValues[numValues];

    getStencilTestValues(target.stencilBits, numValues, &stencilValues[0]);

    for (int ndx = 0; ndx < numValues; ndx++)
    {
        RenderCommand cmd;

        cmd.params.visibleFace        = rr::FACETYPE_FRONT;
        cmd.rect                      = rr::WindowRectangle(0, 0, target.width, target.height);
        cmd.color                     = Vec4(0.0f, colorStep * float(ndx + 1), 0.0f, 0.0f);
        cmd.colorMask                 = tcu::BVec4(false, true, false, false);
        cmd.params.stencilTestEnabled = true;

        cmd.params.stencil[rr::FACETYPE_FRONT].function      = GL_EQUAL;
        cmd.params.stencil[rr::FACETYPE_FRONT].reference     = stencilValues[ndx];
        cmd.params.stencil[rr::FACETYPE_FRONT].compareMask   = ~0u;
        cmd.params.stencil[rr::FACETYPE_FRONT].stencilFailOp = GL_KEEP;
        cmd.params.stencil[rr::FACETYPE_FRONT].depthFailOp   = GL_KEEP;
        cmd.params.stencil[rr::FACETYPE_FRONT].depthPassOp   = GL_KEEP;
        cmd.params.stencil[rr::FACETYPE_FRONT].writeMask     = 0u;

        cmd.params.stencil[rr::FACETYPE_BACK] = cmd.params.stencil[rr::FACETYPE_FRONT];

        commands.push_back(cmd);
    }
}

void translateStencilState(const StencilParams &src, rr::StencilState &dst)
{
    dst.func      = sglr::rr_util::mapGLTestFunc(src.function);
    dst.ref       = src.reference;
    dst.compMask  = src.compareMask;
    dst.sFail     = sglr::rr_util::mapGLStencilOp(src.stencilFailOp);
    dst.dpFail    = sglr::rr_util::mapGLStencilOp(src.depthFailOp);
    dst.dpPass    = sglr::rr_util::mapGLStencilOp(src.depthPassOp);
    dst.writeMask = src.writeMask;
}

void translateCommand(const RenderCommand &src, RefRenderCommand &dst, const TestRenderTarget &renderTarget)
{
    const float far    = 1.0f;
    const float near   = 0.0f;
    bool hasDepth      = renderTarget.depthBits > 0;
    bool hasStencil    = renderTarget.stencilBits > 0;
    bool isFrontFacing = src.params.visibleFace == rr::FACETYPE_FRONT;

    dst.quad.posA = IVec2(isFrontFacing ? src.rect.left : (src.rect.left + src.rect.width - 1), src.rect.bottom);
    dst.quad.posB = IVec2(isFrontFacing ? (src.rect.left + src.rect.width - 1) : src.rect.left,
                          src.rect.bottom + src.rect.height - 1);

    std::fill(DE_ARRAY_BEGIN(dst.quad.color), DE_ARRAY_END(dst.quad.color), src.color);
    std::fill(DE_ARRAY_BEGIN(dst.quad.depth), DE_ARRAY_END(dst.quad.depth),
              ((far - near) / 2.0f) * src.params.depth + (near + far) / 2.0f);

    dst.state.colorMask = src.colorMask;

    dst.state.scissorTestEnabled = false;
    dst.state.stencilTestEnabled = hasStencil && src.params.stencilTestEnabled;
    dst.state.depthTestEnabled   = hasDepth && src.params.depthTestEnabled;
    dst.state.blendMode          = rr::BLENDMODE_NONE;
    dst.state.numStencilBits     = renderTarget.stencilBits;

    if (dst.state.depthTestEnabled)
    {
        dst.state.depthFunc = sglr::rr_util::mapGLTestFunc(src.params.depthFunc);
        dst.state.depthMask = src.params.depthWriteMask;
    }

    if (dst.state.stencilTestEnabled)
    {
        translateStencilState(src.params.stencil[rr::FACETYPE_BACK], dst.state.stencilStates[rr::FACETYPE_BACK]);
        translateStencilState(src.params.stencil[rr::FACETYPE_FRONT], dst.state.stencilStates[rr::FACETYPE_FRONT]);
    }
}

void render(const vector<ClearCommand> &clears, int viewportX, int viewportY)
{
    glEnable(GL_SCISSOR_TEST);

    for (int ndx = 0; ndx < (int)clears.size(); ndx++)
    {
        const ClearCommand &clear = clears[ndx];

        if (clear.buffers & GL_COLOR_BUFFER_BIT)
            glClearColor(clear.color.x(), clear.color.y(), clear.color.z(), clear.color.w());
        if (clear.buffers & GL_STENCIL_BUFFER_BIT)
            glClearStencil(clear.stencil);

        DE_ASSERT(clear.buffers ==
                  (clear.buffers & (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))); // \note Don't use clear for depths.

        glScissor(clear.rect.left + viewportX, clear.rect.bottom + viewportY, clear.rect.width, clear.rect.height);
        glClear(clear.buffers);
    }

    glDisable(GL_SCISSOR_TEST);
}

void render(gls::FragmentOpUtil::QuadRenderer &renderer, const RenderCommand &command, int viewportX, int viewportY)
{
    if (command.params.stencilTestEnabled)
    {
        glEnable(GL_STENCIL_TEST);

        for (int face = 0; face < rr::FACETYPE_LAST; face++)
        {
            uint32_t glFace              = face == rr::FACETYPE_BACK ? GL_BACK : GL_FRONT;
            const StencilParams &sParams = command.params.stencil[face];

            glStencilFuncSeparate(glFace, sParams.function, sParams.reference, sParams.compareMask);
            glStencilOpSeparate(glFace, sParams.stencilFailOp, sParams.depthFailOp, sParams.depthPassOp);
            glStencilMaskSeparate(glFace, sParams.writeMask);
        }
    }
    else
        glDisable(GL_STENCIL_TEST);

    if (command.params.depthTestEnabled)
    {
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(command.params.depthFunc);
        glDepthMask(command.params.depthWriteMask ? GL_TRUE : GL_FALSE);
    }
    else
        glDisable(GL_DEPTH_TEST);

    glColorMask(command.colorMask[0] ? GL_TRUE : GL_FALSE, command.colorMask[1] ? GL_TRUE : GL_FALSE,
                command.colorMask[2] ? GL_TRUE : GL_FALSE, command.colorMask[3] ? GL_TRUE : GL_FALSE);
    glViewport(command.rect.left + viewportX, command.rect.bottom + viewportY, command.rect.width, command.rect.height);

    gls::FragmentOpUtil::Quad quad;

    bool isFrontFacing = command.params.visibleFace == rr::FACETYPE_FRONT;
    quad.posA          = Vec2(isFrontFacing ? -1.0f : 1.0f, -1.0f);
    quad.posB          = Vec2(isFrontFacing ? 1.0f : -1.0f, 1.0f);

    std::fill(DE_ARRAY_BEGIN(quad.color), DE_ARRAY_END(quad.color), command.color);
    std::fill(DE_ARRAY_BEGIN(quad.depth), DE_ARRAY_END(quad.depth), command.params.depth);

    renderer.render(quad);
    GLU_CHECK();
}

void renderReference(const vector<ClearCommand> &clears, const tcu::PixelBufferAccess &dstColor,
                     const tcu::PixelBufferAccess &dstStencil, int stencilBits)
{
    for (int ndx = 0; ndx < (int)clears.size(); ndx++)
    {
        const ClearCommand &clear = clears[ndx];

        if (clear.buffers & GL_COLOR_BUFFER_BIT)
            tcu::clear(
                tcu::getSubregion(dstColor, clear.rect.left, clear.rect.bottom, clear.rect.width, clear.rect.height),
                clear.color);

        if (clear.buffers & GL_STENCIL_BUFFER_BIT && stencilBits > 0)
        {
            int maskedVal = clear.stencil & ((1 << stencilBits) - 1);
            tcu::clearStencil(
                tcu::getSubregion(dstStencil, clear.rect.left, clear.rect.bottom, clear.rect.width, clear.rect.height),
                maskedVal);
        }

        DE_ASSERT(clear.buffers ==
                  (clear.buffers & (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))); // \note Don't use clear for depths.
    }
}

} // namespace DepthStencilCaseUtil

using namespace DepthStencilCaseUtil;

class DepthStencilCase : public TestCase
{
public:
    DepthStencilCase(Context &context, const char *name, const char *desc,
                     const std::vector<DepthStencilParams> &cases);
    ~DepthStencilCase(void);

    void init(void);
    void deinit(void);

    IterateResult iterate(void);

private:
    DepthStencilCase(const DepthStencilCase &other);
    DepthStencilCase &operator=(const DepthStencilCase &other);

    std::vector<DepthStencilParams> m_cases;

    TestRenderTarget m_renderTarget;
    std::vector<ClearCommand> m_baseClears;
    std::vector<RenderCommand> m_baseDepthRenders;
    std::vector<RenderCommand> m_visualizeCommands;
    std::vector<RefRenderCommand> m_refBaseDepthRenders;
    std::vector<RefRenderCommand> m_refVisualizeCommands;

    gls::FragmentOpUtil::QuadRenderer *m_renderer;
    tcu::Surface *m_refColorBuffer;
    tcu::TextureLevel *m_refDepthBuffer;
    tcu::TextureLevel *m_refStencilBuffer;
    gls::FragmentOpUtil::ReferenceQuadRenderer *m_refRenderer;

    int m_iterNdx;
};

DepthStencilCase::DepthStencilCase(Context &context, const char *name, const char *desc,
                                   const std::vector<DepthStencilParams> &cases)
    : TestCase(context, name, desc)
    , m_cases(cases)
    , m_renderer(DE_NULL)
    , m_refColorBuffer(DE_NULL)
    , m_refDepthBuffer(DE_NULL)
    , m_refStencilBuffer(DE_NULL)
    , m_refRenderer(DE_NULL)
    , m_iterNdx(0)
{
}

DepthStencilCase::~DepthStencilCase(void)
{
    delete m_renderer;
    delete m_refColorBuffer;
    delete m_refDepthBuffer;
    delete m_refStencilBuffer;
    delete m_refRenderer;
}

void DepthStencilCase::init(void)
{
    DE_ASSERT(!m_renderer && !m_refColorBuffer && !m_refDepthBuffer && !m_refStencilBuffer && !m_refRenderer);

    // Compute render target.
    int viewportW  = de::min<int>(m_context.getRenderTarget().getWidth(), VIEWPORT_WIDTH);
    int viewportH  = de::min<int>(m_context.getRenderTarget().getHeight(), VIEWPORT_HEIGHT);
    m_renderTarget = TestRenderTarget(viewportW, viewportH, m_context.getRenderTarget().getDepthBits(),
                                      m_context.getRenderTarget().getStencilBits());

    // Compute base clears & visualization commands.
    generateBaseClearAndDepthCommands(m_renderTarget, m_baseClears, m_baseDepthRenders);
    generateDepthVisualizeCommands(m_renderTarget, m_visualizeCommands);
    generateStencilVisualizeCommands(m_renderTarget, m_visualizeCommands);

    // Translate to ref commands.
    m_refBaseDepthRenders.resize(m_baseDepthRenders.size());
    for (int ndx = 0; ndx < (int)m_baseDepthRenders.size(); ndx++)
        translateCommand(m_baseDepthRenders[ndx], m_refBaseDepthRenders[ndx], m_renderTarget);

    m_refVisualizeCommands.resize(m_visualizeCommands.size());
    for (int ndx = 0; ndx < (int)m_visualizeCommands.size(); ndx++)
        translateCommand(m_visualizeCommands[ndx], m_refVisualizeCommands[ndx], m_renderTarget);

    m_renderer         = new gls::FragmentOpUtil::QuadRenderer(m_context.getRenderContext(), glu::GLSL_VERSION_300_ES);
    m_refColorBuffer   = new tcu::Surface(viewportW, viewportH);
    m_refDepthBuffer   = new tcu::TextureLevel(tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT),
                                               viewportW, viewportH);
    m_refStencilBuffer = new tcu::TextureLevel(
        tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT32), viewportW, viewportH);
    m_refRenderer = new gls::FragmentOpUtil::ReferenceQuadRenderer();

    m_iterNdx = 0;
    m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}

void DepthStencilCase::deinit(void)
{
    delete m_renderer;
    delete m_refColorBuffer;
    delete m_refDepthBuffer;
    delete m_refStencilBuffer;
    delete m_refRenderer;

    m_renderer         = DE_NULL;
    m_refColorBuffer   = DE_NULL;
    m_refDepthBuffer   = DE_NULL;
    m_refStencilBuffer = DE_NULL;
    m_refRenderer      = DE_NULL;

    m_baseClears.clear();
    m_baseDepthRenders.clear();
    m_visualizeCommands.clear();
    m_refBaseDepthRenders.clear();
    m_refVisualizeCommands.clear();
}

DepthStencilCase::IterateResult DepthStencilCase::iterate(void)
{
    de::Random rnd(deStringHash(getName()) ^ deInt32Hash(m_iterNdx));
    int viewportX = rnd.getInt(0, m_context.getRenderTarget().getWidth() - m_renderTarget.width);
    int viewportY = rnd.getInt(0, m_context.getRenderTarget().getHeight() - m_renderTarget.height);
    RenderCommand testCmd;

    tcu::Surface renderedImg(m_renderTarget.width, m_renderTarget.height);
    tcu::RGBA threshold = m_context.getRenderTarget().getPixelFormat().getColorThreshold();

    // Fill in test command for this iteration.
    testCmd.color     = Vec4(1.0f, 0.0f, 0.0f, 1.0f);
    testCmd.colorMask = tcu::BVec4(true);
    testCmd.rect      = rr::WindowRectangle(0, 0, m_renderTarget.width, m_renderTarget.height);
    testCmd.params    = m_cases[m_iterNdx];

    if (m_iterNdx == 0)
    {
        m_testCtx.getLog() << TestLog::Message
                           << "Channels:\n"
                              "  RED: passing pixels\n"
                              "  GREEN: stencil values\n"
                              "  BLUE: depth values"
                           << TestLog::EndMessage;
    }

    if (m_cases.size() > 1)
        m_testCtx.getLog() << TestLog::Message << "Iteration " << m_iterNdx << "..." << TestLog::EndMessage;

    m_testCtx.getLog() << m_cases[m_iterNdx];

    // Submit render commands to gl GL.

    // Base clears.
    render(m_baseClears, viewportX, viewportY);

    // Base depths.
    for (vector<RenderCommand>::const_iterator cmd = m_baseDepthRenders.begin(); cmd != m_baseDepthRenders.end(); ++cmd)
        render(*m_renderer, *cmd, viewportX, viewportY);

    // Test command.
    render(*m_renderer, testCmd, viewportX, viewportY);

    // Visualization commands.
    for (vector<RenderCommand>::const_iterator cmd = m_visualizeCommands.begin(); cmd != m_visualizeCommands.end();
         ++cmd)
        render(*m_renderer, *cmd, viewportX, viewportY);

    // Re-enable all write masks.
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glDepthMask(GL_TRUE);
    glStencilMask(~0u);

    // Ask GPU to start rendering.
    glFlush();

    // Render reference while GPU is doing work.
    {
        RefRenderCommand refTestCmd;
        translateCommand(testCmd, refTestCmd, m_renderTarget);

        // Base clears.
        renderReference(m_baseClears, m_refColorBuffer->getAccess(), m_refStencilBuffer->getAccess(),
                        m_renderTarget.stencilBits);

        // Base depths.
        for (vector<RefRenderCommand>::const_iterator cmd = m_refBaseDepthRenders.begin();
             cmd != m_refBaseDepthRenders.end(); ++cmd)
            m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
                                  gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
                                  gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()), cmd->quad,
                                  cmd->state);

        // Test command.
        m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
                              gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
                              gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()),
                              refTestCmd.quad, refTestCmd.state);

        // Visualization commands.
        for (vector<RefRenderCommand>::const_iterator cmd = m_refVisualizeCommands.begin();
             cmd != m_refVisualizeCommands.end(); ++cmd)
            m_refRenderer->render(gls::FragmentOpUtil::getMultisampleAccess(m_refColorBuffer->getAccess()),
                                  gls::FragmentOpUtil::getMultisampleAccess(m_refDepthBuffer->getAccess()),
                                  gls::FragmentOpUtil::getMultisampleAccess(m_refStencilBuffer->getAccess()), cmd->quad,
                                  cmd->state);
    }

    // Read rendered image.
    glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedImg.getAccess());

    m_iterNdx += 1;

    // Compare to reference.
    bool isLastIter = m_iterNdx >= (int)m_cases.size();
    bool compareOk  = tcu::pixelThresholdCompare(m_testCtx.getLog(), "CompareResult", "Image Comparison Result",
                                                 *m_refColorBuffer, renderedImg, threshold, tcu::COMPARE_LOG_RESULT);

    m_testCtx.getLog() << TestLog::Message << (compareOk ? "  Passed." : "  FAILED!") << TestLog::EndMessage;
    if (!compareOk)
        m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");

    if (compareOk && !isLastIter)
        return CONTINUE;
    else
        return STOP;
}

DepthStencilTests::DepthStencilTests(Context &context)
    : TestCaseGroup(context, "depth_stencil", "Depth and Stencil Op Tests")
{
}

DepthStencilTests::~DepthStencilTests(void)
{
}

static void randomDepthStencilState(de::Random &rnd, DepthStencilParams &params)
{
    const float stencilTestProbability = 0.8f;
    const float depthTestProbability   = 0.7f;

    static const uint32_t compareFuncs[] = {GL_NEVER, GL_ALWAYS, GL_LESS,    GL_LEQUAL,
                                            GL_EQUAL, GL_GEQUAL, GL_GREATER, GL_NOTEQUAL};

    static const uint32_t stencilOps[] = {GL_KEEP, GL_ZERO,   GL_REPLACE,   GL_INCR,
                                          GL_DECR, GL_INVERT, GL_INCR_WRAP, GL_DECR_WRAP};

    static const float depthValues[] = {-1.0f, -0.8f, -0.6f, -0.4f, -0.2f, 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f};

    params.visibleFace        = rnd.getBool() ? rr::FACETYPE_FRONT : rr::FACETYPE_BACK;
    params.stencilTestEnabled = rnd.getFloat() < stencilTestProbability;
    params.depthTestEnabled   = !params.stencilTestEnabled || (rnd.getFloat() < depthTestProbability);

    if (params.stencilTestEnabled)
    {
        for (int ndx = 0; ndx < 2; ndx++)
        {
            params.stencil[ndx].function =
                rnd.choose<uint32_t>(DE_ARRAY_BEGIN(compareFuncs), DE_ARRAY_END(compareFuncs));
            params.stencil[ndx].reference   = rnd.getInt(-2, 260);
            params.stencil[ndx].compareMask = rnd.getUint32();
            params.stencil[ndx].stencilFailOp =
                rnd.choose<uint32_t>(DE_ARRAY_BEGIN(stencilOps), DE_ARRAY_END(stencilOps));
            params.stencil[ndx].depthFailOp =
                rnd.choose<uint32_t>(DE_ARRAY_BEGIN(stencilOps), DE_ARRAY_END(stencilOps));
            params.stencil[ndx].depthPassOp =
                rnd.choose<uint32_t>(DE_ARRAY_BEGIN(stencilOps), DE_ARRAY_END(stencilOps));
            params.stencil[ndx].writeMask = rnd.getUint32();
        }
    }

    if (params.depthTestEnabled)
    {
        params.depthFunc      = rnd.choose<uint32_t>(DE_ARRAY_BEGIN(compareFuncs), DE_ARRAY_END(compareFuncs));
        params.depth          = rnd.choose<float>(DE_ARRAY_BEGIN(depthValues), DE_ARRAY_END(depthValues));
        params.depthWriteMask = rnd.getBool();
    }
}

void DepthStencilTests::init(void)
{
    static const struct
    {
        const char *name;
        uint32_t func;
    } compareFuncs[] = {{"never", GL_NEVER}, {"always", GL_ALWAYS}, {"less", GL_LESS},       {"lequal", GL_LEQUAL},
                        {"equal", GL_EQUAL}, {"gequal", GL_GEQUAL}, {"greater", GL_GREATER}, {"notequal", GL_NOTEQUAL}};

    static const struct
    {
        const char *name;
        uint32_t op;
    } stencilOps[] = {{"keep", GL_KEEP},           {"zero", GL_ZERO},          {"replace", GL_REPLACE},
                      {"incr", GL_INCR},           {"decr", GL_DECR},          {"invert", GL_INVERT},
                      {"incr_wrap", GL_INCR_WRAP}, {"decr_wrap", GL_DECR_WRAP}};

    static const struct
    {
        rr::FaceType visibleFace;
        uint32_t sFail;
        uint32_t dFail;
        uint32_t dPass;
        int stencilRef;
        uint32_t compareMask;
        uint32_t writeMask;
        float depth;
    } functionCases[] = {{rr::FACETYPE_BACK, GL_DECR, GL_INCR, GL_INVERT, 4, ~0u, ~0u, -0.7f},
                         {rr::FACETYPE_FRONT, GL_DECR, GL_INCR, GL_INVERT, 2, ~0u, ~0u, 0.0f},
                         {rr::FACETYPE_BACK, GL_DECR, GL_INCR, GL_INVERT, 1, ~0u, ~0u, 0.2f},
                         {rr::FACETYPE_FRONT, GL_DECR_WRAP, GL_INVERT, GL_REPLACE, 4, ~0u, ~0u, 1.0f}};

    // All combinations of depth stencil functions.
    {
        tcu::TestCaseGroup *functionsGroup =
            new tcu::TestCaseGroup(m_testCtx, "stencil_depth_funcs", "Combinations of Depth and Stencil Functions");
        addChild(functionsGroup);

        for (int stencilFunc = 0; stencilFunc < DE_LENGTH_OF_ARRAY(compareFuncs) + 1; stencilFunc++)
        {
            // One extra: depth test disabled.
            for (int depthFunc = 0; depthFunc < DE_LENGTH_OF_ARRAY(compareFuncs) + 1; depthFunc++)
            {
                DepthStencilParams params;
                ostringstream name;
                bool hasStencilFunc = de::inBounds(stencilFunc, 0, DE_LENGTH_OF_ARRAY(compareFuncs));
                bool hasDepthFunc   = de::inBounds(depthFunc, 0, DE_LENGTH_OF_ARRAY(compareFuncs));

                if (hasStencilFunc)
                    name << "stencil_" << compareFuncs[stencilFunc].name << "_";
                else
                    name << "no_stencil_";

                if (hasDepthFunc)
                    name << "depth_" << compareFuncs[depthFunc].name;
                else
                    name << "no_depth";

                params.depthFunc        = hasDepthFunc ? compareFuncs[depthFunc].func : 0;
                params.depthTestEnabled = hasDepthFunc;
                params.depthWriteMask   = true;

                params.stencilTestEnabled = hasStencilFunc;

                vector<DepthStencilParams> cases;
                for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(functionCases); ndx++)
                {
                    rr::FaceType visible    = functionCases[ndx].visibleFace;
                    rr::FaceType notVisible = visible == rr::FACETYPE_FRONT ? rr::FACETYPE_BACK : rr::FACETYPE_FRONT;

                    params.depth       = functionCases[ndx].depth;
                    params.visibleFace = visible;

                    params.stencil[visible].function      = hasStencilFunc ? compareFuncs[stencilFunc].func : 0;
                    params.stencil[visible].reference     = functionCases[ndx].stencilRef;
                    params.stencil[visible].stencilFailOp = functionCases[ndx].sFail;
                    params.stencil[visible].depthFailOp   = functionCases[ndx].dFail;
                    params.stencil[visible].depthPassOp   = functionCases[ndx].dPass;
                    params.stencil[visible].compareMask   = functionCases[ndx].compareMask;
                    params.stencil[visible].writeMask     = functionCases[ndx].writeMask;

                    params.stencil[notVisible].function      = GL_ALWAYS;
                    params.stencil[notVisible].reference     = 0;
                    params.stencil[notVisible].stencilFailOp = GL_REPLACE;
                    params.stencil[notVisible].depthFailOp   = GL_REPLACE;
                    params.stencil[notVisible].depthPassOp   = GL_REPLACE;
                    params.stencil[notVisible].compareMask   = 0u;
                    params.stencil[notVisible].writeMask     = ~0u;

                    cases.push_back(params);
                }

                functionsGroup->addChild(new DepthStencilCase(m_context, name.str().c_str(), "", cases));
            }
        }
    }

    static const struct
    {
        rr::FaceType visibleFace;
        uint32_t func;
        int ref;
        uint32_t compareMask;
        uint32_t writeMask;
    } opCombinationCases[] = {{rr::FACETYPE_BACK, GL_LESS, 4, ~0u, ~0u},
                              {rr::FACETYPE_FRONT, GL_GREATER, 2, ~0u, ~0u},
                              {rr::FACETYPE_BACK, GL_EQUAL, 3, ~2u, ~0u},
                              {rr::FACETYPE_FRONT, GL_NOTEQUAL, 1, ~0u, ~1u}};

    // All combinations of stencil ops.
    {
        tcu::TestCaseGroup *opCombinationGroup =
            new tcu::TestCaseGroup(m_testCtx, "stencil_ops", "Stencil Op Combinations");
        addChild(opCombinationGroup);

        for (int sFail = 0; sFail < DE_LENGTH_OF_ARRAY(stencilOps); sFail++)
        {
            for (int dFail = 0; dFail < DE_LENGTH_OF_ARRAY(stencilOps); dFail++)
            {
                for (int dPass = 0; dPass < DE_LENGTH_OF_ARRAY(stencilOps); dPass++)
                {
                    DepthStencilParams params;
                    ostringstream name;

                    name << stencilOps[sFail].name << "_" << stencilOps[dFail].name << "_" << stencilOps[dPass].name;

                    params.depthFunc        = GL_LEQUAL;
                    params.depth            = 0.0f;
                    params.depthTestEnabled = true;
                    params.depthWriteMask   = true;

                    params.stencilTestEnabled = true;

                    vector<DepthStencilParams> cases;
                    for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(opCombinationCases); ndx++)
                    {
                        rr::FaceType visible = opCombinationCases[ndx].visibleFace;
                        rr::FaceType notVisible =
                            visible == rr::FACETYPE_FRONT ? rr::FACETYPE_BACK : rr::FACETYPE_FRONT;

                        params.visibleFace = visible;

                        params.stencil[visible].function      = opCombinationCases[ndx].func;
                        params.stencil[visible].reference     = opCombinationCases[ndx].ref;
                        params.stencil[visible].stencilFailOp = stencilOps[sFail].op;
                        params.stencil[visible].depthFailOp   = stencilOps[dFail].op;
                        params.stencil[visible].depthPassOp   = stencilOps[dPass].op;
                        params.stencil[visible].compareMask   = opCombinationCases[ndx].compareMask;
                        params.stencil[visible].writeMask     = opCombinationCases[ndx].writeMask;

                        params.stencil[notVisible].function      = GL_ALWAYS;
                        params.stencil[notVisible].reference     = 0;
                        params.stencil[notVisible].stencilFailOp = GL_REPLACE;
                        params.stencil[notVisible].depthFailOp   = GL_REPLACE;
                        params.stencil[notVisible].depthPassOp   = GL_REPLACE;
                        params.stencil[notVisible].compareMask   = 0u;
                        params.stencil[notVisible].writeMask     = ~0u;

                        cases.push_back(params);
                    }

                    opCombinationGroup->addChild(new DepthStencilCase(m_context, name.str().c_str(), "", cases));
                }
            }
        }
    }

    // Write masks
    {
        tcu::TestCaseGroup *writeMaskGroup =
            new tcu::TestCaseGroup(m_testCtx, "write_mask", "Depth and Stencil Write Masks");
        addChild(writeMaskGroup);

        // Depth mask
        {
            DepthStencilParams params;

            params.depthFunc          = GL_LEQUAL;
            params.depth              = 0.0f;
            params.depthTestEnabled   = true;
            params.stencilTestEnabled = true;

            params.stencil[rr::FACETYPE_FRONT].function      = GL_NOTEQUAL;
            params.stencil[rr::FACETYPE_FRONT].reference     = 1;
            params.stencil[rr::FACETYPE_FRONT].stencilFailOp = GL_INVERT;
            params.stencil[rr::FACETYPE_FRONT].depthFailOp   = GL_INCR;
            params.stencil[rr::FACETYPE_FRONT].depthPassOp   = GL_DECR;
            params.stencil[rr::FACETYPE_FRONT].compareMask   = ~0u;
            params.stencil[rr::FACETYPE_FRONT].writeMask     = ~0u;

            params.stencil[rr::FACETYPE_BACK].function      = GL_ALWAYS;
            params.stencil[rr::FACETYPE_BACK].reference     = 0;
            params.stencil[rr::FACETYPE_BACK].stencilFailOp = GL_REPLACE;
            params.stencil[rr::FACETYPE_BACK].depthFailOp   = GL_INVERT;
            params.stencil[rr::FACETYPE_BACK].depthPassOp   = GL_INCR;
            params.stencil[rr::FACETYPE_BACK].compareMask   = ~0u;
            params.stencil[rr::FACETYPE_BACK].writeMask     = ~0u;

            vector<DepthStencilParams> cases;

            // Case 1: front, depth write enabled
            params.visibleFace    = rr::FACETYPE_FRONT;
            params.depthWriteMask = true;
            cases.push_back(params);

            // Case 2: front, depth write disabled
            params.visibleFace    = rr::FACETYPE_FRONT;
            params.depthWriteMask = false;
            cases.push_back(params);

            // Case 3: back, depth write enabled
            params.visibleFace    = rr::FACETYPE_BACK;
            params.depthWriteMask = true;
            cases.push_back(params);

            // Case 4: back, depth write disabled
            params.visibleFace    = rr::FACETYPE_BACK;
            params.depthWriteMask = false;
            cases.push_back(params);

            writeMaskGroup->addChild(new DepthStencilCase(m_context, "depth", "Depth Write Mask", cases));
        }

        // Stencil write masks.
        {
            static const struct
            {
                rr::FaceType visibleFace;
                uint32_t frontWriteMask;
                uint32_t backWriteMask;
            } stencilWmaskCases[] = {{rr::FACETYPE_FRONT, ~0u, 0u},     {rr::FACETYPE_FRONT, 0u, ~0u},
                                     {rr::FACETYPE_FRONT, 0xfu, 0xf0u}, {rr::FACETYPE_FRONT, 0x2u, 0x4u},
                                     {rr::FACETYPE_BACK, 0u, ~0u},      {rr::FACETYPE_BACK, ~0u, 0u},
                                     {rr::FACETYPE_BACK, 0xf0u, 0xfu},  {rr::FACETYPE_BACK, 0x4u, 0x2u}};

            DepthStencilParams params;

            params.depthFunc          = GL_LEQUAL;
            params.depth              = 0.0f;
            params.depthTestEnabled   = true;
            params.depthWriteMask     = true;
            params.stencilTestEnabled = true;

            params.stencil[rr::FACETYPE_FRONT].function      = GL_NOTEQUAL;
            params.stencil[rr::FACETYPE_FRONT].reference     = 1;
            params.stencil[rr::FACETYPE_FRONT].stencilFailOp = GL_INVERT;
            params.stencil[rr::FACETYPE_FRONT].depthFailOp   = GL_INCR;
            params.stencil[rr::FACETYPE_FRONT].depthPassOp   = GL_DECR;
            params.stencil[rr::FACETYPE_FRONT].compareMask   = ~0u;

            params.stencil[rr::FACETYPE_BACK].function      = GL_ALWAYS;
            params.stencil[rr::FACETYPE_BACK].reference     = 0;
            params.stencil[rr::FACETYPE_BACK].stencilFailOp = GL_REPLACE;
            params.stencil[rr::FACETYPE_BACK].depthFailOp   = GL_INVERT;
            params.stencil[rr::FACETYPE_BACK].depthPassOp   = GL_INCR;
            params.stencil[rr::FACETYPE_BACK].compareMask   = ~0u;

            vector<DepthStencilParams> cases;
            for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(stencilWmaskCases); ndx++)
            {
                params.visibleFace                           = stencilWmaskCases[ndx].visibleFace;
                params.stencil[rr::FACETYPE_FRONT].writeMask = stencilWmaskCases[ndx].frontWriteMask;
                params.stencil[rr::FACETYPE_BACK].writeMask  = stencilWmaskCases[ndx].backWriteMask;
                cases.push_back(params);
            }

            writeMaskGroup->addChild(new DepthStencilCase(m_context, "stencil", "Stencil Write Mask", cases));
        }

        // Depth & stencil write masks.
        {
            static const struct
            {
                bool depthWriteMask;
                rr::FaceType visibleFace;
                uint32_t frontWriteMask;
                uint32_t backWriteMask;
            } depthStencilWmaskCases[] = {
                {false, rr::FACETYPE_FRONT, ~0u, 0u},     {false, rr::FACETYPE_FRONT, 0u, ~0u},
                {false, rr::FACETYPE_FRONT, 0xfu, 0xf0u}, {true, rr::FACETYPE_FRONT, ~0u, 0u},
                {true, rr::FACETYPE_FRONT, 0u, ~0u},      {true, rr::FACETYPE_FRONT, 0xfu, 0xf0u},
                {false, rr::FACETYPE_BACK, 0u, ~0u},      {false, rr::FACETYPE_BACK, ~0u, 0u},
                {false, rr::FACETYPE_BACK, 0xf0u, 0xfu},  {true, rr::FACETYPE_BACK, 0u, ~0u},
                {true, rr::FACETYPE_BACK, ~0u, 0u},       {true, rr::FACETYPE_BACK, 0xf0u, 0xfu}};

            DepthStencilParams params;

            params.depthFunc          = GL_LEQUAL;
            params.depth              = 0.0f;
            params.depthTestEnabled   = true;
            params.depthWriteMask     = true;
            params.stencilTestEnabled = true;

            params.stencil[rr::FACETYPE_FRONT].function      = GL_NOTEQUAL;
            params.stencil[rr::FACETYPE_FRONT].reference     = 1;
            params.stencil[rr::FACETYPE_FRONT].stencilFailOp = GL_INVERT;
            params.stencil[rr::FACETYPE_FRONT].depthFailOp   = GL_INCR;
            params.stencil[rr::FACETYPE_FRONT].depthPassOp   = GL_DECR;
            params.stencil[rr::FACETYPE_FRONT].compareMask   = ~0u;

            params.stencil[rr::FACETYPE_BACK].function      = GL_ALWAYS;
            params.stencil[rr::FACETYPE_BACK].reference     = 0;
            params.stencil[rr::FACETYPE_BACK].stencilFailOp = GL_REPLACE;
            params.stencil[rr::FACETYPE_BACK].depthFailOp   = GL_INVERT;
            params.stencil[rr::FACETYPE_BACK].depthPassOp   = GL_INCR;
            params.stencil[rr::FACETYPE_BACK].compareMask   = ~0u;

            vector<DepthStencilParams> cases;
            for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(depthStencilWmaskCases); ndx++)
            {
                params.depthWriteMask                        = depthStencilWmaskCases[ndx].depthWriteMask;
                params.visibleFace                           = depthStencilWmaskCases[ndx].visibleFace;
                params.stencil[rr::FACETYPE_FRONT].writeMask = depthStencilWmaskCases[ndx].frontWriteMask;
                params.stencil[rr::FACETYPE_BACK].writeMask  = depthStencilWmaskCases[ndx].backWriteMask;
                cases.push_back(params);
            }

            writeMaskGroup->addChild(new DepthStencilCase(m_context, "both", "Depth and Stencil Write Masks", cases));
        }
    }

    // Randomized cases
    {
        tcu::TestCaseGroup *randomGroup =
            new tcu::TestCaseGroup(m_testCtx, "random", "Randomized Depth and Stencil Test Cases");
        addChild(randomGroup);

        for (int caseNdx = 0; caseNdx < NUM_RANDOM_CASES; caseNdx++)
        {
            vector<DepthStencilParams> subCases(NUM_RANDOM_SUB_CASES);
            de::Random rnd(deInt32Hash(caseNdx) ^ deInt32Hash(m_testCtx.getCommandLine().getBaseSeed()));

            for (vector<DepthStencilParams>::iterator iter = subCases.begin(); iter != subCases.end(); ++iter)
                randomDepthStencilState(rnd, *iter);

            randomGroup->addChild(new DepthStencilCase(m_context, de::toString(caseNdx).c_str(), "", subCases));
        }
    }
}

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