Add GetFrameTimestamps tests
authorBrian Anderson <brianderson@google.com>
Sun, 22 Jan 2017 00:03:19 +0000 (16:03 -0800)
committerPyry Haulos <phaulos@google.com>
Wed, 22 Feb 2017 18:00:00 +0000 (18:00 +0000)
Add tests for EGL_ANDROID_get_frame_timestamps.

Verifies:
1) Frame IDs properly identify frames.
2) The frame timestamp ordering is correct
3) The compositor timing values are reasonable.
4) All timestamps from the most recently swapped frame can
   be retrieved without additional swaps.

Test: --deqp-case=dEQP-EGL*get_frame_timestamps*
Change-Id: I393a3bc79f019951a2fddb83760b6e1d919663b6

Android.mk
android/cts/master/egl-master.txt
modules/egl/CMakeLists.txt
modules/egl/teglGetFrameTimestampsTests.cpp [new file with mode: 0644]
modules/egl/teglGetFrameTimestampsTests.hpp [new file with mode: 0644]
modules/egl/teglTestPackage.cpp

index 7c29c09..5618aa9 100644 (file)
@@ -270,6 +270,7 @@ LOCAL_SRC_FILES := \
        modules/egl/teglCreateContextExtTests.cpp \
        modules/egl/teglCreateContextTests.cpp \
        modules/egl/teglCreateSurfaceTests.cpp \
+       modules/egl/teglGetFrameTimestampsTests.cpp \
        modules/egl/teglGetProcAddressTests.cpp \
        modules/egl/teglGLES1RenderUtil.cpp \
        modules/egl/teglGLES2RenderUtil.cpp \
index 7c6350b..ba0c55a 100644 (file)
@@ -3549,3 +3549,24 @@ dEQP-EGL.functional.thread_cleanup.multi_context_multi_surface
 dEQP-EGL.functional.mutable_render_buffer.querySurface
 dEQP-EGL.functional.mutable_render_buffer.negativeConfigBit
 dEQP-EGL.functional.mutable_render_buffer.basic
