Add test for EGL_KHR_swap_buffers_with_damage
The tests consist of three groups. One group with no dependency on other APIs,
one group using EGL_BUFFER_PRESERVED and one using EGL_BUFFER_AGE_EXT.
Change-Id: If7e5da80beeef7dffc6286df9632fc78577408cb
diff --git a/Android.mk b/Android.mk
index 5282fc4..9b9b5d2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -284,6 +284,7 @@
modules/egl/teglSimpleConfigCase.cpp \
modules/egl/teglSurfacelessContextTests.cpp \
modules/egl/teglSwapBuffersTests.cpp \
+ modules/egl/teglSwapBuffersWithDamageTests.cpp \
modules/egl/teglSyncTests.cpp \
modules/egl/teglTestCase.cpp \
modules/egl/teglTestPackage.cpp \
diff --git a/android/cts/master/src/egl-new-tests.txt b/android/cts/master/src/egl-new-tests.txt
index 6e1be0b..e3c1538 100644
--- a/android/cts/master/src/egl-new-tests.txt
+++ b/android/cts/master/src/egl-new-tests.txt
@@ -2,3 +2,4 @@
dEQP-EGL.functional.buffer_age.*
dEQP-EGL.functional.partial_update.*
dEQP-EGL.functional.negative_partial_update.*
+dEQP-EGL.functional.swap_buffers_with_damage.*
diff --git a/modules/egl/CMakeLists.txt b/modules/egl/CMakeLists.txt
index 6777eb7..f038943 100644
--- a/modules/egl/CMakeLists.txt
+++ b/modules/egl/CMakeLists.txt
@@ -89,6 +89,8 @@
teglNativeCoordMappingTests.cpp
teglResizeTests.hpp
teglResizeTests.cpp
+ teglSwapBuffersWithDamageTests.hpp
+ teglSwapBuffersWithDamageTests.cpp
)
set(DEQP_EGL_LIBS
diff --git a/modules/egl/teglSwapBuffersWithDamageTests.cpp b/modules/egl/teglSwapBuffersWithDamageTests.cpp
new file mode 100644
index 0000000..5a6181e
--- /dev/null
+++ b/modules/egl/teglSwapBuffersWithDamageTests.cpp
@@ -0,0 +1,719 @@
+/*-------------------------------------------------------------------------
+ * 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
+};
+
+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,
+ 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;
+ EGLDisplay m_eglDisplay;
+ EGLSurface m_eglSurface;
+ glw::Functions m_gl;
+ GLES2Renderer* m_gles2Renderer;
+};
+
+SwapBuffersWithDamageTest::SwapBuffersWithDamageTest (EglTestContext& eglTestCtx, const vector<DrawType>& frameDrawType, int iterationTimes, 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_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]);
+
+ EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
+ }
+ }
+ return STOP;
+}
+
+class SwapBuffersWithDamageAndPreserveBufferTest : public SwapBuffersWithDamageTest
+{
+public:
+ SwapBuffersWithDamageAndPreserveBufferTest (EglTestContext& eglTestCtx,
+ const vector<DrawType>& frameDrawType,
+ int itertionTimes,
+ 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,
+ const char* name,
+ const char* description)
+ : SwapBuffersWithDamageTest (eglTestCtx, frameDrawType, iterationTimes, 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);
+ EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
+ }
+ }
+
+ return STOP;
+}
+
+class SwapBuffersWithDamageAndBufferAgeTest : public SwapBuffersWithDamageTest
+{
+public:
+ SwapBuffersWithDamageAndBufferAgeTest (EglTestContext& eglTestCtx,
+ const vector<DrawType>& frameDrawType,
+ int iterationTimes,
+ 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,
+ const char* name,
+ const char* description)
+ : SwapBuffersWithDamageTest (eglTestCtx, frameDrawType, iterationTimes, 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++)
+ {
+ 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]);
+ }
+
+ EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
+ }
+ }
+ 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();
+}
+
+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
+ };
+
+ 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 drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
+ {
+ string name = generateTestName(frameDrawTypes[drawTypeNdx]);
+ addChild(new SwapBuffersWithDamageTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, name.c_str(), ""));
+ }
+
+ for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
+ {
+ string name = "preserve_buffer_" + generateTestName(frameDrawTypes[drawTypeNdx]);
+ addChild(new SwapBuffersWithDamageAndPreserveBufferTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, name.c_str(), ""));
+ }
+
+ for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
+ {
+ string name = "buffer_age_" + generateTestName(frameDrawTypes[drawTypeNdx]);
+ addChild(new SwapBuffersWithDamageAndBufferAgeTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, name.c_str(), ""));
+ }
+}
+
+} // egl
+} // deqp
diff --git a/modules/egl/teglSwapBuffersWithDamageTests.hpp b/modules/egl/teglSwapBuffersWithDamageTests.hpp
new file mode 100644
index 0000000..384a381
--- /dev/null
+++ b/modules/egl/teglSwapBuffersWithDamageTests.hpp
@@ -0,0 +1,48 @@
+#ifndef _TEGLSWAPBUFFERSWITHDAMAGETESTS_HPP
+#define _TEGLSWAPBUFFERSWITHDAMAGETESTS_HPP
+/*-------------------------------------------------------------------------
+ * 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_buffers_with_damage
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "teglTestCase.hpp"
+
+namespace deqp
+{
+namespace egl
+{
+
+class SwapBuffersWithDamageTests : public TestCaseGroup
+{
+public:
+ SwapBuffersWithDamageTests (EglTestContext& eglTestCtx);
+ void init (void);
+
+private:
+ SwapBuffersWithDamageTests (const SwapBuffersWithDamageTests&);
+ SwapBuffersWithDamageTests& operator= (const SwapBuffersWithDamageTests&);
+};
+
+} // egl
+} // deqp
+
+#endif // _TEGLSWAPBUFFERSWITHDAMAGETESTS_HPP
diff --git a/modules/egl/teglTestPackage.cpp b/modules/egl/teglTestPackage.cpp
index 291d51e..7361506 100644
--- a/modules/egl/teglTestPackage.cpp
+++ b/modules/egl/teglTestPackage.cpp
@@ -59,6 +59,7 @@
#include "teglBufferAgeTests.hpp"
#include "teglPartialUpdateTests.hpp"
#include "teglNegativePartialUpdateTests.hpp"
+#include "teglSwapBuffersWithDamageTests.hpp"
namespace deqp
{
@@ -130,6 +131,7 @@
addChild(new BufferAgeTests (m_eglTestCtx));
addChild(new PartialUpdateTests (m_eglTestCtx));
addChild(new NegativePartialUpdateTests (m_eglTestCtx));
+ addChild(new SwapBuffersWithDamageTests (m_eglTestCtx));
}
};