| // Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <GLES2/gl2.h> |
| #include <GLES2/gl2ext.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <X11/Xutil.h> |
| |
| #include "glbench/egl_stuff.h" |
| #include "glbench/synccontroltest.h" |
| |
| #if !defined(KHRONOS_SUPPORT_INT64) |
| #error int64 not defined by Khronos :-( |
| #endif |
| |
| namespace { |
| |
| #ifndef EGL_CHROMIUM_sync_controls |
| #define EGL_CHROMIUM_sync_controls 1 |
| typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCVALUESCHROMIUMPROC)( |
| EGLDisplay dpy, |
| EGLSurface surface, |
| khronos_uint64_t* ust, |
| khronos_uint64_t* msc, |
| khronos_uint64_t* sbc); |
| #endif |
| |
| struct SyncValues { |
| khronos_uint64_t ust; |
| khronos_uint64_t msc; |
| khronos_uint64_t sbc; |
| }; |
| |
| // Sync2Sync error is for operations on the same clock, so we are mostly testing |
| // the jitter of the clock and that the system isn't dropping swaps |
| const int kAcceptableSync2SyncError = 250; // uS |
| // Clock error is for making sure that the system is reasonably close to the |
| // system clock. Given that we are measuring two seperate clocks and there are |
| // system calls between the values there is substantial variance in the |
| // delta. Problems the this check catch are normally order of magnitude |
| // differences. |
| const khronos_uint64_t kAcceptableClockError = 25000; // uS |
| const float kFillValueRed = 1.0; |
| const float kFillValueGreen = 0.0; |
| const float kFillValueBlue = 0.0; |
| const khronos_uint64_t kMicroSecondsPerSecond = 1000000; |
| const khronos_uint64_t kMicroSecondsPerMilliSecond = 1000; |
| |
| khronos_uint64_t GetAbsTimeDelta(khronos_uint64_t t1, khronos_uint64_t t2) { |
| return (t1 > t2) ? (t1 - t2) : (t2 - t1); |
| } |
| |
| } // namespace |
| |
| class EGLSyncControlTest : public SyncControlTest { |
| public: |
| EGLSyncControlTest(); |
| ~EGLSyncControlTest(); |
| |
| virtual void Init(); |
| virtual bool Loop(int interval); |
| |
| private: |
| EGLInterface* interface_; |
| PFNEGLGETSYNCVALUESCHROMIUMPROC egl_get_sync_values_; |
| |
| EGLBoolean GetSyncValues(struct SyncValues* sync_values); |
| bool TestAgainstSystem(khronos_uint64_t ust); |
| }; |
| |
| SyncControlTest* SyncControlTest::Create() { |
| return new EGLSyncControlTest(); |
| } |
| |
| EGLSyncControlTest::EGLSyncControlTest() |
| : interface_(static_cast<EGLInterface*>(g_main_gl_interface.get())), |
| egl_get_sync_values_(NULL) { } |
| |
| EGLSyncControlTest::~EGLSyncControlTest() { } |
| |
| void EGLSyncControlTest::Init() { |
| // Make sure that extension under test is present and usable |
| std::string extensions(" "); |
| extensions += eglQueryString(interface_->display(), EGL_EXTENSIONS); |
| extensions += " "; |
| std::string name(" EGL_CHROMIUM_sync_control "); |
| CHECK(extensions.find(name) != std::string::npos) |
| << "Extension EGL_CHROMIUM_sync_control not available!"; |
| |
| egl_get_sync_values_ = reinterpret_cast<PFNEGLGETSYNCVALUESCHROMIUMPROC>( |
| eglGetProcAddress("eglGetSyncValuesCHROMIUM")); |
| CHECK(egl_get_sync_values_) |
| << "Function eglGetSyncValuesCHROMIUM is not available!"; |
| } |
| |
| bool EGLSyncControlTest::Loop(int interval) { |
| struct SyncValues first_call = { 0, 0, 0 }; |
| struct SyncValues second_call = { 0, 0, 0 }; |
| khronos_uint64_t real_ust_delta, expected_ust_delta, ust_delta_error; |
| bool test_val = true; |
| EGLBoolean ret; |
| |
| glClearColor(kFillValueRed, kFillValueGreen, kFillValueBlue, 0.0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| ret = GetSyncValues(&first_call); |
| |
| if (!ret) { |
| LOG(ERROR) << "Failure: First eglGetSyncValuesCHROMIUM returned false.\n"; |
| test_val = false; |
| } else if (!TestAgainstSystem(first_call.ust)) { |
| LOG(ERROR) << "Failure: First ust value failed to test against system " |
| << "time!"; |
| test_val = false; |
| } |
| |
| interface_->SwapBuffers(); |
| usleep(interval); |
| ret = GetSyncValues(&second_call); |
| |
| if (!ret) { |
| LOG(ERROR) << "Failure: Second eglGetSyncValuesCHROMIUM returned false.\n"; |
| test_val = false; |
| } else if (!TestAgainstSystem(second_call.ust)) { |
| LOG(ERROR) << "Failure: Second ust value failed to test against system " |
| << "time!"; |
| test_val = false; |
| } |
| |
| if (first_call.ust >= second_call.ust) { |
| LOG(ERROR) << "Failure: First ust value is equal to or greater then the " |
| << "second!"; |
| test_val = false; |
| } |
| |
| if (first_call.msc >= second_call.msc) { |
| LOG(ERROR) << "Failure: First msc value is equal to or greater then the " |
| << "second!"; |
| test_val = false; |
| } |
| |
| if (first_call.sbc >= second_call.sbc) { |
| LOG(ERROR) << "Failure: First sbc value is equal to or greater then the " |
| << "second!"; |
| test_val = false; |
| } |
| |
| real_ust_delta = second_call.ust - first_call.ust; |
| expected_ust_delta = (second_call.msc - first_call.msc) * interval; |
| ust_delta_error = (real_ust_delta > expected_ust_delta) ? |
| (real_ust_delta - expected_ust_delta) : |
| (expected_ust_delta - real_ust_delta); |
| if ((second_call.msc - first_call.msc) * kAcceptableSync2SyncError < |
| ust_delta_error) { |
| LOG(ERROR) << "Failure: ust delta is not within acceptable error " |
| << "(" << (second_call.ust - first_call.ust) << "instead of " |
| << (second_call.msc - first_call.msc) * interval << ")!"; |
| test_val = false; |
| } |
| |
| if (!test_val) { |
| LOG(ERROR) << "First call to eglGetSyncValuesCHROMIUM returned:\n\t" |
| << "ust=" << first_call.ust << "\n\t" |
| << "msc=" << first_call.msc << "\n\t" |
| << "sbc=" << first_call.sbc; |
| LOG(ERROR) << "Second call to eglGetSyncValuesCHROMIUM returned:\n\t" |
| << "ust=" << second_call.ust << "\n\t" |
| << "msc=" << second_call.msc << "\n\t" |
| << "sbc=" << second_call.sbc; |
| } |
| return test_val; |
| } |
| |
| EGLBoolean EGLSyncControlTest::GetSyncValues(struct SyncValues *sync_values) { |
| EGLBoolean ret; |
| ret = egl_get_sync_values_(interface_->display(), |
| interface_->surface(), |
| &sync_values->ust, |
| &sync_values->msc, |
| &sync_values->sbc); |
| interface_->CheckError(); |
| return ret; |
| } |
| |
| bool EGLSyncControlTest::TestAgainstSystem(khronos_uint64_t ust) { |
| struct timespec real_time; |
| struct timespec monotonic_time; |
| khronos_uint64_t sec, nsec; |
| clock_gettime(CLOCK_REALTIME, &real_time); |
| clock_gettime(CLOCK_MONOTONIC, &monotonic_time); |
| |
| sec = real_time.tv_sec; |
| nsec = real_time.tv_nsec; |
| khronos_uint64_t real_time_us = |
| sec * kMicroSecondsPerSecond + |
| nsec / kMicroSecondsPerMilliSecond; |
| |
| sec = monotonic_time.tv_sec; |
| nsec = monotonic_time.tv_nsec; |
| khronos_uint64_t monotonic_time_us = |
| sec * kMicroSecondsPerSecond + |
| nsec / kMicroSecondsPerMilliSecond; |
| |
| if (GetAbsTimeDelta(ust, real_time_us) < kAcceptableClockError) { |
| return true; |
| } |
| |
| if (GetAbsTimeDelta(ust, monotonic_time_us) < kAcceptableClockError) { |
| return true; |
| } |
| |
| LOG(ERROR) << "UST value, " << ust << ", not within error, " |
| << kAcceptableClockError << ", of either real time, " |
| << real_time_us << ", or monotonic time, " << monotonic_time_us; |
| return false; |
| } |