/*-------------------------------------------------------------------------
 * drawElements Quality Program EGL 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 eglMakeCurrent performance tests.
 *//*--------------------------------------------------------------------*/

#include "teglMakeCurrentPerfTests.hpp"

#include "egluNativeWindow.hpp"
#include "egluNativePixmap.hpp"
#include "egluUtil.hpp"

#include "eglwLibrary.hpp"
#include "eglwEnums.hpp"

#include "tcuTestLog.hpp"

#include "deRandom.hpp"
#include "deStringUtil.hpp"

#include "deClock.h"
#include "deString.h"

#include <algorithm>
#include <cmath>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

using std::ostringstream;
using std::string;
using std::vector;

using tcu::TestLog;

using namespace eglw;

namespace deqp
{
namespace egl
{

class MakeCurrentPerfCase : public TestCase
{
public:
    enum SurfaceType
    {
        SURFACETYPE_PBUFFER = (1 << 0),
        SURFACETYPE_WINDOW  = (1 << 1),
        SURFACETYPE_PIXMAP  = (1 << 2)
    };

    struct Spec
    {
        SurfaceType surfaceTypes;
        int contextCount;
        int surfaceCount;

        bool release;

        int iterationCount;
        int sampleCount;

        string toName(void) const;
        string toDescription(void) const;
    };
    MakeCurrentPerfCase(EglTestContext &eglTestCtx, const Spec &spec, const char *name, const char *description);
    ~MakeCurrentPerfCase(void);

    void init(void);
    void deinit(void);
    IterateResult iterate(void);

private:
    Spec m_spec;
    de::Random m_rnd;

    EGLDisplay m_display;
    EGLConfig m_config;
    vector<EGLContext> m_contexts;
    vector<EGLSurface> m_surfaces;

    vector<eglu::NativeWindow *> m_windows;
    vector<eglu::NativePixmap *> m_pixmaps;

    vector<uint64_t> m_samples;

    void chooseConfig(void);
    void createSurfaces(void);
    void createContexts(void);

    void destroySurfaces(void);
    void destroyContexts(void);

    void createPBuffer(void);
    void createWindow(void);
    void createPixmap(void);

    void logTestInfo(void);
    void logResults(void);
    // Disabled
    MakeCurrentPerfCase(const MakeCurrentPerfCase &);
    MakeCurrentPerfCase &operator=(const MakeCurrentPerfCase &);
};

string MakeCurrentPerfCase::Spec::toName(void) const
{
    ostringstream name;

    name << "context";

    if (contextCount > 1)
        name << "s_" << contextCount;

    if ((surfaceTypes & SURFACETYPE_WINDOW) != 0)
        name << "_window" << (surfaceCount > 1 ? "s" : "");

    if ((surfaceTypes & SURFACETYPE_PIXMAP) != 0)
        name << "_pixmap" << (surfaceCount > 1 ? "s" : "");

    if ((surfaceTypes & SURFACETYPE_PBUFFER) != 0)
        name << "_pbuffer" << (surfaceCount > 1 ? "s" : "");

    if (surfaceCount > 1)
        name << "_" << surfaceCount;

    if (release)
        name << "_release";

    return name.str();
}

string MakeCurrentPerfCase::Spec::toDescription(void) const
{
    // \todo [mika] Generate descrpition
    return toName();
}

MakeCurrentPerfCase::MakeCurrentPerfCase(EglTestContext &eglTestCtx, const Spec &spec, const char *name,
                                         const char *description)
    : TestCase(eglTestCtx, tcu::NODETYPE_PERFORMANCE, name, description)
    , m_spec(spec)
    , m_rnd(deStringHash(name))
    , m_display(EGL_NO_DISPLAY)
    , m_config(DE_NULL)
{
}

MakeCurrentPerfCase::~MakeCurrentPerfCase(void)
{
    deinit();
}

void MakeCurrentPerfCase::init(void)
{
    m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());

