/*------------------------------------------------------------------------- * 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 0x3430 #define EGL_COMPOSITE_DEADLINE_ANDROID 0x3431 #define EGL_COMPOSITE_INTERVAL_ANDROID 0x3432 #define EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3433 #define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3434 #define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3435 #define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3436 #define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3437 #define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3438 #define EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID 0x3439 #define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x343A #define EGL_DEQUEUE_READY_TIME_ANDROID 0x343B #define EGL_READS_DONE_TIME_ANDROID 0x343C typedef deInt64 EGLnsecsANDROID; typedef deUint64 EGLuint64KHR; #define EGL_TIMESTAMP_PENDING_ANDROID (-2) #define EGL_TIMESTAMP_INVALID_ANDROID (-1) 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) , 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 readsDone; }; struct TimestampInfo { TimestampInfo() : required(false) , supported(false) , supportedIndex(0) { } TimestampInfo(bool required_, bool supported_, size_t supportedIndex_) : required(required_) , supported(supported_) , supportedIndex(supportedIndex_) { } bool required; bool supported; size_t supportedIndex; }; typedef std::map<eglw::EGLint, TimestampInfo> TimestampInfoMap; EGLnsecsANDROID getTimestamp(eglw::EGLint name, TimestampInfoMap& map, const std::vector<EGLnsecsANDROID>& supportedValues) { TimestampInfo& info = map[name]; return info.supported ? supportedValues[info.supportedIndex] : EGL_TIMESTAMP_INVALID_ANDROID; } void populateFrameTimes(FrameTimes* frameTimes, TimestampInfoMap& map, const std::vector<EGLnsecsANDROID>& supportedValues) { frameTimes->requestedPresent = getTimestamp(EGL_REQUESTED_PRESENT_TIME_ANDROID, map, supportedValues); frameTimes->renderingComplete = getTimestamp(EGL_RENDERING_COMPLETE_TIME_ANDROID, map, supportedValues); frameTimes->latch = getTimestamp(EGL_COMPOSITION_LATCH_TIME_ANDROID, map, supportedValues); frameTimes->firstCompositionStart = getTimestamp(EGL_FIRST_COMPOSITION_START_TIME_ANDROID, map, supportedValues); frameTimes->lastCompositionStart = getTimestamp(EGL_LAST_COMPOSITION_START_TIME_ANDROID, map, supportedValues); frameTimes->firstCompositionGpuFinished = getTimestamp(EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID, map, supportedValues); frameTimes->displayPresent = getTimestamp(EGL_DISPLAY_PRESENT_TIME_ANDROID, map, supportedValues); frameTimes->dequeueReady = getTimestamp(EGL_DEQUEUE_READY_TIME_ANDROID, map, supportedValues); frameTimes->readsDone = getTimestamp(EGL_READS_DONE_TIME_ANDROID, map, supportedValues); } bool timestampValid (EGLnsecsANDROID timestamp) { return (timestamp >= 0) || (timestamp == EGL_TIMESTAMP_PENDING_ANDROID); } bool timestampPending (EGLnsecsANDROID timestamp) { return timestamp == EGL_TIMESTAMP_PENDING_ANDROID; } 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 (timestampValid(frameTimes.firstCompositionGpuFinished)) result.check(frameTimes.renderingComplete < frameTimes.firstCompositionGpuFinished, "Buffer rendering completed after compositor GPU work finished."); if (timestampValid(frameTimes.displayPresent)) result.check(frameTimes.renderingComplete < frameTimes.displayPresent, "Buffer displayed before rendering completed."); if (timestampValid(frameTimes.firstCompositionGpuFinished) && timestampValid(frameTimes.displayPresent)) result.check(frameTimes.firstCompositionGpuFinished < frameTimes.displayPresent, "Buffer displayed 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 (timestampValid(frameTimes.firstCompositionGpuFinished)) result.check(frameTimes.firstCompositionStart < frameTimes.firstCompositionGpuFinished, "Composition CPU work started after GPU work finished."); if (timestampValid(frameTimes.displayPresent)) result.check(frameTimes.firstCompositionStart < frameTimes.displayPresent, "Buffer displayed before it was composited."); } 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 (timestampValid(frame1.firstCompositionGpuFinished) && timestampValid(frame2.firstCompositionGpuFinished)) result.check(frame1.firstCompositionGpuFinished < frame2.firstCompositionGpuFinished, "Composition GPU work complete times not monotonic."); if (timestampValid(frame1.displayPresent) && timestampValid(frame2.displayPresent)) result.check(frame1.displayPresent < frame2.displayPresent, "Display present times not monotonic."); if (verifyReadsDone && timestampValid(frame1.readsDone) && timestampValid(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 { // EGL_DISPLAY_PRESENT_TIME_ANDROID support is currently optional // but should be required once HWC1 is no longer supported. // All HWC2 devices should support EGL_DISPLAY_PRESENT_TIME_ANDROID. TimestampInfoMap timestamps; timestamps[EGL_REQUESTED_PRESENT_TIME_ANDROID] = TimestampInfo(true, false, 0); timestamps[EGL_RENDERING_COMPLETE_TIME_ANDROID] = TimestampInfo(true, false, 0); timestamps[EGL_COMPOSITION_LATCH_TIME_ANDROID] = TimestampInfo(true, false, 0); timestamps[EGL_FIRST_COMPOSITION_START_TIME_ANDROID] = TimestampInfo(true, false, 0); timestamps[EGL_LAST_COMPOSITION_START_TIME_ANDROID] = TimestampInfo(true, false, 0); timestamps[EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID] = TimestampInfo(true, false, 0); timestamps[EGL_DISPLAY_PRESENT_TIME_ANDROID] = TimestampInfo(false, false, 0); timestamps[EGL_DEQUEUE_READY_TIME_ANDROID] = TimestampInfo(true, false, 0); timestamps[EGL_READS_DONE_TIME_ANDROID] = TimestampInfo(true, false, 0); const eglw::EGLint invalidTimestampName = EGL_READS_DONE_TIME_ANDROID + 1; // Verify required timestamps are supported and populate supportedNames. std::vector<eglw::EGLint> supportedNames; for (TimestampInfoMap::iterator i = timestamps.begin(); i != timestamps.end(); i++) { TimestampInfo& info = i->second; info.supported = m_eglGetFrameTimestampSupportedANDROID(display, *surface, i->first) != EGL_FALSE; EGLU_CHECK_MSG(egl, "eglGetFrameTimestampSupportedANDROID failed."); if (info.supported) { info.supportedIndex = supportedNames.size(); supportedNames.push_back(i->first); } else TCU_CHECK_MSG(!info.required, "Required timestamp not supported."); } // Verify unsupported timestamps are reported properly. const bool invalidSupported = m_eglGetFrameTimestampSupportedANDROID(display, *surface, invalidTimestampName) != EGL_FALSE; EGLU_CHECK_MSG(egl, "eglGetFrameTimestampSupportedANDROID failed."); TCU_CHECK_MSG(!invalidSupported, "Non existant timestamp reports that it is supported."); // Verify compositor timings are supported. const bool deadlineSupported = m_eglGetCompositorTimingSupportedANDROID(display, *surface, EGL_COMPOSITE_DEADLINE_ANDROID) != EGL_FALSE; 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) != EGL_FALSE; 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) != EGL_FALSE; EGLU_CHECK_MSG(egl, "eglGetCompositorTimingSupportedANDROID failed."); TCU_CHECK_MSG(latencySupported, "EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID not supported."); 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 current time before making any API calls in case "now" // just happens to get sampled near one of the composite deadlines. EGLnsecsANDROID now = getNanoseconds(); // 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. 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) { // \todo [2017-01-25 brianderson] Remove this work around once reads done is fixed. const bool verifyReadsDone = i > (frameDelay + 3); FrameTimes& frame5ago = frameTimes[i-frameDelay]; std::vector<EGLnsecsANDROID> supportedValues(supportedNames.size(), 0); CHECK_NAKED_EGL_CALL(egl, m_eglGetFrameTimestampsANDROID( display, *surface, frame5ago.frameId, static_cast<eglw::EGLint>(supportedNames.size()), &supportedNames[0], &supportedValues[0])); populateFrameTimes(&frame5ago, timestamps, supportedValues); 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 finalTimestampAvailable = false; do { std::vector<EGLnsecsANDROID> supportedValues(supportedNames.size(), 0); CHECK_NAKED_EGL_CALL(egl, m_eglGetFrameTimestampsANDROID( display, *surface, lastFrame.frameId, static_cast<eglw::EGLint>(supportedNames.size()), &supportedNames[0], &supportedValues[0])); populateFrameTimes(&lastFrame, timestamps, supportedValues); // Poll for present if it's supported. // Otherwise, poll for firstCompositionStart. if (timestamps[EGL_DISPLAY_PRESENT_TIME_ANDROID].supported) finalTimestampAvailable = !timestampPending(lastFrame.displayPresent); else finalTimestampAvailable = !timestampPending(lastFrame.firstCompositionStart); } while (!finalTimestampAvailable && (getNanoseconds() < pollingDeadline)); m_result.check(finalTimestampAvailable, "Timed out polling for timestamps of last swap."); m_result.check((lastFrame.requestedPresent >= 0), "Requested present of last swap not avaiable."); m_result.check((lastFrame.renderingComplete >= 0), "Rendering complete of last swap not avaiable."); m_result.check((lastFrame.latch >= 0), "Latch of last swap not avaiable."); m_result.check((lastFrame.firstCompositionStart >= 0), "First composite time of last swap not avaiable."); m_result.check((lastFrame.lastCompositionStart >= 0), "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