blob: 418af8b07b9a323391d0f702cb14cce0065cf003 [file] [edit]
/*-------------------------------------------------------------------------
* drawElements Quality Program EGL Module
* ---------------------------------------
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Test KHR_swap_buffer_with_damage
*//*--------------------------------------------------------------------*/
#include "teglSwapBuffersWithDamageTests.hpp"
#include "tcuImageCompare.hpp"
#include "tcuSurface.hpp"
#include "tcuTextureUtil.hpp"
#include "egluNativeWindow.hpp"
#include "egluUtil.hpp"
#include "egluConfigFilter.hpp"
#include "eglwLibrary.hpp"
#include "eglwEnums.hpp"
#include "gluDefs.hpp"
#include "gluRenderContext.hpp"
#include "gluShaderProgram.hpp"
#include "glwDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "deRandom.hpp"
#include "deString.h"
#include <string>
#include <vector>
#include <sstream>
using glw::GLubyte;
using std::string;
using std::vector;
using tcu::IVec2;
using namespace eglw;
namespace deqp
{
namespace egl
{
namespace
{
typedef tcu::Vector<GLubyte, 3> Color;
enum DrawType
{
DRAWTYPE_GLES2_CLEAR,
DRAWTYPE_GLES2_RENDER
};
enum ResizeType
{
RESIZETYPE_NONE = 0,
RESIZETYPE_BEFORE_SWAP,
RESIZETYPE_AFTER_SWAP,
RESIZETYPE_LAST
};
struct ColoredRect
{
public:
ColoredRect(const IVec2 &bottomLeft_, const IVec2 &topRight_, const Color &color_);
IVec2 bottomLeft;
IVec2 topRight;
Color color;
};
ColoredRect::ColoredRect(const IVec2 &bottomLeft_, const IVec2 &topRight_, const Color &color_)
: bottomLeft(bottomLeft_)
, topRight(topRight_)
, color(color_)
{
}
struct DrawCommand
{
DrawCommand(DrawType drawType_, const ColoredRect &rect_);
DrawType drawType;
ColoredRect rect;
};
DrawCommand::DrawCommand(DrawType drawType_, const ColoredRect &rect_) : drawType(drawType_), rect(rect_)
{
}
struct Frame
{
Frame(int width_, int height_);
int width;
int height;
vector<DrawCommand> draws;
};
Frame::Frame(int width_, int height_) : width(width_), height(height_)
{
}
typedef vector<Frame> FrameSequence;
//helper function declaration
EGLConfig getEGLConfig(const Library &egl, EGLDisplay eglDisplay, bool preserveBuffer);
void clearColorScreen(const glw::Functions &gl, const tcu::Vec4 &clearColor);
float windowToDeviceCoordinates(int x, int length);
class GLES2Renderer
{
public:
GLES2Renderer(const glw::Functions &gl);
~GLES2Renderer(void);
void render(int width, int height, const Frame &frame) const;
private:
GLES2Renderer(const GLES2Renderer &);
GLES2Renderer &operator=(const GLES2Renderer &);
const glw::Functions &m_gl;
glu::ShaderProgram m_glProgram;
glw::GLuint m_coordLoc;
glw::GLuint m_colorLoc;
};
// generate sources for vertex and fragment buffer
glu::ProgramSources getSources(void)
{
const char *const vertexShaderSource = "attribute mediump vec2 a_pos;\n"
"attribute mediump vec4 a_color;\n"
"varying mediump vec4 v_color;\n"
"void main(void)\n"
"{\n"
"\tv_color = a_color;\n"
"\tgl_Position = vec4(a_pos, 0.0, 1.0);\n"
"}";
const char *const fragmentShaderSource = "varying mediump vec4 v_color;\n"
"void main(void)\n"
"{\n"
"\tgl_FragColor = v_color;\n"
"}";
return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource);
}
GLES2Renderer::GLES2Renderer(const glw::Functions &gl)
: m_gl(gl)
, m_glProgram(gl, getSources())
, m_coordLoc((glw::GLuint)-1)
, m_colorLoc((glw::GLuint)-1)
{
m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color");
m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos");
GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations");
}
GLES2Renderer::~GLES2Renderer(void)
{
}
void GLES2Renderer::render(int width, int height, const Frame &frame) const
{
for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
{
const ColoredRect &coloredRect = frame.draws[drawNdx].rect;
if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_RENDER)
{
const float x1 = windowToDeviceCoordinates(coloredRect.bottomLeft.x(), width);
const float y1 = windowToDeviceCoordinates(coloredRect.bottomLeft.y(), height);
const float x2 = windowToDeviceCoordinates(coloredRect.topRight.x(), width);
const float y2 = windowToDeviceCoordinates(coloredRect.topRight.y(), height);
const glw::GLfloat coords[] = {
x1, y1, x1, y2, x2, y2,
x2, y2, x2, y1, x1, y1,
};
const glw::GLubyte colors[] = {
coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
};
m_gl.useProgram(m_glProgram.getProgram());
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
m_gl.enableVertexAttribArray(m_coordLoc);
m_gl.enableVertexAttribArray(m_colorLoc);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes");
m_gl.vertexAttribPointer(m_coordLoc, 2, GL_FLOAT, GL_FALSE, 0, coords);
m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers");
m_gl.drawArrays(GL_TRIANGLES, 0, DE_LENGTH_OF_ARRAY(coords) / 2);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays(), failed");
m_gl.disableVertexAttribArray(m_coordLoc);
m_gl.disableVertexAttribArray(m_colorLoc);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes");
m_gl.useProgram(0);
GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
}
else if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_CLEAR)
{
m_gl.enable(GL_SCISSOR_TEST);
m_gl.scissor(coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
coloredRect.topRight.x() - coloredRect.bottomLeft.x(),
coloredRect.topRight.y() - coloredRect.bottomLeft.y());
m_gl.clearColor(coloredRect.color.x() / 255.0f, coloredRect.color.y() / 255.0f,
coloredRect.color.z() / 255.0f, 1.0f);
m_gl.clear(GL_COLOR_BUFFER_BIT);
m_gl.disable(GL_SCISSOR_TEST);
}
else
DE_FATAL("Invalid drawtype");
}
}
class SwapBuffersWithDamageTest : public TestCase
{
public:
SwapBuffersWithDamageTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType, int iterationTimes,
ResizeType resizeType, const char *name, const char *description);
~SwapBuffersWithDamageTest(void);
virtual void init(void);
void deinit(void);
virtual IterateResult iterate(void);
protected:
virtual EGLConfig getConfig(const Library &egl, EGLDisplay eglDisplay);
virtual void checkExtension(const Library &egl, EGLDisplay eglDisplay);
void initEGLSurface(EGLConfig config);
void initEGLContext(EGLConfig config);
eglu::NativeWindow *m_window;
EGLConfig m_eglConfig;
EGLContext m_eglContext;
const int m_seed;
const int m_iterationTimes;
const vector<DrawType> m_frameDrawType;
const ResizeType m_resizeType;
EGLDisplay m_eglDisplay;
EGLSurface m_eglSurface;
glw::Functions m_gl;
GLES2Renderer *m_gles2Renderer;
};
SwapBuffersWithDamageTest::SwapBuffersWithDamageTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType,
int iterationTimes, ResizeType resizeType, const char *name,
const char *description)
: TestCase(eglTestCtx, name, description)
, m_window(nullptr)
, m_eglContext(EGL_NO_CONTEXT)
, m_seed(deStringHash(name))
, m_iterationTimes(iterationTimes)
, m_frameDrawType(frameDrawType)
, m_resizeType(resizeType)
, m_eglDisplay(EGL_NO_DISPLAY)
, m_eglSurface(EGL_NO_SURFACE)
, m_gles2Renderer(nullptr)
{
}
SwapBuffersWithDamageTest::~SwapBuffersWithDamageTest(void)
{
deinit();
}
EGLConfig SwapBuffersWithDamageTest::getConfig(const Library &egl, EGLDisplay eglDisplay)
{
return getEGLConfig(egl, eglDisplay, false);
}
void SwapBuffersWithDamageTest::checkExtension(const Library &egl, EGLDisplay eglDisplay)
{
if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
}
void SwapBuffersWithDamageTest::init(void)
{
const Library &egl = m_eglTestCtx.getLibrary();
m_eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
m_eglConfig = getConfig(egl, m_eglDisplay);
checkExtension(egl, m_eglDisplay);
initEGLSurface(m_eglConfig);
initEGLContext(m_eglConfig);
m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
m_gles2Renderer = new GLES2Renderer(m_gl);
}
void SwapBuffersWithDamageTest::deinit(void)
{
const Library &egl = m_eglTestCtx.getLibrary();
delete m_gles2Renderer;
m_gles2Renderer = nullptr;
if (m_eglContext != EGL_NO_CONTEXT)
{
egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
egl.destroyContext(m_eglDisplay, m_eglContext);
m_eglContext = EGL_NO_CONTEXT;
}
if (m_eglSurface != EGL_NO_SURFACE)
{
egl.destroySurface(m_eglDisplay, m_eglSurface);
m_eglSurface = EGL_NO_SURFACE;
}
if (m_eglDisplay != EGL_NO_DISPLAY)
{
egl.terminate(m_eglDisplay);
m_eglDisplay = EGL_NO_DISPLAY;
}
delete m_window;
m_window = nullptr;
}
void SwapBuffersWithDamageTest::initEGLSurface(EGLConfig config)
{
const eglu::NativeWindowFactory &factory =
eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
m_window =
factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, nullptr,
eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, nullptr);
}
void SwapBuffersWithDamageTest::initEGLContext(EGLConfig config)
{
const Library &egl = m_eglTestCtx.getLibrary();
const EGLint attribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
egl.bindAPI(EGL_OPENGL_ES_API);
m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
EGLU_CHECK_MSG(egl, "eglCreateContext");
TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
EGLU_CHECK_MSG(egl, "eglMakeCurrent");
}
FrameSequence generateFrameSequence(const vector<DrawType> &frameDrawType, de::Random &rnd, int numFrames, int width,
int height);
vector<EGLint> getDamageRegion(const Frame &frame);
TestCase::IterateResult SwapBuffersWithDamageTest::iterate(void)
{
de::Random rnd(m_seed);
const Library &egl = m_eglTestCtx.getLibrary();
const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
const float clearRed = rnd.getFloat();
const float clearGreen = rnd.getFloat();
const float clearBlue = rnd.getFloat();
const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
const int numFrames = 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
const FrameSequence frameSequence = generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
{
for (int currentFrameNdx = 0; currentFrameNdx < numFrames; currentFrameNdx++)
{
vector<EGLint> damageRegion = getDamageRegion(frameSequence[currentFrameNdx]);
clearColorScreen(m_gl, clearColor);
for (int ndx = 0; ndx <= currentFrameNdx; ndx++)
m_gles2Renderer->render(width, height, frameSequence[ndx]);
if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
{
if (currentFrameNdx % 2 == 0)
m_window->setSurfaceSize(IVec2(width * 2, height / 2));
else
m_window->setSurfaceSize(IVec2(height / 2, width * 2));
}
EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0],
(EGLint)damageRegion.size() / 4));
if (m_resizeType == RESIZETYPE_AFTER_SWAP)
{
if (currentFrameNdx % 2 == 0)
m_window->setSurfaceSize(IVec2(width * 2, height / 2));
else
m_window->setSurfaceSize(IVec2(height / 2, width * 2));
}
}
}
return STOP;
}
class SwapBuffersWithDamageAndPreserveBufferTest : public SwapBuffersWithDamageTest
{
public:
SwapBuffersWithDamageAndPreserveBufferTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType,
int iterationTimes, ResizeType resizeType, const char *name,
const char *description);
IterateResult iterate(void);
protected:
EGLConfig getConfig(const Library &egl, EGLDisplay eglDisplay);
};
SwapBuffersWithDamageAndPreserveBufferTest::SwapBuffersWithDamageAndPreserveBufferTest(
EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType, int iterationTimes, ResizeType resizeType,
const char *name, const char *description)
: SwapBuffersWithDamageTest(eglTestCtx, frameDrawType, iterationTimes, resizeType, name, description)
{
}
EGLConfig SwapBuffersWithDamageAndPreserveBufferTest::getConfig(const Library &egl, EGLDisplay eglDisplay)
{
return getEGLConfig(egl, eglDisplay, true);
}
TestCase::IterateResult SwapBuffersWithDamageAndPreserveBufferTest::iterate(void)
{
de::Random rnd(m_seed);
const Library &egl = m_eglTestCtx.getLibrary();
const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
const float clearRed = rnd.getFloat();
const float clearGreen = rnd.getFloat();
const float clearBlue = rnd.getFloat();
const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
const int numFrames = 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
const FrameSequence frameSequence = generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
{
clearColorScreen(m_gl, clearColor);
EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, nullptr, 0));
for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
{
const Frame &currentFrame = frameSequence[frameNdx];
vector<EGLint> damageRegion = getDamageRegion(currentFrame);
m_gles2Renderer->render(width, height, currentFrame);
if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
{
if (frameNdx % 2 == 0)
m_window->setSurfaceSize(IVec2(width * 2, height / 2));
else
m_window->setSurfaceSize(IVec2(height / 2, width * 2));
}
EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0],
(EGLint)damageRegion.size() / 4));
if (m_resizeType == RESIZETYPE_AFTER_SWAP)
{
if (frameNdx % 2 == 0)
m_window->setSurfaceSize(IVec2(width * 2, height / 2));
else
m_window->setSurfaceSize(IVec2(height / 2, width * 2));
}
}
}
return STOP;
}
class SwapBuffersWithDamageAndBufferAgeTest : public SwapBuffersWithDamageTest
{
public:
SwapBuffersWithDamageAndBufferAgeTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType,
int iterationTimes, ResizeType resizeType, const char *name,
const char *description);
IterateResult iterate(void);
protected:
void checkExtension(const Library &egl, EGLDisplay eglDisplay);
};
SwapBuffersWithDamageAndBufferAgeTest::SwapBuffersWithDamageAndBufferAgeTest(EglTestContext &eglTestCtx,
const vector<DrawType> &frameDrawType,
int iterationTimes, ResizeType resizeType,
const char *name, const char *description)
: SwapBuffersWithDamageTest(eglTestCtx, frameDrawType, iterationTimes, resizeType, name, description)
{
}
void SwapBuffersWithDamageAndBufferAgeTest::checkExtension(const Library &egl, EGLDisplay eglDisplay)
{
if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
if (!eglu::hasExtension(egl, eglDisplay, "EGL_EXT_buffer_age"))
TCU_THROW(NotSupportedError, "EGL_EXT_buffer_age not supported");
}
TestCase::IterateResult SwapBuffersWithDamageAndBufferAgeTest::iterate(void)
{
de::Random rnd(m_seed);
const Library &egl = m_eglTestCtx.getLibrary();
const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
const float clearRed = rnd.getFloat();
const float clearGreen = rnd.getFloat();
const float clearBlue = rnd.getFloat();
const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
const int numFrames = 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
const FrameSequence frameSequence = generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
{
clearColorScreen(m_gl, clearColor);
EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, nullptr, 0));
for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
{
vector<EGLint> damageRegion;
int bufferAge = -1;
int startFrameNdx = -1;
int endFrameNdx = frameNdx;
EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_EXT, &bufferAge));
if (bufferAge < 0) // invalid buffer age
{
std::ostringstream stream;
stream << "Fail, the age is invalid. Age: " << bufferAge << ", frameNdx: " << frameNdx;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
return STOP;
}
if (bufferAge == 0 || bufferAge > frameNdx)
{
clearColorScreen(m_gl, clearColor);
startFrameNdx = 0;
}
else
startFrameNdx = frameNdx - bufferAge + 1;
for (int ndx = startFrameNdx; ndx <= endFrameNdx; ndx++)
{
const vector<EGLint> partialDamageRegion = getDamageRegion(frameSequence[ndx]);
damageRegion.insert(damageRegion.end(), partialDamageRegion.begin(), partialDamageRegion.end());
m_gles2Renderer->render(width, height, frameSequence[ndx]);
}
if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
{
if (frameNdx % 2 == 0)
m_window->setSurfaceSize(IVec2(width * 2, height / 2));
else
m_window->setSurfaceSize(IVec2(height / 2, width * 2));
}
EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0],
(EGLint)damageRegion.size() / 4));
if (m_resizeType == RESIZETYPE_AFTER_SWAP)
{
if (frameNdx % 2 == 0)
m_window->setSurfaceSize(IVec2(width * 2, height / 2));
else
m_window->setSurfaceSize(IVec2(height / 2, width * 2));
}
}
}
return STOP;
}
// generate a frame sequence with certain frame for visual verification
FrameSequence generateFrameSequence(const vector<DrawType> &frameDrawType, de::Random &rnd, int numFrames, int width,
int height)
{
const int frameDiff = height / numFrames;
const GLubyte r = rnd.getUint8();
const GLubyte g = rnd.getUint8();
const GLubyte b = rnd.getUint8();
const Color color(r, g, b);
FrameSequence frameSequence;
for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
{
Frame frame(width, height);
for (int rectNdx = 0; rectNdx < (int)frameDrawType.size(); rectNdx++)
{
const int rectHeight = frameDiff / (int)frameDrawType.size();
const ColoredRect rect(IVec2(0, frameNdx * frameDiff + rectNdx * rectHeight),
IVec2(width, frameNdx * frameDiff + (rectNdx + 1) * rectHeight), color);
const DrawCommand drawCommand(frameDrawType[rectNdx], rect);
frame.draws.push_back(drawCommand);
}
frameSequence.push_back(frame);
}
return frameSequence;
}
vector<EGLint> getDamageRegion(const Frame &frame)
{
vector<EGLint> damageRegion;
for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
{
const ColoredRect &rect = frame.draws[drawNdx].rect;
damageRegion.push_back(rect.bottomLeft.x());
damageRegion.push_back(rect.bottomLeft.y());
damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x());
damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y());
}
DE_ASSERT(damageRegion.size() % 4 == 0);
return damageRegion;
}
string generateTestName(const vector<DrawType> &frameDrawType)
{
std::ostringstream stream;
for (size_t ndx = 0; ndx < frameDrawType.size(); ndx++)
{
if (frameDrawType[ndx] == DRAWTYPE_GLES2_RENDER)
stream << "render";
else if (frameDrawType[ndx] == DRAWTYPE_GLES2_CLEAR)
stream << "clear";
else
DE_ASSERT(false);
if (ndx < frameDrawType.size() - 1)
stream << "_";
}
return stream.str();
}
string generateResizeGroupName(ResizeType resizeType)
{
switch (resizeType)
{
case RESIZETYPE_NONE:
return "no_resize";
case RESIZETYPE_AFTER_SWAP:
return "resize_after_swap";
case RESIZETYPE_BEFORE_SWAP:
return "resize_before_swap";
default:
DE_FATAL("Unknown resize type");
return "";
}
}
bool isWindow(const eglu::CandidateConfig &c)
{
return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
}
bool isES2Renderable(const eglu::CandidateConfig &c)
{
return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
}
bool hasPreserveSwap(const eglu::CandidateConfig &c)
{
return (c.surfaceType() & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) == EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
}
EGLConfig getEGLConfig(const Library &egl, EGLDisplay eglDisplay, bool preserveBuffer)
{
eglu::FilterList filters;
filters << isWindow << isES2Renderable;
if (preserveBuffer)
filters << hasPreserveSwap;
return eglu::chooseSingleConfig(egl, eglDisplay, filters);
}
void clearColorScreen(const glw::Functions &gl, const tcu::Vec4 &clearColor)
{
gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
gl.clear(GL_COLOR_BUFFER_BIT);
}
float windowToDeviceCoordinates(int x, int length)
{
return (2.0f * float(x) / float(length)) - 1.0f;
}
} // namespace
SwapBuffersWithDamageTests::SwapBuffersWithDamageTests(EglTestContext &eglTestCtx)
: TestCaseGroup(eglTestCtx, "swap_buffers_with_damage", "Swap buffers with damages tests")
{
}
void SwapBuffersWithDamageTests::init(void)
{
const DrawType clearRender[2] = {DRAWTYPE_GLES2_CLEAR, DRAWTYPE_GLES2_RENDER};
const DrawType renderClear[2] = {DRAWTYPE_GLES2_RENDER, DRAWTYPE_GLES2_CLEAR};
const ResizeType resizeTypes[] = {RESIZETYPE_NONE, RESIZETYPE_BEFORE_SWAP, RESIZETYPE_AFTER_SWAP};
vector<vector<DrawType>> frameDrawTypes;
frameDrawTypes.push_back(vector<DrawType>(1, DRAWTYPE_GLES2_CLEAR));
frameDrawTypes.push_back(vector<DrawType>(1, DRAWTYPE_GLES2_RENDER));
frameDrawTypes.push_back(vector<DrawType>(2, DRAWTYPE_GLES2_CLEAR));
frameDrawTypes.push_back(vector<DrawType>(2, DRAWTYPE_GLES2_RENDER));
frameDrawTypes.push_back(vector<DrawType>(DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
frameDrawTypes.push_back(vector<DrawType>(DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
for (size_t resizeTypeNdx = 0; resizeTypeNdx < DE_LENGTH_OF_ARRAY(resizeTypes); resizeTypeNdx++)
{
const ResizeType resizeType = resizeTypes[resizeTypeNdx];
TestCaseGroup *const resizeGroup =
new TestCaseGroup(m_eglTestCtx, generateResizeGroupName(resizeType).c_str(), "");
for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
{
string name = generateTestName(frameDrawTypes[drawTypeNdx]);
resizeGroup->addChild(new SwapBuffersWithDamageTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4,
resizeType, name.c_str(), ""));
}
for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
{
string name = "preserve_buffer_" + generateTestName(frameDrawTypes[drawTypeNdx]);
resizeGroup->addChild(new SwapBuffersWithDamageAndPreserveBufferTest(
m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, resizeType, name.c_str(), ""));
}
for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
{
string name = "buffer_age_" + generateTestName(frameDrawTypes[drawTypeNdx]);
resizeGroup->addChild(new SwapBuffersWithDamageAndBufferAgeTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx],
4, resizeType, name.c_str(), ""));
}
addChild(resizeGroup);
}
}
} // namespace egl
} // namespace deqp