    chooseConfig();
    createContexts();
    createSurfaces();
}

void MakeCurrentPerfCase::deinit(void)
{
    destroyContexts();
    destroySurfaces();

    if (m_display != EGL_NO_DISPLAY)
    {
        m_eglTestCtx.getLibrary().terminate(m_display);
        m_display = EGL_NO_DISPLAY;
    }
}

void MakeCurrentPerfCase::chooseConfig(void)
{
    const EGLint surfaceBits = ((m_spec.surfaceTypes & SURFACETYPE_WINDOW) != 0 ? EGL_WINDOW_BIT : 0) |
                               ((m_spec.surfaceTypes & SURFACETYPE_PIXMAP) != 0 ? EGL_PIXMAP_BIT : 0) |
                               ((m_spec.surfaceTypes & SURFACETYPE_PBUFFER) != 0 ? EGL_PBUFFER_BIT : 0);

    const EGLint attribList[] = {EGL_SURFACE_TYPE, surfaceBits, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};

    const Library &egl = m_eglTestCtx.getLibrary();
    EGLint configCount = 0;

    EGLU_CHECK_CALL(egl, chooseConfig(m_display, attribList, &m_config, 1, &configCount));

    if (configCount <= 0)
        throw tcu::NotSupportedError("No compatible configs found");
}

void MakeCurrentPerfCase::createSurfaces(void)
{
    vector<SurfaceType> types;

    if ((m_spec.surfaceTypes & SURFACETYPE_WINDOW) != 0)
        types.push_back(SURFACETYPE_WINDOW);

    if ((m_spec.surfaceTypes & SURFACETYPE_PIXMAP) != 0)
        types.push_back(SURFACETYPE_PIXMAP);

    if ((m_spec.surfaceTypes & SURFACETYPE_PBUFFER) != 0)
        types.push_back(SURFACETYPE_PBUFFER);

    DE_ASSERT((int)types.size() <= m_spec.surfaceCount);

    // Create surfaces
    for (int surfaceNdx = 0; surfaceNdx < m_spec.surfaceCount; surfaceNdx++)
    {
        SurfaceType type = types[surfaceNdx % types.size()];

        switch (type)
        {
        case SURFACETYPE_PBUFFER:
            createPBuffer();
            break;

        case SURFACETYPE_WINDOW:
            createWindow();
            break;

        case SURFACETYPE_PIXMAP:
            createPixmap();
            break;

        default:
            DE_ASSERT(false);
        }
    }
}

void MakeCurrentPerfCase::createPBuffer(void)
{
    const Library &egl  = m_eglTestCtx.getLibrary();
    const EGLint width  = 256;
    const EGLint height = 256;

    const EGLint attribList[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};

    EGLSurface surface = egl.createPbufferSurface(m_display, m_config, attribList);

    EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface()");

    m_surfaces.push_back(surface);
}

void MakeCurrentPerfCase::createWindow(void)
{
    const Library &egl  = m_eglTestCtx.getLibrary();
    const EGLint width  = 256;
    const EGLint height = 256;

    const eglu::NativeWindowFactory &windowFactory =
        eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());

    eglu::NativeWindow *window = DE_NULL;
    EGLSurface surface         = EGL_NO_SURFACE;

    try
    {
        window = windowFactory.createWindow(
            &m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL,
            eglu::WindowParams(width, height,
                               eglu::parseWindowVisibility(m_eglTestCtx.getTestContext().getCommandLine())));
        surface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, m_display, m_config, DE_NULL);
    }
    catch (...)
    {
        if (surface != EGL_NO_SURFACE)
            egl.destroySurface(m_display, surface);

        delete window;
        throw;
    }

    m_windows.push_back(window);
    m_surfaces.push_back(surface);
}

void MakeCurrentPerfCase::createPixmap(void)
{
    const Library &egl  = m_eglTestCtx.getLibrary();
    const EGLint width  = 256;
    const EGLint height = 256;

    const eglu::NativePixmapFactory &pixmapFactory =
        eglu::selectNativePixmapFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());

    eglu::NativePixmap *pixmap = DE_NULL;
    EGLSurface surface         = EGL_NO_SURFACE;

    try
    {
        pixmap =
            pixmapFactory.createPixmap(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, width, height);
        surface = eglu::createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, m_display, m_config, DE_NULL);
    }
    catch (...)
    {
        if (surface != EGL_NO_SURFACE)
            egl.destroySurface(m_display, surface);

        delete pixmap;
        throw;
    }

    m_pixmaps.push_back(pixmap);
    m_surfaces.push_back(surface);
}

