Compare commits
50 Commits
android-22
...
android-23
Author | SHA1 | Date | |
---|---|---|---|
28ffc4fe25 | |||
0801807e05 | |||
f57363e72e | |||
3b65148d6b | |||
91d08325b8 | |||
205b1e409c | |||
f771c3ef31 | |||
b0fedab4bb | |||
b634e5f270 | |||
46c8ca59cd | |||
625aba8de7 | |||
4cccbe7989 | |||
5da55cbac9 | |||
81cc4df1f9 | |||
25f3d358b1 | |||
a3c8bb251d | |||
327533be1f | |||
61ea2115c7 | |||
108a72ea8a | |||
fb3ef957bb | |||
78f72b3bf5 | |||
6baf965777 | |||
3f86b339f3 | |||
32d38a5df6 | |||
3ac46aeced | |||
58cf2ee1f9 | |||
c74b5f9ee6 | |||
11a8ef6640 | |||
b51b47e707 | |||
35e3c68028 | |||
818721d12d | |||
2c421a7046 | |||
a595e9e8a7 | |||
10cf058518 | |||
9bdf09bd76 | |||
d4de04584f | |||
dd2918efd8 | |||
2ed9586130 | |||
b78900e956 | |||
60ee29aac3 | |||
b90eff4bc6 | |||
0c2e5b64c9 | |||
9568b310be | |||
2b1dd3bef5 | |||
453091f611 | |||
80de01a5b4 | |||
442aad9b27 | |||
8e0f97ac96 | |||
345d691328 | |||
41149d061d |
@ -307,7 +307,7 @@ find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
|
||||
if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
|
||||
find_package(Vulkan 1.3.274 REQUIRED)
|
||||
find_package(VulkanHeaders 1.3.274 REQUIRED)
|
||||
endif()
|
||||
|
||||
if (NOT YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
|
||||
|
18
README.md
18
README.md
@ -1,3 +1,21 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [12461](https://github.com/yuzu-emu/yuzu-android//pull/12461) | [`4c08a0e6d`](https://github.com/yuzu-emu/yuzu-android//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
|
||||
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12756](https://github.com/yuzu-emu/yuzu-android//pull/12756) | [`cd3de0848`](https://github.com/yuzu-emu/yuzu-android//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12873](https://github.com/yuzu-emu/yuzu-android//pull/12873) | [`023c3aa65`](https://github.com/yuzu-emu/yuzu-android//pull/12873/files) | GPU: Implement channel scheduling. | [FernandoS27](https://github.com/FernandoS27/) | Yes |
|
||||
| [12903](https://github.com/yuzu-emu/yuzu-android//pull/12903) | [`f296a9ce9`](https://github.com/yuzu-emu/yuzu-android//pull/12903/files) | shader_recompiler: use only ConstOffset for OpImageFetch | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12905](https://github.com/yuzu-emu/yuzu-android//pull/12905) | [`5eb5c9675`](https://github.com/yuzu-emu/yuzu-android//pull/12905/files) | nvnflinger: release buffers before presentation sleep | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
| [12914](https://github.com/yuzu-emu/yuzu-android//pull/12914) | [`3a6d8ae2c`](https://github.com/yuzu-emu/yuzu-android//pull/12914/files) | VideoCore Refactor Part 1. | [FernandoS27](https://github.com/FernandoS27/) | Yes |
|
||||
| [12915](https://github.com/yuzu-emu/yuzu-android//pull/12915) | [`8113f55f4`](https://github.com/yuzu-emu/yuzu-android//pull/12915/files) | dmnt: cheats: Update cheat vm to latest version | [german77](https://github.com/german77/) | Yes |
|
||||
| [12920](https://github.com/yuzu-emu/yuzu-android//pull/12920) | [`62fc6d5c3`](https://github.com/yuzu-emu/yuzu-android//pull/12920/files) | android: Move JNI setup and helpers to common | [t895](https://github.com/t895/) | Yes |
|
||||
| [12924](https://github.com/yuzu-emu/yuzu-android//pull/12924) | [`0e950baf4`](https://github.com/yuzu-emu/yuzu-android//pull/12924/files) | typed_address: test values are unsigned | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
-----
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
@ -164,6 +164,7 @@ else()
|
||||
|
||||
if (MINGW)
|
||||
add_definitions(-DMINGW_HAS_SECURE_API)
|
||||
add_compile_options("-msse4.1")
|
||||
|
||||
if (MINGW_STATIC_BUILD)
|
||||
add_definitions(-DQT_STATICPLUGIN)
|
||||
|
@ -2,14 +2,8 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
add_library(yuzu-android SHARED
|
||||
android_common/android_common.cpp
|
||||
android_common/android_common.h
|
||||
applets/software_keyboard.cpp
|
||||
applets/software_keyboard.h
|
||||
emu_window/emu_window.cpp
|
||||
emu_window/emu_window.h
|
||||
id_cache.cpp
|
||||
id_cache.h
|
||||
native.cpp
|
||||
native.h
|
||||
native_config.cpp
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/drivers/touch_screen.h"
|
||||
#include "input_common/drivers/virtual_amiibo.h"
|
||||
@ -60,7 +61,8 @@ void EmuWindow_Android::OnRemoveNfcTag() {
|
||||
|
||||
void EmuWindow_Android::OnFrameDisplayed() {
|
||||
if (!m_first_frame) {
|
||||
EmulationSession::GetInstance().OnEmulationStarted();
|
||||
Common::Android::RunJNIOnFiber<void>(
|
||||
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
|
||||
m_first_frame = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/android/android_common.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/loader/nro.h"
|
||||
#include "jni.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "native.h"
|
||||
|
||||
struct RomMetadata {
|
||||
@ -79,7 +78,7 @@ extern "C" {
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||
GetJString(env, jpath), FileSys::OpenMode::Read);
|
||||
Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read);
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
@ -104,27 +103,31 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title);
|
||||
return Common::Android::ToJString(
|
||||
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title);
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId));
|
||||
return Common::Android::ToJString(
|
||||
env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId));
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer);
|
||||
return Common::Android::ToJString(
|
||||
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer);
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
|
||||
jstring jpath, jboolean jreload) {
|
||||
return ToJString(env, GetRomMetadata(GetJString(env, jpath), jreload).version);
|
||||
return Common::Android::ToJString(
|
||||
env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version);
|
||||
}
|
||||
|
||||
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon;
|
||||
auto icon_data = GetRomMetadata(Common::Android::GetJString(env, jpath)).icon;
|
||||
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
||||
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
||||
reinterpret_cast<jbyte*>(icon_data.data()));
|
||||
@ -133,7 +136,8 @@ jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobje
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew);
|
||||
return static_cast<jboolean>(
|
||||
GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <frontend_common/content_manager.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/fs/path_util.h"
|
||||
@ -57,8 +59,6 @@
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
#include "hid_core/hid_core.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/id_cache.h"
|
||||
#include "jni/native.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
@ -228,7 +228,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
|
||||
|
||||
// Initialize system.
|
||||
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
|
||||
jauto android_keyboard = std::make_unique<Common::Android::SoftwareKeyboard::AndroidKeyboard>();
|
||||
m_software_keyboard = android_keyboard.get();
|
||||
m_system.SetShuttingDown(false);
|
||||
m_system.ApplySettings();
|
||||
@ -411,37 +411,39 @@ void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
||||
controller->Disconnect();
|
||||
}
|
||||
|
||||
SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||
return m_software_keyboard;
|
||||
}
|
||||
|
||||
void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
|
||||
int max) {
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
||||
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
||||
JNIEnv* env = Common::Android::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(Common::Android::GetDiskCacheProgressClass(),
|
||||
Common::Android::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
||||
static_cast<jint>(progress), static_cast<jint>(max));
|
||||
}
|
||||
|
||||
void EmulationSession::OnEmulationStarted() {
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted());
|
||||
JNIEnv* env = Common::Android::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
|
||||
Common::Android::GetOnEmulationStarted());
|
||||
}
|
||||
|
||||
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(),
|
||||
static_cast<jint>(result));
|
||||
JNIEnv* env = Common::Android::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
|
||||
Common::Android::GetOnEmulationStopped(), static_cast<jint>(result));
|
||||
}
|
||||
|
||||
void EmulationSession::ChangeProgram(std::size_t program_index) {
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(),
|
||||
JNIEnv* env = Common::Android::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
|
||||
Common::Android::GetOnProgramChanged(),
|
||||
static_cast<jint>(program_index));
|
||||
}
|
||||
|
||||
u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
|
||||
auto program_id_string = GetJString(env, jprogramId);
|
||||
auto program_id_string = Common::Android::GetJString(env, jprogramId);
|
||||
try {
|
||||
return std::stoull(program_id_string);
|
||||
} catch (...) {
|
||||
@ -491,7 +493,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
|
||||
[[maybe_unused]] jstring j_directory) {
|
||||
Common::FS::SetAppDirectory(GetJString(env, j_directory));
|
||||
Common::FS::SetAppDirectory(Common::Android::GetJString(env, j_directory));
|
||||
}
|
||||
|
||||
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
|
||||
@ -501,21 +503,22 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
||||
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
|
||||
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
|
||||
ToJDouble(env, max), ToJDouble(env, progress));
|
||||
return GetJBoolean(env, jwasCancelled);
|
||||
Common::Android::ToJDouble(env, max),
|
||||
Common::Android::ToJDouble(env, progress));
|
||||
return Common::Android::GetJBoolean(env, jwasCancelled);
|
||||
};
|
||||
|
||||
return static_cast<int>(
|
||||
ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
|
||||
*EmulationSession::GetInstance().System().GetFilesystem(),
|
||||
GetJString(env, j_file), callback));
|
||||
Common::Android::GetJString(env, j_file), callback));
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId,
|
||||
jstring jupdatePath) {
|
||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
std::string updatePath = GetJString(env, jupdatePath);
|
||||
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||
updatePath, FileSys::OpenMode::Read));
|
||||
@ -538,8 +541,10 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* e
|
||||
jstring custom_driver_name,
|
||||
jstring file_redirect_dir) {
|
||||
EmulationSession::GetInstance().InitializeGpuDriver(
|
||||
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
|
||||
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
|
||||
Common::Android::GetJString(env, hook_lib_dir),
|
||||
Common::Android::GetJString(env, custom_driver_dir),
|
||||
Common::Android::GetJString(env, custom_driver_name),
|
||||
Common::Android::GetJString(env, file_redirect_dir));
|
||||
}
|
||||
|
||||
[[maybe_unused]] static bool CheckKgslPresent() {
|
||||
@ -566,7 +571,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
|
||||
JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
|
||||
const char* file_redirect_dir_{};
|
||||
int featureFlags{};
|
||||
std::string hook_lib_dir = GetJString(env, j_hook_lib_dir);
|
||||
std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
|
||||
auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
||||
nullptr, nullptr, file_redirect_dir_, nullptr);
|
||||
auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
|
||||
@ -587,9 +592,10 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
|
||||
fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version),
|
||||
VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version));
|
||||
|
||||
jobjectArray j_driver_info =
|
||||
env->NewObjectArray(2, IDCache::GetStringClass(), ToJString(env, version_string));
|
||||
env->SetObjectArrayElement(j_driver_info, 1, ToJString(env, device.GetDriverName()));
|
||||
jobjectArray j_driver_info = env->NewObjectArray(
|
||||
2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string));
|
||||
env->SetObjectArrayElement(j_driver_info, 1,
|
||||
Common::Android::ToJString(env, device.GetDriverName()));
|
||||
return j_driver_info;
|
||||
}
|
||||
|
||||
@ -742,15 +748,15 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
|
||||
if (Settings::IsNceEnabled()) {
|
||||
return ToJString(env, "NCE");
|
||||
return Common::Android::ToJString(env, "NCE");
|
||||
}
|
||||
|
||||
return ToJString(env, "JIT");
|
||||
return Common::Android::ToJString(env, "JIT");
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
|
||||
return ToJString(env,
|
||||
EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
|
||||
return Common::Android::ToJString(
|
||||
env, EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
||||
@ -764,13 +770,14 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logSettings(JNIEnv* env, jobject jobj
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
|
||||
jint j_program_index,
|
||||
jboolean j_frontend_initiated) {
|
||||
const std::string path = GetJString(env, j_path);
|
||||
const std::string path = Common::Android::GetJString(env, j_path);
|
||||
|
||||
const Core::SystemResultStatus result{
|
||||
RunEmulation(path, j_program_index, j_frontend_initiated)};
|
||||
if (result != Core::SystemResultStatus::Success) {
|
||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
||||
IDCache::GetExitEmulationActivity(), static_cast<int>(result));
|
||||
env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
|
||||
Common::Android::GetExitEmulationActivity(),
|
||||
static_cast<int>(result));
|
||||
}
|
||||
}
|
||||
|
||||
@ -781,7 +788,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass cla
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz,
|
||||
jstring j_text) {
|
||||
const std::u16string input = Common::UTF8ToUTF16(GetJString(env, j_text));
|
||||
const std::u16string input = Common::UTF8ToUTF16(Common::Android::GetJString(env, j_text));
|
||||
EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input);
|
||||
}
|
||||
|
||||
@ -815,16 +822,16 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j
|
||||
auto bis_system =
|
||||
EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
return ToJString(env, "");
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
|
||||
auto applet_nca =
|
||||
bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program);
|
||||
if (!applet_nca) {
|
||||
return ToJString(env, "");
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
|
||||
return ToJString(env, applet_nca->GetFullPath());
|
||||
return Common::Android::ToJString(env, applet_nca->GetFullPath());
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz,
|
||||
@ -857,7 +864,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env,
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
|
||||
jstring jpath,
|
||||
jstring jprogramId) {
|
||||
const auto path = GetJString(env, jpath);
|
||||
const auto path = Common::Android::GetJString(env, jpath);
|
||||
const auto vFile =
|
||||
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
||||
if (vFile == nullptr) {
|
||||
@ -875,14 +882,15 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
|
||||
|
||||
auto patches = pm.GetPatches(update_raw);
|
||||
jobjectArray jpatchArray =
|
||||
env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr);
|
||||
env->NewObjectArray(patches.size(), Common::Android::GetPatchClass(), nullptr);
|
||||
int i = 0;
|
||||
for (const auto& patch : patches) {
|
||||
jobject jpatch = env->NewObject(
|
||||
IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled,
|
||||
ToJString(env, patch.name), ToJString(env, patch.version),
|
||||
static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)),
|
||||
ToJString(env, std::to_string(patch.title_id)));
|
||||
Common::Android::GetPatchClass(), Common::Android::GetPatchConstructor(), patch.enabled,
|
||||
Common::Android::ToJString(env, patch.name),
|
||||
Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
|
||||
Common::Android::ToJString(env, std::to_string(patch.program_id)),
|
||||
Common::Android::ToJString(env, std::to_string(patch.title_id)));
|
||||
env->SetObjectArrayElement(jpatchArray, i, jpatch);
|
||||
++i;
|
||||
}
|
||||
@ -906,7 +914,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
|
||||
jstring jname) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||
program_id, GetJString(env, jname));
|
||||
program_id, Common::Android::GetJString(env, jname));
|
||||
}
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
|
||||
@ -917,17 +925,18 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEn
|
||||
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
|
||||
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
|
||||
ToJDouble(env, max), ToJDouble(env, progress));
|
||||
return GetJBoolean(env, jwasCancelled);
|
||||
Common::Android::ToJDouble(env, max),
|
||||
Common::Android::ToJDouble(env, progress));
|
||||
return Common::Android::GetJBoolean(env, jwasCancelled);
|
||||
};
|
||||
|
||||
auto& session = EmulationSession::GetInstance();
|
||||
std::vector<std::string> result = ContentManager::VerifyInstalledContents(
|
||||
session.System(), *session.GetContentProvider(), callback);
|
||||
jobjectArray jresult =
|
||||
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
|
||||
jobjectArray jresult = env->NewObjectArray(result.size(), Common::Android::GetStringClass(),
|
||||
Common::Android::ToJString(env, ""));
|
||||
for (size_t i = 0; i < result.size(); ++i) {
|
||||
env->SetObjectArrayElement(jresult, i, ToJString(env, result[i]));
|
||||
env->SetObjectArrayElement(jresult, i, Common::Android::ToJString(env, result[i]));
|
||||
}
|
||||
return jresult;
|
||||
}
|
||||
@ -939,19 +948,20 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
|
||||
jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
|
||||
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
|
||||
ToJDouble(env, max), ToJDouble(env, progress));
|
||||
return GetJBoolean(env, jwasCancelled);
|
||||
Common::Android::ToJDouble(env, max),
|
||||
Common::Android::ToJDouble(env, progress));
|
||||
return Common::Android::GetJBoolean(env, jwasCancelled);
|
||||
};
|
||||
auto& session = EmulationSession::GetInstance();
|
||||
return static_cast<jint>(
|
||||
ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback));
|
||||
return static_cast<jint>(ContentManager::VerifyGameContents(
|
||||
session.System(), Common::Android::GetJString(env, jpath), callback));
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
if (program_id == 0) {
|
||||
return ToJString(env, "");
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
|
||||
auto& system = EmulationSession::GetInstance().System();
|
||||
@ -968,7 +978,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
|
||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
||||
program_id, user_id->AsU128(), 0);
|
||||
return ToJString(env, user_save_data_path);
|
||||
return Common::Android::ToJString(env, user_save_data_path);
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env,
|
||||
@ -981,12 +991,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIE
|
||||
|
||||
const auto user_save_data_root =
|
||||
FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture);
|
||||
return ToJString(env, user_save_data_root);
|
||||
return Common::Android::ToJString(env, user_save_data_root);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
|
||||
jstring jpath) {
|
||||
EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath));
|
||||
EmulationSession::GetInstance().ConfigureFilesystemProvider(
|
||||
Common::Android::GetJString(env, jpath));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) {
|
||||
|
@ -2,13 +2,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
#include "common/android/applets/software_keyboard.h"
|
||||
#include "common/detached_tasks.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "frontend_common/content_manager.h"
|
||||
#include "jni/applets/software_keyboard.h"
|
||||
#include "jni/emu_window/emu_window.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
@ -54,7 +54,7 @@ public:
|
||||
void SetDeviceType([[maybe_unused]] int index, int type);
|
||||
void OnGamepadConnectEvent([[maybe_unused]] int index);
|
||||
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
|
||||
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
|
||||
|
||||
static void OnEmulationStarted();
|
||||
|
||||
@ -79,7 +79,7 @@ private:
|
||||
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
|
||||
std::atomic<bool> m_is_running = false;
|
||||
std::atomic<bool> m_is_paused = false;
|
||||
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||
int m_applet_id{1};
|
||||
|
||||
|
@ -8,11 +8,11 @@
|
||||
|
||||
#include "android_config.h"
|
||||
#include "android_settings.h"
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "frontend_common/config.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/id_cache.h"
|
||||
#include "native.h"
|
||||
|
||||
std::unique_ptr<AndroidConfig> global_config;
|
||||
@ -20,7 +20,7 @@ std::unique_ptr<AndroidConfig> per_game_config;
|
||||
|
||||
template <typename T>
|
||||
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||
auto key = GetJString(env, jkey);
|
||||
auto key = Common::Android::GetJString(env, jkey);
|
||||
auto basic_setting = Settings::values.linkage.by_key[key];
|
||||
if (basic_setting != 0) {
|
||||
return static_cast<Settings::Setting<T>*>(basic_setting);
|
||||
@ -55,7 +55,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv*
|
||||
jstring jprogramId,
|
||||
jstring jfileName) {
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
auto file_name = GetJString(env, jfileName);
|
||||
auto file_name = Common::Android::GetJString(env, jfileName);
|
||||
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
|
||||
per_game_config =
|
||||
std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig);
|
||||
@ -186,9 +186,9 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobjec
|
||||
jboolean needGlobal) {
|
||||
auto setting = getSetting<std::string>(env, jkey);
|
||||
if (setting == nullptr) {
|
||||
return ToJString(env, "");
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
return ToJString(env, setting->GetValue(static_cast<bool>(needGlobal)));
|
||||
return Common::Android::ToJString(env, setting->GetValue(static_cast<bool>(needGlobal)));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
|
||||
@ -198,7 +198,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject o
|
||||
return;
|
||||
}
|
||||
|
||||
setting->SetValue(GetJString(env, value));
|
||||
setting->SetValue(Common::Android::GetJString(env, value));
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
|
||||
@ -214,13 +214,13 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* e
|
||||
jstring jkey) {
|
||||
auto setting = getSetting<std::string>(env, jkey);
|
||||
if (setting == nullptr) {
|
||||
return ToJString(env, "");
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
if (setting->PairedSetting() == nullptr) {
|
||||
return ToJString(env, "");
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
|
||||
return ToJString(env, setting->PairedSetting()->GetLabel());
|
||||
return Common::Android::ToJString(env, setting->PairedSetting()->GetLabel());
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSwitchable(JNIEnv* env, jobject obj,
|
||||
@ -262,21 +262,21 @@ jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultToString(JNIEnv* en
|
||||
jstring jkey) {
|
||||
auto setting = getSetting<std::string>(env, jkey);
|
||||
if (setting != nullptr) {
|
||||
return ToJString(env, setting->DefaultToString());
|
||||
return Common::Android::ToJString(env, setting->DefaultToString());
|
||||
}
|
||||
return ToJString(env, "");
|
||||
return Common::Android::ToJString(env, "");
|
||||
}
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) {
|
||||
jclass gameDirClass = IDCache::GetGameDirClass();
|
||||
jmethodID gameDirConstructor = IDCache::GetGameDirConstructor();
|
||||
jclass gameDirClass = Common::Android::GetGameDirClass();
|
||||
jmethodID gameDirConstructor = Common::Android::GetGameDirConstructor();
|
||||
jobjectArray jgameDirArray =
|
||||
env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr);
|
||||
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
|
||||
jobject jgameDir =
|
||||
env->NewObject(gameDirClass, gameDirConstructor,
|
||||
ToJString(env, AndroidSettings::values.game_dirs[i].path),
|
||||
static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan));
|
||||
jobject jgameDir = env->NewObject(
|
||||
gameDirClass, gameDirConstructor,
|
||||
Common::Android::ToJString(env, AndroidSettings::values.game_dirs[i].path),
|
||||
static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan));
|
||||
env->SetObjectArrayElement(jgameDirArray, i, jgameDir);
|
||||
}
|
||||
return jgameDirArray;
|
||||
@ -292,14 +292,14 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject
|
||||
}
|
||||
|
||||
jobject dir = env->GetObjectArrayElement(gameDirs, 0);
|
||||
jclass gameDirClass = IDCache::GetGameDirClass();
|
||||
jclass gameDirClass = Common::Android::GetGameDirClass();
|
||||
jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
|
||||
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
|
||||
for (int i = 0; i < size; ++i) {
|
||||
dir = env->GetObjectArrayElement(gameDirs, i);
|
||||
jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField));
|
||||
jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField);
|
||||
std::string uriString = GetJString(env, juriString);
|
||||
std::string uriString = Common::Android::GetJString(env, juriString);
|
||||
AndroidSettings::values.game_dirs.push_back(
|
||||
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
|
||||
}
|
||||
@ -307,13 +307,13 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj,
|
||||
jobject gameDir) {
|
||||
jclass gameDirClass = IDCache::GetGameDirClass();
|
||||
jclass gameDirClass = Common::Android::GetGameDirClass();
|
||||
jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
|
||||
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
|
||||
|
||||
jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField));
|
||||
jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField);
|
||||
std::string uriString = GetJString(env, juriString);
|
||||
std::string uriString = Common::Android::GetJString(env, juriString);
|
||||
AndroidSettings::values.game_dirs.push_back(
|
||||
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)});
|
||||
}
|
||||
@ -323,9 +323,11 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv
|
||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
||||
jobjectArray jdisabledAddonsArray =
|
||||
env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, ""));
|
||||
env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
|
||||
Common::Android::ToJString(env, ""));
|
||||
for (size_t i = 0; i < disabledAddons.size(); ++i) {
|
||||
env->SetObjectArrayElement(jdisabledAddonsArray, i, ToJString(env, disabledAddons[i]));
|
||||
env->SetObjectArrayElement(jdisabledAddonsArray, i,
|
||||
Common::Android::ToJString(env, disabledAddons[i]));
|
||||
}
|
||||
return jdisabledAddonsArray;
|
||||
}
|
||||
@ -339,7 +341,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
|
||||
const int size = env->GetArrayLength(jdisabledAddons);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, i));
|
||||
disabled_addons.push_back(GetJString(env, jaddon));
|
||||
disabled_addons.push_back(Common::Android::GetJString(env, jaddon));
|
||||
}
|
||||
Settings::values.disabled_addons[program_id] = disabled_addons;
|
||||
}
|
||||
@ -348,26 +350,27 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JN
|
||||
jobject obj) {
|
||||
jobjectArray joverlayControlDataArray =
|
||||
env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
|
||||
IDCache::GetOverlayControlDataClass(), nullptr);
|
||||
Common::Android::GetOverlayControlDataClass(), nullptr);
|
||||
for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
|
||||
const auto& control_data = AndroidSettings::values.overlay_control_data[i];
|
||||
jobject jlandscapePosition =
|
||||
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
||||
ToJDouble(env, control_data.landscape_position.first),
|
||||
ToJDouble(env, control_data.landscape_position.second));
|
||||
env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
|
||||
Common::Android::ToJDouble(env, control_data.landscape_position.first),
|
||||
Common::Android::ToJDouble(env, control_data.landscape_position.second));
|
||||
jobject jportraitPosition =
|
||||
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
||||
ToJDouble(env, control_data.portrait_position.first),
|
||||
ToJDouble(env, control_data.portrait_position.second));
|
||||
env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
|
||||
Common::Android::ToJDouble(env, control_data.portrait_position.first),
|
||||
Common::Android::ToJDouble(env, control_data.portrait_position.second));
|
||||
jobject jfoldablePosition =
|
||||
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
||||
ToJDouble(env, control_data.foldable_position.first),
|
||||
ToJDouble(env, control_data.foldable_position.second));
|
||||
env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
|
||||
Common::Android::ToJDouble(env, control_data.foldable_position.first),
|
||||
Common::Android::ToJDouble(env, control_data.foldable_position.second));
|
||||
|
||||
jobject jcontrolData = env->NewObject(
|
||||
IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
|
||||
ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
|
||||
jportraitPosition, jfoldablePosition);
|
||||
jobject jcontrolData =
|
||||
env->NewObject(Common::Android::GetOverlayControlDataClass(),
|
||||
Common::Android::GetOverlayControlDataConstructor(),
|
||||
Common::Android::ToJString(env, control_data.id), control_data.enabled,
|
||||
jlandscapePosition, jportraitPosition, jfoldablePosition);
|
||||
env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
|
||||
}
|
||||
return joverlayControlDataArray;
|
||||
@ -384,33 +387,41 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
|
||||
jstring jidString = static_cast<jstring>(
|
||||
env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
|
||||
jstring jidString = static_cast<jstring>(env->GetObjectField(
|
||||
joverlayControlData, Common::Android::GetOverlayControlDataIdField()));
|
||||
bool enabled = static_cast<bool>(env->GetBooleanField(
|
||||
joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
|
||||
joverlayControlData, Common::Android::GetOverlayControlDataEnabledField()));
|
||||
|
||||
jobject jlandscapePosition = env->GetObjectField(
|
||||
joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
|
||||
joverlayControlData, Common::Android::GetOverlayControlDataLandscapePositionField());
|
||||
std::pair<double, double> landscape_position = std::make_pair(
|
||||
GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
|
||||
GetJDouble(env,
|
||||
env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
|
||||
Common::Android::GetJDouble(
|
||||
env, env->GetObjectField(jlandscapePosition, Common::Android::GetPairFirstField())),
|
||||
Common::Android::GetJDouble(
|
||||
env,
|
||||
env->GetObjectField(jlandscapePosition, Common::Android::GetPairSecondField())));
|
||||
|
||||
jobject jportraitPosition = env->GetObjectField(
|
||||
joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
|
||||
joverlayControlData, Common::Android::GetOverlayControlDataPortraitPositionField());
|
||||
std::pair<double, double> portrait_position = std::make_pair(
|
||||
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
|
||||
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
|
||||
Common::Android::GetJDouble(
|
||||
env, env->GetObjectField(jportraitPosition, Common::Android::GetPairFirstField())),
|
||||
Common::Android::GetJDouble(
|
||||
env,
|
||||
env->GetObjectField(jportraitPosition, Common::Android::GetPairSecondField())));
|
||||
|
||||
jobject jfoldablePosition = env->GetObjectField(
|
||||
joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
|
||||
joverlayControlData, Common::Android::GetOverlayControlDataFoldablePositionField());
|
||||
std::pair<double, double> foldable_position = std::make_pair(
|
||||
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
|
||||
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
|
||||
Common::Android::GetJDouble(
|
||||
env, env->GetObjectField(jfoldablePosition, Common::Android::GetPairFirstField())),
|
||||
Common::Android::GetJDouble(
|
||||
env,
|
||||
env->GetObjectField(jfoldablePosition, Common::Android::GetPairSecondField())));
|
||||
|
||||
AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
|
||||
GetJString(env, jidString), enabled, landscape_position, portrait_position,
|
||||
foldable_position});
|
||||
Common::Android::GetJString(env, jidString), enabled, landscape_position,
|
||||
portrait_position, foldable_position});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,31 +1,30 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <common/android/android_common.h>
|
||||
#include <common/logging/log.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include "android_common/android_common.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||
LOG_DEBUG(Frontend, "{}", GetJString(env, jmessage));
|
||||
LOG_DEBUG(Frontend, "{}", Common::Android::GetJString(env, jmessage));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_Log_warning(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||
LOG_WARNING(Frontend, "{}", GetJString(env, jmessage));
|
||||
LOG_WARNING(Frontend, "{}", Common::Android::GetJString(env, jmessage));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_Log_info(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||
LOG_INFO(Frontend, "{}", GetJString(env, jmessage));
|
||||
LOG_INFO(Frontend, "{}", Common::Android::GetJString(env, jmessage));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_Log_error(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||
LOG_ERROR(Frontend, "{}", GetJString(env, jmessage));
|
||||
LOG_ERROR(Frontend, "{}", Common::Android::GetJString(env, jmessage));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_Log_critical(JNIEnv* env, jobject obj, jstring jmessage) {
|
||||
LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage));
|
||||
LOG_CRITICAL(Frontend, "{}", Common::Android::GetJString(env, jmessage));
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
@ -107,6 +107,8 @@ add_library(common STATIC
|
||||
quaternion.h
|
||||
range_map.h
|
||||
range_mutex.h
|
||||
range_sets.h
|
||||
range_sets.inc
|
||||
reader_writer_queue.h
|
||||
ring_buffer.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
|
||||
@ -121,6 +123,7 @@ add_library(common STATIC
|
||||
settings_input.cpp
|
||||
settings_input.h
|
||||
settings_setting.h
|
||||
slot_vector.h
|
||||
socket_types.h
|
||||
spin_lock.cpp
|
||||
spin_lock.h
|
||||
@ -179,9 +182,15 @@ endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(common
|
||||
PRIVATE
|
||||
PUBLIC
|
||||
fs/fs_android.cpp
|
||||
fs/fs_android.h
|
||||
android/android_common.cpp
|
||||
android/android_common.h
|
||||
android/id_cache.cpp
|
||||
android/id_cache.h
|
||||
android/applets/software_keyboard.cpp
|
||||
android/applets/software_keyboard.h
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "android_common.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@ -9,7 +9,9 @@
|
||||
#include <jni.h>
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "jni/id_cache.h"
|
||||
#include "id_cache.h"
|
||||
|
||||
namespace Common::Android {
|
||||
|
||||
std::string GetJString(JNIEnv* env, jstring jstr) {
|
||||
if (!jstr) {
|
||||
@ -18,7 +20,8 @@ std::string GetJString(JNIEnv* env, jstring jstr) {
|
||||
|
||||
const jchar* jchars = env->GetStringChars(jstr, nullptr);
|
||||
const jsize length = env->GetStringLength(jstr);
|
||||
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length);
|
||||
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars),
|
||||
static_cast<u32>(length));
|
||||
const std::string converted_string = Common::UTF16ToUTF8(string_view);
|
||||
env->ReleaseStringChars(jstr, jchars);
|
||||
|
||||
@ -36,25 +39,27 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
|
||||
}
|
||||
|
||||
double GetJDouble(JNIEnv* env, jobject jdouble) {
|
||||
return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
|
||||
return env->GetDoubleField(jdouble, GetDoubleValueField());
|
||||
}
|
||||
|
||||
jobject ToJDouble(JNIEnv* env, double value) {
|
||||
return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
|
||||
return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value);
|
||||
}
|
||||
|
||||
s32 GetJInteger(JNIEnv* env, jobject jinteger) {
|
||||
return env->GetIntField(jinteger, IDCache::GetIntegerValueField());
|
||||
return env->GetIntField(jinteger, GetIntegerValueField());
|
||||
}
|
||||
|
||||
jobject ToJInteger(JNIEnv* env, s32 value) {
|
||||
return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value);
|
||||
return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value);
|
||||
}
|
||||
|
||||
bool GetJBoolean(JNIEnv* env, jobject jboolean) {
|
||||
return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField());
|
||||
return env->GetBooleanField(jboolean, GetBooleanValueField());
|
||||
}
|
||||
|
||||
jobject ToJBoolean(JNIEnv* env, bool value) {
|
||||
return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value);
|
||||
return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value);
|
||||
}
|
||||
|
||||
} // namespace Common::Android
|
@ -8,6 +8,8 @@
|
||||
#include <jni.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::Android {
|
||||
|
||||
std::string GetJString(JNIEnv* env, jstring jstr);
|
||||
jstring ToJString(JNIEnv* env, std::string_view str);
|
||||
jstring ToJString(JNIEnv* env, std::u16string_view str);
|
||||
@ -20,3 +22,5 @@ jobject ToJInteger(JNIEnv* env, s32 value);
|
||||
|
||||
bool GetJBoolean(JNIEnv* env, jobject jboolean);
|
||||
jobject ToJBoolean(JNIEnv* env, bool value);
|
||||
|
||||
} // namespace Common::Android
|
@ -6,12 +6,12 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/applets/software_keyboard.h"
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "jni/android_common/android_common.h"
|
||||
#include "jni/applets/software_keyboard.h"
|
||||
#include "jni/id_cache.h"
|
||||
|
||||
static jclass s_software_keyboard_class;
|
||||
static jclass s_keyboard_config_class;
|
||||
@ -19,10 +19,10 @@ static jclass s_keyboard_data_class;
|
||||
static jmethodID s_swkbd_execute_normal;
|
||||
static jmethodID s_swkbd_execute_inline;
|
||||
|
||||
namespace SoftwareKeyboard {
|
||||
namespace Common::Android::SoftwareKeyboard {
|
||||
|
||||
static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) {
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
JNIEnv* env = GetEnvForThread();
|
||||
jobject object = env->AllocObject(s_keyboard_config_class);
|
||||
|
||||
env->SetObjectField(object,
|
||||
@ -78,7 +78,7 @@ static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParamet
|
||||
}
|
||||
|
||||
AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) {
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
JNIEnv* env = GetEnvForThread();
|
||||
const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
|
||||
object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
|
||||
return ResultData{GetJString(env, string),
|
||||
@ -141,7 +141,7 @@ void AndroidKeyboard::ShowNormalKeyboard() const {
|
||||
|
||||
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
|
||||
std::thread([&] {
|
||||
data = ResultData::CreateFromFrontend(IDCache::GetEnvForThread()->CallStaticObjectMethod(
|
||||
data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod(
|
||||
s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters)));
|
||||
}).join();
|
||||
|
||||
@ -183,8 +183,8 @@ void AndroidKeyboard::ShowInlineKeyboard(
|
||||
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
|
||||
m_is_inline_active = true;
|
||||
std::thread([&] {
|
||||
IDCache::GetEnvForThread()->CallStaticVoidMethod(
|
||||
s_software_keyboard_class, s_swkbd_execute_inline, ToJKeyboardParams(parameters));
|
||||
GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline,
|
||||
ToJKeyboardParams(parameters));
|
||||
}).join();
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
|
||||
m_current_text += submitted_text;
|
||||
|
||||
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
|
||||
m_current_text.size());
|
||||
static_cast<int>(m_current_text.size()));
|
||||
}
|
||||
|
||||
void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
|
||||
@ -242,7 +242,7 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
|
||||
case KEYCODE_DEL:
|
||||
m_current_text.pop_back();
|
||||
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
|
||||
m_current_text.size());
|
||||
static_cast<int>(m_current_text.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -274,4 +274,4 @@ void CleanupJNI(JNIEnv* env) {
|
||||
env->DeleteGlobalRef(s_keyboard_data_class);
|
||||
}
|
||||
|
||||
} // namespace SoftwareKeyboard
|
||||
} // namespace Common::Android::SoftwareKeyboard
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
namespace SoftwareKeyboard {
|
||||
namespace Common::Android::SoftwareKeyboard {
|
||||
|
||||
class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet {
|
||||
public:
|
||||
@ -66,7 +66,7 @@ void InitJNI(JNIEnv* env);
|
||||
// Should be called in JNI_Unload
|
||||
void CleanupJNI(JNIEnv* env);
|
||||
|
||||
} // namespace SoftwareKeyboard
|
||||
} // namespace Common::Android::SoftwareKeyboard
|
||||
|
||||
// Native function calls
|
||||
extern "C" {
|
@ -3,10 +3,10 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "applets/software_keyboard.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs_android.h"
|
||||
#include "jni/applets/software_keyboard.h"
|
||||
#include "jni/id_cache.h"
|
||||
#include "id_cache.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
static JavaVM* s_java_vm;
|
||||
@ -67,7 +67,7 @@ static jfieldID s_boolean_value_field;
|
||||
|
||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||
|
||||
namespace IDCache {
|
||||
namespace Common::Android {
|
||||
|
||||
JNIEnv* GetEnvForThread() {
|
||||
thread_local static struct OwnedEnv {
|
||||
@ -276,8 +276,6 @@ jfieldID GetBooleanValueField() {
|
||||
return s_boolean_value_field;
|
||||
}
|
||||
|
||||
} // namespace IDCache
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -393,7 +391,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
||||
|
||||
// Initialize applets
|
||||
SoftwareKeyboard::InitJNI(env);
|
||||
Common::Android::SoftwareKeyboard::InitJNI(env);
|
||||
|
||||
return JNI_VERSION;
|
||||
}
|
||||
@ -426,3 +424,5 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common::Android
|
@ -3,20 +3,40 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <jni.h>
|
||||
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace IDCache {
|
||||
namespace Common::Android {
|
||||
|
||||
JNIEnv* GetEnvForThread();
|
||||
|
||||
/**
|
||||
* Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber.
|
||||
* @tparam T Typename of the return value for the work param
|
||||
* @param work Lambda that runs JNI code. This function will take care of attaching this thread to
|
||||
* the JVM
|
||||
* @return The result from the work lambda param
|
||||
*/
|
||||
template <typename T = void>
|
||||
T RunJNIOnFiber(const std::function<T(JNIEnv*)>& work) {
|
||||
std::future<T> j_result = std::async(std::launch::async, [&] {
|
||||
auto env = GetEnvForThread();
|
||||
return work(env);
|
||||
});
|
||||
return j_result.get();
|
||||
}
|
||||
|
||||
jclass GetNativeLibraryClass();
|
||||
|
||||
jclass GetDiskCacheProgressClass();
|
||||
jclass GetDiskCacheLoadCallbackStageClass();
|
||||
jclass GetGameDirClass();
|
||||
jmethodID GetGameDirConstructor();
|
||||
jmethodID GetExitEmulationActivity();
|
||||
jmethodID GetDiskCacheLoadProgress();
|
||||
|
||||
jmethodID GetExitEmulationActivity();
|
||||
jmethodID GetOnEmulationStarted();
|
||||
jmethodID GetOnEmulationStopped();
|
||||
jmethodID GetOnProgramChanged();
|
||||
@ -65,4 +85,4 @@ jclass GetBooleanClass();
|
||||
jmethodID GetBooleanConstructor();
|
||||
jfieldID GetBooleanValueField();
|
||||
|
||||
} // namespace IDCache
|
||||
} // namespace Common::Android
|
@ -1,63 +1,38 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs_android.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
namespace Common::FS::Android {
|
||||
|
||||
JNIEnv* GetEnvForThread() {
|
||||
thread_local static struct OwnedEnv {
|
||||
OwnedEnv() {
|
||||
status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
|
||||
if (status == JNI_EDETACHED)
|
||||
g_jvm->AttachCurrentThread(&env, nullptr);
|
||||
}
|
||||
|
||||
~OwnedEnv() {
|
||||
if (status == JNI_EDETACHED)
|
||||
g_jvm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
int status;
|
||||
JNIEnv* env = nullptr;
|
||||
} owned;
|
||||
return owned.env;
|
||||
}
|
||||
|
||||
void RegisterCallbacks(JNIEnv* env, jclass clazz) {
|
||||
env->GetJavaVM(&g_jvm);
|
||||
native_library = clazz;
|
||||
|
||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||
F(JMethodID, JMethodName, Signature)
|
||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
||||
F(JMethodID, JMethodName, Signature)
|
||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
||||
F(JMethodID, JMethodName, Signature)
|
||||
#define F(JMethodID, JMethodName, Signature) \
|
||||
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
|
||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||
#undef F
|
||||
#undef FS
|
||||
#undef FR
|
||||
#undef FH
|
||||
s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||
s_get_filename = env->GetStaticMethodID(native_library, "getFilename",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||
s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J");
|
||||
s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z");
|
||||
s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z");
|
||||
s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)I");
|
||||
}
|
||||
|
||||
void UnRegisterCallbacks() {
|
||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||
#define F(JMethodID) JMethodID = nullptr;
|
||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||
#undef F
|
||||
#undef FS
|
||||
#undef FR
|
||||
#undef FH
|
||||
s_get_parent_directory = nullptr;
|
||||
s_get_filename = nullptr;
|
||||
|
||||
s_get_size = nullptr;
|
||||
s_is_directory = nullptr;
|
||||
s_file_exists = nullptr;
|
||||
|
||||
s_open_content_uri = nullptr;
|
||||
}
|
||||
|
||||
bool IsContentUri(const std::string& path) {
|
||||
@ -70,7 +45,7 @@ bool IsContentUri(const std::string& path) {
|
||||
}
|
||||
|
||||
int OpenContentUri(const std::string& filepath, OpenMode openmode) {
|
||||
if (open_content_uri == nullptr)
|
||||
if (s_open_content_uri == nullptr)
|
||||
return -1;
|
||||
|
||||
const char* mode = "";
|
||||
@ -82,50 +57,66 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
|
||||
UNIMPLEMENTED();
|
||||
return -1;
|
||||
}
|
||||
auto env = GetEnvForThread();
|
||||
jstring j_filepath = env->NewStringUTF(filepath.c_str());
|
||||
jstring j_mode = env->NewStringUTF(mode);
|
||||
return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode);
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
jstring j_filepath = Common::Android::ToJString(env, filepath);
|
||||
jstring j_mode = Common::Android::ToJString(env, mode);
|
||||
return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode);
|
||||
}
|
||||
|
||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
||||
F(FunctionName, ReturnValue, JMethodID, Caller)
|
||||
#define F(FunctionName, ReturnValue, JMethodID, Caller) \
|
||||
ReturnValue FunctionName(const std::string& filepath) { \
|
||||
if (JMethodID == nullptr) { \
|
||||
return 0; \
|
||||
} \
|
||||
auto env = GetEnvForThread(); \
|
||||
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
|
||||
return env->Caller(native_library, JMethodID, j_filepath); \
|
||||
std::uint64_t GetSize(const std::string& filepath) {
|
||||
if (s_get_size == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||
#undef F
|
||||
#undef FR
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
return static_cast<u64>(env->CallStaticLongMethod(
|
||||
native_library, s_get_size,
|
||||
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)));
|
||||
}
|
||||
|
||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||
F(FunctionName, JMethodID, Caller)
|
||||
#define F(FunctionName, JMethodID, Caller) \
|
||||
std::string FunctionName(const std::string& filepath) { \
|
||||
if (JMethodID == nullptr) { \
|
||||
return 0; \
|
||||
} \
|
||||
auto env = GetEnvForThread(); \
|
||||
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
|
||||
jstring j_return = \
|
||||
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
|
||||
if (!j_return) { \
|
||||
return {}; \
|
||||
} \
|
||||
const jchar* jchars = env->GetStringChars(j_return, nullptr); \
|
||||
const jsize length = env->GetStringLength(j_return); \
|
||||
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
|
||||
const std::string converted_string = Common::UTF16ToUTF8(string_view); \
|
||||
env->ReleaseStringChars(j_return, jchars); \
|
||||
return converted_string; \
|
||||
bool IsDirectory(const std::string& filepath) {
|
||||
if (s_is_directory == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||
#undef F
|
||||
#undef FH
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
return env->CallStaticBooleanMethod(
|
||||
native_library, s_is_directory,
|
||||
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
|
||||
}
|
||||
|
||||
bool Exists(const std::string& filepath) {
|
||||
if (s_file_exists == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
return env->CallStaticBooleanMethod(
|
||||
native_library, s_file_exists,
|
||||
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
|
||||
}
|
||||
|
||||
std::string GetParentDirectory(const std::string& filepath) {
|
||||
if (s_get_parent_directory == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
|
||||
native_library, s_get_parent_directory, Common::Android::ToJString(env, filepath)));
|
||||
if (!j_return) {
|
||||
return {};
|
||||
}
|
||||
return Common::Android::GetJString(env, j_return);
|
||||
}
|
||||
|
||||
std::string GetFilename(const std::string& filepath) {
|
||||
if (s_get_filename == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
|
||||
native_library, s_get_filename, Common::Android::ToJString(env, filepath)));
|
||||
if (!j_return) {
|
||||
return {};
|
||||
}
|
||||
return Common::Android::GetJString(env, j_return);
|
||||
}
|
||||
|
||||
} // namespace Common::FS::Android
|
||||
|
@ -7,38 +7,17 @@
|
||||
#include <vector>
|
||||
#include <jni.h>
|
||||
|
||||
#define ANDROID_STORAGE_FUNCTIONS(V) \
|
||||
V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \
|
||||
"openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
|
||||
|
||||
#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
|
||||
V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \
|
||||
V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
|
||||
"(Ljava/lang/String;)Z") \
|
||||
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
|
||||
|
||||
#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
|
||||
V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
|
||||
"(Ljava/lang/String;)Ljava/lang/String;") \
|
||||
V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
|
||||
"(Ljava/lang/String;)Ljava/lang/String;")
|
||||
|
||||
namespace Common::FS::Android {
|
||||
|
||||
static JavaVM* g_jvm = nullptr;
|
||||
static jclass native_library = nullptr;
|
||||
|
||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||
#define F(JMethodID) static jmethodID JMethodID = nullptr;
|
||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||
#undef F
|
||||
#undef FS
|
||||
#undef FR
|
||||
#undef FH
|
||||
static jmethodID s_get_parent_directory;
|
||||
static jmethodID s_get_filename;
|
||||
static jmethodID s_get_size;
|
||||
static jmethodID s_is_directory;
|
||||
static jmethodID s_file_exists;
|
||||
static jmethodID s_open_content_uri;
|
||||
|
||||
enum class OpenMode {
|
||||
Read,
|
||||
@ -57,24 +36,11 @@ void UnRegisterCallbacks();
|
||||
|
||||
bool IsContentUri(const std::string& path);
|
||||
|
||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
||||
F(FunctionName, Parameters, ReturnValue)
|
||||
#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters;
|
||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||
#undef F
|
||||
#undef FS
|
||||
|
||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
||||
F(FunctionName, ReturnValue)
|
||||
#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
|
||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||
#undef F
|
||||
#undef FR
|
||||
|
||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
|
||||
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
|
||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||
#undef F
|
||||
#undef FH
|
||||
int OpenContentUri(const std::string& filepath, OpenMode openmode);
|
||||
std::uint64_t GetSize(const std::string& filepath);
|
||||
bool IsDirectory(const std::string& filepath);
|
||||
bool Exists(const std::string& filepath);
|
||||
std::string GetParentDirectory(const std::string& filepath);
|
||||
std::string GetFilename(const std::string& filepath);
|
||||
|
||||
} // namespace Common::FS::Android
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
@ -29,6 +30,8 @@ namespace Common {
|
||||
|
||||
template <std::size_t Size, bool le = false>
|
||||
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
||||
|
||||
std::array<u8, Size> out{};
|
||||
if constexpr (le) {
|
||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
||||
|
73
src/common/range_sets.h
Normal file
73
src/common/range_sets.h
Normal file
@ -0,0 +1,73 @@
|
||||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename AddressType>
|
||||
class RangeSet {
|
||||
public:
|
||||
RangeSet();
|
||||
~RangeSet();
|
||||
|
||||
RangeSet(RangeSet const&) = delete;
|
||||
RangeSet& operator=(RangeSet const&) = delete;
|
||||
|
||||
RangeSet(RangeSet&& other);
|
||||
RangeSet& operator=(RangeSet&& other);
|
||||
|
||||
void Add(AddressType base_address, size_t size);
|
||||
void Subtract(AddressType base_address, size_t size);
|
||||
void Clear();
|
||||
bool Empty() const;
|
||||
|
||||
template <typename Func>
|
||||
void ForEach(Func&& func) const;
|
||||
|
||||
template <typename Func>
|
||||
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
|
||||
|
||||
private:
|
||||
struct RangeSetImpl;
|
||||
std::unique_ptr<RangeSetImpl> m_impl;
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
class OverlapRangeSet {
|
||||
public:
|
||||
OverlapRangeSet();
|
||||
~OverlapRangeSet();
|
||||
|
||||
OverlapRangeSet(OverlapRangeSet const&) = delete;
|
||||
OverlapRangeSet& operator=(OverlapRangeSet const&) = delete;
|
||||
|
||||
OverlapRangeSet(OverlapRangeSet&& other);
|
||||
OverlapRangeSet& operator=(OverlapRangeSet&& other);
|
||||
|
||||
void Add(AddressType base_address, size_t size);
|
||||
void Subtract(AddressType base_address, size_t size);
|
||||
|
||||
template <typename Func>
|
||||
void Subtract(AddressType base_address, size_t size, Func&& on_delete);
|
||||
|
||||
void DeleteAll(AddressType base_address, size_t size);
|
||||
void Clear();
|
||||
bool Empty() const;
|
||||
|
||||
template <typename Func>
|
||||
void ForEach(Func&& func) const;
|
||||
|
||||
template <typename Func>
|
||||
void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
|
||||
|
||||
private:
|
||||
struct OverlapRangeSetImpl;
|
||||
std::unique_ptr<OverlapRangeSetImpl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace Common
|
304
src/common/range_sets.inc
Normal file
304
src/common/range_sets.inc
Normal file
@ -0,0 +1,304 @@
|
||||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/icl/interval.hpp>
|
||||
#include <boost/icl/interval_base_set.hpp>
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <boost/icl/split_interval_map.hpp>
|
||||
#include <boost/pool/pool.hpp>
|
||||
#include <boost/pool/pool_alloc.hpp>
|
||||
#include <boost/pool/poolfwd.hpp>
|
||||
|
||||
#include "common/range_sets.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
template <class T>
|
||||
using RangeSetsAllocator =
|
||||
boost::fast_pool_allocator<T, boost::default_user_allocator_new_delete,
|
||||
boost::details::pool::default_mutex, 1024, 2048>;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
struct RangeSet<AddressType>::RangeSetImpl {
|
||||
using IntervalSet = boost::icl::interval_set<
|
||||
AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less),
|
||||
RangeSetsAllocator>;
|
||||
using IntervalType = typename IntervalSet::interval_type;
|
||||
|
||||
RangeSetImpl() = default;
|
||||
~RangeSetImpl() = default;
|
||||
|
||||
void Add(AddressType base_address, size_t size) {
|
||||
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||
IntervalType interval{base_address, end_address};
|
||||
m_ranges_set.add(interval);
|
||||
}
|
||||
|
||||
void Subtract(AddressType base_address, size_t size) {
|
||||
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||
IntervalType interval{base_address, end_address};
|
||||
m_ranges_set.subtract(interval);
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEach(Func&& func) const {
|
||||
if (m_ranges_set.empty()) {
|
||||
return;
|
||||
}
|
||||
auto it = m_ranges_set.begin();
|
||||
auto end_it = m_ranges_set.end();
|
||||
for (; it != end_it; it++) {
|
||||
const AddressType inter_addr_end = it->upper();
|
||||
const AddressType inter_addr = it->lower();
|
||||
func(inter_addr, inter_addr_end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachInRange(AddressType base_addr, size_t size, Func&& func) const {
|
||||
if (m_ranges_set.empty()) {
|
||||
return;
|
||||
}
|
||||
const AddressType start_address = base_addr;
|
||||
const AddressType end_address = start_address + size;
|
||||
const RangeSetImpl::IntervalType search_interval{start_address, end_address};
|
||||
auto it = m_ranges_set.lower_bound(search_interval);
|
||||
if (it == m_ranges_set.end()) {
|
||||
return;
|
||||
}
|
||||
auto end_it = m_ranges_set.upper_bound(search_interval);
|
||||
for (; it != end_it; it++) {
|
||||
AddressType inter_addr_end = it->upper();
|
||||
AddressType inter_addr = it->lower();
|
||||
if (inter_addr_end > end_address) {
|
||||
inter_addr_end = end_address;
|
||||
}
|
||||
if (inter_addr < start_address) {
|
||||
inter_addr = start_address;
|
||||
}
|
||||
func(inter_addr, inter_addr_end);
|
||||
}
|
||||
}
|
||||
|
||||
IntervalSet m_ranges_set;
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
struct OverlapRangeSet<AddressType>::OverlapRangeSetImpl {
|
||||
using IntervalSet = boost::icl::split_interval_map<
|
||||
AddressType, s32, boost::icl::partial_enricher, std::less, boost::icl::inplace_plus,
|
||||
boost::icl::inter_section,
|
||||
ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), RangeSetsAllocator>;
|
||||
using IntervalType = typename IntervalSet::interval_type;
|
||||
|
||||
OverlapRangeSetImpl() = default;
|
||||
~OverlapRangeSetImpl() = default;
|
||||
|
||||
void Add(AddressType base_address, size_t size) {
|
||||
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||
IntervalType interval{base_address, end_address};
|
||||
m_split_ranges_set += std::make_pair(interval, 1);
|
||||
}
|
||||
|
||||
template <bool has_on_delete, typename Func>
|
||||
void Subtract(AddressType base_address, size_t size, s32 amount,
|
||||
[[maybe_unused]] Func&& on_delete) {
|
||||
if (m_split_ranges_set.empty()) {
|
||||
return;
|
||||
}
|
||||
AddressType end_address = base_address + static_cast<AddressType>(size);
|
||||
IntervalType interval{base_address, end_address};
|
||||
bool any_removals = false;
|
||||
m_split_ranges_set += std::make_pair(interval, -amount);
|
||||
do {
|
||||
any_removals = false;
|
||||
auto it = m_split_ranges_set.lower_bound(interval);
|
||||
if (it == m_split_ranges_set.end()) {
|
||||
return;
|
||||
}
|
||||
auto end_it = m_split_ranges_set.upper_bound(interval);
|
||||
for (; it != end_it; it++) {
|
||||
if (it->second <= 0) {
|
||||
if constexpr (has_on_delete) {
|
||||
if (it->second == 0) {
|
||||
on_delete(it->first.lower(), it->first.upper());
|
||||
}
|
||||
}
|
||||
any_removals = true;
|
||||
m_split_ranges_set.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (any_removals);
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEach(Func&& func) const {
|
||||
if (m_split_ranges_set.empty()) {
|
||||
return;
|
||||
}
|
||||
auto it = m_split_ranges_set.begin();
|
||||
auto end_it = m_split_ranges_set.end();
|
||||
for (; it != end_it; it++) {
|
||||
const AddressType inter_addr_end = it->first.upper();
|
||||
const AddressType inter_addr = it->first.lower();
|
||||
func(inter_addr, inter_addr_end, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void ForEachInRange(AddressType base_address, size_t size, Func&& func) const {
|
||||
if (m_split_ranges_set.empty()) {
|
||||
return;
|
||||
}
|
||||
const AddressType start_address = base_address;
|
||||
const AddressType end_address = start_address + size;
|
||||
const OverlapRangeSetImpl::IntervalType search_interval{start_address, end_address};
|
||||
auto it = m_split_ranges_set.lower_bound(search_interval);
|
||||
if (it == m_split_ranges_set.end()) {
|
||||
return;
|
||||
}
|
||||
auto end_it = m_split_ranges_set.upper_bound(search_interval);
|
||||
for (; it != end_it; it++) {
|
||||
auto& inter = it->first;
|
||||
AddressType inter_addr_end = inter.upper();
|
||||
AddressType inter_addr = inter.lower();
|
||||
if (inter_addr_end > end_address) {
|
||||
inter_addr_end = end_address;
|
||||
}
|
||||
if (inter_addr < start_address) {
|
||||
inter_addr = start_address;
|
||||
}
|
||||
func(inter_addr, inter_addr_end, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
IntervalSet m_split_ranges_set;
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
RangeSet<AddressType>::RangeSet() {
|
||||
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
RangeSet<AddressType>::~RangeSet() = default;
|
||||
|
||||
template <typename AddressType>
|
||||
RangeSet<AddressType>::RangeSet(RangeSet&& other) {
|
||||
m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
|
||||
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
RangeSet<AddressType>& RangeSet<AddressType>::operator=(RangeSet&& other) {
|
||||
m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void RangeSet<AddressType>::Add(AddressType base_address, size_t size) {
|
||||
m_impl->Add(base_address, size);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void RangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
|
||||
m_impl->Subtract(base_address, size);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void RangeSet<AddressType>::Clear() {
|
||||
m_impl->m_ranges_set.clear();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool RangeSet<AddressType>::Empty() const {
|
||||
return m_impl->m_ranges_set.empty();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
template <typename Func>
|
||||
void RangeSet<AddressType>::ForEach(Func&& func) const {
|
||||
m_impl->ForEach(std::move(func));
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
template <typename Func>
|
||||
void RangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
|
||||
Func&& func) const {
|
||||
m_impl->ForEachInRange(base_address, size, std::move(func));
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
OverlapRangeSet<AddressType>::OverlapRangeSet() {
|
||||
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
OverlapRangeSet<AddressType>::~OverlapRangeSet() = default;
|
||||
|
||||
template <typename AddressType>
|
||||
OverlapRangeSet<AddressType>::OverlapRangeSet(OverlapRangeSet&& other) {
|
||||
m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
|
||||
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
OverlapRangeSet<AddressType>& OverlapRangeSet<AddressType>::operator=(OverlapRangeSet&& other) {
|
||||
m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void OverlapRangeSet<AddressType>::Add(AddressType base_address, size_t size) {
|
||||
m_impl->Add(base_address, size);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
|
||||
m_impl->template Subtract<false>(base_address, size, 1, [](AddressType, AddressType) {});
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
template <typename Func>
|
||||
void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size,
|
||||
Func&& on_delete) {
|
||||
m_impl->template Subtract<true, Func>(base_address, size, 1, std::move(on_delete));
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void OverlapRangeSet<AddressType>::DeleteAll(AddressType base_address, size_t size) {
|
||||
m_impl->template Subtract<false>(base_address, size, std::numeric_limits<s32>::max(),
|
||||
[](AddressType, AddressType) {});
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
void OverlapRangeSet<AddressType>::Clear() {
|
||||
m_impl->m_split_ranges_set.clear();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool OverlapRangeSet<AddressType>::Empty() const {
|
||||
return m_impl->m_split_ranges_set.empty();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
template <typename Func>
|
||||
void OverlapRangeSet<AddressType>::ForEach(Func&& func) const {
|
||||
m_impl->ForEach(func);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
template <typename Func>
|
||||
void OverlapRangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
|
||||
Func&& func) const {
|
||||
m_impl->ForEachInRange(base_address, size, std::move(func));
|
||||
}
|
||||
|
||||
} // namespace Common
|
@ -30,6 +30,7 @@ namespace Settings {
|
||||
#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
|
||||
#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
|
||||
|
||||
SETTING(AppletMode, false);
|
||||
SETTING(AudioEngine, false);
|
||||
SETTING(bool, false);
|
||||
SETTING(int, false);
|
||||
@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) {
|
||||
return "Debugging";
|
||||
case Category::GpuDriver:
|
||||
return "GpuDriver";
|
||||
case Category::LibraryApplet:
|
||||
return "LibraryApplet";
|
||||
case Category::Miscellaneous:
|
||||
return "Miscellaneous";
|
||||
case Category::Network:
|
||||
|
@ -133,6 +133,38 @@ struct TouchFromButtonMap {
|
||||
struct Values {
|
||||
Linkage linkage{};
|
||||
|
||||
// Applet
|
||||
Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> player_select_applet_mode{
|
||||
linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet};
|
||||
Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> photo_viewer_applet_mode{
|
||||
linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
|
||||
Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
Setting<AppletMode> wifi_web_auth_applet_mode{
|
||||
linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet};
|
||||
Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode",
|
||||
Category::LibraryApplet};
|
||||
|
||||
// Audio
|
||||
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
|
||||
Category::Audio, Specialization::RuntimeList};
|
||||
|
@ -44,6 +44,7 @@ enum class Category : u32 {
|
||||
Services,
|
||||
Paths,
|
||||
Linux,
|
||||
LibraryApplet,
|
||||
MaxEnum,
|
||||
};
|
||||
|
||||
|
@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
||||
|
||||
ENUM(ConsoleMode, Handheld, Docked);
|
||||
|
||||
ENUM(AppletMode, HLE, LLE);
|
||||
|
||||
template <typename Type>
|
||||
inline std::string CanonicalizeEnum(Type id) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/polyfill_ranges.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
namespace Common {
|
||||
|
||||
struct SlotId {
|
||||
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
|
||||
@ -217,11 +217,11 @@ private:
|
||||
std::vector<u32> free_list;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
} // namespace Common
|
||||
|
||||
template <>
|
||||
struct std::hash<VideoCommon::SlotId> {
|
||||
size_t operator()(const VideoCommon::SlotId& id) const noexcept {
|
||||
struct std::hash<Common::SlotId> {
|
||||
size_t operator()(const Common::SlotId& id) const noexcept {
|
||||
return std::hash<u32>{}(id.index);
|
||||
}
|
||||
};
|
@ -186,68 +186,68 @@ static_assert(std::is_trivially_destructible_v<PhysicalAddress>);
|
||||
static_assert(std::is_trivially_destructible_v<VirtualAddress>);
|
||||
static_assert(std::is_trivially_destructible_v<ProcessAddress>);
|
||||
|
||||
static_assert(Null<uint64_t> == 0);
|
||||
static_assert(Null<uint64_t> == 0U);
|
||||
static_assert(Null<PhysicalAddress> == Null<uint64_t>);
|
||||
static_assert(Null<VirtualAddress> == Null<uint64_t>);
|
||||
static_assert(Null<ProcessAddress> == Null<uint64_t>);
|
||||
|
||||
// Constructor/assignment validations.
|
||||
static_assert([] {
|
||||
const PhysicalAddress a(5);
|
||||
const PhysicalAddress a(5U);
|
||||
PhysicalAddress b(a);
|
||||
return b;
|
||||
}() == PhysicalAddress(5));
|
||||
}() == PhysicalAddress(5U));
|
||||
static_assert([] {
|
||||
const PhysicalAddress a(5);
|
||||
PhysicalAddress b(10);
|
||||
const PhysicalAddress a(5U);
|
||||
PhysicalAddress b(10U);
|
||||
b = a;
|
||||
return b;
|
||||
}() == PhysicalAddress(5));
|
||||
}() == PhysicalAddress(5U));
|
||||
|
||||
// Arithmetic validations.
|
||||
static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15));
|
||||
static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5));
|
||||
static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U));
|
||||
static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U));
|
||||
static_assert([] {
|
||||
PhysicalAddress v(10);
|
||||
v += 5;
|
||||
PhysicalAddress v(10U);
|
||||
v += 5U;
|
||||
return v;
|
||||
}() == PhysicalAddress(15));
|
||||
}() == PhysicalAddress(15U));
|
||||
static_assert([] {
|
||||
PhysicalAddress v(10);
|
||||
v -= 5;
|
||||
PhysicalAddress v(10U);
|
||||
v -= 5U;
|
||||
return v;
|
||||
}() == PhysicalAddress(5));
|
||||
static_assert(PhysicalAddress(10)++ == PhysicalAddress(10));
|
||||
static_assert(++PhysicalAddress(10) == PhysicalAddress(11));
|
||||
static_assert(PhysicalAddress(10)-- == PhysicalAddress(10));
|
||||
static_assert(--PhysicalAddress(10) == PhysicalAddress(9));
|
||||
}() == PhysicalAddress(5U));
|
||||
static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U));
|
||||
static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U));
|
||||
static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U));
|
||||
static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U));
|
||||
|
||||
// Logical validations.
|
||||
static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111);
|
||||
static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101);
|
||||
static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110);
|
||||
static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010);
|
||||
static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101);
|
||||
static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010);
|
||||
static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000);
|
||||
static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101);
|
||||
static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111);
|
||||
static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111);
|
||||
static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U);
|
||||
static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U);
|
||||
static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U);
|
||||
static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U);
|
||||
static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U);
|
||||
static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U);
|
||||
static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U);
|
||||
static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U);
|
||||
static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U);
|
||||
static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U);
|
||||
|
||||
// Comparisons.
|
||||
static_assert(PhysicalAddress(0) == PhysicalAddress(0));
|
||||
static_assert(PhysicalAddress(0) != PhysicalAddress(1));
|
||||
static_assert(PhysicalAddress(0) < PhysicalAddress(1));
|
||||
static_assert(PhysicalAddress(0) <= PhysicalAddress(1));
|
||||
static_assert(PhysicalAddress(1) > PhysicalAddress(0));
|
||||
static_assert(PhysicalAddress(1) >= PhysicalAddress(0));
|
||||
static_assert(PhysicalAddress(0U) == PhysicalAddress(0U));
|
||||
static_assert(PhysicalAddress(0U) != PhysicalAddress(1U));
|
||||
static_assert(PhysicalAddress(0U) < PhysicalAddress(1U));
|
||||
static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U));
|
||||
static_assert(PhysicalAddress(1U) > PhysicalAddress(0U));
|
||||
static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U));
|
||||
|
||||
static_assert(!(PhysicalAddress(0) == PhysicalAddress(1)));
|
||||
static_assert(!(PhysicalAddress(0) != PhysicalAddress(0)));
|
||||
static_assert(!(PhysicalAddress(1) < PhysicalAddress(0)));
|
||||
static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0)));
|
||||
static_assert(!(PhysicalAddress(0) > PhysicalAddress(1)));
|
||||
static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1)));
|
||||
static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U)));
|
||||
static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U)));
|
||||
static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U)));
|
||||
static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U)));
|
||||
static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U)));
|
||||
static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U)));
|
||||
|
||||
} // namespace Common
|
||||
|
||||
|
@ -775,6 +775,9 @@ add_library(core STATIC
|
||||
hle/service/nvnflinger/graphic_buffer_producer.h
|
||||
hle/service/nvnflinger/hos_binder_driver_server.cpp
|
||||
hle/service/nvnflinger/hos_binder_driver_server.h
|
||||
hle/service/nvnflinger/hardware_composer.cpp
|
||||
hle/service/nvnflinger/hardware_composer.h
|
||||
hle/service/nvnflinger/hwc_layer.h
|
||||
hle/service/nvnflinger/nvnflinger.cpp
|
||||
hle/service/nvnflinger/nvnflinger.h
|
||||
hle/service/nvnflinger/parcel.h
|
||||
|
@ -43,6 +43,8 @@ public:
|
||||
DeviceMemoryManager(const DeviceMemory& device_memory);
|
||||
~DeviceMemoryManager();
|
||||
|
||||
static constexpr bool HAS_FLUSH_INVALIDATION = true;
|
||||
|
||||
void BindInterface(DeviceInterface* device_inter);
|
||||
|
||||
DAddr Allocate(size_t size);
|
||||
|
@ -44,15 +44,32 @@ public:
|
||||
GuestMemory() = delete;
|
||||
explicit GuestMemory(M& memory, u64 addr, std::size_t size,
|
||||
Common::ScratchBuffer<T>* backup = nullptr)
|
||||
: m_memory{memory}, m_addr{addr}, m_size{size} {
|
||||
: m_memory{&memory}, m_addr{addr}, m_size{size} {
|
||||
static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Read) {
|
||||
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
|
||||
if (!this->TrySetSpan()) {
|
||||
if (backup) {
|
||||
backup->resize_destructive(this->size());
|
||||
m_data_span = *backup;
|
||||
m_span_valid = true;
|
||||
m_is_data_copy = true;
|
||||
} else {
|
||||
m_data_copy.resize(this->size());
|
||||
m_data_span = std::span(m_data_copy);
|
||||
m_span_valid = true;
|
||||
m_is_data_copy = true;
|
||||
}
|
||||
}
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Read) {
|
||||
Read(addr, size, backup);
|
||||
}
|
||||
}
|
||||
|
||||
~GuestMemory() = default;
|
||||
|
||||
GuestMemory(GuestMemory&& rhs) = default;
|
||||
GuestMemory& operator=(GuestMemory&& rhs) = default;
|
||||
|
||||
T* data() noexcept {
|
||||
return m_data_span.data();
|
||||
}
|
||||
@ -109,8 +126,8 @@ public:
|
||||
}
|
||||
|
||||
if (this->TrySetSpan()) {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
m_memory.FlushRegion(m_addr, this->size_bytes());
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe && M::HAS_FLUSH_INVALIDATION) {
|
||||
m_memory->FlushRegion(m_addr, this->size_bytes());
|
||||
}
|
||||
} else {
|
||||
if (backup) {
|
||||
@ -123,9 +140,9 @@ public:
|
||||
m_is_data_copy = true;
|
||||
m_span_valid = true;
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
|
||||
m_memory->ReadBlock(m_addr, this->data(), this->size_bytes());
|
||||
} else {
|
||||
m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
|
||||
m_memory->ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
|
||||
}
|
||||
}
|
||||
return m_data_span;
|
||||
@ -133,18 +150,19 @@ public:
|
||||
|
||||
void Write(std::span<T> write_data) noexcept {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
||||
m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
|
||||
m_memory->WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
|
||||
m_memory->WriteBlock(m_addr, write_data.data(), this->size_bytes());
|
||||
} else {
|
||||
m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
|
||||
m_memory->WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
bool TrySetSpan() noexcept {
|
||||
if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
|
||||
if (u8* ptr = m_memory->GetSpan(m_addr, this->size_bytes()); ptr) {
|
||||
m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
|
||||
m_span_valid = true;
|
||||
m_is_data_copy = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -159,7 +177,7 @@ protected:
|
||||
return m_addr_changed;
|
||||
}
|
||||
|
||||
M& m_memory;
|
||||
M* m_memory;
|
||||
u64 m_addr{};
|
||||
size_t m_size{};
|
||||
std::span<T> m_data_span{};
|
||||
@ -175,17 +193,7 @@ public:
|
||||
GuestMemoryScoped() = delete;
|
||||
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
|
||||
Common::ScratchBuffer<T>* backup = nullptr)
|
||||
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
|
||||
if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
|
||||
if (!this->TrySetSpan()) {
|
||||
if (backup) {
|
||||
this->m_data_span = *backup;
|
||||
this->m_span_valid = true;
|
||||
this->m_is_data_copy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {}
|
||||
|
||||
~GuestMemoryScoped() {
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Write) {
|
||||
@ -196,15 +204,17 @@ public:
|
||||
if (this->AddressChanged() || this->IsDataCopy()) {
|
||||
ASSERT(this->m_span_valid);
|
||||
if constexpr (FLAGS & GuestMemoryFlags::Cached) {
|
||||
this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
|
||||
this->m_memory->WriteBlockCached(this->m_addr, this->data(),
|
||||
this->size_bytes());
|
||||
} else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
|
||||
this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
|
||||
this->m_memory->WriteBlock(this->m_addr, this->data(), this->size_bytes());
|
||||
} else {
|
||||
this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
|
||||
this->m_memory->WriteBlockUnsafe(this->m_addr, this->data(),
|
||||
this->size_bytes());
|
||||
}
|
||||
} else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
|
||||
(FLAGS & GuestMemoryFlags::Cached)) {
|
||||
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
|
||||
this->m_memory->InvalidateRegion(this->m_addr, this->size_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,9 @@
|
||||
#include <random>
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/gpu_dirty_memory_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
@ -1258,6 +1259,10 @@ void KProcess::InitializeInterfaces() {
|
||||
|
||||
#ifdef HAS_NCE
|
||||
if (this->IsApplication() && Settings::IsNceEnabled()) {
|
||||
// Register the scoped JIT handler before creating any NCE instances
|
||||
// so that its signal handler will appear first in the signal chain.
|
||||
Core::ScopedJitExecution::RegisterHandler();
|
||||
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
|
||||
}
|
||||
|
@ -130,9 +130,9 @@ enum class AppletProgramId : u64 {
|
||||
|
||||
enum class LibraryAppletMode : u32 {
|
||||
AllForeground = 0,
|
||||
Background = 1,
|
||||
NoUI = 2,
|
||||
BackgroundIndirectDisplay = 3,
|
||||
PartialForeground = 1,
|
||||
NoUi = 2,
|
||||
PartialForegroundIndirectDisplay = 3,
|
||||
AllForegroundInitiallyHidden = 4,
|
||||
};
|
||||
|
||||
|
@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() {
|
||||
case LibraryAppletMode::AllForeground:
|
||||
InitializeForeground();
|
||||
break;
|
||||
case LibraryAppletMode::Background:
|
||||
case LibraryAppletMode::BackgroundIndirectDisplay:
|
||||
InitializeBackground(applet_mode);
|
||||
case LibraryAppletMode::PartialForeground:
|
||||
case LibraryAppletMode::PartialForegroundIndirectDisplay:
|
||||
InitializePartialForeground(applet_mode);
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
|
||||
@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
|
||||
InitializeFrontendNormalKeyboard();
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
|
||||
void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) {
|
||||
LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
|
||||
|
||||
is_background = true;
|
||||
@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
|
||||
swkbd_inline_initialize_arg.size());
|
||||
|
||||
if (swkbd_initialize_arg.library_applet_mode_flag) {
|
||||
ASSERT(library_applet_mode == LibraryAppletMode::Background);
|
||||
ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground);
|
||||
} else {
|
||||
ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay);
|
||||
ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ private:
|
||||
void InitializeForeground();
|
||||
|
||||
/// Initializes the inline software keyboard.
|
||||
void InitializeBackground(LibraryAppletMode library_applet_mode);
|
||||
void InitializePartialForeground(LibraryAppletMode library_applet_mode);
|
||||
|
||||
/// Processes the text check sent by the application.
|
||||
void ProcessTextCheck();
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/service/am/applet_data_broker.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
@ -16,6 +17,34 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
bool ShouldCreateGuestApplet(AppletId applet_id) {
|
||||
#define X(Name, name) \
|
||||
if (applet_id == AppletId::Name && \
|
||||
Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
X(Cabinet, cabinet)
|
||||
X(Controller, controller)
|
||||
X(DataErase, data_erase)
|
||||
X(Error, error)
|
||||
X(NetConnect, net_connect)
|
||||
X(ProfileSelect, player_select)
|
||||
X(SoftwareKeyboard, swkbd)
|
||||
X(MiiEdit, mii_edit)
|
||||
X(Web, web)
|
||||
X(Shop, shop)
|
||||
X(PhotoViewer, photo_viewer)
|
||||
X(OfflineWeb, offline_web)
|
||||
X(LoginShare, login_share)
|
||||
X(WebAuth, wifi_web_auth)
|
||||
X(MyPage, my_page)
|
||||
|
||||
#undef X
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||
switch (applet_id) {
|
||||
case AppletId::OverlayDisplay:
|
||||
@ -63,9 +92,10 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(
|
||||
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
|
||||
LibraryAppletMode mode) {
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
std::shared_ptr<Applet> caller_applet,
|
||||
AppletId applet_id,
|
||||
LibraryAppletMode mode) {
|
||||
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
|
||||
if (program_id == 0) {
|
||||
// Unknown applet
|
||||
@ -87,24 +117,18 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||
// Set focus state
|
||||
switch (mode) {
|
||||
case LibraryAppletMode::AllForeground:
|
||||
case LibraryAppletMode::NoUI:
|
||||
applet->focus_state = FocusState::InFocus;
|
||||
case LibraryAppletMode::NoUi:
|
||||
case LibraryAppletMode::PartialForeground:
|
||||
case LibraryAppletMode::PartialForegroundIndirectDisplay:
|
||||
applet->hid_registration.EnableAppletToGetInput(true);
|
||||
applet->focus_state = FocusState::InFocus;
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
||||
break;
|
||||
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||
applet->system_buffer_manager.SetWindowVisibility(false);
|
||||
applet->focus_state = FocusState::NotInFocus;
|
||||
applet->hid_registration.EnableAppletToGetInput(false);
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
||||
break;
|
||||
case LibraryAppletMode::Background:
|
||||
case LibraryAppletMode::BackgroundIndirectDisplay:
|
||||
default:
|
||||
applet->focus_state = FocusState::Background;
|
||||
applet->hid_registration.EnableAppletToGetInput(true);
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
||||
applet->focus_state = FocusState::NotInFocus;
|
||||
applet->system_buffer_manager.SetWindowVisibility(false);
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -117,9 +141,10 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
}
|
||||
|
||||
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(
|
||||
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
|
||||
LibraryAppletMode mode) {
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||
std::shared_ptr<Applet> caller_applet,
|
||||
AppletId applet_id,
|
||||
LibraryAppletMode mode) {
|
||||
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
|
||||
|
||||
auto process = std::make_unique<Process>(system);
|
||||
@ -163,7 +188,13 @@ void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
|
||||
applet_mode);
|
||||
|
||||
auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
|
||||
std::shared_ptr<ILibraryAppletAccessor> library_applet;
|
||||
if (ShouldCreateGuestApplet(applet_id)) {
|
||||
library_applet = CreateGuestApplet(system, applet, applet_id, applet_mode);
|
||||
}
|
||||
if (!library_applet) {
|
||||
library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
|
||||
}
|
||||
if (!library_applet) {
|
||||
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/self_controller.h"
|
||||
#include "core/hle/service/caps/caps_su.h"
|
||||
#include "core/hle/service/hle_ipc.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
@ -47,7 +50,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
|
||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
|
||||
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
||||
{61, nullptr, "SetMediaPlaybackState"},
|
||||
{61, &ISelfController::SetMediaPlaybackState, "SetMediaPlaybackState"},
|
||||
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
||||
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
|
||||
{64, nullptr, "SetInputDetectionSourceSet"},
|
||||
@ -288,7 +291,8 @@ void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
|
||||
if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) {
|
||||
if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id,
|
||||
applet->library_applet_mode)) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@ -323,6 +327,16 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void ISelfController::SetMediaPlaybackState(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u8 state = rp.Pop<u8>();
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, state={}", state);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/hle_ipc.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@ -38,6 +39,7 @@ private:
|
||||
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
|
||||
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
|
||||
void ApproveToDisplay(HLERequestContext& ctx);
|
||||
void SetMediaPlaybackState(HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||
void ReportUserIsActive(HLERequestContext& ctx);
|
||||
|
@ -17,11 +17,12 @@ SystemBufferManager::~SystemBufferManager() {
|
||||
|
||||
// Clean up shared layers.
|
||||
if (m_buffer_sharing_enabled) {
|
||||
m_nvnflinger->GetSystemBufferManager().Finalize(m_process);
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
|
||||
AppletId applet_id) {
|
||||
AppletId applet_id, LibraryAppletMode mode) {
|
||||
if (m_nvnflinger) {
|
||||
return m_buffer_sharing_enabled;
|
||||
}
|
||||
@ -36,9 +37,15 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel:
|
||||
return false;
|
||||
}
|
||||
|
||||
Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
|
||||
if (mode == LibraryAppletMode::PartialForeground ||
|
||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
|
||||
blending = Nvnflinger::LayerBlending::Coverage;
|
||||
}
|
||||
|
||||
const auto display_id = m_nvnflinger->OpenDisplay("Default").value();
|
||||
const auto res = m_nvnflinger->GetSystemBufferManager().Initialize(
|
||||
&m_system_shared_buffer_id, &m_system_shared_layer_id, display_id);
|
||||
m_process, &m_system_shared_buffer_id, &m_system_shared_layer_id, display_id, blending);
|
||||
|
||||
if (res.IsSuccess()) {
|
||||
m_buffer_sharing_enabled = true;
|
||||
@ -62,8 +69,12 @@ void SystemBufferManager::SetWindowVisibility(bool visible) {
|
||||
|
||||
Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index) {
|
||||
// TODO
|
||||
R_SUCCEED();
|
||||
if (!m_buffer_sharing_enabled) {
|
||||
return VI::ResultPermissionDenied;
|
||||
}
|
||||
|
||||
return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -27,7 +27,8 @@ public:
|
||||
SystemBufferManager();
|
||||
~SystemBufferManager();
|
||||
|
||||
bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id);
|
||||
bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id,
|
||||
LibraryAppletMode mode);
|
||||
|
||||
void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
|
||||
u64* out_system_shared_layer_id) {
|
||||
|
@ -62,12 +62,12 @@ void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
|
||||
applet->hid_registration.EnableAppletToGetInput(visible);
|
||||
|
||||
if (visible) {
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||
applet->focus_state = FocusState::InFocus;
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||
} else {
|
||||
applet->focus_state = FocusState::NotInFocus;
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
|
||||
}
|
||||
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
|
@ -115,6 +115,11 @@ struct ArgumentTraits {
|
||||
static constexpr ArgumentType Type = ArgumentType::InData;
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
consteval bool ConstIfReference() {
|
||||
return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true);
|
||||
}
|
||||
|
||||
struct RequestLayout {
|
||||
u32 copy_handle_count;
|
||||
u32 move_handle_count;
|
||||
@ -435,6 +440,7 @@ void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
|
||||
}
|
||||
const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;
|
||||
|
||||
static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
|
||||
using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;
|
||||
|
||||
OutTemporaryBuffers buffers{};
|
||||
|
@ -4,10 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/hle_ipc.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
@ -22,8 +21,10 @@ class Out {
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
/* implicit */ Out(const Out& t) : raw(t.raw) {}
|
||||
/* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {}
|
||||
/* implicit */ Out(Type* t) : raw(t) {}
|
||||
Out& operator=(const Out&) = delete;
|
||||
|
||||
Type* Get() const {
|
||||
return raw;
|
||||
@ -37,6 +38,10 @@ public:
|
||||
return raw;
|
||||
}
|
||||
|
||||
operator Type*() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
private:
|
||||
Type* raw;
|
||||
};
|
||||
@ -113,8 +118,10 @@ class OutCopyHandle {
|
||||
public:
|
||||
using Type = T*;
|
||||
|
||||
/* implicit */ OutCopyHandle(const OutCopyHandle& t) : raw(t.raw) {}
|
||||
/* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {}
|
||||
/* implicit */ OutCopyHandle(Type* t) : raw(t) {}
|
||||
OutCopyHandle& operator=(const OutCopyHandle&) = delete;
|
||||
|
||||
Type* Get() const {
|
||||
return raw;
|
||||
@ -128,6 +135,10 @@ public:
|
||||
return raw;
|
||||
}
|
||||
|
||||
operator Type*() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
private:
|
||||
Type* raw;
|
||||
};
|
||||
@ -137,8 +148,10 @@ class OutMoveHandle {
|
||||
public:
|
||||
using Type = T*;
|
||||
|
||||
/* implicit */ OutMoveHandle(const OutMoveHandle& t) : raw(t.raw) {}
|
||||
/* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {}
|
||||
/* implicit */ OutMoveHandle(Type* t) : raw(t) {}
|
||||
OutMoveHandle& operator=(const OutMoveHandle&) = delete;
|
||||
|
||||
Type* Get() const {
|
||||
return raw;
|
||||
@ -152,6 +165,10 @@ public:
|
||||
return raw;
|
||||
}
|
||||
|
||||
operator Type*() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
private:
|
||||
Type* raw;
|
||||
};
|
||||
@ -248,8 +265,10 @@ public:
|
||||
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize);
|
||||
using Type = T;
|
||||
|
||||
/* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {}
|
||||
/* implicit */ OutLargeData(Type* t) : raw(t) {}
|
||||
/* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {}
|
||||
OutLargeData& operator=(const OutLargeData&) = delete;
|
||||
|
||||
Type* Get() const {
|
||||
return raw;
|
||||
@ -263,6 +282,10 @@ public:
|
||||
return raw;
|
||||
}
|
||||
|
||||
operator Type*() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
private:
|
||||
Type* raw;
|
||||
};
|
||||
|
@ -115,6 +115,11 @@ private:
|
||||
if (type->GetName() == "save") {
|
||||
for (const auto& save_id : type->GetSubdirectories()) {
|
||||
for (const auto& user_id : save_id->GetSubdirectories()) {
|
||||
// Skip non user id subdirectories
|
||||
if (user_id->GetName().size() != 0x20) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto save_id_numeric = stoull_be(save_id->GetName());
|
||||
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
|
||||
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
|
||||
@ -160,6 +165,10 @@ private:
|
||||
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
|
||||
// Temporary Storage
|
||||
for (const auto& user_id : type->GetSubdirectories()) {
|
||||
// Skip non user id subdirectories
|
||||
if (user_id->GetName().size() != 0x20) {
|
||||
continue;
|
||||
}
|
||||
for (const auto& title_id : user_id->GetSubdirectories()) {
|
||||
if (!title_id->GetFiles().empty() ||
|
||||
!title_id->GetSubdirectories().empty()) {
|
||||
|
@ -31,8 +31,11 @@ void LoopProcess(Core::System& system) {
|
||||
// Error Context
|
||||
server_manager->RegisterNamedService("ectx:aw", std::make_shared<ECTX_AW>(system));
|
||||
|
||||
// Notification Services for application
|
||||
server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system));
|
||||
// Notification Services
|
||||
server_manager->RegisterNamedService(
|
||||
"notif:a", std::make_shared<INotificationServicesForApplication>(system));
|
||||
server_manager->RegisterNamedService("notif:s",
|
||||
std::make_shared<INotificationServices>(system));
|
||||
|
||||
// Time
|
||||
auto time = std::make_shared<Time::TimeManager>(system);
|
||||
|
@ -6,48 +6,31 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/glue/notif.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{500, &NOTIF_A::RegisterAlarmSetting, "RegisterAlarmSetting"},
|
||||
{510, &NOTIF_A::UpdateAlarmSetting, "UpdateAlarmSetting"},
|
||||
{520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
|
||||
{530, &NOTIF_A::LoadApplicationParameter, "LoadApplicationParameter"},
|
||||
{540, &NOTIF_A::DeleteAlarmSetting, "DeleteAlarmSetting"},
|
||||
{1000, &NOTIF_A::Initialize, "Initialize"},
|
||||
};
|
||||
// clang-format on
|
||||
namespace {
|
||||
|
||||
constexpr inline std::size_t MaxAlarms = 8;
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
NOTIF_A::~NOTIF_A() = default;
|
||||
|
||||
void NOTIF_A::RegisterAlarmSetting(HLERequestContext& ctx) {
|
||||
const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0);
|
||||
const auto application_parameter_size = ctx.GetReadBufferSize(1);
|
||||
|
||||
ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting),
|
||||
"alarm_setting_buffer_size is not 0x40 bytes");
|
||||
ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter),
|
||||
"application_parameter_size is bigger than 0x400 bytes");
|
||||
|
||||
AlarmSetting new_alarm{};
|
||||
memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting));
|
||||
|
||||
// TODO: Count alarms per game id
|
||||
if (alarms.size() >= max_alarms) {
|
||||
Result NotificationServiceImpl::RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id,
|
||||
const AlarmSetting& alarm_setting,
|
||||
std::span<const u8> application_parameter) {
|
||||
if (alarms.size() > MaxAlarms) {
|
||||
LOG_ERROR(Service_NOTIF, "Alarm limit reached");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter),
|
||||
"application_parameter_size is bigger than 0x400 bytes");
|
||||
|
||||
AlarmSetting new_alarm = alarm_setting;
|
||||
new_alarm.alarm_setting_id = last_alarm_setting_id++;
|
||||
alarms.push_back(new_alarm);
|
||||
|
||||
@ -55,100 +38,82 @@ void NOTIF_A::RegisterAlarmSetting(HLERequestContext& ctx) {
|
||||
|
||||
LOG_WARNING(Service_NOTIF,
|
||||
"(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
|
||||
application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind,
|
||||
application_parameter.size(), new_alarm.alarm_setting_id, new_alarm.kind,
|
||||
new_alarm.muted);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(new_alarm.alarm_setting_id);
|
||||
*out_alarm_setting_id = new_alarm.alarm_setting_id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void NOTIF_A::UpdateAlarmSetting(HLERequestContext& ctx) {
|
||||
const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0);
|
||||
const auto application_parameter_size = ctx.GetReadBufferSize(1);
|
||||
|
||||
ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting),
|
||||
"alarm_setting_buffer_size is not 0x40 bytes");
|
||||
ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter),
|
||||
Result NotificationServiceImpl::UpdateAlarmSetting(const AlarmSetting& alarm_setting,
|
||||
std::span<const u8> application_parameter) {
|
||||
ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter),
|
||||
"application_parameter_size is bigger than 0x400 bytes");
|
||||
|
||||
AlarmSetting alarm_setting{};
|
||||
memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting));
|
||||
|
||||
const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id);
|
||||
if (alarm_it != alarms.end()) {
|
||||
LOG_DEBUG(Service_NOTIF, "Alarm updated");
|
||||
*alarm_it = alarm_setting;
|
||||
// TODO: Save application parameter data
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_NOTIF,
|
||||
"(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
|
||||
application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind,
|
||||
application_parameter.size(), alarm_setting.alarm_setting_id, alarm_setting.kind,
|
||||
alarm_setting.muted);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void NOTIF_A::ListAlarmSettings(HLERequestContext& ctx) {
|
||||
Result NotificationServiceImpl::ListAlarmSettings(s32* out_count,
|
||||
std::span<AlarmSetting> out_alarms) {
|
||||
LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size());
|
||||
|
||||
// TODO: Only return alarms of this game id
|
||||
ctx.WriteBuffer(alarms);
|
||||
const auto count = std::min(out_alarms.size(), alarms.size());
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
out_alarms[i] = alarms[i];
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(alarms.size()));
|
||||
*out_count = static_cast<s32>(count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void NOTIF_A::LoadApplicationParameter(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto alarm_setting_id{rp.Pop<AlarmSettingId>()};
|
||||
|
||||
Result NotificationServiceImpl::LoadApplicationParameter(u32* out_size,
|
||||
std::span<u8> out_application_parameter,
|
||||
AlarmSettingId alarm_setting_id) {
|
||||
const auto alarm_it = GetAlarmFromId(alarm_setting_id);
|
||||
if (alarm_it == alarms.end()) {
|
||||
LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
// TODO: Read application parameter related to this setting id
|
||||
ApplicationParameter application_parameter{};
|
||||
|
||||
LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id);
|
||||
std::memcpy(out_application_parameter.data(), application_parameter.data(),
|
||||
std::min(sizeof(application_parameter), out_application_parameter.size()));
|
||||
|
||||
ctx.WriteBuffer(application_parameter);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(application_parameter.size()));
|
||||
*out_size = static_cast<u32>(application_parameter.size());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void NOTIF_A::DeleteAlarmSetting(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto alarm_setting_id{rp.Pop<AlarmSettingId>()};
|
||||
|
||||
Result NotificationServiceImpl::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) {
|
||||
std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) {
|
||||
return alarm.alarm_setting_id == alarm_setting_id;
|
||||
});
|
||||
|
||||
LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void NOTIF_A::Initialize(HLERequestContext& ctx) {
|
||||
Result NotificationServiceImpl::Initialize(u64 aruid) {
|
||||
// TODO: Load previous alarms from config
|
||||
|
||||
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId(
|
||||
std::vector<AlarmSetting>::iterator NotificationServiceImpl::GetAlarmFromId(
|
||||
AlarmSettingId alarm_setting_id) {
|
||||
return std::find_if(alarms.begin(), alarms.end(),
|
||||
[alarm_setting_id](const AlarmSetting& alarm) {
|
||||
@ -156,4 +121,174 @@ std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId(
|
||||
});
|
||||
}
|
||||
|
||||
INotificationServicesForApplication::INotificationServicesForApplication(Core::System& system_)
|
||||
: ServiceFramework{system_, "notif:a"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{500, D<&INotificationServicesForApplication::RegisterAlarmSetting>, "RegisterAlarmSetting"},
|
||||
{510, D<&INotificationServicesForApplication::UpdateAlarmSetting>, "UpdateAlarmSetting"},
|
||||
{520, D<&INotificationServicesForApplication::ListAlarmSettings>, "ListAlarmSettings"},
|
||||
{530, D<&INotificationServicesForApplication::LoadApplicationParameter>, "LoadApplicationParameter"},
|
||||
{540, D<&INotificationServicesForApplication::DeleteAlarmSetting>, "DeleteAlarmSetting"},
|
||||
{1000, D<&INotificationServicesForApplication::Initialize>, "Initialize"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
INotificationServicesForApplication::~INotificationServicesForApplication() = default;
|
||||
|
||||
Result INotificationServicesForApplication::RegisterAlarmSetting(
|
||||
Out<AlarmSettingId> out_alarm_setting_id,
|
||||
InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
|
||||
R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting,
|
||||
application_parameter));
|
||||
}
|
||||
|
||||
Result INotificationServicesForApplication::UpdateAlarmSetting(
|
||||
InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
|
||||
R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter));
|
||||
}
|
||||
|
||||
Result INotificationServicesForApplication::ListAlarmSettings(
|
||||
Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) {
|
||||
R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms));
|
||||
}
|
||||
|
||||
Result INotificationServicesForApplication::LoadApplicationParameter(
|
||||
Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
|
||||
AlarmSettingId alarm_setting_id) {
|
||||
R_RETURN(
|
||||
impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id));
|
||||
}
|
||||
|
||||
Result INotificationServicesForApplication::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) {
|
||||
R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id));
|
||||
}
|
||||
|
||||
Result INotificationServicesForApplication::Initialize(ClientAppletResourceUserId aruid) {
|
||||
R_RETURN(impl.Initialize(*aruid));
|
||||
}
|
||||
|
||||
class INotificationSystemEventAccessor final
|
||||
: public ServiceFramework<INotificationSystemEventAccessor> {
|
||||
public:
|
||||
explicit INotificationSystemEventAccessor(Core::System& system_)
|
||||
: ServiceFramework{system_, "INotificationSystemEventAccessor"},
|
||||
service_context{system_, "INotificationSystemEventAccessor"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&INotificationSystemEventAccessor::GetSystemEvent>, "GetSystemEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
notification_event =
|
||||
service_context.CreateEvent("INotificationSystemEventAccessor:NotificationEvent");
|
||||
}
|
||||
|
||||
~INotificationSystemEventAccessor() {
|
||||
service_context.CloseEvent(notification_event);
|
||||
}
|
||||
|
||||
private:
|
||||
Result GetSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_readable_event) {
|
||||
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
|
||||
|
||||
*out_readable_event = ¬ification_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* notification_event;
|
||||
};
|
||||
|
||||
INotificationServices::INotificationServices(Core::System& system_)
|
||||
: ServiceFramework{system_, "notif:s"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{500, D<&INotificationServices::RegisterAlarmSetting>, "RegisterAlarmSetting"},
|
||||
{510, D<&INotificationServices::UpdateAlarmSetting>, "UpdateAlarmSetting"},
|
||||
{520, D<&INotificationServices::ListAlarmSettings>, "ListAlarmSettings"},
|
||||
{530, D<&INotificationServices::LoadApplicationParameter>, "LoadApplicationParameter"},
|
||||
{540, D<&INotificationServices::DeleteAlarmSetting>, "DeleteAlarmSetting"},
|
||||
{1000, D<&INotificationServices::Initialize>, "Initialize"},
|
||||
{1010, nullptr, "ListNotifications"},
|
||||
{1020, nullptr, "DeleteNotification"},
|
||||
{1030, nullptr, "ClearNotifications"},
|
||||
{1040, D<&INotificationServices::OpenNotificationSystemEventAccessor>, "OpenNotificationSystemEventAccessor"},
|
||||
{1500, nullptr, "SetNotificationPresentationSetting"},
|
||||
{1510, D<&INotificationServices::GetNotificationPresentationSetting>, "GetNotificationPresentationSetting"},
|
||||
{2000, nullptr, "GetAlarmSetting"},
|
||||
{2001, nullptr, "GetAlarmSettingWithApplicationParameter"},
|
||||
{2010, nullptr, "MuteAlarmSetting"},
|
||||
{2020, nullptr, "IsAlarmSettingReady"},
|
||||
{8000, nullptr, "RegisterAppletResourceUserId"},
|
||||
{8010, nullptr, "UnregisterAppletResourceUserId"},
|
||||
{8999, nullptr, "GetCurrentTime"},
|
||||
{9000, nullptr, "GetAlarmSettingNextNotificationTime"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
INotificationServices::~INotificationServices() = default;
|
||||
|
||||
Result INotificationServices::RegisterAlarmSetting(
|
||||
Out<AlarmSettingId> out_alarm_setting_id,
|
||||
InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
|
||||
R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting,
|
||||
application_parameter));
|
||||
}
|
||||
|
||||
Result INotificationServices::UpdateAlarmSetting(
|
||||
InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
|
||||
R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter));
|
||||
}
|
||||
|
||||
Result INotificationServices::ListAlarmSettings(
|
||||
Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) {
|
||||
R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms));
|
||||
}
|
||||
|
||||
Result INotificationServices::LoadApplicationParameter(
|
||||
Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
|
||||
AlarmSettingId alarm_setting_id) {
|
||||
R_RETURN(
|
||||
impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id));
|
||||
}
|
||||
|
||||
Result INotificationServices::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) {
|
||||
R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id));
|
||||
}
|
||||
|
||||
Result INotificationServices::Initialize(ClientAppletResourceUserId aruid) {
|
||||
R_RETURN(impl.Initialize(*aruid));
|
||||
}
|
||||
|
||||
Result INotificationServices::OpenNotificationSystemEventAccessor(
|
||||
Out<SharedPointer<INotificationSystemEventAccessor>> out_notification_system_event_accessor) {
|
||||
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
|
||||
|
||||
*out_notification_system_event_accessor =
|
||||
std::make_shared<INotificationSystemEventAccessor>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result INotificationServices::GetNotificationPresentationSetting(
|
||||
Out<NotificationPresentationSetting> out_notification_presentation_setting,
|
||||
NotificationChannel notification_channel) {
|
||||
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
|
||||
|
||||
*out_notification_presentation_setting = {};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
@ -15,58 +16,117 @@ class System;
|
||||
|
||||
namespace Service::Glue {
|
||||
|
||||
class NOTIF_A final : public ServiceFramework<NOTIF_A> {
|
||||
// This is nn::notification::AlarmSettingId
|
||||
using AlarmSettingId = u16;
|
||||
static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size");
|
||||
|
||||
using ApplicationParameter = std::array<u8, 0x400>;
|
||||
static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size");
|
||||
|
||||
struct DailyAlarmSetting {
|
||||
s8 hour;
|
||||
s8 minute;
|
||||
};
|
||||
static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size");
|
||||
|
||||
struct WeeklyScheduleAlarmSetting {
|
||||
INSERT_PADDING_BYTES_NOINIT(0xA);
|
||||
std::array<DailyAlarmSetting, 0x7> day_of_week;
|
||||
};
|
||||
static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18,
|
||||
"WeeklyScheduleAlarmSetting is an invalid size");
|
||||
|
||||
// This is nn::notification::AlarmSetting
|
||||
struct AlarmSetting {
|
||||
AlarmSettingId alarm_setting_id;
|
||||
u8 kind;
|
||||
u8 muted;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
Common::UUID account_id;
|
||||
u64 application_id;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x8);
|
||||
WeeklyScheduleAlarmSetting schedule;
|
||||
};
|
||||
static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size");
|
||||
|
||||
enum class NotificationChannel : u8 {
|
||||
Unknown0 = 0,
|
||||
};
|
||||
|
||||
struct NotificationPresentationSetting {
|
||||
INSERT_PADDING_BYTES_NOINIT(0x10);
|
||||
};
|
||||
static_assert(sizeof(NotificationPresentationSetting) == 0x10,
|
||||
"NotificationPresentationSetting is an invalid size");
|
||||
|
||||
class NotificationServiceImpl {
|
||||
public:
|
||||
explicit NOTIF_A(Core::System& system_);
|
||||
~NOTIF_A() override;
|
||||
Result RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id,
|
||||
const AlarmSetting& alarm_setting,
|
||||
std::span<const u8> application_parameter);
|
||||
Result UpdateAlarmSetting(const AlarmSetting& alarm_setting,
|
||||
std::span<const u8> application_parameter);
|
||||
Result ListAlarmSettings(s32* out_count, std::span<AlarmSetting> out_alarms);
|
||||
Result LoadApplicationParameter(u32* out_size, std::span<u8> out_application_parameter,
|
||||
AlarmSettingId alarm_setting_id);
|
||||
Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id);
|
||||
Result Initialize(u64 aruid);
|
||||
|
||||
private:
|
||||
static constexpr std::size_t max_alarms = 8;
|
||||
|
||||
// This is nn::notification::AlarmSettingId
|
||||
using AlarmSettingId = u16;
|
||||
static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size");
|
||||
|
||||
using ApplicationParameter = std::array<u8, 0x400>;
|
||||
static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size");
|
||||
|
||||
struct DailyAlarmSetting {
|
||||
s8 hour;
|
||||
s8 minute;
|
||||
};
|
||||
static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size");
|
||||
|
||||
struct WeeklyScheduleAlarmSetting {
|
||||
INSERT_PADDING_BYTES(0xA);
|
||||
std::array<DailyAlarmSetting, 0x7> day_of_week;
|
||||
};
|
||||
static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18,
|
||||
"WeeklyScheduleAlarmSetting is an invalid size");
|
||||
|
||||
// This is nn::notification::AlarmSetting
|
||||
struct AlarmSetting {
|
||||
AlarmSettingId alarm_setting_id;
|
||||
u8 kind;
|
||||
u8 muted;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
Common::UUID account_id;
|
||||
u64 application_id;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
WeeklyScheduleAlarmSetting schedule;
|
||||
};
|
||||
static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size");
|
||||
|
||||
void RegisterAlarmSetting(HLERequestContext& ctx);
|
||||
void UpdateAlarmSetting(HLERequestContext& ctx);
|
||||
void ListAlarmSettings(HLERequestContext& ctx);
|
||||
void LoadApplicationParameter(HLERequestContext& ctx);
|
||||
void DeleteAlarmSetting(HLERequestContext& ctx);
|
||||
void Initialize(HLERequestContext& ctx);
|
||||
|
||||
std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id);
|
||||
|
||||
std::vector<AlarmSetting> alarms{};
|
||||
AlarmSettingId last_alarm_setting_id{};
|
||||
};
|
||||
|
||||
class INotificationServicesForApplication final
|
||||
: public ServiceFramework<INotificationServicesForApplication> {
|
||||
public:
|
||||
explicit INotificationServicesForApplication(Core::System& system_);
|
||||
~INotificationServicesForApplication() override;
|
||||
|
||||
private:
|
||||
Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id,
|
||||
InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter);
|
||||
Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter);
|
||||
Result ListAlarmSettings(Out<s32> out_count,
|
||||
OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms);
|
||||
Result LoadApplicationParameter(Out<u32> out_size,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
|
||||
AlarmSettingId alarm_setting_id);
|
||||
Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id);
|
||||
Result Initialize(ClientAppletResourceUserId aruid);
|
||||
|
||||
NotificationServiceImpl impl;
|
||||
};
|
||||
|
||||
class INotificationSystemEventAccessor;
|
||||
|
||||
class INotificationServices final : public ServiceFramework<INotificationServices> {
|
||||
public:
|
||||
explicit INotificationServices(Core::System& system_);
|
||||
~INotificationServices() override;
|
||||
|
||||
private:
|
||||
Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id,
|
||||
InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter);
|
||||
Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
|
||||
InBuffer<BufferAttr_HipcMapAlias> application_parameter);
|
||||
Result ListAlarmSettings(Out<s32> out_count,
|
||||
OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms);
|
||||
Result LoadApplicationParameter(Out<u32> out_size,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
|
||||
AlarmSettingId alarm_setting_id);
|
||||
Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id);
|
||||
Result Initialize(ClientAppletResourceUserId aruid);
|
||||
Result OpenNotificationSystemEventAccessor(Out<SharedPointer<INotificationSystemEventAccessor>>
|
||||
out_notification_system_event_accessor);
|
||||
Result GetNotificationPresentationSetting(
|
||||
Out<NotificationPresentationSetting> out_notification_presentation_setting,
|
||||
NotificationChannel notification_channel);
|
||||
|
||||
NotificationServiceImpl impl;
|
||||
};
|
||||
} // namespace Service::Glue
|
||||
|
@ -200,7 +200,7 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
}
|
||||
|
||||
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
|
||||
Out<s64> out_time, Service::PSC::Time::SystemClockContext& context) {
|
||||
Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context) {
|
||||
SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); });
|
||||
|
||||
R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
|
||||
@ -216,8 +216,8 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot,
|
||||
|
||||
Result StaticService::GetClockSnapshotFromSystemClockContext(
|
||||
Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot,
|
||||
Service::PSC::Time::SystemClockContext& user_context,
|
||||
Service::PSC::Time::SystemClockContext& network_context) {
|
||||
const Service::PSC::Time::SystemClockContext& user_context,
|
||||
const Service::PSC::Time::SystemClockContext& network_context) {
|
||||
SCOPE_EXIT({
|
||||
LOG_DEBUG(Service_Time,
|
||||
"called. type={} out_snapshot={} user_context={} network_context={}", type,
|
||||
|
@ -58,12 +58,12 @@ public:
|
||||
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
|
||||
Result CalculateMonotonicSystemClockBaseTimePoint(
|
||||
Out<s64> out_time, Service::PSC::Time::SystemClockContext& context);
|
||||
Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context);
|
||||
Result GetClockSnapshot(OutClockSnapshot out_snapshot, Service::PSC::Time::TimeType type);
|
||||
Result GetClockSnapshotFromSystemClockContext(
|
||||
Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot,
|
||||
Service::PSC::Time::SystemClockContext& user_context,
|
||||
Service::PSC::Time::SystemClockContext& network_context);
|
||||
const Service::PSC::Time::SystemClockContext& user_context,
|
||||
const Service::PSC::Time::SystemClockContext& network_context);
|
||||
Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference,
|
||||
InClockSnapshot a, InClockSnapshot b);
|
||||
Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b);
|
||||
|
@ -62,7 +62,8 @@ Result TimeZoneService::GetDeviceLocationName(
|
||||
R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
|
||||
}
|
||||
|
||||
Result TimeZoneService::SetDeviceLocationName(Service::PSC::Time::LocationName& location_name) {
|
||||
Result TimeZoneService::SetDeviceLocationName(
|
||||
const Service::PSC::Time::LocationName& location_name) {
|
||||
LOG_DEBUG(Service_Time, "called. location_name={}", location_name);
|
||||
|
||||
R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
|
||||
@ -110,7 +111,8 @@ Result TimeZoneService::LoadLocationNameList(
|
||||
R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index));
|
||||
}
|
||||
|
||||
Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, Service::PSC::Time::LocationName& name) {
|
||||
Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule,
|
||||
const Service::PSC::Time::LocationName& name) {
|
||||
LOG_DEBUG(Service_Time, "called. name={}", name);
|
||||
|
||||
std::scoped_lock l{m_mutex};
|
||||
@ -139,7 +141,8 @@ Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
|
||||
}
|
||||
|
||||
Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(
|
||||
Service::PSC::Time::LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) {
|
||||
const Service::PSC::Time::LocationName& location_name,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> binary) {
|
||||
LOG_DEBUG(Service_Time, "called. location_name={}", location_name);
|
||||
|
||||
R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
|
||||
|
@ -46,18 +46,20 @@ public:
|
||||
~TimeZoneService() override;
|
||||
|
||||
Result GetDeviceLocationName(Out<Service::PSC::Time::LocationName> out_location_name);
|
||||
Result SetDeviceLocationName(Service::PSC::Time::LocationName& location_name);
|
||||
Result SetDeviceLocationName(const Service::PSC::Time::LocationName& location_name);
|
||||
Result GetTotalLocationNameCount(Out<u32> out_count);
|
||||
Result LoadLocationNameList(
|
||||
Out<u32> out_count,
|
||||
OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index);
|
||||
Result LoadTimeZoneRule(OutRule out_rule, Service::PSC::Time::LocationName& location_name);
|
||||
Result LoadTimeZoneRule(OutRule out_rule,
|
||||
const Service::PSC::Time::LocationName& location_name);
|
||||
Result GetTimeZoneRuleVersion(Out<Service::PSC::Time::RuleVersion> out_rule_version);
|
||||
Result GetDeviceLocationNameAndUpdatedTime(
|
||||
Out<Service::PSC::Time::LocationName> location_name,
|
||||
Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
|
||||
Result SetDeviceLocationNameWithTimeZoneRule(Service::PSC::Time::LocationName& location_name,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> binary);
|
||||
Result SetDeviceLocationNameWithTimeZoneRule(
|
||||
const Service::PSC::Time::LocationName& location_name,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> binary);
|
||||
Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary);
|
||||
Result GetDeviceLocationNameOperationEventReadableHandle(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
@ -65,6 +65,7 @@ Result MountTimeZoneBinary(Core::System& system) {
|
||||
// Validate that the romfs is readable, using invalid firmware keys can cause this to get
|
||||
// set but the files to be garbage. In that case, we want to hit the next path and
|
||||
// synthesise them instead.
|
||||
g_time_zone_binary_mount_result = ResultSuccess;
|
||||
Service::PSC::Time::LocationName name{"Etc/GMT"};
|
||||
if (!IsTimeZoneBinaryValid(name)) {
|
||||
ResetTimeZoneBinary();
|
||||
@ -98,7 +99,7 @@ void GetTimeZoneBinaryVersionPath(std::string& out_path) {
|
||||
out_path = "/version.txt";
|
||||
}
|
||||
|
||||
void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) {
|
||||
void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name) {
|
||||
if (g_time_zone_binary_mount_result != ResultSuccess) {
|
||||
return;
|
||||
}
|
||||
@ -106,7 +107,7 @@ void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName
|
||||
out_path = fmt::format("/zoneinfo/{}", name.data());
|
||||
}
|
||||
|
||||
bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
|
||||
bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name) {
|
||||
std::string path{};
|
||||
GetTimeZoneZonePath(path, name);
|
||||
|
||||
@ -155,7 +156,7 @@ Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
|
||||
}
|
||||
|
||||
Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
|
||||
Service::PSC::Time::LocationName& name) {
|
||||
const Service::PSC::Time::LocationName& name) {
|
||||
std::string path{};
|
||||
GetTimeZoneZonePath(path, name);
|
||||
|
||||
|
@ -19,12 +19,12 @@ void ResetTimeZoneBinary();
|
||||
Result MountTimeZoneBinary(Core::System& system);
|
||||
void GetTimeZoneBinaryListPath(std::string& out_path);
|
||||
void GetTimeZoneBinaryVersionPath(std::string& out_path);
|
||||
void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name);
|
||||
bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name);
|
||||
void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name);
|
||||
bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name);
|
||||
u32 GetTimeZoneCount();
|
||||
Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version);
|
||||
Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
|
||||
Service::PSC::Time::LocationName& name);
|
||||
const Service::PSC::Time::LocationName& name);
|
||||
Result GetTimeZoneLocationList(u32& out_count,
|
||||
std::span<Service::PSC::Time::LocationName> out_names,
|
||||
size_t max_names, u32 index);
|
||||
|
@ -126,7 +126,7 @@ public:
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem,
|
||||
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory> tmem,
|
||||
InBuffer<BufferAttr_HipcMapAlias> nrr,
|
||||
InBuffer<BufferAttr_HipcMapAlias> nro) {
|
||||
if (!tmem) {
|
||||
@ -268,9 +268,9 @@ public:
|
||||
|
||||
private:
|
||||
Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment,
|
||||
u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process,
|
||||
InCopyHandle<Kernel::KCodeMemory>& rx_mem,
|
||||
InCopyHandle<Kernel::KCodeMemory>& ro_mem) {
|
||||
u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess> process,
|
||||
InCopyHandle<Kernel::KCodeMemory> rx_mem,
|
||||
InCopyHandle<Kernel::KCodeMemory> ro_mem) {
|
||||
if (!process) {
|
||||
LOG_ERROR(Service_JIT, "process is null");
|
||||
R_THROW(ResultUnknown);
|
||||
|
@ -189,7 +189,7 @@ private:
|
||||
R_RETURN(manager->Move(metadata, new_index, create_id));
|
||||
}
|
||||
|
||||
Result AddOrReplace(StoreData& store_data) {
|
||||
Result AddOrReplace(const StoreData& store_data) {
|
||||
LOG_INFO(Service_Mii, "called");
|
||||
R_UNLESS(is_system, ResultPermissionDenied);
|
||||
|
||||
|
@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
continue;
|
||||
}
|
||||
if (session.process == process) {
|
||||
session.ref_count++;
|
||||
return session.id;
|
||||
}
|
||||
}
|
||||
@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
}
|
||||
auto& session = impl->sessions[new_id];
|
||||
session.is_active = true;
|
||||
session.ref_count = 1;
|
||||
// Optimization
|
||||
if (process->IsApplication()) {
|
||||
auto& page_table = process->GetPageTable().GetBasePageTable();
|
||||
@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
|
||||
void Container::CloseSession(SessionId session_id) {
|
||||
std::scoped_lock lk(impl->session_guard);
|
||||
impl->file.UnmapAllHandles(session_id);
|
||||
auto& session = impl->sessions[session_id.id];
|
||||
if (--session.ref_count > 0) {
|
||||
return;
|
||||
}
|
||||
impl->file.UnmapAllHandles(session_id);
|
||||
auto& smmu = impl->host1x.MemoryManager();
|
||||
if (session.has_preallocated_area) {
|
||||
const DAddr region_start = session.mapper->GetRegionStart();
|
||||
|
@ -46,6 +46,7 @@ struct Session {
|
||||
bool has_preallocated_area{};
|
||||
std::unique_ptr<HeapMapper> mapper{};
|
||||
bool is_active{};
|
||||
s32 ref_count{};
|
||||
};
|
||||
|
||||
class Container {
|
||||
@ -67,10 +68,7 @@ public:
|
||||
const SyncpointManager& GetSyncpointManager() const;
|
||||
|
||||
struct Host1xDeviceFileData {
|
||||
std::unordered_map<DeviceFD, u32> fd_to_id{};
|
||||
std::deque<u32> syncpts_accumulated{};
|
||||
u32 nvdec_next_id{};
|
||||
u32 vic_next_id{};
|
||||
};
|
||||
|
||||
Host1xDeviceFileData& Host1xDeviceFile();
|
||||
|
@ -3,110 +3,21 @@
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#define BOOST_NO_MT
|
||||
#include <boost/pool/detail/mutex.hpp>
|
||||
#undef BOOST_NO_MT
|
||||
#include <boost/icl/interval.hpp>
|
||||
#include <boost/icl/interval_base_set.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <boost/icl/split_interval_map.hpp>
|
||||
#include <boost/pool/pool.hpp>
|
||||
#include <boost/pool/pool_alloc.hpp>
|
||||
#include <boost/pool/poolfwd.hpp>
|
||||
|
||||
#include "common/range_sets.h"
|
||||
#include "common/range_sets.inc"
|
||||
#include "core/hle/service/nvdrv/core/heap_mapper.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
|
||||
namespace boost {
|
||||
template <typename T>
|
||||
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
|
||||
using IntervalCompare = std::less<DAddr>;
|
||||
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
|
||||
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
|
||||
using IntervalSet = boost::icl::interval_set<DAddr>;
|
||||
using IntervalType = typename IntervalSet::interval_type;
|
||||
|
||||
template <typename Type>
|
||||
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
|
||||
// types
|
||||
typedef counter_add_functor<Type> type;
|
||||
typedef boost::icl::identity_based_inplace_combine<Type> base_type;
|
||||
|
||||
// public member functions
|
||||
void operator()(Type& current, const Type& added) const {
|
||||
current += added;
|
||||
if (current < base_type::identity_element()) {
|
||||
current = base_type::identity_element();
|
||||
}
|
||||
}
|
||||
|
||||
// public static functions
|
||||
static void version(Type&){};
|
||||
};
|
||||
|
||||
using OverlapCombine = counter_add_functor<int>;
|
||||
using OverlapSection = boost::icl::inter_section<int>;
|
||||
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;
|
||||
|
||||
struct HeapMapper::HeapMapperInternal {
|
||||
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {}
|
||||
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
|
||||
~HeapMapperInternal() = default;
|
||||
|
||||
template <typename Func>
|
||||
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
|
||||
Func&& func) {
|
||||
const DAddr start_address = cpu_addr;
|
||||
const DAddr end_address = start_address + size;
|
||||
const IntervalType search_interval{start_address, end_address};
|
||||
auto it = current_range.lower_bound(search_interval);
|
||||
if (it == current_range.end()) {
|
||||
return;
|
||||
}
|
||||
auto end_it = current_range.upper_bound(search_interval);
|
||||
for (; it != end_it; it++) {
|
||||
auto& inter = it->first;
|
||||
DAddr inter_addr_end = inter.upper();
|
||||
DAddr inter_addr = inter.lower();
|
||||
if (inter_addr_end > end_address) {
|
||||
inter_addr_end = end_address;
|
||||
}
|
||||
if (inter_addr < start_address) {
|
||||
inter_addr = start_address;
|
||||
}
|
||||
func(inter_addr, inter_addr_end, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
|
||||
const IntervalType search_interval, int subtract_value) {
|
||||
bool any_removals = false;
|
||||
current_range.add(std::make_pair(search_interval, subtract_value));
|
||||
do {
|
||||
any_removals = false;
|
||||
auto it = current_range.lower_bound(search_interval);
|
||||
if (it == current_range.end()) {
|
||||
return;
|
||||
}
|
||||
auto end_it = current_range.upper_bound(search_interval);
|
||||
for (; it != end_it; it++) {
|
||||
if (it->second <= 0) {
|
||||
any_removals = true;
|
||||
current_range.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (any_removals);
|
||||
}
|
||||
|
||||
IntervalSet base_set;
|
||||
OverlapCounter mapping_overlaps;
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||
std::mutex guard;
|
||||
Common::RangeSet<VAddr> m_temporary_set;
|
||||
Common::OverlapRangeSet<VAddr> m_mapped_ranges;
|
||||
Tegra::MaxwellDeviceMemoryManager& m_device_memory;
|
||||
std::mutex m_guard;
|
||||
};
|
||||
|
||||
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
|
||||
@ -116,60 +27,48 @@ HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size,
|
||||
}
|
||||
|
||||
HeapMapper::~HeapMapper() {
|
||||
m_internal->device_memory.Unmap(m_daddress, m_size);
|
||||
// Unmap whatever has been mapped.
|
||||
m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) {
|
||||
const size_t sub_size = end_addr - start_addr;
|
||||
const size_t offset = start_addr - m_vaddress;
|
||||
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
|
||||
});
|
||||
}
|
||||
|
||||
DAddr HeapMapper::Map(VAddr start, size_t size) {
|
||||
std::scoped_lock lk(m_internal->guard);
|
||||
m_internal->base_set.clear();
|
||||
const IntervalType interval{start, start + size};
|
||||
m_internal->base_set.insert(interval);
|
||||
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
|
||||
[this](VAddr start_addr, VAddr end_addr, int) {
|
||||
const IntervalType other{start_addr, end_addr};
|
||||
m_internal->base_set.subtract(other);
|
||||
});
|
||||
if (!m_internal->base_set.empty()) {
|
||||
auto it = m_internal->base_set.begin();
|
||||
auto end_it = m_internal->base_set.end();
|
||||
for (; it != end_it; it++) {
|
||||
const VAddr inter_addr_end = it->upper();
|
||||
const VAddr inter_addr = it->lower();
|
||||
const size_t offset = inter_addr - m_vaddress;
|
||||
const size_t sub_size = inter_addr_end - inter_addr;
|
||||
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size,
|
||||
m_asid);
|
||||
}
|
||||
}
|
||||
m_internal->mapping_overlaps += std::make_pair(interval, 1);
|
||||
m_internal->base_set.clear();
|
||||
return m_daddress + (start - m_vaddress);
|
||||
std::scoped_lock lk(m_internal->m_guard);
|
||||
// Add the mapping range to a temporary range set.
|
||||
m_internal->m_temporary_set.Clear();
|
||||
m_internal->m_temporary_set.Add(start, size);
|
||||
|
||||
// Remove anything that's already mapped from the temporary range set.
|
||||
m_internal->m_mapped_ranges.ForEachInRange(
|
||||
start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
|
||||
m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
|
||||
});
|
||||
|
||||
// Map anything that has not been mapped yet.
|
||||
m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
|
||||
const size_t sub_size = end_addr - start_addr;
|
||||
const size_t offset = start_addr - m_vaddress;
|
||||
m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
|
||||
});
|
||||
|
||||
// Add the mapping range to the split map, to register the map and overlaps.
|
||||
m_internal->m_mapped_ranges.Add(start, size);
|
||||
m_internal->m_temporary_set.Clear();
|
||||
return m_daddress + static_cast<DAddr>(start - m_vaddress);
|
||||
}
|
||||
|
||||
void HeapMapper::Unmap(VAddr start, size_t size) {
|
||||
std::scoped_lock lk(m_internal->guard);
|
||||
m_internal->base_set.clear();
|
||||
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
|
||||
[this](VAddr start_addr, VAddr end_addr, int value) {
|
||||
if (value <= 1) {
|
||||
const IntervalType other{start_addr, end_addr};
|
||||
m_internal->base_set.insert(other);
|
||||
}
|
||||
});
|
||||
if (!m_internal->base_set.empty()) {
|
||||
auto it = m_internal->base_set.begin();
|
||||
auto end_it = m_internal->base_set.end();
|
||||
for (; it != end_it; it++) {
|
||||
const VAddr inter_addr_end = it->upper();
|
||||
const VAddr inter_addr = it->lower();
|
||||
const size_t offset = inter_addr - m_vaddress;
|
||||
const size_t sub_size = inter_addr_end - inter_addr;
|
||||
m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
|
||||
}
|
||||
}
|
||||
const IntervalType to_remove{start, start + size};
|
||||
m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
|
||||
m_internal->base_set.clear();
|
||||
std::scoped_lock lk(m_internal->m_guard);
|
||||
|
||||
// Just subtract the range and whatever is deleted, unmap it.
|
||||
m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
|
||||
const size_t sub_size = end_addr - start_addr;
|
||||
const size_t offset = start_addr - m_vaddress;
|
||||
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
@ -333,9 +333,13 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
|
||||
}();
|
||||
|
||||
for (auto& [id, handle] : handles_copy) {
|
||||
if (handle->session_id.id == session_id.id) {
|
||||
FreeHandle(id, false);
|
||||
{
|
||||
std::scoped_lock lk{handle->mutex};
|
||||
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
FreeHandle(id, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
@ -13,6 +15,22 @@
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
namespace {
|
||||
|
||||
Tegra::BlendMode ConvertBlending(Service::Nvnflinger::LayerBlending blending) {
|
||||
switch (blending) {
|
||||
case Service::Nvnflinger::LayerBlending::None:
|
||||
default:
|
||||
return Tegra::BlendMode::Opaque;
|
||||
case Service::Nvnflinger::LayerBlending::Premultiplied:
|
||||
return Tegra::BlendMode::Premultiplied;
|
||||
case Service::Nvnflinger::LayerBlending::Coverage:
|
||||
return Tegra::BlendMode::Coverage;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
|
||||
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
|
||||
nvdisp_disp0::~nvdisp_disp0() = default;
|
||||
@ -38,19 +56,31 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
||||
void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
|
||||
void nvdisp_disp0::OnClose(DeviceFD fd) {}
|
||||
|
||||
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
|
||||
u32 height, u32 stride, android::BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect,
|
||||
std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) {
|
||||
const DAddr addr = nvmap.GetHandleAddress(buffer_handle);
|
||||
LOG_TRACE(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
addr, offset, width, height, stride, format);
|
||||
void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers) {
|
||||
std::vector<Tegra::FramebufferConfig> output_layers;
|
||||
std::vector<Service::Nvidia::NvFence> output_fences;
|
||||
output_layers.reserve(sorted_layers.size());
|
||||
output_fences.reserve(sorted_layers.size());
|
||||
|
||||
const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
|
||||
stride, format, transform, crop_rect};
|
||||
for (auto& layer : sorted_layers) {
|
||||
output_layers.emplace_back(Tegra::FramebufferConfig{
|
||||
.address = nvmap.GetHandleAddress(layer.buffer_handle),
|
||||
.offset = layer.offset,
|
||||
.width = layer.width,
|
||||
.height = layer.height,
|
||||
.stride = layer.stride,
|
||||
.pixel_format = layer.format,
|
||||
.transform_flags = layer.transform,
|
||||
.crop_rect = layer.crop_rect,
|
||||
.blending = ConvertBlending(layer.blending),
|
||||
});
|
||||
|
||||
system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences);
|
||||
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
||||
output_fences.push_back(layer.acquire_fence.fences[i]);
|
||||
}
|
||||
}
|
||||
|
||||
system.GPU().RequestComposite(std::move(output_layers), std::move(output_fences));
|
||||
system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
|
||||
system.GetPerfStats().EndSystemFrame();
|
||||
system.GetPerfStats().BeginSystemFrame();
|
||||
|
@ -8,8 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_transform_flags.h"
|
||||
#include "core/hle/service/nvnflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class Container;
|
||||
@ -35,11 +34,8 @@ public:
|
||||
void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
|
||||
void OnClose(DeviceFD fd) override;
|
||||
|
||||
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
||||
void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
|
||||
u32 stride, android::BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect,
|
||||
std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences);
|
||||
/// Performs a screen flip, compositing each buffer.
|
||||
void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers);
|
||||
|
||||
Kernel::KEvent* QueryEvent(u32 event_id) override;
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
@ -21,13 +22,8 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
|
||||
switch (command.group) {
|
||||
case 0x0:
|
||||
switch (command.cmd) {
|
||||
case 0x1: {
|
||||
auto& host1x_file = core.Host1xDeviceFile();
|
||||
if (!host1x_file.fd_to_id.contains(fd)) {
|
||||
host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
|
||||
}
|
||||
case 0x1:
|
||||
return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
|
||||
}
|
||||
case 0x2:
|
||||
return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
|
||||
case 0x3:
|
||||
@ -72,15 +68,12 @@ void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||
system.SetNVDECActive(true);
|
||||
sessions[fd] = session_id;
|
||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||
}
|
||||
|
||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||
auto& host1x_file = core.Host1xDeviceFile();
|
||||
const auto iter = host1x_file.fd_to_id.find(fd);
|
||||
if (iter != host1x_file.fd_to_id.end()) {
|
||||
system.GPU().ClearCdmaInstance(iter->second);
|
||||
}
|
||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||
system.SetNVDECActive(false);
|
||||
auto it = sessions.find(fd);
|
||||
if (it != sessions.end()) {
|
||||
|
@ -55,8 +55,9 @@ std::size_t WriteVectors(std::span<u8> dst, const std::vector<T>& src, std::size
|
||||
|
||||
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_,
|
||||
NvCore::ChannelType channel_type_)
|
||||
: nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()},
|
||||
nvmap{core.GetNvMapFile()}, channel_type{channel_type_} {
|
||||
: nvdevice{system_}, host1x{system_.Host1x()}, core{core_},
|
||||
syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()},
|
||||
channel_type{channel_type_} {
|
||||
auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated;
|
||||
if (syncpts_accumulated.empty()) {
|
||||
channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
|
||||
@ -95,24 +96,24 @@ NvResult nvhost_nvdec_common::Submit(IoctlSubmit& params, std::span<u8> data, De
|
||||
offset += SliceVectors(data, syncpt_increments, params.syncpoint_count, offset);
|
||||
offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
auto* session = core.GetSession(sessions[fd]);
|
||||
|
||||
if (gpu.UseNvdec()) {
|
||||
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
|
||||
const SyncptIncr& syncpt_incr = syncpt_increments[i];
|
||||
fence_thresholds[i] =
|
||||
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
|
||||
}
|
||||
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
|
||||
const SyncptIncr& syncpt_incr = syncpt_increments[i];
|
||||
fence_thresholds[i] =
|
||||
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
|
||||
}
|
||||
|
||||
for (const auto& cmd_buffer : command_buffers) {
|
||||
const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
|
||||
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
|
||||
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
|
||||
session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
|
||||
cmdlist.size() * sizeof(u32));
|
||||
gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
|
||||
Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader,
|
||||
Core::Memory::GuestMemoryFlags::SafeRead>
|
||||
cmdlist(session->process->GetMemory(), object->address + cmd_buffer.offset,
|
||||
cmd_buffer.word_count);
|
||||
host1x.PushEntries(fd, std::move(cmdlist));
|
||||
}
|
||||
|
||||
// Some games expect command_buffers to be written back
|
||||
offset = 0;
|
||||
offset += WriteVectors(data, command_buffers, offset);
|
||||
|
@ -119,6 +119,7 @@ protected:
|
||||
|
||||
Kernel::KEvent* QueryEvent(u32 event_id) override;
|
||||
|
||||
Tegra::Host1x::Host1x& host1x;
|
||||
u32 channel_syncpoint;
|
||||
s32_le nvmap_fd{};
|
||||
u32_le submit_timeout{};
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
@ -21,13 +22,8 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
switch (command.group) {
|
||||
case 0x0:
|
||||
switch (command.cmd) {
|
||||
case 0x1: {
|
||||
auto& host1x_file = core.Host1xDeviceFile();
|
||||
if (!host1x_file.fd_to_id.contains(fd)) {
|
||||
host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
|
||||
}
|
||||
case 0x1:
|
||||
return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd);
|
||||
}
|
||||
case 0x2:
|
||||
return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output);
|
||||
case 0x3:
|
||||
@ -70,14 +66,11 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
|
||||
void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||
sessions[fd] = session_id;
|
||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::VIC, channel_syncpoint);
|
||||
}
|
||||
|
||||
void nvhost_vic::OnClose(DeviceFD fd) {
|
||||
auto& host1x_file = core.Host1xDeviceFile();
|
||||
const auto iter = host1x_file.fd_to_id.find(fd);
|
||||
if (iter != host1x_file.fd_to_id.end()) {
|
||||
system.GPU().ClearCdmaInstance(iter->second);
|
||||
}
|
||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::VIC);
|
||||
sessions.erase(fd);
|
||||
}
|
||||
|
||||
|
@ -14,24 +14,20 @@
|
||||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
|
||||
namespace {
|
||||
|
||||
Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||
std::unique_ptr<Kernel::KPageGroup>* out_page_group,
|
||||
Core::System& system, u32 size) {
|
||||
Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group,
|
||||
Core::System& system, u32 size) {
|
||||
using Core::Memory::YUZU_PAGESIZE;
|
||||
|
||||
// Allocate memory for the system shared buffer.
|
||||
// FIXME: Because the gmmu can only point to cpu addresses, we need
|
||||
// to map this in the application space to allow it to be used.
|
||||
// FIXME: Add proper smmu emulation.
|
||||
// FIXME: This memory belongs to vi's .data section.
|
||||
auto& kernel = system.Kernel();
|
||||
auto* process = system.ApplicationProcess();
|
||||
auto& page_table = process->GetPageTable();
|
||||
|
||||
// Hold a temporary page group reference while we try to map it.
|
||||
auto pg = std::make_unique<Kernel::KPageGroup>(
|
||||
@ -43,6 +39,30 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
|
||||
Kernel::KMemoryManager::Direction::FromBack)));
|
||||
|
||||
// Fill the output data with red.
|
||||
for (auto& block : *pg) {
|
||||
u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
|
||||
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
|
||||
|
||||
for (; start < end; start++) {
|
||||
*start = 0xFF0000FF;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the mapped page group.
|
||||
*out_page_group = std::move(pg);
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MapSharedBufferIntoProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||
std::unique_ptr<Kernel::KPageGroup>& pg,
|
||||
Kernel::KProcess* process, Core::System& system) {
|
||||
using Core::Memory::YUZU_PAGESIZE;
|
||||
|
||||
auto& page_table = process->GetPageTable();
|
||||
|
||||
// Get bounds of where mapping is possible.
|
||||
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
|
||||
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
|
||||
@ -64,9 +84,6 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||
// Return failure, if necessary
|
||||
R_UNLESS(i < 64, res);
|
||||
|
||||
// Return the mapped page group.
|
||||
*out_page_group = std::move(pg);
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -135,6 +152,13 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D
|
||||
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size, nvmap_fd));
|
||||
}
|
||||
|
||||
void FreeHandle(u32 handle, Nvidia::Module& nvdrv, Nvidia::DeviceFD nvmap_fd) {
|
||||
auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
|
||||
ASSERT(nvmap != nullptr);
|
||||
|
||||
R_ASSERT(FreeNvMapHandle(*nvmap, handle, nvmap_fd));
|
||||
}
|
||||
|
||||
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
|
||||
constexpr u32 SharedBufferBlockLinearBpp = 4;
|
||||
|
||||
@ -186,53 +210,97 @@ FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& fli
|
||||
|
||||
FbShareBufferManager::~FbShareBufferManager() = default;
|
||||
|
||||
Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) {
|
||||
Result FbShareBufferManager::Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id,
|
||||
u64* out_layer_handle, u64 display_id,
|
||||
LayerBlending blending) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
// Ensure we have not already created a buffer.
|
||||
R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed);
|
||||
// Ensure we haven't already created.
|
||||
const u64 aruid = owner_process->GetProcessId();
|
||||
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
|
||||
|
||||
// Allocate memory and space for the shared buffer.
|
||||
Common::ProcessAddress map_address;
|
||||
R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address),
|
||||
std::addressof(m_buffer_page_group), m_system,
|
||||
SharedBufferSize));
|
||||
// Allocate memory for the shared buffer if needed.
|
||||
if (!m_buffer_page_group) {
|
||||
R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
|
||||
SharedBufferSize));
|
||||
|
||||
// Record buffer id.
|
||||
m_buffer_id = m_next_buffer_id++;
|
||||
|
||||
// Record display id.
|
||||
m_display_id = display_id;
|
||||
}
|
||||
|
||||
// Map into process.
|
||||
Common::ProcessAddress map_address{};
|
||||
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
||||
owner_process, m_system));
|
||||
|
||||
// Create new session.
|
||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, FbShareSession{});
|
||||
auto& session = it->second;
|
||||
|
||||
auto& container = m_nvdrv->GetContainer();
|
||||
m_session_id = container.OpenSession(m_system.ApplicationProcess());
|
||||
m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id);
|
||||
session.session_id = container.OpenSession(owner_process);
|
||||
session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
|
||||
|
||||
// Create an nvmap handle for the buffer and assign the memory to it.
|
||||
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, m_nvmap_fd,
|
||||
map_address, SharedBufferSize));
|
||||
|
||||
// Record the display id.
|
||||
m_display_id = display_id;
|
||||
R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
|
||||
session.nvmap_fd, map_address, SharedBufferSize));
|
||||
|
||||
// Create and open a layer for the display.
|
||||
m_layer_id = m_flinger.CreateLayer(m_display_id).value();
|
||||
m_flinger.OpenLayer(m_layer_id);
|
||||
|
||||
// Set up the buffer.
|
||||
m_buffer_id = m_next_buffer_id++;
|
||||
session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value();
|
||||
m_flinger.OpenLayer(session.layer_id);
|
||||
|
||||
// Get the layer.
|
||||
VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id);
|
||||
VI::Layer* layer = m_flinger.FindLayer(m_display_id, session.layer_id);
|
||||
ASSERT(layer != nullptr);
|
||||
|
||||
// Get the producer and set preallocated buffers.
|
||||
auto& producer = layer->GetBufferQueue();
|
||||
MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle);
|
||||
MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle);
|
||||
MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle);
|
||||
MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle);
|
||||
|
||||
// Assign outputs.
|
||||
*out_buffer_id = m_buffer_id;
|
||||
*out_layer_id = m_layer_id;
|
||||
*out_layer_handle = session.layer_id;
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FbShareBufferManager::Finalize(Kernel::KProcess* owner_process) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
if (m_buffer_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 aruid = owner_process->GetProcessId();
|
||||
const auto it = m_sessions.find(aruid);
|
||||
if (it == m_sessions.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& session = it->second;
|
||||
|
||||
// Destroy the layer.
|
||||
m_flinger.DestroyLayer(session.layer_id);
|
||||
|
||||
// Close nvmap handle.
|
||||
FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);
|
||||
|
||||
// Close nvmap device.
|
||||
m_nvdrv->Close(session.nvmap_fd);
|
||||
|
||||
// Close session.
|
||||
auto& container = m_nvdrv->GetContainer();
|
||||
container.CloseSession(session.session_id);
|
||||
|
||||
// Erase.
|
||||
m_sessions.erase(it);
|
||||
}
|
||||
|
||||
Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||
s32* out_nvmap_handle,
|
||||
SharedMemoryPoolLayout* out_pool_layout,
|
||||
@ -242,17 +310,18 @@ Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||
|
||||
R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
|
||||
R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
|
||||
R_UNLESS(m_sessions.contains(applet_resource_user_id), VI::ResultNotFound);
|
||||
|
||||
*out_pool_layout = SharedBufferPoolLayout;
|
||||
*out_buffer_size = SharedBufferSize;
|
||||
*out_nvmap_handle = m_buffer_nvmap_handle;
|
||||
*out_nvmap_handle = m_sessions[applet_resource_user_id].buffer_nvmap_handle;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
|
||||
// Ensure the layer id is valid.
|
||||
R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound);
|
||||
R_UNLESS(layer_id > 0, VI::ResultNotFound);
|
||||
|
||||
// Get the layer.
|
||||
VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
|
||||
@ -309,6 +378,10 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
ON_RESULT_FAILURE {
|
||||
producer.CancelBuffer(static_cast<s32>(slot), fence);
|
||||
};
|
||||
|
||||
// Queue the buffer to the producer.
|
||||
android::QueueBufferInput input{};
|
||||
android::QueueBufferOutput output{};
|
||||
@ -342,4 +415,33 @@ Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadab
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FbShareBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
|
||||
std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
|
||||
Common::ScratchBuffer<u32> scratch;
|
||||
|
||||
// TODO: this could be optimized
|
||||
s64 e = -1280 * 768 * 4;
|
||||
for (auto& block : *m_buffer_page_group) {
|
||||
u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
|
||||
u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
|
||||
|
||||
for (; start < end; start++) {
|
||||
*start = 0;
|
||||
|
||||
if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
|
||||
*start = capture_buffer[e];
|
||||
}
|
||||
e++;
|
||||
}
|
||||
|
||||
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
|
||||
m_system.GPU().InvalidateRegion(addr, end - start);
|
||||
});
|
||||
}
|
||||
|
||||
*out_was_written = true;
|
||||
*out_layer_index = 1;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Nvnflinger
|
||||
|
@ -3,9 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||
|
||||
@ -29,13 +32,18 @@ struct SharedMemoryPoolLayout {
|
||||
};
|
||||
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
|
||||
|
||||
struct FbShareSession;
|
||||
|
||||
class FbShareBufferManager final {
|
||||
public:
|
||||
explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
|
||||
std::shared_ptr<Nvidia::Module> nvdrv);
|
||||
~FbShareBufferManager();
|
||||
|
||||
Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id);
|
||||
Result Initialize(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle,
|
||||
u64 display_id, LayerBlending blending);
|
||||
void Finalize(Kernel::KProcess* owner_process);
|
||||
|
||||
Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
|
||||
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
|
||||
u64 applet_resource_user_id);
|
||||
@ -45,6 +53,8 @@ public:
|
||||
u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
|
||||
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
|
||||
|
||||
private:
|
||||
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
|
||||
|
||||
@ -52,11 +62,8 @@ private:
|
||||
u64 m_next_buffer_id = 1;
|
||||
u64 m_display_id = 0;
|
||||
u64 m_buffer_id = 0;
|
||||
u64 m_layer_id = 0;
|
||||
u32 m_buffer_nvmap_handle = 0;
|
||||
SharedMemoryPoolLayout m_pool_layout = {};
|
||||
Nvidia::DeviceFD m_nvmap_fd = {};
|
||||
Nvidia::NvCore::SessionId m_session_id = {};
|
||||
std::map<u64, FbShareSession> m_sessions;
|
||||
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
|
||||
|
||||
std::mutex m_guard;
|
||||
@ -65,4 +72,11 @@ private:
|
||||
std::shared_ptr<Nvidia::Module> m_nvdrv;
|
||||
};
|
||||
|
||||
struct FbShareSession {
|
||||
Nvidia::DeviceFD nvmap_fd = {};
|
||||
Nvidia::NvCore::SessionId session_id = {};
|
||||
u64 layer_id = {};
|
||||
u32 buffer_nvmap_handle = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvnflinger
|
||||
|
216
src/core/hle/service/nvnflinger/hardware_composer.cpp
Normal file
216
src/core/hle/service/nvnflinger/hardware_composer.cpp
Normal file
@ -0,0 +1,216 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvnflinger/hardware_composer.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||
#include "core/hle/service/vi/display/vi_display.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
|
||||
namespace {
|
||||
|
||||
s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) {
|
||||
if (swap_interval <= 0) {
|
||||
// As an extension, treat nonpositive swap interval as speed multiplier.
|
||||
if (out_speed_scale) {
|
||||
*out_speed_scale = 2.f * static_cast<f32>(1 - swap_interval);
|
||||
}
|
||||
|
||||
swap_interval = 1;
|
||||
}
|
||||
|
||||
if (swap_interval >= 5) {
|
||||
// As an extension, treat high swap interval as precise speed control.
|
||||
if (out_speed_scale) {
|
||||
*out_speed_scale = static_cast<f32>(swap_interval) / 100.f;
|
||||
}
|
||||
|
||||
swap_interval = 1;
|
||||
}
|
||||
|
||||
return swap_interval;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HardwareComposer::HardwareComposer() = default;
|
||||
HardwareComposer::~HardwareComposer() = default;
|
||||
|
||||
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
||||
Nvidia::Devices::nvdisp_disp0& nvdisp) {
|
||||
boost::container::small_vector<HwcLayer, 2> composition_stack;
|
||||
|
||||
// Set default speed limit to 100%.
|
||||
*out_speed_scale = 1.0f;
|
||||
|
||||
// Determine the number of vsync periods to wait before composing again.
|
||||
std::optional<s32> swap_interval{};
|
||||
bool has_acquired_buffer{};
|
||||
|
||||
// Acquire all necessary framebuffers.
|
||||
for (size_t i = 0; i < display.GetNumLayers(); i++) {
|
||||
auto& layer = display.GetLayer(i);
|
||||
auto layer_id = layer.GetLayerId();
|
||||
|
||||
// Try to fetch the framebuffer (either new or stale).
|
||||
const auto result = this->CacheFramebufferLocked(layer, layer_id);
|
||||
|
||||
// If we failed, skip this layer.
|
||||
if (result == CacheStatus::NoBufferAvailable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we acquired a new buffer, we need to present.
|
||||
if (result == CacheStatus::BufferAcquired) {
|
||||
has_acquired_buffer = true;
|
||||
}
|
||||
|
||||
const auto& buffer = m_framebuffers[layer_id];
|
||||
const auto& item = buffer.item;
|
||||
const auto& igbp_buffer = *item.graphic_buffer;
|
||||
|
||||
// TODO: get proper Z-index from layer
|
||||
composition_stack.emplace_back(HwcLayer{
|
||||
.buffer_handle = igbp_buffer.BufferId(),
|
||||
.offset = igbp_buffer.Offset(),
|
||||
.format = igbp_buffer.ExternalFormat(),
|
||||
.width = igbp_buffer.Width(),
|
||||
.height = igbp_buffer.Height(),
|
||||
.stride = igbp_buffer.Stride(),
|
||||
.z_index = 0,
|
||||
.blending = layer.GetBlending(),
|
||||
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
||||
.crop_rect = item.crop,
|
||||
.acquire_fence = item.fence,
|
||||
});
|
||||
|
||||
// We need to compose again either before this frame is supposed to
|
||||
// be released, or exactly on the vsync period it should be released.
|
||||
const s32 item_swap_interval = NormalizeSwapInterval(out_speed_scale, item.swap_interval);
|
||||
|
||||
// TODO: handle cases where swap intervals are relatively prime. So far,
|
||||
// only swap intervals of 0, 1 and 2 have been observed, but if 3 were
|
||||
// to be introduced, this would cause an issue.
|
||||
if (swap_interval) {
|
||||
swap_interval = std::min(*swap_interval, item_swap_interval);
|
||||
} else {
|
||||
swap_interval = item_swap_interval;
|
||||
}
|
||||
}
|
||||
|
||||
// If any new buffers were acquired, we can present.
|
||||
if (has_acquired_buffer) {
|
||||
// Sort by Z-index.
|
||||
std::stable_sort(composition_stack.begin(), composition_stack.end(),
|
||||
[&](auto& l, auto& r) { return l.z_index < r.z_index; });
|
||||
|
||||
// Composite.
|
||||
nvdisp.Composite(composition_stack);
|
||||
}
|
||||
|
||||
// Render MicroProfile.
|
||||
MicroProfileFlip();
|
||||
|
||||
// Advance by at least one frame.
|
||||
const u32 frame_advance = swap_interval.value_or(1);
|
||||
m_frame_number += frame_advance;
|
||||
|
||||
// Release any necessary framebuffers.
|
||||
for (auto& [layer_id, framebuffer] : m_framebuffers) {
|
||||
if (framebuffer.release_frame_number > m_frame_number) {
|
||||
// Not yet ready to release this framebuffer.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!framebuffer.is_acquired) {
|
||||
// Already released.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto* layer = display.FindLayer(layer_id); layer != nullptr) {
|
||||
// TODO: support release fence
|
||||
// This is needed to prevent screen tearing
|
||||
layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
|
||||
framebuffer.is_acquired = false;
|
||||
}
|
||||
}
|
||||
|
||||
return frame_advance;
|
||||
}
|
||||
|
||||
void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {
|
||||
// Check if we are tracking a slot with this layer_id.
|
||||
const auto it = m_framebuffers.find(layer_id);
|
||||
if (it == m_framebuffers.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to release the buffer item.
|
||||
auto* const layer = display.FindLayer(layer_id);
|
||||
if (layer && it->second.is_acquired) {
|
||||
layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence());
|
||||
}
|
||||
|
||||
// Erase the slot.
|
||||
m_framebuffers.erase(it);
|
||||
}
|
||||
|
||||
bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) {
|
||||
// Attempt the update.
|
||||
const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false);
|
||||
if (status != android::Status::NoError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We succeeded, so set the new release frame info.
|
||||
framebuffer.release_frame_number =
|
||||
NormalizeSwapInterval(nullptr, framebuffer.item.swap_interval);
|
||||
framebuffer.is_acquired = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer,
|
||||
LayerId layer_id) {
|
||||
// Check if this framebuffer is already present.
|
||||
const auto it = m_framebuffers.find(layer_id);
|
||||
if (it != m_framebuffers.end()) {
|
||||
// If it's currently still acquired, we are done.
|
||||
if (it->second.is_acquired) {
|
||||
return CacheStatus::CachedBufferReused;
|
||||
}
|
||||
|
||||
// Try to acquire a new item.
|
||||
if (this->TryAcquireFramebufferLocked(layer, it->second)) {
|
||||
// We got a new item.
|
||||
return CacheStatus::BufferAcquired;
|
||||
} else {
|
||||
// We didn't acquire a new item, but we can reuse the slot.
|
||||
return CacheStatus::CachedBufferReused;
|
||||
}
|
||||
}
|
||||
|
||||
// Framebuffer is not present, so try to create it.
|
||||
Framebuffer framebuffer{};
|
||||
|
||||
if (this->TryAcquireFramebufferLocked(layer, framebuffer)) {
|
||||
// Move the buffer item into a new slot.
|
||||
m_framebuffers.emplace(layer_id, std::move(framebuffer));
|
||||
|
||||
// We succeeded.
|
||||
return CacheStatus::BufferAcquired;
|
||||
}
|
||||
|
||||
// We couldn't acquire the buffer item, so don't create a slot.
|
||||
return CacheStatus::NoBufferAvailable;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvnflinger
|
59
src/core/hle/service/nvnflinger/hardware_composer.h
Normal file
59
src/core/hle/service/nvnflinger/hardware_composer.h
Normal file
@ -0,0 +1,59 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
|
||||
#include "core/hle/service/nvnflinger/buffer_item.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
class nvdisp_disp0;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
class Display;
|
||||
class Layer;
|
||||
} // namespace Service::VI
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
|
||||
using LayerId = u64;
|
||||
|
||||
class HardwareComposer {
|
||||
public:
|
||||
explicit HardwareComposer();
|
||||
~HardwareComposer();
|
||||
|
||||
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
|
||||
Nvidia::Devices::nvdisp_disp0& nvdisp);
|
||||
void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
|
||||
|
||||
private:
|
||||
// TODO: do we want to track frame number in vi instead?
|
||||
u64 m_frame_number{0};
|
||||
|
||||
private:
|
||||
using ReleaseFrameNumber = u64;
|
||||
|
||||
struct Framebuffer {
|
||||
android::BufferItem item{};
|
||||
ReleaseFrameNumber release_frame_number{};
|
||||
bool is_acquired{false};
|
||||
};
|
||||
|
||||
enum class CacheStatus : u32 {
|
||||
NoBufferAvailable,
|
||||
BufferAcquired,
|
||||
CachedBufferReused,
|
||||
};
|
||||
|
||||
boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{};
|
||||
|
||||
private:
|
||||
bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer);
|
||||
CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvnflinger
|
40
src/core/hle/service/nvnflinger/hwc_layer.h
Normal file
40
src/core/hle/service/nvnflinger/hwc_layer.h
Normal file
@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_transform_flags.h"
|
||||
#include "core/hle/service/nvnflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
|
||||
// hwc_layer_t::blending values
|
||||
enum class LayerBlending : u32 {
|
||||
// No blending
|
||||
None = 0x100,
|
||||
|
||||
// ONE / ONE_MINUS_SRC_ALPHA
|
||||
Premultiplied = 0x105,
|
||||
|
||||
// SRC_ALPHA / ONE_MINUS_SRC_ALPHA
|
||||
Coverage = 0x405,
|
||||
};
|
||||
|
||||
struct HwcLayer {
|
||||
u32 buffer_handle;
|
||||
u32 offset;
|
||||
android::PixelFormat format;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 stride;
|
||||
s32 z_index;
|
||||
LayerBlending blending;
|
||||
android::BufferTransformFlags transform;
|
||||
Common::Rectangle<int> crop_rect;
|
||||
android::Fence acquire_fence;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvnflinger
|
@ -18,6 +18,7 @@
|
||||
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||
#include "core/hle/service/nvnflinger/hardware_composer.h"
|
||||
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
|
||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||
@ -156,7 +157,7 @@ bool Nvnflinger::CloseDisplay(u64 display_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
|
||||
std::optional<u64> Nvnflinger::CreateLayer(u64 display_id, LayerBlending blending) {
|
||||
const auto lock_guard = Lock();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
@ -165,13 +166,14 @@ std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
|
||||
}
|
||||
|
||||
const u64 layer_id = next_layer_id++;
|
||||
CreateLayerAtId(*display, layer_id);
|
||||
CreateLayerAtId(*display, layer_id, blending);
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
|
||||
void Nvnflinger::CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending) {
|
||||
const auto buffer_id = next_buffer_queue_id++;
|
||||
display.CreateLayer(layer_id, buffer_id, nvdrv->container);
|
||||
display.FindLayer(layer_id)->SetBlending(blending);
|
||||
}
|
||||
|
||||
bool Nvnflinger::OpenLayer(u64 layer_id) {
|
||||
@ -279,45 +281,18 @@ void Nvnflinger::Compose() {
|
||||
SCOPE_EXIT({ display.SignalVSyncEvent(); });
|
||||
|
||||
// Don't do anything for displays without layers.
|
||||
if (!display.HasLayers())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): Support more than 1 layer.
|
||||
VI::Layer& layer = display.GetLayer(0);
|
||||
|
||||
android::BufferItem buffer{};
|
||||
const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
|
||||
|
||||
if (status != android::Status::NoError) {
|
||||
if (!display.HasLayers()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& igbp_buffer = *buffer.graphic_buffer;
|
||||
|
||||
if (!system.IsPoweredOn()) {
|
||||
return; // We are likely shutting down
|
||||
}
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
// on which display we're drawing (Default, Internal, External, etc)
|
||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
|
||||
ASSERT(nvdisp);
|
||||
|
||||
Common::Rectangle<int> crop_rect{
|
||||
static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
|
||||
static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
|
||||
|
||||
nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
|
||||
igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
|
||||
static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect,
|
||||
buffer.fence.fences, buffer.fence.num_fences);
|
||||
|
||||
MicroProfileFlip();
|
||||
|
||||
swap_interval = buffer.swap_interval;
|
||||
|
||||
layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence());
|
||||
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,15 +309,16 @@ s64 Nvnflinger::GetNextTicks() const {
|
||||
speed_scale = 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust by speed limit determined during composition.
|
||||
speed_scale /= compose_speed_scale;
|
||||
|
||||
if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
|
||||
// Run at intended presentation rate during video playback.
|
||||
speed_scale = 1.f;
|
||||
}
|
||||
|
||||
// As an extension, treat nonpositive swap interval as framerate multiplier.
|
||||
const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval)
|
||||
: 60.f / static_cast<f32>(swap_interval);
|
||||
|
||||
const f32 effective_fps = 60.f / static_cast<f32>(swap_interval);
|
||||
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "common/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
|
||||
namespace Common {
|
||||
class Event;
|
||||
@ -46,6 +47,7 @@ class BufferQueueProducer;
|
||||
namespace Service::Nvnflinger {
|
||||
|
||||
class FbShareBufferManager;
|
||||
class HardwareComposer;
|
||||
class HosBinderDriverServer;
|
||||
|
||||
class Nvnflinger final {
|
||||
@ -71,7 +73,8 @@ public:
|
||||
/// Creates a layer on the specified display and returns the layer ID.
|
||||
///
|
||||
/// If an invalid display ID is specified, then an empty optional is returned.
|
||||
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
|
||||
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id,
|
||||
LayerBlending blending = LayerBlending::None);
|
||||
|
||||
/// Opens a layer on all displays for the given layer ID.
|
||||
bool OpenLayer(u64 layer_id);
|
||||
@ -127,7 +130,7 @@ private:
|
||||
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Creates a layer with the specified layer ID in the desired display.
|
||||
void CreateLayerAtId(VI::Display& display, u64 layer_id);
|
||||
void CreateLayerAtId(VI::Display& display, u64 layer_id, LayerBlending blending);
|
||||
|
||||
void SplitVSync(std::stop_token stop_token);
|
||||
|
||||
@ -143,6 +146,7 @@ private:
|
||||
u32 next_buffer_queue_id = 1;
|
||||
|
||||
s32 swap_interval = 1;
|
||||
f32 compose_speed_scale = 1.0f;
|
||||
|
||||
bool is_abandoned = false;
|
||||
|
||||
|
@ -22,7 +22,7 @@ LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& syste
|
||||
SharedMemory& shared_memory)
|
||||
: m_system{system}, m_shared_memory{shared_memory} {}
|
||||
|
||||
Result LocalSystemClockContextWriter::Write(SystemClockContext& context) {
|
||||
Result LocalSystemClockContextWriter::Write(const SystemClockContext& context) {
|
||||
if (m_in_use) {
|
||||
R_SUCCEED_IF(context == m_context);
|
||||
m_context = context;
|
||||
@ -43,7 +43,7 @@ NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& s
|
||||
SystemClockCore& system_clock)
|
||||
: m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {}
|
||||
|
||||
Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) {
|
||||
Result NetworkSystemClockContextWriter::Write(const SystemClockContext& context) {
|
||||
s64 time{};
|
||||
[[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time);
|
||||
|
||||
@ -66,7 +66,7 @@ EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWrit
|
||||
Core::System& system)
|
||||
: m_system{system} {}
|
||||
|
||||
Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) {
|
||||
Result EphemeralNetworkSystemClockContextWriter::Write(const SystemClockContext& context) {
|
||||
if (m_in_use) {
|
||||
R_SUCCEED_IF(context == m_context);
|
||||
m_context = context;
|
||||
|
@ -24,7 +24,7 @@ private:
|
||||
public:
|
||||
virtual ~ContextWriter() = default;
|
||||
|
||||
virtual Result Write(SystemClockContext& context) = 0;
|
||||
virtual Result Write(const SystemClockContext& context) = 0;
|
||||
void SignalAllNodes();
|
||||
void Link(OperationEvent& operation_event);
|
||||
|
||||
@ -37,7 +37,7 @@ class LocalSystemClockContextWriter : public ContextWriter {
|
||||
public:
|
||||
explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory);
|
||||
|
||||
Result Write(SystemClockContext& context) override;
|
||||
Result Write(const SystemClockContext& context) override;
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
@ -52,7 +52,7 @@ public:
|
||||
explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory,
|
||||
SystemClockCore& system_clock);
|
||||
|
||||
Result Write(SystemClockContext& context) override;
|
||||
Result Write(const SystemClockContext& context) override;
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
@ -67,7 +67,7 @@ class EphemeralNetworkSystemClockContextWriter : public ContextWriter {
|
||||
public:
|
||||
EphemeralNetworkSystemClockContextWriter(Core::System& system);
|
||||
|
||||
Result Write(SystemClockContext& context) override;
|
||||
Result Write(const SystemClockContext& context) override;
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
|
||||
void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) {
|
||||
void StandardLocalSystemClockCore::Initialize(const SystemClockContext& context, s64 time) {
|
||||
SteadyClockTimePoint time_point{};
|
||||
if (GetCurrentTimePoint(time_point) == ResultSuccess &&
|
||||
context.steady_time_point.IdMatches(time_point)) {
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
: SystemClockCore{steady_clock} {}
|
||||
~StandardLocalSystemClockCore() override = default;
|
||||
|
||||
void Initialize(SystemClockContext& context, s64 time);
|
||||
void Initialize(const SystemClockContext& context, s64 time);
|
||||
};
|
||||
|
||||
} // namespace Service::PSC::Time
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
namespace Service::PSC::Time {
|
||||
|
||||
void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) {
|
||||
void StandardNetworkSystemClockCore::Initialize(const SystemClockContext& context, s64 accuracy) {
|
||||
if (SetContextAndWrite(context) != ResultSuccess) {
|
||||
LOG_ERROR(Service_Time, "Failed to SetContext");
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public:
|
||||
: SystemClockCore{steady_clock} {}
|
||||
~StandardNetworkSystemClockCore() override = default;
|
||||
|
||||
void Initialize(SystemClockContext& context, s64 accuracy);
|
||||
void Initialize(const SystemClockContext& context, s64 accuracy);
|
||||
bool IsAccuracySufficient();
|
||||
|
||||
private:
|
||||
|
@ -46,7 +46,7 @@ Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context)
|
||||
R_RETURN(m_local_system_clock.GetContext(out_context));
|
||||
}
|
||||
|
||||
Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) {
|
||||
Result StandardUserSystemClockCore::SetContext(const SystemClockContext& context) {
|
||||
R_RETURN(ResultNotImplemented);
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
Result SetAutomaticCorrection(bool automatic_correction);
|
||||
|
||||
Result GetContext(SystemClockContext& out_context) const override;
|
||||
Result SetContext(SystemClockContext& context) override;
|
||||
Result SetContext(const SystemClockContext& context) override;
|
||||
|
||||
Result GetTimePoint(SteadyClockTimePoint& out_time_point);
|
||||
void SetTimePointAndSignal(SteadyClockTimePoint& time_point);
|
||||
|
@ -51,12 +51,12 @@ Result SystemClockCore::GetContext(SystemClockContext& out_context) const {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemClockCore::SetContext(SystemClockContext& context) {
|
||||
Result SystemClockCore::SetContext(const SystemClockContext& context) {
|
||||
m_context = context;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) {
|
||||
Result SystemClockCore::SetContextAndWrite(const SystemClockContext& context) {
|
||||
R_TRY(SetContext(context));
|
||||
|
||||
if (m_context_writer) {
|
||||
|
@ -41,8 +41,8 @@ public:
|
||||
}
|
||||
|
||||
virtual Result GetContext(SystemClockContext& out_context) const;
|
||||
virtual Result SetContext(SystemClockContext& context);
|
||||
Result SetContextAndWrite(SystemClockContext& context);
|
||||
virtual Result SetContext(const SystemClockContext& context);
|
||||
Result SetContextAndWrite(const SystemClockContext& context);
|
||||
|
||||
void LinkOperationEvent(OperationEvent& operation_event);
|
||||
|
||||
|
@ -78,8 +78,9 @@ Result ServiceManager::GetStaticServiceAsServiceManager(OutInterface<StaticServi
|
||||
}
|
||||
|
||||
Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected,
|
||||
Common::UUID& clock_source_id, s64 rtc_offset,
|
||||
s64 internal_offset, s64 test_offset) {
|
||||
const Common::UUID& clock_source_id,
|
||||
s64 rtc_offset, s64 internal_offset,
|
||||
s64 test_offset) {
|
||||
LOG_DEBUG(Service_Time,
|
||||
"called. is_rtc_reset_detected={} clock_source_id={} rtc_offset={} "
|
||||
"internal_offset={} test_offset={}",
|
||||
@ -102,7 +103,8 @@ Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected,
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) {
|
||||
Result ServiceManager::SetupStandardLocalSystemClockCore(const SystemClockContext& context,
|
||||
s64 time) {
|
||||
LOG_DEBUG(Service_Time,
|
||||
"called. context={} context.steady_time_point.clock_source_id={} time={}", context,
|
||||
context.steady_time_point.clock_source_id.RawString(), time);
|
||||
@ -114,7 +116,7 @@ Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& con
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context,
|
||||
Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext context,
|
||||
s64 accuracy) {
|
||||
LOG_DEBUG(Service_Time, "called. context={} steady_time_point.clock_source_id={} accuracy={}",
|
||||
context, context.steady_time_point.clock_source_id.RawString(), accuracy);
|
||||
@ -131,7 +133,7 @@ Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& c
|
||||
}
|
||||
|
||||
Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correction,
|
||||
SteadyClockTimePoint& time_point) {
|
||||
SteadyClockTimePoint time_point) {
|
||||
LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}",
|
||||
automatic_correction, time_point, time_point.clock_source_id.RawString());
|
||||
|
||||
@ -144,9 +146,9 @@ Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correctio
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name, RuleVersion& rule_version,
|
||||
u32 location_count,
|
||||
SteadyClockTimePoint& time_point,
|
||||
Result ServiceManager::SetupTimeZoneServiceCore(const LocationName& name,
|
||||
const RuleVersion& rule_version, u32 location_count,
|
||||
const SteadyClockTimePoint& time_point,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> rule_buffer) {
|
||||
LOG_DEBUG(Service_Time,
|
||||
"called. name={} rule_version={} location_count={} time_point={} "
|
||||
|
@ -34,14 +34,15 @@ public:
|
||||
Result GetStaticServiceAsAdmin(OutInterface<StaticService> out_service);
|
||||
Result GetStaticServiceAsRepair(OutInterface<StaticService> out_service);
|
||||
Result GetStaticServiceAsServiceManager(OutInterface<StaticService> out_service);
|
||||
Result SetupStandardSteadyClockCore(bool is_rtc_reset_detected, Common::UUID& clock_source_id,
|
||||
s64 rtc_offset, s64 internal_offset, s64 test_offset);
|
||||
Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time);
|
||||
Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy);
|
||||
Result SetupStandardSteadyClockCore(bool is_rtc_reset_detected,
|
||||
const Common::UUID& clock_source_id, s64 rtc_offset,
|
||||
s64 internal_offset, s64 test_offset);
|
||||
Result SetupStandardLocalSystemClockCore(const SystemClockContext& context, s64 time);
|
||||
Result SetupStandardNetworkSystemClockCore(SystemClockContext context, s64 accuracy);
|
||||
Result SetupStandardUserSystemClockCore(bool automatic_correction,
|
||||
SteadyClockTimePoint& time_point);
|
||||
Result SetupTimeZoneServiceCore(LocationName& name, RuleVersion& rule_version,
|
||||
u32 location_count, SteadyClockTimePoint& time_point,
|
||||
SteadyClockTimePoint time_point);
|
||||
Result SetupTimeZoneServiceCore(const LocationName& name, const RuleVersion& rule_version,
|
||||
u32 location_count, const SteadyClockTimePoint& time_point,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> rule_buffer);
|
||||
Result SetupEphemeralNetworkSystemClockCore();
|
||||
Result GetStandardLocalClockOperationEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
@ -51,11 +51,11 @@ SharedMemory::SharedMemory(Core::System& system)
|
||||
std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr));
|
||||
}
|
||||
|
||||
void SharedMemory::SetLocalSystemContext(SystemClockContext& context) {
|
||||
void SharedMemory::SetLocalSystemContext(const SystemClockContext& context) {
|
||||
WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context);
|
||||
}
|
||||
|
||||
void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) {
|
||||
void SharedMemory::SetNetworkSystemContext(const SystemClockContext& context) {
|
||||
WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context);
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 ti
|
||||
{time_point, clock_source_id});
|
||||
}
|
||||
|
||||
void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) {
|
||||
void SharedMemory::SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point) {
|
||||
WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point);
|
||||
}
|
||||
|
||||
|
@ -54,10 +54,10 @@ public:
|
||||
return m_k_shared_memory;
|
||||
}
|
||||
|
||||
void SetLocalSystemContext(SystemClockContext& context);
|
||||
void SetNetworkSystemContext(SystemClockContext& context);
|
||||
void SetLocalSystemContext(const SystemClockContext& context);
|
||||
void SetNetworkSystemContext(const SystemClockContext& context);
|
||||
void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff);
|
||||
void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point);
|
||||
void SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point);
|
||||
void SetAutomaticCorrection(bool automatic_correction);
|
||||
void UpdateBaseTime(s64 time);
|
||||
|
||||
|
@ -198,8 +198,8 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time,
|
||||
SystemClockContext& context) {
|
||||
Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
|
||||
Out<s64> out_time, const SystemClockContext& context) {
|
||||
SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); });
|
||||
|
||||
R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized);
|
||||
@ -231,10 +231,9 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType t
|
||||
R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
|
||||
}
|
||||
|
||||
Result StaticService::GetClockSnapshotFromSystemClockContext(TimeType type,
|
||||
OutClockSnapshot out_snapshot,
|
||||
SystemClockContext& user_context,
|
||||
SystemClockContext& network_context) {
|
||||
Result StaticService::GetClockSnapshotFromSystemClockContext(
|
||||
TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context,
|
||||
const SystemClockContext& network_context) {
|
||||
SCOPE_EXIT({
|
||||
LOG_DEBUG(Service_Time,
|
||||
"called. type={} user_context={} network_context={} out_snapshot={}", type,
|
||||
@ -294,8 +293,9 @@ Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a,
|
||||
}
|
||||
|
||||
Result StaticService::GetClockSnapshotImpl(OutClockSnapshot out_snapshot,
|
||||
SystemClockContext& user_context,
|
||||
SystemClockContext& network_context, TimeType type) {
|
||||
const SystemClockContext& user_context,
|
||||
const SystemClockContext& network_context,
|
||||
TimeType type) {
|
||||
out_snapshot->user_context = user_context;
|
||||
out_snapshot->network_context = network_context;
|
||||
|
||||
|
@ -55,18 +55,19 @@ public:
|
||||
Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
|
||||
Out<SteadyClockTimePoint> out_time_point);
|
||||
Result CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time,
|
||||
SystemClockContext& context);
|
||||
const SystemClockContext& context);
|
||||
Result GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type);
|
||||
Result GetClockSnapshotFromSystemClockContext(TimeType type, OutClockSnapshot out_snapshot,
|
||||
SystemClockContext& user_context,
|
||||
SystemClockContext& network_context);
|
||||
const SystemClockContext& user_context,
|
||||
const SystemClockContext& network_context);
|
||||
Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference,
|
||||
InClockSnapshot a, InClockSnapshot b);
|
||||
Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b);
|
||||
|
||||
private:
|
||||
Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot, SystemClockContext& user_context,
|
||||
SystemClockContext& network_context, TimeType type);
|
||||
Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot,
|
||||
const SystemClockContext& user_context,
|
||||
const SystemClockContext& network_context, TimeType type);
|
||||
|
||||
Core::System& m_system;
|
||||
StaticServiceSetupInfo m_setup_info;
|
||||
|
@ -53,7 +53,7 @@ Result SystemClock::GetSystemClockContext(Out<SystemClockContext> out_context) {
|
||||
R_RETURN(m_clock_core.GetContext(*out_context));
|
||||
}
|
||||
|
||||
Result SystemClock::SetSystemClockContext(SystemClockContext& context) {
|
||||
Result SystemClock::SetSystemClockContext(const SystemClockContext& context) {
|
||||
LOG_DEBUG(Service_Time, "called. context={}", context);
|
||||
|
||||
R_UNLESS(m_can_write_clock, ResultPermissionDenied);
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
Result GetCurrentTime(Out<s64> out_time);
|
||||
Result SetCurrentTime(s64 time);
|
||||
Result GetSystemClockContext(Out<SystemClockContext> out_context);
|
||||
Result SetSystemClockContext(SystemClockContext& context);
|
||||
Result SetSystemClockContext(const SystemClockContext& context);
|
||||
Result GetOperationEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
||||
private:
|
||||
|
@ -55,7 +55,7 @@ constexpr bool GetTimeZoneTime(s64& out_time, const Tz::Rule& rule, s64 time, s3
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) {
|
||||
void TimeZone::SetTimePoint(const SteadyClockTimePoint& time_point) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
m_steady_clock_time_point = time_point;
|
||||
}
|
||||
@ -65,7 +65,7 @@ void TimeZone::SetTotalLocationNameCount(u32 count) {
|
||||
m_total_location_name_count = count;
|
||||
}
|
||||
|
||||
void TimeZone::SetRuleVersion(RuleVersion& rule_version) {
|
||||
void TimeZone::SetRuleVersion(const RuleVersion& rule_version) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
m_rule_version = rule_version;
|
||||
}
|
||||
@ -123,7 +123,7 @@ Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
|
||||
R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule));
|
||||
}
|
||||
|
||||
Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) {
|
||||
Result TimeZone::ParseBinary(const LocationName& name, std::span<const u8> binary) {
|
||||
std::scoped_lock l{m_mutex};
|
||||
|
||||
Tz::Rule tmp_rule{};
|
||||
|
@ -23,9 +23,9 @@ public:
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void SetTimePoint(SteadyClockTimePoint& time_point);
|
||||
void SetTimePoint(const SteadyClockTimePoint& time_point);
|
||||
void SetTotalLocationNameCount(u32 count);
|
||||
void SetRuleVersion(RuleVersion& rule_version);
|
||||
void SetRuleVersion(const RuleVersion& rule_version);
|
||||
Result GetLocationName(LocationName& out_name);
|
||||
Result GetTotalLocationCount(u32& out_count);
|
||||
Result GetRuleVersion(RuleVersion& out_rule_version);
|
||||
@ -36,7 +36,7 @@ public:
|
||||
const Tz::Rule& rule);
|
||||
Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
|
||||
CalendarAdditionalInfo& calendar_additional, s64 time);
|
||||
Result ParseBinary(LocationName& name, std::span<const u8> binary);
|
||||
Result ParseBinary(const LocationName& name, std::span<const u8> binary);
|
||||
Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary);
|
||||
Result ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count,
|
||||
const CalendarTime& calendar, const Tz::Rule& rule);
|
||||
|
@ -42,7 +42,7 @@ Result TimeZoneService::GetDeviceLocationName(Out<LocationName> out_location_nam
|
||||
R_RETURN(m_time_zone.GetLocationName(*out_location_name));
|
||||
}
|
||||
|
||||
Result TimeZoneService::SetDeviceLocationName(LocationName& location_name) {
|
||||
Result TimeZoneService::SetDeviceLocationName(const LocationName& location_name) {
|
||||
LOG_DEBUG(Service_Time, "called. This function is not implemented!");
|
||||
|
||||
R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied);
|
||||
@ -62,7 +62,7 @@ Result TimeZoneService::LoadLocationNameList(
|
||||
R_RETURN(ResultNotImplemented);
|
||||
}
|
||||
|
||||
Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, LocationName& location_name) {
|
||||
Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, const LocationName& location_name) {
|
||||
LOG_DEBUG(Service_Time, "called. This function is not implemented!");
|
||||
|
||||
R_RETURN(ResultNotImplemented);
|
||||
@ -86,7 +86,7 @@ Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
|
||||
}
|
||||
|
||||
Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(
|
||||
LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) {
|
||||
const LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) {
|
||||
LOG_DEBUG(Service_Time, "called. location_name={}", location_name);
|
||||
|
||||
R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied);
|
||||
|
@ -31,16 +31,16 @@ public:
|
||||
~TimeZoneService() override = default;
|
||||
|
||||
Result GetDeviceLocationName(Out<LocationName> out_location_name);
|
||||
Result SetDeviceLocationName(LocationName& location_name);
|
||||
Result SetDeviceLocationName(const LocationName& location_name);
|
||||
Result GetTotalLocationNameCount(Out<u32> out_count);
|
||||
Result LoadLocationNameList(Out<u32> out_count,
|
||||
OutArray<LocationName, BufferAttr_HipcMapAlias> out_names,
|
||||
u32 index);
|
||||
Result LoadTimeZoneRule(OutRule out_rule, LocationName& location_name);
|
||||
Result LoadTimeZoneRule(OutRule out_rule, const LocationName& location_name);
|
||||
Result GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version);
|
||||
Result GetDeviceLocationNameAndUpdatedTime(Out<LocationName> location_name,
|
||||
Out<SteadyClockTimePoint> out_time_point);
|
||||
Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name,
|
||||
Result SetDeviceLocationNameWithTimeZoneRule(const LocationName& location_name,
|
||||
InBuffer<BufferAttr_HipcAutoSelect> binary);
|
||||
Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary);
|
||||
Result GetDeviceLocationNameOperationEventReadableHandle(
|
||||
|
@ -549,13 +549,13 @@ public:
|
||||
}
|
||||
|
||||
Result RegisterProcessHandle(ClientProcessId client_pid,
|
||||
InCopyHandle<Kernel::KProcess>& process) {
|
||||
InCopyHandle<Kernel::KProcess> process) {
|
||||
// Register the process.
|
||||
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.Get(), *client_pid));
|
||||
}
|
||||
|
||||
Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size,
|
||||
InCopyHandle<Kernel::KProcess>& process) {
|
||||
InCopyHandle<Kernel::KProcess> process) {
|
||||
// Validate the process.
|
||||
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
||||
#include "core/hle/service/nvnflinger/hardware_composer.h"
|
||||
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
|
||||
#include "core/hle/service/vi/display/vi_display.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
@ -43,6 +44,7 @@ Display::Display(u64 id, std::string name_,
|
||||
KernelHelpers::ServiceContext& service_context_, Core::System& system_)
|
||||
: display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
|
||||
service_context{service_context_} {
|
||||
hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>();
|
||||
vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
|
||||
}
|
||||
|
||||
@ -81,8 +83,6 @@ void Display::SignalVSyncEvent() {
|
||||
|
||||
void Display::CreateLayer(u64 layer_id, u32 binder_id,
|
||||
Service::Nvidia::NvCore::Container& nv_core) {
|
||||
ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
|
||||
|
||||
auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
|
||||
|
||||
auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
|
||||
|
@ -11,9 +11,14 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::android {
|
||||
class BufferQueueProducer;
|
||||
@ -24,8 +29,9 @@ class ServiceContext;
|
||||
}
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
class HardwareComposer;
|
||||
class HosBinderDriverServer;
|
||||
}
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class Container;
|
||||
@ -118,6 +124,10 @@ public:
|
||||
///
|
||||
const Layer* FindLayer(u64 layer_id) const;
|
||||
|
||||
Nvnflinger::HardwareComposer& GetComposer() const {
|
||||
return *hardware_composer;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 display_id;
|
||||
std::string name;
|
||||
@ -125,6 +135,7 @@ private:
|
||||
KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::vector<std::unique_ptr<Layer>> layers;
|
||||
std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer;
|
||||
Kernel::KEvent* vsync_event{};
|
||||
bool is_abandoned{};
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
|
||||
namespace Service::VI {
|
||||
@ -8,8 +9,9 @@ namespace Service::VI {
|
||||
Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
|
||||
android::BufferQueueProducer& binder_,
|
||||
std::shared_ptr<android::BufferItemConsumer>&& consumer_)
|
||||
: layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_},
|
||||
consumer{std::move(consumer_)}, open{false}, visible{true} {}
|
||||
: layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
|
||||
consumer_)},
|
||||
blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {}
|
||||
|
||||
Layer::~Layer() = default;
|
||||
|
||||
|
@ -14,6 +14,10 @@ class BufferQueueCore;
|
||||
class BufferQueueProducer;
|
||||
} // namespace Service::android
|
||||
|
||||
namespace Service::Nvnflinger {
|
||||
enum class LayerBlending : u32;
|
||||
}
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
/// Represents a single display layer.
|
||||
@ -92,12 +96,21 @@ public:
|
||||
return !std::exchange(open, true);
|
||||
}
|
||||
|
||||
Nvnflinger::LayerBlending GetBlending() {
|
||||
return blending;
|
||||
}
|
||||
|
||||
void SetBlending(Nvnflinger::LayerBlending b) {
|
||||
blending = b;
|
||||
}
|
||||
|
||||
private:
|
||||
const u64 layer_id;
|
||||
const u32 binder_id;
|
||||
android::BufferQueueCore& core;
|
||||
android::BufferQueueProducer& binder;
|
||||
std::shared_ptr<android::BufferItemConsumer> consumer;
|
||||
Service::Nvnflinger::LayerBlending blending;
|
||||
bool open;
|
||||
bool visible;
|
||||
};
|
||||
|
@ -195,8 +195,9 @@ private:
|
||||
void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 buffer_id = rp.PopRaw<u64>();
|
||||
const u64 aruid = ctx.GetPID();
|
||||
|
||||
LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id);
|
||||
LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid);
|
||||
|
||||
struct OutputParameters {
|
||||
s32 nvmap_handle;
|
||||
@ -206,7 +207,7 @@ private:
|
||||
OutputParameters out{};
|
||||
Nvnflinger::SharedMemoryPoolLayout layout{};
|
||||
const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId(
|
||||
&out.size, &out.nvmap_handle, &layout, buffer_id, 0);
|
||||
&out.size, &out.nvmap_handle, &layout, buffer_id, aruid);
|
||||
|
||||
ctx.WriteBuffer(&layout, sizeof(layout));
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user