Files
UnrealEngine/Engine/Source/ThirdParty/GoogleGameSDK/gamesdk/games-frame-pacing/common/SwappyDisplayManager.cpp
2025-05-18 13:04:45 +08:00

195 lines
6.9 KiB
C++

/*
* Copyright 2019 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.
*/
#define LOG_TAG "SwappyDisplayManager"
#include "SwappyDisplayManager.h"
#include <android/looper.h>
#include <jni.h>
#include <map>
#include "JNIUtil.h"
#include "Settings.h"
#include "SwappyLog.h"
namespace swappy {
// Forward declaration of the native methods of Java SwappyDisplayManager class
extern "C" {
JNIEXPORT void JNICALL
Java_com_google_androidgamesdk_SwappyDisplayManager_nSetSupportedRefreshPeriods(
JNIEnv *env, jobject /* this */, jlong cookie, jlongArray refreshPeriods,
jintArray modeIds);
JNIEXPORT void JNICALL
Java_com_google_androidgamesdk_SwappyDisplayManager_nOnRefreshPeriodChanged(
JNIEnv *env, jobject /* this */, jlong cookie, jlong refreshPeriod,
jlong appOffset, jlong sfOffset);
}
const char *SwappyDisplayManager::SDM_CLASS =
"com/google/androidgamesdk/SwappyDisplayManager";
const JNINativeMethod SwappyDisplayManager::SDMNativeMethods[] = {
{"nSetSupportedRefreshPeriods", "(J[J[I)V",
(void
*)&Java_com_google_androidgamesdk_SwappyDisplayManager_nSetSupportedRefreshPeriods},
{"nOnRefreshPeriodChanged", "(JJJJ)V",
(void
*)&Java_com_google_androidgamesdk_SwappyDisplayManager_nOnRefreshPeriodChanged}};
bool SwappyDisplayManager::usesMinSdkOrLater(SdkVersion sdkVersion) {
return sdkVersion.sdkInt >= MIN_SDK_VERSION;
}
bool SwappyDisplayManager::useSwappyDisplayManager(SdkVersion sdkVersion) {
// SwappyDisplayManager uses APIs introduced in SDK 23 and we get spurious
// window messages for SDK < 28, so restrict here.
if (!usesMinSdkOrLater(sdkVersion)) {
return false;
}
// SDK 31 and above doesn't need SwappyDisplayManager as it has native
// support in NDK. SDK 30 has partial native support
// (AChoreographer_registerRefreshRateCallback) but lacks synchronization
// with DisplayManager to query app/sf offsets
return true;//!(sdkVersion.sdkInt >= 31 ||
//(sdkVersion.sdkInt == 30 && sdkVersion.previewSdkInt == 1));
}
SwappyDisplayManager::SwappyDisplayManager(JavaVM *vm, jobject mainActivity)
: mJVM(vm) {
if (!vm || !mainActivity) {
return;
}
JNIEnv *env;
mJVM->AttachCurrentThread(&env, nullptr);
jclass swappyDisplayManagerClass = gamesdk::loadClass(
env, mainActivity, SwappyDisplayManager::SDM_CLASS,
(JNINativeMethod *)SwappyDisplayManager::SDMNativeMethods,
SwappyDisplayManager::SDMNativeMethodsSize);
if (!swappyDisplayManagerClass) return;
jmethodID constructor = env->GetMethodID(
swappyDisplayManagerClass, "<init>", "(JLandroid/app/Activity;)V");
mSetPreferredDisplayModeId = env->GetMethodID(
swappyDisplayManagerClass, "setPreferredDisplayModeId", "(I)V");
mTerminate =
env->GetMethodID(swappyDisplayManagerClass, "terminate", "()V");
jobject swappyDisplayManager = env->NewObject(
swappyDisplayManagerClass, constructor, (jlong)this, mainActivity);
mJthis = env->NewGlobalRef(swappyDisplayManager);
mInitialized = true;
}
SwappyDisplayManager::~SwappyDisplayManager() {
JNIEnv *env;
mJVM->AttachCurrentThread(&env, nullptr);
env->CallVoidMethod(mJthis, mTerminate);
env->DeleteGlobalRef(mJthis);
}
std::shared_ptr<SwappyDisplayManager::RefreshPeriodMap>
SwappyDisplayManager::getSupportedRefreshPeriods() {
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(
lock, [&]() { return mSupportedRefreshPeriods.get() != nullptr; });
return mSupportedRefreshPeriods;
}
void SwappyDisplayManager::setPreferredDisplayModeId(int index) {
JNIEnv *env;
mJVM->AttachCurrentThread(&env, nullptr);
env->CallVoidMethod(mJthis, mSetPreferredDisplayModeId, index);
}
// Helper class to wrap JNI entry points to SwappyDisplayManager
class SwappyDisplayManagerJNI {
public:
static void onSetSupportedRefreshPeriods(
jlong, std::shared_ptr<SwappyDisplayManager::RefreshPeriodMap>);
static void onRefreshPeriodChanged(jlong, long, long, long);
};
void SwappyDisplayManagerJNI::onSetSupportedRefreshPeriods(
jlong cookie,
std::shared_ptr<SwappyDisplayManager::RefreshPeriodMap> refreshPeriods) {
auto *sDM = reinterpret_cast<SwappyDisplayManager *>(cookie);
std::lock_guard<std::mutex> lock(sDM->mMutex);
sDM->mSupportedRefreshPeriods = std::move(refreshPeriods);
sDM->mCondition.notify_one();
}
void SwappyDisplayManagerJNI::onRefreshPeriodChanged(jlong /*cookie*/,
long refreshPeriod,
long appOffset,
long sfOffset) {
SWAPPY_LOGV("onRefreshPeriodChanged: refresh rate: %.0fHz",
1e9f / refreshPeriod);
using std::chrono::nanoseconds;
Settings::DisplayTimings displayTimings;
displayTimings.refreshPeriod = nanoseconds(refreshPeriod);
displayTimings.appOffset = nanoseconds(appOffset);
displayTimings.sfOffset = nanoseconds(sfOffset);
Settings::getInstance()->setDisplayTimings(displayTimings);
}
extern "C" {
JNIEXPORT void JNICALL
Java_com_google_androidgamesdk_SwappyDisplayManager_nSetSupportedRefreshPeriods(
JNIEnv *env, jobject /* this */, jlong cookie, jlongArray refreshPeriods,
jintArray modeIds) {
int length = env->GetArrayLength(refreshPeriods);
auto refreshPeriodsMap =
std::make_shared<SwappyDisplayManager::RefreshPeriodMap>();
jlong *refreshPeriodsArr = env->GetLongArrayElements(refreshPeriods, 0);
jint *modeIdsArr = env->GetIntArrayElements(modeIds, 0);
for (int i = 0; i < length; i++) {
(*refreshPeriodsMap)[std::chrono::nanoseconds(refreshPeriodsArr[i])] =
modeIdsArr[i];
}
env->ReleaseLongArrayElements(refreshPeriods, refreshPeriodsArr, 0);
env->ReleaseIntArrayElements(modeIds, modeIdsArr, 0);
SwappyDisplayManagerJNI::onSetSupportedRefreshPeriods(cookie,
refreshPeriodsMap);
}
JNIEXPORT void JNICALL
Java_com_google_androidgamesdk_SwappyDisplayManager_nOnRefreshPeriodChanged(
JNIEnv *env, jobject /* this */, jlong cookie, jlong refreshPeriod,
jlong appOffset, jlong sfOffset) {
SwappyDisplayManagerJNI::onRefreshPeriodChanged(cookie, refreshPeriod,
appOffset, sfOffset);
}
} // extern "C"
} // namespace swappy