void MakeCurrentPerfCase::destroySurfaces(void)
{
    const Library &egl = m_eglTestCtx.getLibrary();

    if (m_surfaces.size() > 0)
    {
        EGLDisplay display = m_display;

        // Destroy surfaces
        for (vector<EGLSurface>::iterator iter = m_surfaces.begin(); iter != m_surfaces.end(); ++iter)
        {
            if (*iter != EGL_NO_SURFACE)
                EGLU_CHECK_CALL(egl, destroySurface(display, *iter));
            *iter = EGL_NO_SURFACE;
        }

        m_surfaces.clear();

        // Destroy pixmaps
        for (vector<eglu::NativePixmap *>::iterator iter = m_pixmaps.begin(); iter != m_pixmaps.end(); ++iter)
        {
            delete *iter;
            *iter = NULL;
        }

        m_pixmaps.clear();

        // Destroy windows
        for (vector<eglu::NativeWindow *>::iterator iter = m_windows.begin(); iter != m_windows.end(); ++iter)
        {
            delete *iter;
            *iter = NULL;
        }

        m_windows.clear();

        // Clear all surface handles
        m_surfaces.clear();
    }
}

void MakeCurrentPerfCase::createContexts(void)
{
    const Library &egl = m_eglTestCtx.getLibrary();

    for (int contextNdx = 0; contextNdx < m_spec.contextCount; contextNdx++)
    {
        const EGLint attribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};

        EGLU_CHECK_CALL(egl, bindAPI(EGL_OPENGL_ES_API));
        EGLContext context = egl.createContext(m_display, m_config, EGL_NO_CONTEXT, attribList);
        EGLU_CHECK_MSG(egl, "eglCreateContext()");

        m_contexts.push_back(context);
    }
}

void MakeCurrentPerfCase::destroyContexts(void)
{
    const Library &egl = m_eglTestCtx.getLibrary();
    if (m_contexts.size() > 0)
    {
        EGLDisplay display = m_display;

        for (vector<EGLContext>::iterator iter = m_contexts.begin(); iter != m_contexts.end(); ++iter)
        {
            if (*iter != EGL_NO_CONTEXT)
                EGLU_CHECK_CALL(egl, destroyContext(display, *iter));
            *iter = EGL_NO_CONTEXT;
        }

        m_contexts.clear();
    }
}

void MakeCurrentPerfCase::logTestInfo(void)
{
    TestLog &log = m_testCtx.getLog();

    {
        tcu::ScopedLogSection section(log, "Test Info", "Test case information.");

        log << TestLog::Message << "Context count: " << m_contexts.size() << TestLog::EndMessage;
        log << TestLog::Message << "Surfaces count: " << m_surfaces.size() << TestLog::EndMessage;
        log << TestLog::Message << "Sample count: " << m_spec.sampleCount << TestLog::EndMessage;
        log << TestLog::Message << "Iteration count: " << m_spec.iterationCount << TestLog::EndMessage;
        log << TestLog::Message << "Window count: " << m_windows.size() << TestLog::EndMessage;
        log << TestLog::Message << "Pixmap count: " << m_pixmaps.size() << TestLog::EndMessage;
        log << TestLog::Message << "PBuffer count: " << (m_surfaces.size() - m_windows.size() - m_pixmaps.size())
            << TestLog::EndMessage;

        if (m_spec.release)
            log << TestLog::Message
                << "Context is released after each use. Both binding and releasing context are included in result time."
                << TestLog::EndMessage;
    }
}

