blob: 16664921cc23327d8510fef04d273296cd945746 [file] [log] [blame]
/*
* 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