+dEQP-EGL.functional.get_frame_timestamps.rgb565_no_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgb565_no_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgb565_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgb565_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgb888_no_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgb888_no_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgb888_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgb888_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba4444_no_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba4444_no_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba4444_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba4444_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba5551_no_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba5551_no_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba5551_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba5551_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba8888_no_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba8888_no_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba8888_depth_no_stencil
+dEQP-EGL.functional.get_frame_timestamps.rgba8888_depth_stencil
+dEQP-EGL.functional.get_frame_timestamps.other
index a191391..519f15c 100644 (file)
@@ -21,6 +21,8 @@ set(DEQP_EGL_SRCS
        teglConfigList.hpp
        teglCreateContextTests.cpp
        teglCreateContextTests.hpp
+       teglGetFrameTimestampsTests.cpp
+       teglGetFrameTimestampsTests.hpp
        teglQueryContextTests.cpp
        teglQueryContextTests.hpp
        teglCreateSurfaceTests.cpp
diff --git a/modules/egl/teglGetFrameTimestampsTests.cpp b/modules/egl/teglGetFrameTimestampsTests.cpp
new file mode 100644 (file)
index 0000000..ba75bba
--- /dev/null
@@ -0,0 +1,757 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program EGL Module
+ * ---------------------------------------
+ *
+ * Copyright 2017 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 the EGL_ANDROID_get_frame_timestamps extension.
+ *//*--------------------------------------------------------------------*/
+
+#include "teglGetFrameTimestampsTests.hpp"
+
+#include "teglSimpleConfigCase.hpp"
+
+#include "egluNativeWindow.hpp"
+#include "egluUtil.hpp"
+#include "egluUnique.hpp"
+#include "eglwLibrary.hpp"
+#include "eglwEnums.hpp"
+
+#include "gluDefs.hpp"
+#include "glwEnums.hpp"
+#include "glwFunctions.hpp"
+
+#include "tcuResultCollector.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuSurface.hpp"
+#include "tcuTexture.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuImageCompare.hpp"
+#include "tcuVector.hpp"
+#include "tcuVectorUtil.hpp"
+
+#include "deClock.h"
+#include "deMath.h"
+#include "deUniquePtr.hpp"
+#include "deThread.hpp"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Tentative EGL header definitions for EGL_ANDROID_get_Frame_timestamps.
+// \todo [2017-01-25 brianderson] Remove once defined in the official headers.
+#define EGL_TIMESTAMPS_ANDROID 0x314D
+#define EGL_COMPOSITE_DEADLINE_ANDROID 0x314E
+#define EGL_COMPOSITE_INTERVAL_ANDROID 0x314F
+#define EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3150
+#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3151
+#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3152
+#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3153
+#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3154
+#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3155
+#define EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID 0x3156
+#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3157
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3158
+#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3159
+#define EGL_READS_DONE_TIME_ANDROID 0x315A
+typedef deInt64 EGLnsecsANDROID;
+typedef deUint64 EGLuint64KHR;
+typedef EGLW_APICALL eglw::EGLBoolean (EGLW_APIENTRY* eglGetNextFrameIdANDROIDFunc) (eglw::EGLDisplay dpy, eglw::EGLSurface surface, EGLuint64KHR *frameId);
+typedef EGLW_APICALL eglw::EGLBoolean (EGLW_APIENTRY* eglGetCompositorTimingANDROIDFunc) (eglw::EGLDisplay dpy, eglw::EGLSurface surface, eglw::EGLint numTimestamps, const eglw::EGLint *names, EGLnsecsANDROID *values);
+typedef EGLW_APICALL eglw::EGLBoolean (EGLW_APIENTRY* eglGetCompositorTimingSupportedANDROIDFunc) (eglw::EGLDisplay dpy, eglw::EGLSurface surface, eglw::EGLint name);
+typedef EGLW_APICALL eglw::EGLBoolean (EGLW_APIENTRY* eglGetFrameTimestampsANDROIDFunc) (eglw::EGLDisplay dpy, eglw::EGLSurface surface, EGLuint64KHR frameId, eglw::EGLint numTimestamps, const eglw::EGLint *timestamps, EGLnsecsANDROID *values);
+typedef EGLW_APICALL eglw::EGLBoolean (EGLW_APIENTRY* eglGetFrameTimestampSupportedANDROIDFunc) (eglw::EGLDisplay dpy, eglw::EGLSurface surface, eglw::EGLint timestamp);
+
+#define CHECK_NAKED_EGL_CALL(EGLW, CALL)       do { CALL; eglu::checkError((EGLW).getError(), #CALL, __FILE__, __LINE__); } while (deGetFalse())
+
+namespace deqp
+{
+namespace egl
+{
+
+using tcu::TestLog;
+using std::string;
+using std::vector;
+using namespace eglw;
+
+namespace
+{
+
+// Careful: This has microsecond precision, which can cause timestamps to
+// appear non monotonic when compared to the nanosecond precision timestamps
+// we get from the eglGetFrameTimestamps extension.
+// Current test expectations only make sure microsecond precision timestamps
+// are less than the nanosecond precision timestamps, so this is okay.
+EGLnsecsANDROID getNanoseconds (void)
+{
+       return deGetMicroseconds() * 1000;
+}
+
+struct FrameTimes
+{
+       FrameTimes (void)
+               : frameId                                               (-1)
+               , swapBufferBeginNs                             (-1)
+               , compositeDeadline                             (-1)
+               , compositeInterval                             (-1)
+               , compositeToPresentLatency             (-1)
+               , requestedPresent                              (-1)
+               , latch                                                 (-1)
+               , firstCompositionStart                 (-1)
+               , lastCompositionStart                  (-1)
+               , dequeueReady                                  (-1)
+               , renderingComplete                             (-1)
+               , firstCompositionGpuFinished   (-1)
+               , displayPresent                                (-1)
+               , displayRetire                                 (-1)
+               , readsDone                                             (-1)
+       {
+       }
+
+       EGLuint64KHR    frameId;
+
+       // Timestamps sampled by the test.
+       EGLnsecsANDROID swapBufferBeginNs;
+
+       // Compositor info.
+       EGLnsecsANDROID compositeDeadline;
+       EGLnsecsANDROID compositeInterval;
+       EGLnsecsANDROID compositeToPresentLatency;
+
+       // CPU Timeline.
+       EGLnsecsANDROID requestedPresent;
+       EGLnsecsANDROID latch;
+       EGLnsecsANDROID firstCompositionStart;
+       EGLnsecsANDROID lastCompositionStart;
+       EGLnsecsANDROID dequeueReady;
+
+       // GPU Timeline.
+       EGLnsecsANDROID renderingComplete;
+       EGLnsecsANDROID firstCompositionGpuFinished;
+       EGLnsecsANDROID displayPresent;
+       EGLnsecsANDROID displayRetire;
+       EGLnsecsANDROID readsDone;
+};
+
+bool timestampExists (EGLnsecsANDROID timestamp)
+{
+       return timestamp > 0;
+}
+
+void verifySingleFrame (const FrameTimes& frameTimes, tcu::ResultCollector& result, bool verifyReadsDone)
+{
+       // Verify CPU timeline is monotonic.
+       result.check(frameTimes.swapBufferBeginNs < frameTimes.latch, "Buffer latched before it was swapped.");
+       result.check(frameTimes.latch < frameTimes.firstCompositionStart, "Buffer composited before it was latched.");
+       result.check(frameTimes.firstCompositionStart <= frameTimes.lastCompositionStart, "First composition start after last composition start.");
+       result.check(frameTimes.lastCompositionStart < frameTimes.dequeueReady, "Buffer composited after it was ready to be dequeued.");
+
+       // Verify GPU timeline is monotonic.
+       if (timestampExists(frameTimes.firstCompositionGpuFinished))
+               result.check(frameTimes.renderingComplete < frameTimes.firstCompositionGpuFinished, "Buffer rendering completed after compositor GPU work finished.");
+
+       if (timestampExists(frameTimes.displayPresent))
+               result.check(frameTimes.renderingComplete < frameTimes.displayPresent, "Buffer displayed before rendering completed.");
+
+       if (timestampExists(frameTimes.firstCompositionGpuFinished) && timestampExists(frameTimes.displayPresent))
+               result.check(frameTimes.firstCompositionGpuFinished < frameTimes.displayPresent, "Buffer displayed before compositor GPU work completed");
+
+       if (timestampExists(frameTimes.displayRetire))
+               result.check(frameTimes.renderingComplete < frameTimes.displayRetire, "Buffer retired before rendering completed.");
+
+       if (timestampExists(frameTimes.firstCompositionGpuFinished) && timestampExists(frameTimes.displayRetire))
+               result.check(frameTimes.firstCompositionGpuFinished < frameTimes.displayRetire, "Buffer retired before compositor GPU work completed.");
+
+       // Drivers may maintain shadow copies of the buffer, so the readsDone time
+       // of the real buffer may be earlier than apparent dependencies. We can only
+       // be sure that the readsDone time must be after the renderingComplete time.
+       if (verifyReadsDone)
+               result.check(frameTimes.renderingComplete < frameTimes.readsDone, "Buffer rendering completed after reads completed.");
+
+       // Verify CPU/GPU dependencies
+       result.check(frameTimes.renderingComplete < frameTimes.latch, "Buffer latched before rendering completed.");
+       if (timestampExists(frameTimes.firstCompositionGpuFinished))
+               result.check(frameTimes.firstCompositionStart < frameTimes.firstCompositionGpuFinished, "Composition CPU work started after GPU work finished.");
+
+       if (timestampExists(frameTimes.displayPresent))
+               result.check(frameTimes.firstCompositionStart < frameTimes.displayPresent, "Buffer displayed before it was composited.");
+
+       if (timestampExists(frameTimes.displayRetire))
+               result.check(frameTimes.lastCompositionStart < frameTimes.displayRetire, "Buffer retired before final composition.");
+
+       // One of Present or retire must exist.
+       result.check(timestampExists(frameTimes.displayPresent) != timestampExists(frameTimes.displayRetire), "Either present or retire must exist.");
+}
+
+void verifyNeighboringFrames (const FrameTimes& frame1, const FrameTimes& frame2, tcu::ResultCollector& result, bool verifyReadsDone)
+{
+       // CPU timeline.
+       result.check(frame1.swapBufferBeginNs < frame2.swapBufferBeginNs, "Swap begin times not monotonic.");
+       result.check(frame1.latch < frame2.latch, "Latch times not monotonic.");
+       result.check(frame1.lastCompositionStart < frame2.latch, "Old buffer composited after new buffer latched.");
+       result.check(frame1.lastCompositionStart < frame2.firstCompositionStart, "Composition times overlap.");
+       result.check(frame1.dequeueReady < frame2.dequeueReady, "Dequeue ready times not monotonic.");
+
+       // GPU timeline.
+       result.check(frame1.renderingComplete < frame2.renderingComplete, "Rendering complete times not monotonic.");
+
+       if (timestampExists(frame1.firstCompositionGpuFinished) && timestampExists(frame2.firstCompositionGpuFinished))
+               result.check(frame1.firstCompositionGpuFinished < frame2.firstCompositionGpuFinished, "Composition GPU work complete times not monotonic.");
+
+       if (timestampExists(frame1.displayPresent) && timestampExists(frame2.displayPresent))
+               result.check(frame1.displayPresent < frame2.displayPresent, "Display present times not monotonic.");
+
+       if (timestampExists(frame1.displayRetire) && timestampExists(frame2.displayRetire))
+               result.check(frame1.displayRetire < frame2.displayRetire, "Display retire times not monotonic.");
+
+       if (verifyReadsDone && timestampExists(frame1.readsDone) && timestampExists(frame2.readsDone))
+               result.check(frame1.readsDone < frame2.readsDone, "Reads done times not monotonic.");
+}
+
+EGLContext createGLES2Context (const Library& egl, EGLDisplay display, EGLConfig config)
+{
+       EGLContext              context = EGL_NO_CONTEXT;
+       const EGLint    attribList[] =
+       {
+               EGL_CONTEXT_CLIENT_VERSION, 2,
+               EGL_NONE
+       };
+
+       EGLU_CHECK_CALL(egl, bindAPI(EGL_OPENGL_ES_API));
+
+       context = egl.createContext(display, config, EGL_NO_CONTEXT, attribList);
+       EGLU_CHECK_MSG(egl, "eglCreateContext() failed");
+       TCU_CHECK(context);
+
+       return context;
+}
+
+class GetFrameTimestampTest : public SimpleConfigCase
+{
+public:
+                                                       GetFrameTimestampTest   (EglTestContext& eglTestCtx, const NamedFilterList& filters);
+                                                       ~GetFrameTimestampTest  (void);
+
+private:
+       void                                    executeForConfig                (EGLDisplay display, EGLConfig config);
+       void                                    initializeExtension             (const Library& egl);
+
+       // Not allowed
+                                                       GetFrameTimestampTest   (const GetFrameTimestampTest&);
+       GetFrameTimestampTest&  operator=                               (const GetFrameTimestampTest&);
+
+       // TODO: Move these to eglw::Library.
+       eglGetNextFrameIdANDROIDFunc                            m_eglGetNextFrameIdANDROID;
+       eglGetCompositorTimingANDROIDFunc                       m_eglGetCompositorTimingANDROID;
+       eglGetCompositorTimingSupportedANDROIDFunc      m_eglGetCompositorTimingSupportedANDROID;
+       eglGetFrameTimestampsANDROIDFunc                        m_eglGetFrameTimestampsANDROID;
+       eglGetFrameTimestampSupportedANDROIDFunc        m_eglGetFrameTimestampSupportedANDROID;
+
+       tcu::ResultCollector                                            m_result;
+};
+
+GetFrameTimestampTest::GetFrameTimestampTest (EglTestContext& eglTestCtx, const NamedFilterList& filters)
+       : SimpleConfigCase                                                      (eglTestCtx, filters.getName(), filters.getDescription(), filters)
+       , m_eglGetNextFrameIdANDROID                            (DE_NULL)
+       , m_eglGetCompositorTimingANDROID                       (DE_NULL)
+       , m_eglGetCompositorTimingSupportedANDROID      (DE_NULL)
+       , m_eglGetFrameTimestampsANDROID                        (DE_NULL)
+       , m_eglGetFrameTimestampSupportedANDROID        (DE_NULL)
+       , m_result                                                                      (m_testCtx.getLog())
+{
+}
+
+GetFrameTimestampTest::~GetFrameTimestampTest (void)
+{
+}
+
+void GetFrameTimestampTest::initializeExtension (const Library& egl)
+{
+       m_eglGetNextFrameIdANDROID = reinterpret_cast<eglGetNextFrameIdANDROIDFunc>(egl.getProcAddress("eglGetNextFrameIdANDROID"));
+       EGLU_CHECK_MSG(egl, "getProcAddress of eglGetNextFrameIdANDROID failed.");
+       m_eglGetCompositorTimingANDROID = reinterpret_cast<eglGetCompositorTimingANDROIDFunc>(egl.getProcAddress("eglGetCompositorTimingANDROID"));
+       EGLU_CHECK_MSG(egl, "getProcAddress of eglGetCompositorTimingANDROID failed.");
+       m_eglGetCompositorTimingSupportedANDROID = reinterpret_cast<eglGetCompositorTimingSupportedANDROIDFunc>(egl.getProcAddress("eglGetCompositorTimingSupportedANDROID"));
+       EGLU_CHECK_MSG(egl, "getProcAddress of eglGetCompositorTimingSupportedANDROID failed.");
+       m_eglGetFrameTimestampsANDROID = reinterpret_cast<eglGetFrameTimestampsANDROIDFunc>(egl.getProcAddress("eglGetFrameTimestampsANDROID"));
+       EGLU_CHECK_MSG(egl, "getProcAddress of eglGetFrameTimestampsANDROID failed.");
+       m_eglGetFrameTimestampSupportedANDROID = reinterpret_cast<eglGetFrameTimestampSupportedANDROIDFunc>(egl.getProcAddress("eglGetFrameTimestampSupportedANDROID"));
+       EGLU_CHECK_MSG(egl, "getProcAddress of eglGetFrameTimestampSupportedANDROID failed.");
+}
+
+
+string getConfigIdString (const Library& egl, EGLDisplay display, EGLConfig config)
+{
+       std::ostringstream      stream;
+       EGLint                          id;
+
+       EGLU_CHECK_CALL(egl, getConfigAttrib(display, config , EGL_CONFIG_ID, &id));
+
+       stream << id;
+
+       return stream.str();
+}
+
+deUint32 createGLES2Program (const glw::Functions& gl, TestLog& log)
+{
+       const char* const vertexShaderSource =
+       "attribute highp vec2 a_pos;\n"
+       "void main (void)\n"
+       "{\n"
+       "\tgl_Position = vec4(a_pos, 0.0, 1.0);\n"
+       "}";
+
+       const char* const fragmentShaderSource =
+       "void main (void)\n"
+       "{\n"
+       "\tgl_FragColor = vec4(0.9, 0.1, 0.4, 1.0);\n"
+       "}";
+
+       deUint32        program                 = 0;
+       deUint32        vertexShader    = 0;
+       deUint32        fragmentShader  = 0;
+
+       deInt32         vertexCompileStatus;
+       string          vertexInfoLog;
+       deInt32         fragmentCompileStatus;
+       string          fragmentInfoLog;
+       deInt32         linkStatus;
+       string          programInfoLog;
+
+       try
+       {
+               program                 = gl.createProgram();
+               vertexShader    = gl.createShader(GL_VERTEX_SHADER);
+               fragmentShader  = gl.createShader(GL_FRAGMENT_SHADER);
+
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create shaders and program");
+
+               gl.shaderSource(vertexShader, 1, &vertexShaderSource, DE_NULL);
+               gl.compileShader(vertexShader);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup vertex shader");
+
+               gl.shaderSource(fragmentShader, 1, &fragmentShaderSource, DE_NULL);
+               gl.compileShader(fragmentShader);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup fragment shader");
+
+               {
+                       deInt32         infoLogLength = 0;
+
+                       gl.getShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexCompileStatus);
+                       gl.getShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &infoLogLength);
+
+                       vertexInfoLog.resize(infoLogLength, '\0');
+
+                       gl.getShaderInfoLog(vertexShader, (glw::GLsizei)vertexInfoLog.length(), &infoLogLength, &(vertexInfoLog[0]));
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get vertex shader compile info");
+
+                       vertexInfoLog.resize(infoLogLength);
+               }
+
+               {
+                       deInt32         infoLogLength = 0;
+
+                       gl.getShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentCompileStatus);
+                       gl.getShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &infoLogLength);
+
+                       fragmentInfoLog.resize(infoLogLength, '\0');
+
+                       gl.getShaderInfoLog(fragmentShader, (glw::GLsizei)fragmentInfoLog.length(), &infoLogLength, &(fragmentInfoLog[0]));
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get fragment shader compile info");
+
+                       fragmentInfoLog.resize(infoLogLength);
+               }
+
+               gl.attachShader(program, vertexShader);
+               gl.attachShader(program, fragmentShader);
+               gl.linkProgram(program);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup program");
+
+               {
+                       deInt32         infoLogLength = 0;
+
+                       gl.getProgramiv(program, GL_LINK_STATUS, &linkStatus);
+                       gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
+
+                       programInfoLog.resize(infoLogLength, '\0');
+
+                       gl.getProgramInfoLog(program, (glw::GLsizei)programInfoLog.length(), &infoLogLength, &(programInfoLog[0]));
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get program link info");
+
+                       programInfoLog.resize(infoLogLength);
+               }
+
+               if (linkStatus == 0 || vertexCompileStatus == 0 || fragmentCompileStatus == 0)
+               {
+
+                       log.startShaderProgram(linkStatus != 0, programInfoLog.c_str());
+
+                       log << TestLog::Shader(QP_SHADER_TYPE_VERTEX, vertexShaderSource, vertexCompileStatus != 0, vertexInfoLog);
+                       log << TestLog::Shader(QP_SHADER_TYPE_FRAGMENT, fragmentShaderSource, fragmentCompileStatus != 0, fragmentInfoLog);
+
+                       log.endShaderProgram();
+               }
+
+               gl.deleteShader(vertexShader);
+               gl.deleteShader(fragmentShader);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to delete shaders");
+
+               TCU_CHECK(linkStatus != 0 && vertexCompileStatus != 0 && fragmentCompileStatus != 0);
+       }
+       catch (...)
+       {
+               if (program)
+                       gl.deleteProgram(program);
+
+               if (vertexShader)
+                       gl.deleteShader(vertexShader);
+
+               if (fragmentShader)
+                       gl.deleteShader(fragmentShader);
+
+               throw;
+       }
+
+       return program;
+}
+
+void GetFrameTimestampTest::executeForConfig (EGLDisplay display, EGLConfig config)
+{
+       const Library&                                          egl                     = m_eglTestCtx.getLibrary();
+
+       if (!eglu::hasExtension(egl, display, "EGL_ANDROID_get_frame_timestamps"))
+               TCU_THROW(NotSupportedError, "EGL_ANDROID_get_frame_timestamps is not supported");
+
+       initializeExtension(egl);
+
+       const string                                            configIdStr     (getConfigIdString(egl, display, config));
+       tcu::ScopedLogSection                           logSection      (m_testCtx.getLog(), ("Config ID " + configIdStr).c_str(), ("Config ID " + configIdStr).c_str());
+       const eglu::NativeWindowFactory&        factory         = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
+
+       {
+               TestLog& log = m_testCtx.getLog();
+
+               log << TestLog::Message << "EGL_RED_SIZE: "             << eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE)         << TestLog::EndMessage;
+               log << TestLog::Message << "EGL_GREEN_SIZE: "   << eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE)       << TestLog::EndMessage;
+               log << TestLog::Message << "EGL_BLUE_SIZE: "    << eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE)        << TestLog::EndMessage;
+               log << TestLog::Message << "EGL_ALPHA_SIZE: "   << eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE)       << TestLog::EndMessage;
+               log << TestLog::Message << "EGL_DEPTH_SIZE: "   << eglu::getConfigAttribInt(egl, display, config, EGL_DEPTH_SIZE)       << TestLog::EndMessage;
+               log << TestLog::Message << "EGL_STENCIL_SIZE: " << eglu::getConfigAttribInt(egl, display, config, EGL_STENCIL_SIZE)     << TestLog::EndMessage;
+               log << TestLog::Message << "EGL_SAMPLES: "              << eglu::getConfigAttribInt(egl, display, config, EGL_SAMPLES)          << TestLog::EndMessage;
+       }
+
+       de::UniquePtr<eglu::NativeWindow>       window  (factory.createWindow(&m_eglTestCtx.getNativeDisplay(), display, config, DE_NULL, eglu::WindowParams(128, 128, eglu::WindowParams::VISIBILITY_VISIBLE)));
+
+       eglu::UniqueSurface                                     surface (egl, display, eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, display, config, DE_NULL));
+       eglu::UniqueContext                                     context (egl, display, createGLES2Context(egl, display, config));
+       glw::Functions                                          gl;
+       deUint32                                                        program = 0;
+
+       EGLU_CHECK_CALL(egl, surfaceAttrib(display, *surface, EGL_TIMESTAMPS_ANDROID, EGL_TRUE));
+
+       m_eglTestCtx.initGLFunctions(&gl, glu::ApiType::es(2,0));
+
+       EGLU_CHECK_CALL(egl, makeCurrent(display, *surface, *surface, *context));
+
+       try
+       {
+               // Verify required timestamps are supported.
+               const eglw::EGLint requiredTimestamps[] =
+               {
+                       EGL_REQUESTED_PRESENT_TIME_ANDROID,
+                       EGL_RENDERING_COMPLETE_TIME_ANDROID,
+                       EGL_COMPOSITION_LATCH_TIME_ANDROID,
+                       EGL_FIRST_COMPOSITION_START_TIME_ANDROID,
+                       EGL_LAST_COMPOSITION_START_TIME_ANDROID,
+                       EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID,
+                       EGL_DEQUEUE_READY_TIME_ANDROID,
+                       EGL_READS_DONE_TIME_ANDROID,
+               };
+               const size_t requiredTimestampsCount = DE_LENGTH_OF_ARRAY(requiredTimestamps);
+
+               for (size_t i = 0; i < requiredTimestampsCount; i++)
+               {
+                       const bool supported = m_eglGetFrameTimestampSupportedANDROID(display, *surface, requiredTimestamps[i]);
+                       EGLU_CHECK_MSG(egl, "eglGetFrameTimestampSupportedANDROID failed.");
+                       TCU_CHECK_MSG(supported, "Required timestamp not supported.");
+               }
+
+               // Verify either retire or present is supported.
+               const bool retireSupported = m_eglGetFrameTimestampSupportedANDROID(display, *surface, EGL_DISPLAY_RETIRE_TIME_ANDROID);
+               EGLU_CHECK_MSG(egl, "eglGetFrameTimestampSupportedANDROID failed.");
+               const bool presentSupported = m_eglGetFrameTimestampSupportedANDROID(display, *surface, EGL_DISPLAY_PRESENT_TIME_ANDROID);
+               EGLU_CHECK_MSG(egl, "eglGetFrameTimestampSupportedANDROID failed.");
+               TCU_CHECK_MSG(retireSupported != presentSupported, "DISPLAY_RETIRE or DISPLAY_PRESENT must be supported, but not both.");
+
+               // Verify compositor timings are supported.
+               const bool deadlineSupported = m_eglGetCompositorTimingSupportedANDROID(display, *surface, EGL_COMPOSITE_DEADLINE_ANDROID);
+               EGLU_CHECK_MSG(egl, "eglGetCompositorTimingSupportedANDROID failed.");
+               TCU_CHECK_MSG(deadlineSupported, "EGL_COMPOSITE_DEADLINE_ANDROID not supported.");
+               const bool intervalSupported = m_eglGetCompositorTimingSupportedANDROID(display, *surface, EGL_COMPOSITE_INTERVAL_ANDROID);
+               EGLU_CHECK_MSG(egl, "eglGetCompositorTimingSupportedANDROID failed.");
+               TCU_CHECK_MSG(intervalSupported, "EGL_COMPOSITE_INTERVAL_ANDROID not supported.");
+               const bool latencySupported = m_eglGetCompositorTimingSupportedANDROID(display, *surface, EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID);
+               EGLU_CHECK_MSG(egl, "eglGetCompositorTimingSupportedANDROID failed.");
+               TCU_CHECK_MSG(latencySupported, "EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID not supported.");
+
+
+               const eglw::EGLint frameTimestampNames[] =
+               {
+                       EGL_REQUESTED_PRESENT_TIME_ANDROID,
+                       EGL_RENDERING_COMPLETE_TIME_ANDROID,
+                       EGL_COMPOSITION_LATCH_TIME_ANDROID,
+                       EGL_FIRST_COMPOSITION_START_TIME_ANDROID,
+                       EGL_LAST_COMPOSITION_START_TIME_ANDROID,
+                       EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID,
+                       presentSupported ? EGL_DISPLAY_PRESENT_TIME_ANDROID : EGL_DISPLAY_RETIRE_TIME_ANDROID,
+                       EGL_DEQUEUE_READY_TIME_ANDROID,
+                       EGL_READS_DONE_TIME_ANDROID,
+               };
+               const size_t frameTimestampCount = DE_LENGTH_OF_ARRAY(frameTimestampNames);
+
+               const float positions1[] =
+               {
+                        0.00f,  0.00f,
+                        0.75f,  0.00f,
+                        0.75f,  0.75f,
+
+                        0.75f,  0.75f,
+                        0.00f,  0.75f,
+                        0.00f,  0.00f
+               };
+
+               const float positions2[] =
+               {
+                       -0.75f, -0.75f,
+                        0.00f, -0.75f,
+                        0.00f,  0.00f,
+
+                        0.00f,  0.00f,
+                       -0.75f,  0.00f,
+                       -0.75f, -0.75f
+               };
+
+               deUint32 posLocation;
+
+               program = createGLES2Program(gl, m_testCtx.getLog());
+
+               gl.useProgram(program);
+               posLocation     = gl.getAttribLocation(program, "a_pos");
+               gl.enableVertexAttribArray(posLocation);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup shader program for rendering");
+
+               const size_t frameCount = 120;
+               std::vector<FrameTimes> frameTimes(frameCount);
+               for (size_t i = 0; i < frameCount; i++)
+               {
+                       FrameTimes& frame = frameTimes[i];
+
+                       const eglw::EGLint compositorTimingNames[] =
+                       {
+                               EGL_COMPOSITE_DEADLINE_ANDROID,
+                               EGL_COMPOSITE_INTERVAL_ANDROID,
+                               EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID,
+                       };
+                       const EGLint compositorTimingCount = DE_LENGTH_OF_ARRAY(compositorTimingNames);
+                       EGLnsecsANDROID compositorTimingValues[compositorTimingCount] = { -2 };
+
+                       // Get the frame id.
+                       EGLuint64KHR nextFrameId = 0;
+                       CHECK_NAKED_EGL_CALL(egl, m_eglGetNextFrameIdANDROID(display, *surface, &nextFrameId));
+                       frame.frameId                           =       nextFrameId;
+
+                       // Get the compositor timing.
+                       CHECK_NAKED_EGL_CALL(egl, m_eglGetCompositorTimingANDROID(
+                               display, *surface, compositorTimingCount,
+                               compositorTimingNames, compositorTimingValues));
+                       frame.compositeDeadline                 =       compositorTimingValues[0];
+                       frame.compositeInterval                 =       compositorTimingValues[1];
+                       frame.compositeToPresentLatency =       compositorTimingValues[2];
+
+                       // Verify compositor timing is sane.
+                       EGLnsecsANDROID now = getNanoseconds();
+                       m_result.check(1000000 < frame.compositeInterval, "Reported refresh rate greater than 1kHz.");
+                       m_result.check(frame.compositeInterval < 1000000000, "Reported refresh rate less than 1Hz.");
+                       m_result.check(0 < frame.compositeToPresentLatency, "Composite to present latency must be greater than 0.");
+                       m_result.check(frame.compositeToPresentLatency < frame.compositeInterval * 3, "Composite to present latency is more than 3 vsyncs.");
+                       const EGLnsecsANDROID minDeadline = now;
+                       m_result.check(minDeadline < frame.compositeDeadline, "Next composite deadline is in the past.");
+                       const EGLnsecsANDROID maxDeadline = now + frame.compositeInterval * 2;
+                       m_result.check(frame.compositeDeadline < maxDeadline, "Next composite deadline over two intervals away.");
+
+                       const float colorAngle = (static_cast<float>(i) / static_cast<float>(frameCount)) * 6.28318f;
+                       gl.clearColor((1.0f + deFloatSin(colorAngle)) / 2.0f, 0.7f, (1.0f + deFloatCos(colorAngle)) / 2.0f, 1.0f);
+                       gl.clear(GL_COLOR_BUFFER_BIT);
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to clear surface");
+
+                       const bool posSelect  = ((i % 2) == 0);
+                       gl.vertexAttribPointer(posLocation, 2, GL_FLOAT, GL_FALSE, 0, posSelect ? positions1 : positions2);
+                       gl.drawArrays(GL_TRIANGLES, 0, 6);
+                       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to render");
+
+                       frame.swapBufferBeginNs = getNanoseconds();
+                       EGLU_CHECK_CALL(egl, swapBuffers(display, *surface));
+
+                       // All timestamps from 5 frames ago should definitely be available.
+                       const size_t frameDelay = 5;
+                       if (i >= frameDelay)
+                       {
+                               FrameTimes&             frame5ago                                                                       =       frameTimes[i-frameDelay];
+                               EGLnsecsANDROID frameTimestampValues[frameTimestampCount]       =       { 0 };
+                               // \todo [2017-01-25 brianderson] Remove this work around once reads done is fixed.
+                               const bool verifyReadsDone                                                                      =       i > (frameDelay + 3);
+
+                               CHECK_NAKED_EGL_CALL(egl, m_eglGetFrameTimestampsANDROID(
+                                       display, *surface, frame5ago.frameId, frameTimestampCount,
+                                       frameTimestampNames, frameTimestampValues));
+
+                               frame5ago.requestedPresent                              =       frameTimestampValues[0];
+                               frame5ago.renderingComplete                             =       frameTimestampValues[1];
+                               frame5ago.latch                                                 =       frameTimestampValues[2];
+                               frame5ago.firstCompositionStart                 =       frameTimestampValues[3];
+                               frame5ago.lastCompositionStart                  =       frameTimestampValues[4];
+                               frame5ago.firstCompositionGpuFinished   =       frameTimestampValues[5];
+                               frame5ago.dequeueReady                                  =       frameTimestampValues[7];
+                               frame5ago.readsDone                                             =       frameTimestampValues[8];
+                               if (presentSupported)
+                                       frame5ago.displayPresent                        =       frameTimestampValues[6];
+                               else
+                                       frame5ago.displayRetire                 =       frameTimestampValues[6];
+
+                               verifySingleFrame(frame5ago, m_result, verifyReadsDone);
+                               if (i >= frameDelay + 1)
+                               {
+                                       FrameTimes& frame6ago = frameTimes[i-frameDelay-1];
+                                       verifyNeighboringFrames(frame6ago, frame5ago, m_result, verifyReadsDone);
+                               }
+                       }
+               }
+
+               // All timestamps for the most recently swapped frame should
+               // become available by only polling eglGetFrametimestamps.
+               // No additional swaps should be necessary.
+               FrameTimes&                             lastFrame                               =       frameTimes.back();
+               const EGLnsecsANDROID   pollingDeadline                 =       lastFrame.swapBufferBeginNs + 1000000000;
+               bool                                    finalTimestampAvaiable  =       false;
+
+               do
+               {
+                       EGLnsecsANDROID frameTimestampValues[frameTimestampCount] = { 0 };
+                       CHECK_NAKED_EGL_CALL(egl, m_eglGetFrameTimestampsANDROID(
+                               display, *surface, lastFrame.frameId, frameTimestampCount,
+                               frameTimestampNames, frameTimestampValues));
+
+                       lastFrame.requestedPresent                              =       frameTimestampValues[0];
+                       lastFrame.renderingComplete                             =       frameTimestampValues[1];
+                       lastFrame.latch                                                 =       frameTimestampValues[2];
+                       lastFrame.firstCompositionStart                 =       frameTimestampValues[3];
+                       lastFrame.lastCompositionStart                  =       frameTimestampValues[4];
+                       lastFrame.firstCompositionGpuFinished   =       frameTimestampValues[5];
+                       lastFrame.dequeueReady                                  =       frameTimestampValues[7];
+                       lastFrame.readsDone                                             =       frameTimestampValues[8];
+                       if (presentSupported)
+                       {
+                               lastFrame.displayPresent = frameTimestampValues[6];
+                               if (timestampExists(lastFrame.displayPresent))
+                                       finalTimestampAvaiable = true;
+                       }
+                       else
+                       {
+                               lastFrame.displayRetire = frameTimestampValues[6];
+                               if (timestampExists(lastFrame.firstCompositionStart))
+                                       finalTimestampAvaiable = true;
+                       }
+
+                       if (getNanoseconds() > pollingDeadline)
+                               break;
+               } while (!finalTimestampAvaiable);
+
+               m_result.check(finalTimestampAvaiable, "Timed out polling for timestamps of last swap.");
+               m_result.check(timestampExists(lastFrame.requestedPresent), "Rendering complete of last swap not avaiable.");
+               m_result.check(timestampExists(lastFrame.renderingComplete), "Rendering complete of last swap not avaiable.");
+               m_result.check(timestampExists(lastFrame.latch), "Latch of last swap not avaiable.");
+               m_result.check(timestampExists(lastFrame.firstCompositionStart), "First composite time of last swap not avaiable.");
+               m_result.check(timestampExists(lastFrame.lastCompositionStart), "Last composite time of last swap not avaiable.");
+
+               window->processEvents();
+               gl.disableVertexAttribArray(posLocation);
+               gl.useProgram(0);
+               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to release program state");
+
+               gl.deleteProgram(program);
+               program = 0;
+               GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteProgram()");
+
+               m_result.setTestContextResult(m_testCtx);
+       }
+       catch (...)
+       {
+               if (program != 0)
+                       gl.deleteProgram(program);
+
+               EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
+               throw;
+       }
+
+       EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
+}
+
+class GetFrameTimestampsTests : public TestCaseGroup
+{
+public:
+                                                               GetFrameTimestampsTests (EglTestContext& eglTestCtx);
+       void                                            init                                    (void);
+
+private:
+                                                               GetFrameTimestampsTests (const GetFrameTimestampsTests&);
+       GetFrameTimestampsTests&        operator=                               (const GetFrameTimestampsTests&);
+};
+
+
+GetFrameTimestampsTests::GetFrameTimestampsTests (EglTestContext& eglTestCtx)
+       : TestCaseGroup(eglTestCtx, "get_frame_timestamps", "Get frame timestamp tests")
+{
+}
+
+bool isWindow (const eglu::CandidateConfig& c)
+{
+       return (c.surfaceType() & EGL_WINDOW_BIT) != 0;
+}
+
+void GetFrameTimestampsTests::init (void)
+{
+       eglu::FilterList baseFilters;
+       baseFilters << isWindow;
+
+       vector<NamedFilterList> filterLists;
+       getDefaultFilterLists(filterLists, baseFilters);
+
+       for (vector<NamedFilterList>::iterator i = filterLists.begin(); i != filterLists.end(); i++)
+               addChild(new GetFrameTimestampTest(m_eglTestCtx, *i));
+}
+
+} // anonymous
+
+TestCaseGroup* createGetFrameTimestampsTests (EglTestContext& eglTestCtx)
+{
+       return new GetFrameTimestampsTests(eglTestCtx);
+}
+
+} // egl
+} // deqp
diff --git a/modules/egl/teglGetFrameTimestampsTests.hpp b/modules/egl/teglGetFrameTimestampsTests.hpp
new file mode 100644 (file)
index 0000000..d55d6a4
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _TEGLGETFRAMETIMESTAMPSTESTS_HPP
+#define _TEGLGETFRAMETIMESTAMPSTESTS_HPP
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program EGL Module
+ * ---------------------------------------
+ *
+ * Copyright 2017 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 the EGL_ANDROID_get_frame_timestamps extension.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "teglTestCase.hpp"
+
+namespace deqp
+{
+namespace egl
+{
+
+TestCaseGroup* createGetFrameTimestampsTests (EglTestContext& eglTestCtx);
+
+} // egl
+} // deqp
+
+#endif // _TEGLGETFRAMETIMESTAMPSTESTS_HPP
index 443fad6..0c4ee77 100644 (file)
@@ -63,6 +63,7 @@
 #include "teglMultiContextTests.hpp"
 #include "teglThreadCleanUpTests.hpp"
 #include "teglMutableRenderBufferTests.hpp"
+#include "teglGetFrameTimestampsTests.hpp"
 
 namespace deqp
 {
@@ -138,6 +139,7 @@ public:
                addChild(createMultiContextTests                (m_eglTestCtx));
                addChild(createThreadCleanUpTest                (m_eglTestCtx));
                addChild(new MutableRenderBufferTests   (m_eglTestCtx));
+               addChild(createGetFrameTimestampsTests  (m_eglTestCtx));
        }
 };