void MakeCurrentPerfCase::logResults(void)
{
    TestLog &log = m_testCtx.getLog();

    log << TestLog::SampleList("Result", "Result") << TestLog::SampleInfo
        << TestLog::ValueInfo("Time", "Time", "us", QP_SAMPLE_VALUE_TAG_RESPONSE) << TestLog::EndSampleInfo;

    for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
        log << TestLog::Sample << int64_t(m_samples[sampleNdx]) << TestLog::EndSample;

    log << TestLog::EndSampleList;

    // Log stats
    {
        uint64_t totalTimeUs         = 0;
        uint64_t totalIterationCount = 0;

        float iterationTimeMeanUs     = 0.0f;
        float iterationTimeMedianUs   = 0.0f;
        float iterationTimeVarianceUs = 0.0f;
        float iterationTimeSkewnessUs = 0.0f;
        float iterationTimeMinUs      = std::numeric_limits<float>::max();
        float iterationTimeMaxUs      = 0.0f;

        std::sort(m_samples.begin(), m_samples.end());

        // Calculate totals
        for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
        {
            totalTimeUs += m_samples[sampleNdx];
            totalIterationCount += m_spec.iterationCount;
        }

        // Calculate mean and median
        iterationTimeMeanUs   = ((float)(((double)totalTimeUs) / (double)totalIterationCount));
        iterationTimeMedianUs = ((float)(((double)m_samples[m_samples.size() / 2]) / (double)m_spec.iterationCount));

        // Calculate variance
        for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
        {
            float iterationTimeUs = (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount);
            iterationTimeVarianceUs += std::pow(iterationTimeUs - iterationTimeMedianUs, 2.0f);
        }

        // Calculate min and max
        for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
        {
            float iterationTimeUs = (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount);
            iterationTimeMinUs    = std::min<float>(iterationTimeMinUs, iterationTimeUs);
            iterationTimeMaxUs    = std::max<float>(iterationTimeMaxUs, iterationTimeUs);
        }

        iterationTimeVarianceUs /= (float)m_samples.size();

        // Calculate skewness
        for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
        {
            float iterationTimeUs = (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount);
            iterationTimeSkewnessUs =
                std::pow((iterationTimeUs - iterationTimeMedianUs) / iterationTimeVarianceUs, 2.0f);
        }

        iterationTimeSkewnessUs /= (float)m_samples.size();

        {
            tcu::ScopedLogSection section(log, "Result", "Statistics from results.");

            log << TestLog::Message << "Total time: " << totalTimeUs << "us" << TestLog::EndMessage;
            log << TestLog::Message << "Mean: " << iterationTimeMeanUs << "us" << TestLog::EndMessage;
            log << TestLog::Message << "Median: " << iterationTimeMedianUs << "us" << TestLog::EndMessage;
            log << TestLog::Message << "Variance: " << iterationTimeVarianceUs << "us" << TestLog::EndMessage;
            log << TestLog::Message << "Skewness: " << iterationTimeSkewnessUs << "us" << TestLog::EndMessage;
            log << TestLog::Message << "Min: " << iterationTimeMinUs << "us" << TestLog::EndMessage;
            log << TestLog::Message << "Max: " << iterationTimeMaxUs << "us" << TestLog::EndMessage;
        }

        m_testCtx.setTestResult(
            QP_TEST_RESULT_PASS,
            de::floatToString((float)(((double)totalTimeUs) / (double)totalIterationCount), 2).c_str());
    }
}

TestCase::IterateResult MakeCurrentPerfCase::iterate(void)
{
    const Library &egl = m_eglTestCtx.getLibrary();
    if (m_samples.size() == 0)
        logTestInfo();

    {
        EGLDisplay display   = m_display;
        uint64_t beginTimeUs = deGetMicroseconds();

        for (int iteration = 0; iteration < m_spec.iterationCount; iteration++)
        {
            EGLContext context = m_contexts[m_rnd.getUint32() % m_contexts.size()];
            EGLSurface surface = m_surfaces[m_rnd.getUint32() % m_surfaces.size()];

            egl.makeCurrent(display, surface, surface, context);

            if (m_spec.release)
                egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        }

        m_samples.push_back(deGetMicroseconds() - beginTimeUs);

        egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        EGLU_CHECK_MSG(egl, "eglMakeCurrent()");
    }

    if ((int)m_samples.size() == m_spec.sampleCount)
    {
        logResults();
        return STOP;
    }
    else
        return CONTINUE;
}

MakeCurrentPerfTests::MakeCurrentPerfTests(EglTestContext &eglTestCtx)
    : TestCaseGroup(eglTestCtx, "make_current", "eglMakeCurrent performance tests")
{
}

