| /* |
| * Copyright 2018 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. |
| */ |
| |
| #include "EGL.h" |
| |
| #include <vector> |
| |
| #define LOG_TAG "Swappy::EGL" |
| |
| #include "Log.h" |
| |
| using namespace std::chrono_literals; |
| |
| namespace swappy { |
| |
| std::unique_ptr<EGL> EGL::create(std::chrono::nanoseconds refreshPeriod) { |
| auto eglPresentationTimeANDROID = reinterpret_cast<eglPresentationTimeANDROID_type>( |
| eglGetProcAddress("eglPresentationTimeANDROID")); |
| if (eglPresentationTimeANDROID == nullptr) { |
| ALOGE("Failed to load eglPresentationTimeANDROID"); |
| return nullptr; |
| } |
| |
| auto eglCreateSyncKHR = reinterpret_cast<eglCreateSyncKHR_type>( |
| eglGetProcAddress("eglCreateSyncKHR")); |
| if (eglCreateSyncKHR == nullptr) { |
| ALOGE("Failed to load eglCreateSyncKHR"); |
| return nullptr; |
| } |
| |
| auto eglDestroySyncKHR = reinterpret_cast<eglDestroySyncKHR_type>( |
| eglGetProcAddress("eglDestroySyncKHR")); |
| if (eglDestroySyncKHR == nullptr) { |
| ALOGE("Failed to load eglDestroySyncKHR"); |
| return nullptr; |
| } |
| |
| auto eglGetSyncAttribKHR = reinterpret_cast<eglGetSyncAttribKHR_type>( |
| eglGetProcAddress("eglGetSyncAttribKHR")); |
| if (eglGetSyncAttribKHR == nullptr) { |
| ALOGE("Failed to load eglGetSyncAttribKHR"); |
| return nullptr; |
| } |
| |
| auto eglGetError = reinterpret_cast<eglGetError_type>( |
| eglGetProcAddress("eglGetError")); |
| if (eglGetError == nullptr) { |
| ALOGE("Failed to load eglGetError"); |
| return nullptr; |
| } |
| |
| auto eglSurfaceAttrib = reinterpret_cast<eglSurfaceAttrib_type>( |
| eglGetProcAddress("eglSurfaceAttrib")); |
| if (eglSurfaceAttrib == nullptr) { |
| ALOGE("Failed to load eglSurfaceAttrib"); |
| return nullptr; |
| } |
| |
| // stats may not be supported on all versions |
| auto eglGetNextFrameIdANDROID = reinterpret_cast<eglGetNextFrameIdANDROID_type>( |
| eglGetProcAddress("eglGetNextFrameIdANDROID")); |
| if (eglGetNextFrameIdANDROID == nullptr) { |
| ALOGI("Failed to load eglGetNextFrameIdANDROID"); |
| } |
| |
| auto eglGetFrameTimestampsANDROID = reinterpret_cast<eglGetFrameTimestampsANDROID_type>( |
| eglGetProcAddress("eglGetFrameTimestampsANDROID")); |
| if (eglGetFrameTimestampsANDROID == nullptr) { |
| ALOGI("Failed to load eglGetFrameTimestampsANDROID"); |
| } |
| |
| auto egl = std::make_unique<EGL>(refreshPeriod, ConstructorTag{}); |
| egl->eglPresentationTimeANDROID = eglPresentationTimeANDROID; |
| egl->eglCreateSyncKHR = eglCreateSyncKHR; |
| egl->eglDestroySyncKHR = eglDestroySyncKHR; |
| egl->eglGetSyncAttribKHR = eglGetSyncAttribKHR; |
| egl->eglGetError = eglGetError; |
| egl->eglSurfaceAttrib = eglSurfaceAttrib; |
| egl->eglGetNextFrameIdANDROID = eglGetNextFrameIdANDROID; |
| egl->eglGetFrameTimestampsANDROID = eglGetFrameTimestampsANDROID; |
| return egl; |
| } |
| |
| void EGL::resetSyncFence(EGLDisplay display) { |
| std::lock_guard<std::mutex> lock(mSyncFenceMutex); |
| |
| if (mSyncFence != EGL_NO_SYNC_KHR) { |
| EGLBoolean result = eglDestroySyncKHR(display, mSyncFence); |
| if (result == EGL_FALSE) { |
| ALOGE("Failed to destroy sync fence"); |
| } |
| } |
| |
| mSyncFence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr); |
| } |
| |
| bool EGL::lastFrameIsComplete(EGLDisplay display) { |
| std::lock_guard<std::mutex> lock(mSyncFenceMutex); |
| |
| // This will be the case on the first frame |
| if (mSyncFence == EGL_NO_SYNC_KHR) { |
| return true; |
| } |
| |
| EGLint status = 0; |
| EGLBoolean result = eglGetSyncAttribKHR(display, mSyncFence, EGL_SYNC_STATUS_KHR, &status); |
| if (result == EGL_FALSE) { |
| ALOGE("Failed to get sync status"); |
| return true; |
| } |
| |
| if (status == EGL_SIGNALED_KHR) { |
| return true; |
| } else if (status == EGL_UNSIGNALED_KHR) { |
| return false; |
| } else { |
| ALOGE("Unexpected sync status: %d", status); |
| return true; |
| } |
| } |
| |
| bool EGL::setPresentationTime(EGLDisplay display, |
| EGLSurface surface, |
| std::chrono::steady_clock::time_point time) { |
| eglPresentationTimeANDROID(display, surface, time.time_since_epoch().count()); |
| return EGL_TRUE; |
| } |
| |
| bool EGL::statsSupported() { |
| return (eglGetNextFrameIdANDROID != nullptr && eglGetFrameTimestampsANDROID != nullptr); |
| } |
| |
| std::optional<EGLuint64KHR> EGL::getNextFrameId(EGLDisplay dpy, EGLSurface surface) { |
| if (eglGetNextFrameIdANDROID == nullptr) { |
| ALOGE("stats are not supported on this platform"); |
| return std::nullopt; |
| } |
| |
| EGLuint64KHR frameId; |
| EGLBoolean result = eglGetNextFrameIdANDROID(dpy, surface, &frameId); |
| if (result == EGL_FALSE) { |
| ALOGE("Failed to get next frame ID"); |
| return std::nullopt; |
| } |
| |
| return std::make_optional(frameId); |
| } |
| |
| std::unique_ptr<EGL::FrameTimestamps> EGL::getFrameTimestamps(EGLDisplay dpy, |
| EGLSurface surface, |
| EGLuint64KHR frameId) { |
| if (eglGetFrameTimestampsANDROID == nullptr) { |
| ALOGE("stats are not supported on this platform"); |
| return nullptr; |
| } |
| |
| const std::vector<EGLint> timestamps = { |
| EGL_REQUESTED_PRESENT_TIME_ANDROID, |
| EGL_RENDERING_COMPLETE_TIME_ANDROID, |
| EGL_COMPOSITION_LATCH_TIME_ANDROID, |
| EGL_DISPLAY_PRESENT_TIME_ANDROID, |
| }; |
| |
| std::vector<EGLnsecsANDROID> values(timestamps.size()); |
| |
| EGLBoolean result = eglGetFrameTimestampsANDROID(dpy, surface, frameId, |
| timestamps.size(), timestamps.data(), values.data()); |
| if (result == EGL_FALSE) { |
| EGLint reason = eglGetError(); |
| if (reason == EGL_BAD_SURFACE) { |
| eglSurfaceAttrib(dpy, surface, EGL_TIMESTAMPS_ANDROID, EGL_TRUE); |
| } else { |
| ALOGE("Failed to get timestamps for frame %llu", (unsigned long long) frameId); |
| } |
| return nullptr; |
| } |
| |
| // try again if we got some pending stats |
| for (auto i : values) { |
| if (i == EGL_TIMESTAMP_PENDING_ANDROID) return nullptr; |
| } |
| |
| std::unique_ptr<EGL::FrameTimestamps> frameTimestamps = |
| std::make_unique<EGL::FrameTimestamps>(); |
| frameTimestamps->requested = values[0]; |
| frameTimestamps->renderingCompleted = values[1]; |
| frameTimestamps->compositionLatched = values[2]; |
| frameTimestamps->presented = values[3]; |
| |
| return frameTimestamps; |
| } |
| |
| } // namespace swappy |