Files
2025-05-18 13:04:45 +08:00

367 lines
9.7 KiB
C++

/*
* 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 "SwappyGL.h"
#define LOG_TAG "SwappyGL"
#include <cinttypes>
#include <cmath>
#include <cstdlib>
#include "SwappyLog.h"
#include "Thread.h"
#include "Trace.h"
#include "system_utils.h"
namespace swappy {
using std::chrono::milliseconds;
using std::chrono::nanoseconds;
std::mutex SwappyGL::sInstanceMutex;
std::unique_ptr<SwappyGL> SwappyGL::sInstance;
bool SwappyGL::init(JNIEnv *env, jobject jactivity) {
std::lock_guard<std::mutex> lock(sInstanceMutex);
if (sInstance) {
SWAPPY_LOGE("Attempted to initialize SwappyGL twice");
return false;
}
sInstance = std::make_unique<SwappyGL>(env, jactivity, ConstructorTag{});
if (!sInstance->mEnableSwappy) {
SWAPPY_LOGE("Failed to initialize SwappyGL");
return false;
}
return true;
}
void SwappyGL::onChoreographer(int64_t frameTimeNanos) {
TRACE_CALL();
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
swappy->mCommonBase.onChoreographer(frameTimeNanos);
}
bool SwappyGL::setWindow(ANativeWindow *window) {
TRACE_CALL();
SwappyGL *swappy = getInstance();
if (!swappy) {
SWAPPY_LOGE("Failed to get SwappyGL instance in setWindow");
return false;
}
swappy->mCommonBase.setANativeWindow(window);
return true;
}
bool SwappyGL::swap(EGLDisplay display, EGLSurface surface) {
TRACE_CALL();
SwappyGL *swappy = getInstance();
if (!swappy) {
return EGL_FALSE;
}
if (swappy->enabled()) {
return swappy->swapInternal(display, surface);
} else {
return swappy->getEgl()->swapBuffers(display, surface) == EGL_TRUE;
}
}
bool SwappyGL::lastFrameIsComplete(EGLDisplay display) {
bool pipelineMode = (mCommonBase.getCurrentPipelineMode() ==
SwappyCommon::PipelineMode::On);
if (!getEgl()->lastFrameIsComplete(display, pipelineMode)) {
gamesdk::ScopedTrace trace("lastFrameIncomplete");
SWAPPY_LOGV("lastFrameIncomplete");
return false;
}
return true;
}
bool SwappyGL::swapInternal(EGLDisplay display, EGLSurface surface) {
const SwappyCommon::SwapHandlers handlers = {
.lastFrameIsComplete = [&]() { return lastFrameIsComplete(display); },
.getPrevFrameGpuTime =
[&]() { return getEgl()->getFencePendingTime(); },
};
getEgl()->insertSyncFence(display);
mCommonBase.onPreSwap(handlers);
if (mCommonBase.needToSetPresentationTime()) {
bool setPresentationTimeResult = setPresentationTime(display, surface);
if (!setPresentationTimeResult) {
return setPresentationTimeResult;
}
}
bool swapBuffersResult =
(getEgl()->swapBuffers(display, surface) == EGL_TRUE);
mCommonBase.onPostSwap(handlers);
return swapBuffersResult;
}
void SwappyGL::addTracer(const SwappyTracer *tracer) {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->enabled() && tracer != nullptr)
swappy->mCommonBase.addTracerCallbacks(*tracer);
}
void SwappyGL::removeTracer(const SwappyTracer *tracer) {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->enabled() && tracer != nullptr)
swappy->mCommonBase.removeTracerCallbacks(*tracer);
}
nanoseconds SwappyGL::getSwapDuration() {
SwappyGL *swappy = getInstance();
if (!swappy || !swappy->enabled()) {
return -1ns;
}
return swappy->mCommonBase.getSwapDuration();
};
void SwappyGL::setAutoSwapInterval(bool enabled) {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->enabled()) swappy->mCommonBase.setAutoSwapInterval(enabled);
}
void SwappyGL::setAutoPipelineMode(bool enabled) {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->enabled()) swappy->mCommonBase.setAutoPipelineMode(enabled);
}
void SwappyGL::setMaxAutoSwapDuration(std::chrono::nanoseconds maxDuration) {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->enabled())
swappy->mCommonBase.setMaxAutoSwapDuration(maxDuration);
}
void SwappyGL::enableStats(bool enabled) {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->mFrameStatistics) {
swappy->mFrameStatistics->enableStats(enabled);
}
}
void SwappyGL::recordFrameStart(EGLDisplay display, EGLSurface surface) {
TRACE_CALL();
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->mFrameStatistics) {
swappy->mFrameStatistics->capture(display, surface);
}
}
void SwappyGL::getStats(SwappyStats *stats) {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->mFrameStatistics) {
*stats = swappy->mFrameStatistics->getStats();
}
}
void SwappyGL::clearStats() {
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
if (swappy->mFrameStatistics) {
swappy->mFrameStatistics->clearStats();
}
}
SwappyGL *SwappyGL::getInstance() {
std::lock_guard<std::mutex> lock(sInstanceMutex);
return sInstance.get();
}
bool SwappyGL::isEnabled() {
SwappyGL *swappy = getInstance();
if (!swappy) {
// This is a case of error.
// We do not log anything here, so that we do not spam
// the user when this function is called each frame.
return false;
}
return swappy->enabled();
}
void SwappyGL::destroyInstance() {
std::lock_guard<std::mutex> lock(sInstanceMutex);
sInstance.reset();
}
void SwappyGL::setFenceTimeout(std::chrono::nanoseconds t) {
SwappyGL *swappy = getInstance();
if (!swappy || !swappy->enabled()) {
return;
}
swappy->mCommonBase.setFenceTimeout(t);
}
std::chrono::nanoseconds SwappyGL::getFenceTimeout() {
SwappyGL *swappy = getInstance();
if (!swappy || !swappy->enabled()) {
return std::chrono::nanoseconds(0);
}
return swappy->mCommonBase.getFenceTimeout();
}
EGL *SwappyGL::getEgl() {
static thread_local EGL *egl = nullptr;
if (!egl) {
std::lock_guard<std::mutex> lock(mEglMutex);
egl = mEgl.get();
}
return egl;
}
SwappyGL::SwappyGL(JNIEnv *env, jobject jactivity, ConstructorTag)
: mFrameStatistics(nullptr), mCommonBase(env, jactivity) {
{
std::lock_guard<std::mutex> lock(mEglMutex);
mEgl = EGL::create(mCommonBase.getFenceTimeout());
if (!mEgl) {
SWAPPY_LOGE("Failed to load EGL functions");
mEnableSwappy = false;
return;
}
}
if (!mCommonBase.isValid()) {
SWAPPY_LOGE("SwappyCommon could not initialize correctly.");
mEnableSwappy = false;
return;
}
mEnableSwappy =
!gamesdk::GetSystemPropAsBool(SWAPPY_SYSTEM_PROP_KEY_DISABLE, false);
if (!enabled()) {
SWAPPY_LOGI("Swappy is disabled");
return;
}
if (mEgl->statsSupported()) {
mFrameStatistics =
std::make_unique<FrameStatisticsGL>(*mEgl, mCommonBase);
mCommonBase.setLastLatencyRecordedCallback(
[this]() { return this->mFrameStatistics->lastLatencyRecorded(); });
} else {
SWAPPY_LOGI("stats are not suppored on this platform");
}
SWAPPY_LOGI("SwappyGL initialized successfully");
}
bool SwappyGL::setPresentationTime(EGLDisplay display, EGLSurface surface) {
TRACE_CALL();
auto displayTimings = Settings::getInstance()->getDisplayTimings();
// if we are too close to the vsync, there is no need to set presentation
// time
if ((mCommonBase.getPresentationTime() - std::chrono::steady_clock::now()) <
(mCommonBase.getRefreshPeriod() - displayTimings.sfOffset)) {
return EGL_TRUE;
}
return getEgl()->setPresentationTime(display, surface,
mCommonBase.getPresentationTime());
}
void SwappyGL::setBufferStuffingFixWait(int32_t n_frames) {
TRACE_CALL();
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
swappy->mCommonBase.setBufferStuffingFixWait(n_frames);
}
int SwappyGL::getSupportedRefreshPeriodsNS(uint64_t *out_refreshrates,
int allocated_entries) {
TRACE_CALL();
SwappyGL *swappy = getInstance();
if (!swappy) {
return -1;
}
return swappy->mCommonBase.getSupportedRefreshPeriodsNS(out_refreshrates,
allocated_entries);
}
void SwappyGL::resetFramePacing() {
TRACE_CALL();
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
swappy->mCommonBase.resetFramePacing();
}
void SwappyGL::enableFramePacing(bool enable) {
TRACE_INT("enableFramePacing", (int)enable);
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
swappy->mCommonBase.enableFramePacing(enable);
}
void SwappyGL::enableBlockingWait(bool enable) {
TRACE_INT("enableBlockingWait", (int)enable);
SwappyGL *swappy = getInstance();
if (!swappy) {
return;
}
swappy->mCommonBase.enableBlockingWait(enable);
}
} // namespace swappy