825 lines
26 KiB
C++
825 lines
26 KiB
C++
/*-------------------------------------------------------------------------
|
|
* 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 std::string;
|
|
using std::vector;
|
|
using glw::GLubyte;
|
|
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 (DE_NULL)
|
|
, 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 (DE_NULL)
|
|
{
|
|
}
|
|
|
|
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 = DE_NULL;
|
|
|
|
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 = DE_NULL;
|
|
}
|
|
|
|
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, DE_NULL,
|
|
eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
|
|
m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
|
|
}
|
|
|
|
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, DE_NULL, 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, DE_NULL, 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;
|
|
}
|
|
|
|
} // anonymous
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
} // egl
|
|
} // deqp
|