void MakeCurrentPerfTests::init(void)
{
    const int iterationCount = 100;
    const int sampleCount    = 100;

    // Add simple test group
    {
        TestCaseGroup *simple = new TestCaseGroup(
            m_eglTestCtx, "simple", "Simple eglMakeCurrent performance tests using single context and surface");

        const MakeCurrentPerfCase::SurfaceType types[] = {MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
                                                          MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
                                                          MakeCurrentPerfCase::SURFACETYPE_WINDOW};

        for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
        {
            for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
            {
                MakeCurrentPerfCase::Spec spec;

                spec.surfaceTypes   = types[typeNdx];
                spec.contextCount   = 1;
                spec.surfaceCount   = 1;
                spec.release        = (releaseNdx == 1);
                spec.iterationCount = iterationCount;
                spec.sampleCount    = sampleCount;

                simple->addChild(
                    new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str()));
            }
        }

        addChild(simple);
    }

    // Add multi context test group
    {
        TestCaseGroup *multiContext =
            new TestCaseGroup(m_eglTestCtx, "multi_context",
                              "eglMakeCurrent performance tests using multiple contexts and single surface");

        const MakeCurrentPerfCase::SurfaceType types[] = {MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
                                                          MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
                                                          MakeCurrentPerfCase::SURFACETYPE_WINDOW};

        const int contextCounts[] = {10, 100};

        for (int contextCountNdx = 0; contextCountNdx < DE_LENGTH_OF_ARRAY(contextCounts); contextCountNdx++)
        {
            for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
            {
                for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
                {
                    MakeCurrentPerfCase::Spec spec;

                    spec.surfaceTypes   = types[typeNdx];
                    spec.contextCount   = contextCounts[contextCountNdx];
                    spec.surfaceCount   = 1;
                    spec.release        = (releaseNdx == 1);
                    spec.iterationCount = iterationCount;
                    spec.sampleCount    = sampleCount;

                    multiContext->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(),
                                                                   spec.toDescription().c_str()));
                }
            }
        }

        addChild(multiContext);
    }

    // Add multi surface test group
    {
        TestCaseGroup *multiSurface =
            new TestCaseGroup(m_eglTestCtx, "multi_surface",
                              "eglMakeCurrent performance tests using single context and multiple surfaces");

        const MakeCurrentPerfCase::SurfaceType types[] = {
            MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
            MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
            MakeCurrentPerfCase::SURFACETYPE_WINDOW,

            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |
                                               MakeCurrentPerfCase::SURFACETYPE_PIXMAP),
            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |
                                               MakeCurrentPerfCase::SURFACETYPE_WINDOW),
            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PIXMAP |
                                               MakeCurrentPerfCase::SURFACETYPE_WINDOW),

            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |
                                               MakeCurrentPerfCase::SURFACETYPE_PIXMAP |
                                               MakeCurrentPerfCase::SURFACETYPE_WINDOW)};

        const int surfaceCounts[] = {10, 100};

        for (int surfaceCountNdx = 0; surfaceCountNdx < DE_LENGTH_OF_ARRAY(surfaceCounts); surfaceCountNdx++)
        {
            for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
            {
                for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
                {
                    MakeCurrentPerfCase::Spec spec;

                    spec.surfaceTypes   = types[typeNdx];
                    spec.surfaceCount   = surfaceCounts[surfaceCountNdx];
                    spec.contextCount   = 1;
                    spec.release        = (releaseNdx == 1);
                    spec.iterationCount = iterationCount;
                    spec.sampleCount    = sampleCount;

                    multiSurface->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(),
                                                                   spec.toDescription().c_str()));
                }
            }
        }

        addChild(multiSurface);
    }

    // Add Complex? test group
    {
        TestCaseGroup *multi = new TestCaseGroup(
            m_eglTestCtx, "complex", "eglMakeCurrent performance tests using multiple contexts and multiple surfaces");

        const MakeCurrentPerfCase::SurfaceType types[] = {
            MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
            MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
            MakeCurrentPerfCase::SURFACETYPE_WINDOW,

            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |
                                               MakeCurrentPerfCase::SURFACETYPE_PIXMAP),
            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |
                                               MakeCurrentPerfCase::SURFACETYPE_WINDOW),
            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PIXMAP |
                                               MakeCurrentPerfCase::SURFACETYPE_WINDOW),

            (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |
                                               MakeCurrentPerfCase::SURFACETYPE_PIXMAP |
                                               MakeCurrentPerfCase::SURFACETYPE_WINDOW)};

        const int surfaceCounts[] = {10, 100};

        const int contextCounts[] = {10, 100};

        for (int surfaceCountNdx = 0; surfaceCountNdx < DE_LENGTH_OF_ARRAY(surfaceCounts); surfaceCountNdx++)
        {
            for (int contextCountNdx = 0; contextCountNdx < DE_LENGTH_OF_ARRAY(contextCounts); contextCountNdx++)
            {
                for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
                {
                    for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
                    {
                        MakeCurrentPerfCase::Spec spec;

                        spec.surfaceTypes   = types[typeNdx];
                        spec.contextCount   = contextCounts[contextCountNdx];
                        spec.surfaceCount   = surfaceCounts[surfaceCountNdx];
                        spec.release        = (releaseNdx == 1);
                        spec.iterationCount = iterationCount;
                        spec.sampleCount    = sampleCount;

                        multi->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(),
                                                                spec.toDescription().c_str()));
                    }
                }
            }
        }

        addChild(multi);
    }
}

} // namespace egl
} // namespace deqp
