Compare commits

...

29 Commits

Author SHA1 Message Date
38f31530f6 Android 235 2024-02-07 01:00:14 +00:00
aec2a6609d Merge yuzu-emu#12920 2024-02-07 01:00:14 +00:00
7b077b11d4 Merge yuzu-emu#12914 2024-02-07 01:00:14 +00:00
81f45ede66 Merge yuzu-emu#12903 2024-02-07 01:00:14 +00:00
4f069aa827 Merge yuzu-emu#12756 2024-02-07 01:00:14 +00:00
707e928788 Merge yuzu-emu#12749 2024-02-07 01:00:14 +00:00
0dfe24176d Merge yuzu-emu#12461 2024-02-07 01:00:14 +00:00
c10e720ba9 Merge pull request #12883 from FernandoS27/memory_manager_mem
MemoryManager: Reduce the page table size based on last big page address.
2024-02-06 10:25:03 -05:00
5016de3626 Merge pull request #12928 from german77/motion-mp
service: hid: Add multiprocess support to six axis input
2024-02-06 10:24:46 -05:00
d5fb9fd12c Merge pull request #12933 from german77/irs-interface
service: irs: Migrate service to new interface
2024-02-06 10:24:30 -05:00
c79b3af610 Merge pull request #12934 from german77/hid_debug_interface
service: hid: Migrate hid debug service to new interface
2024-02-06 10:24:20 -05:00
b6106604c4 service: hid: Migrate hid debug service to new interface 2024-02-06 00:38:46 -06:00
12b6162852 service: irs: Migrate service to new interface 2024-02-06 00:14:16 -06:00
8f192b494a service: hid: Add multiprocess support to six axis input 2024-02-05 17:19:31 -06:00
372897aac4 service: hid: Ensure aruid data is initialized 2024-02-05 17:17:21 -06:00
a2f23746c2 Merge pull request #12905 from liamwhite/hwc-release
nvnflinger: release buffers before presentation sleep
2024-02-05 13:43:22 -05:00
215b13f2a2 Merge pull request #12924 from liamwhite/pedantic-unsigned
typed_address: test values are unsigned
2024-02-05 13:43:06 -05:00
35ed9425d7 Merge pull request #12925 from german77/linux-tab
yuzu: Fully hide linux tab
2024-02-05 13:41:31 -05:00
74cc8721c7 Merge pull request #12915 from german77/cheat
dmnt: cheats: Update cheat vm to latest version
2024-02-05 13:41:21 -05:00
8ef1db78b0 Merge pull request #12916 from liamwhite/float-fix
gdb: fix load/save of fp values in a32
2024-02-05 13:41:15 -05:00
18c8f10ff2 Merge pull request #12922 from FearlessTobi/lang-mappins
.tx/config: Use language mappings for android "tx pull"
2024-02-05 13:40:53 -05:00
96d881f087 yuzu: Fully hide linux tab 2024-02-05 11:58:20 -06:00
0e950baf41 typed_address: test values are unsigned 2024-02-05 12:47:10 -05:00
8113f55f4b dmnt: cheats: Silence memory errors 2024-02-05 11:08:24 -06:00
ddbefc71cb .tx/config: Use language mappings for android "tx pull"
The language names we are using in the android resources differ from those on Transifex.

We need to manually specify mappings for them, so Transifex is able to place the files in the correct folders.
2024-02-05 15:57:13 +01:00
85143e8376 gdb: fix load/save of fp values in a32 2024-02-04 20:28:43 -05:00
504abbd6e0 dmnt: cheats: Update cheat vm to latest version 2024-02-04 17:46:20 -06:00
5eb5c96750 nvnflinger: release buffers before presentation sleep 2024-02-03 17:14:43 -05:00
f740d8b9be MemoryManager: Reduce the page table size based on last big page address. 2024-02-01 13:00:36 +01:00
168 changed files with 5787 additions and 3076 deletions

View File

@ -1,3 +1,17 @@
| 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 |
| [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 |
| [12914](https://github.com/yuzu-emu/yuzu-android//pull/12914) | [`fa47ac1c9`](https://github.com/yuzu-emu/yuzu-android//pull/12914/files) | VideoCore Refactor Part 1. | [FernandoS27](https://github.com/FernandoS27/) | 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 |
End of merge log. You can find the original README.md below the break.
-----
<!-- <!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -11,3 +11,4 @@ type = QT
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID type = ANDROID
lang_map = ja_JP:ja, ko_KR:ko, pt_BR:pt-rBR, pt_PT:pt-rPT, ru_RU:ru, vi_VN:vi, zh_CN:zh-rCN, zh_TW:zh-rTW

View File

@ -164,6 +164,7 @@ else()
if (MINGW) if (MINGW)
add_definitions(-DMINGW_HAS_SECURE_API) add_definitions(-DMINGW_HAS_SECURE_API)
add_compile_options("-msse4.1")
if (MINGW_STATIC_BUILD) if (MINGW_STATIC_BUILD)
add_definitions(-DQT_STATICPLUGIN) add_definitions(-DQT_STATICPLUGIN)

View File

@ -2,14 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
add_library(yuzu-android SHARED 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.cpp
emu_window/emu_window.h emu_window/emu_window.h
id_cache.cpp
id_cache.h
native.cpp native.cpp
native.h native.h
native_config.cpp native_config.cpp

View File

@ -3,6 +3,7 @@
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include "common/android/id_cache.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "input_common/drivers/touch_screen.h" #include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h" #include "input_common/drivers/virtual_amiibo.h"
@ -60,7 +61,8 @@ void EmuWindow_Android::OnRemoveNfcTag() {
void EmuWindow_Android::OnFrameDisplayed() { void EmuWindow_Android::OnFrameDisplayed() {
if (!m_first_frame) { if (!m_first_frame) {
EmulationSession::GetInstance().OnEmulationStarted(); Common::Android::RunJNIOnFiber<void>(
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
m_first_frame = true; m_first_frame = true;
} }
} }

View File

@ -1,13 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/android/android_common.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/loader/nro.h" #include "core/loader/nro.h"
#include "jni.h"
#include "jni/android_common/android_common.h"
#include "native.h" #include "native.h"
struct RomMetadata { struct RomMetadata {
@ -79,7 +78,7 @@ extern "C" {
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
jstring jpath) { jstring jpath) {
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
GetJString(env, jpath), FileSys::OpenMode::Read); Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read);
if (!file) { if (!file) {
return false; 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 Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
jstring jpath) { 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 Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
jstring jpath) { 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 Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
jstring jpath) { 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 Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
jstring jpath, jboolean jreload) { 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, jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
jstring jpath) { 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())); jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data())); 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, jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
jstring jpath) { 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) { void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {

View File

@ -20,6 +20,8 @@
#include <frontend_common/content_manager.h> #include <frontend_common/content_manager.h>
#include <jni.h> #include <jni.h>
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "common/dynamic_library.h" #include "common/dynamic_library.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
@ -57,8 +59,6 @@
#include "hid_core/frontend/emulated_controller.h" #include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h" #include "hid_core/hid_core.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include "jni/android_common/android_common.h"
#include "jni/id_cache.h"
#include "jni/native.h" #include "jni/native.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/renderer_vulkan/renderer_vulkan.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); std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
// Initialize system. // 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_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false); m_system.SetShuttingDown(false);
m_system.ApplySettings(); m_system.ApplySettings();
@ -411,37 +411,39 @@ void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
controller->Disconnect(); controller->Disconnect();
} }
SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
return m_software_keyboard; return m_software_keyboard;
} }
void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
int max) { int max) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(), env->CallStaticVoidMethod(Common::Android::GetDiskCacheProgressClass(),
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage), Common::Android::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
static_cast<jint>(progress), static_cast<jint>(max)); static_cast<jint>(progress), static_cast<jint>(max));
} }
void EmulationSession::OnEmulationStarted() { void EmulationSession::OnEmulationStarted() {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted()); env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
Common::Android::GetOnEmulationStarted());
} }
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(), env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
static_cast<jint>(result)); Common::Android::GetOnEmulationStopped(), static_cast<jint>(result));
} }
void EmulationSession::ChangeProgram(std::size_t program_index) { void EmulationSession::ChangeProgram(std::size_t program_index) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = Common::Android::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(), env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
Common::Android::GetOnProgramChanged(),
static_cast<jint>(program_index)); static_cast<jint>(program_index));
} }
u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) { u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
auto program_id_string = GetJString(env, jprogramId); auto program_id_string = Common::Android::GetJString(env, jprogramId);
try { try {
return std::stoull(program_id_string); return std::stoull(program_id_string);
} catch (...) { } 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, void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
[[maybe_unused]] jstring j_directory) { [[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, 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;"); jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
ToJDouble(env, max), ToJDouble(env, progress)); Common::Android::ToJDouble(env, max),
return GetJBoolean(env, jwasCancelled); Common::Android::ToJDouble(env, progress));
return Common::Android::GetJBoolean(env, jwasCancelled);
}; };
return static_cast<int>( return static_cast<int>(
ContentManager::InstallNSP(EmulationSession::GetInstance().System(), ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
*EmulationSession::GetInstance().System().GetFilesystem(), *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, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { jstring jupdatePath) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId); 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>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
updatePath, FileSys::OpenMode::Read)); updatePath, FileSys::OpenMode::Read));
@ -538,8 +541,10 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* e
jstring custom_driver_name, jstring custom_driver_name,
jstring file_redirect_dir) { jstring file_redirect_dir) {
EmulationSession::GetInstance().InitializeGpuDriver( EmulationSession::GetInstance().InitializeGpuDriver(
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir), Common::Android::GetJString(env, hook_lib_dir),
GetJString(env, custom_driver_name), GetJString(env, file_redirect_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() { [[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) { JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
const char* file_redirect_dir_{}; const char* file_redirect_dir_{};
int featureFlags{}; 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(), auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
nullptr, nullptr, file_redirect_dir_, nullptr); nullptr, nullptr, file_redirect_dir_, nullptr);
auto driver_library = std::make_shared<Common::DynamicLibrary>(handle); 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), fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version),
VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version)); VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version));
jobjectArray j_driver_info = jobjectArray j_driver_info = env->NewObjectArray(
env->NewObjectArray(2, IDCache::GetStringClass(), ToJString(env, version_string)); 2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string));
env->SetObjectArrayElement(j_driver_info, 1, ToJString(env, device.GetDriverName())); env->SetObjectArrayElement(j_driver_info, 1,
Common::Android::ToJString(env, device.GetDriverName()));
return j_driver_info; 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) { jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass clazz) {
if (Settings::IsNceEnabled()) { 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) { jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
return ToJString(env, return Common::Android::ToJString(
EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor()); env, EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { 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, void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
jint j_program_index, jint j_program_index,
jboolean j_frontend_initiated) { 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{ const Core::SystemResultStatus result{
RunEmulation(path, j_program_index, j_frontend_initiated)}; RunEmulation(path, j_program_index, j_frontend_initiated)};
if (result != Core::SystemResultStatus::Success) { if (result != Core::SystemResultStatus::Success) {
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(),
IDCache::GetExitEmulationActivity(), static_cast<int>(result)); 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, void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardText(JNIEnv* env, jclass clazz,
jstring j_text) { 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); EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input);
} }
@ -815,16 +822,16 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j
auto bis_system = auto bis_system =
EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents(); EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
if (!bis_system) { if (!bis_system) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
auto applet_nca = auto applet_nca =
bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program); bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program);
if (!applet_nca) { 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, 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, jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env, jobject jobj,
jstring jpath, jstring jpath,
jstring jprogramId) { jstring jprogramId) {
const auto path = GetJString(env, jpath); const auto path = Common::Android::GetJString(env, jpath);
const auto vFile = const auto vFile =
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path); Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
if (vFile == nullptr) { if (vFile == nullptr) {
@ -875,14 +882,15 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
auto patches = pm.GetPatches(update_raw); auto patches = pm.GetPatches(update_raw);
jobjectArray jpatchArray = jobjectArray jpatchArray =
env->NewObjectArray(patches.size(), IDCache::GetPatchClass(), nullptr); env->NewObjectArray(patches.size(), Common::Android::GetPatchClass(), nullptr);
int i = 0; int i = 0;
for (const auto& patch : patches) { for (const auto& patch : patches) {
jobject jpatch = env->NewObject( jobject jpatch = env->NewObject(
IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled, Common::Android::GetPatchClass(), Common::Android::GetPatchConstructor(), patch.enabled,
ToJString(env, patch.name), ToJString(env, patch.version), Common::Android::ToJString(env, patch.name),
static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)), Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
ToJString(env, std::to_string(patch.title_id))); 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); env->SetObjectArrayElement(jpatchArray, i, jpatch);
++i; ++i;
} }
@ -906,7 +914,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
jstring jname) { jstring jname) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); auto program_id = EmulationSession::GetProgramId(env, jprogramId);
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(), 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, 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;"); jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
ToJDouble(env, max), ToJDouble(env, progress)); Common::Android::ToJDouble(env, max),
return GetJBoolean(env, jwasCancelled); Common::Android::ToJDouble(env, progress));
return Common::Android::GetJBoolean(env, jwasCancelled);
}; };
auto& session = EmulationSession::GetInstance(); auto& session = EmulationSession::GetInstance();
std::vector<std::string> result = ContentManager::VerifyInstalledContents( std::vector<std::string> result = ContentManager::VerifyInstalledContents(
session.System(), *session.GetContentProvider(), callback); session.System(), *session.GetContentProvider(), callback);
jobjectArray jresult = jobjectArray jresult = env->NewObjectArray(result.size(), Common::Android::GetStringClass(),
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); Common::Android::ToJString(env, ""));
for (size_t i = 0; i < result.size(); ++i) { 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; 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;"); jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) { const auto callback = [env, jcallback, jlambdaInvokeMethod](size_t max, size_t progress) {
auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod, auto jwasCancelled = env->CallObjectMethod(jcallback, jlambdaInvokeMethod,
ToJDouble(env, max), ToJDouble(env, progress)); Common::Android::ToJDouble(env, max),
return GetJBoolean(env, jwasCancelled); Common::Android::ToJDouble(env, progress));
return Common::Android::GetJBoolean(env, jwasCancelled);
}; };
auto& session = EmulationSession::GetInstance(); auto& session = EmulationSession::GetInstance();
return static_cast<jint>( return static_cast<jint>(ContentManager::VerifyGameContents(
ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback)); session.System(), Common::Android::GetJString(env, jpath), callback));
} }
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
jstring jprogramId) { jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); auto program_id = EmulationSession::GetProgramId(env, jprogramId);
if (program_id == 0) { if (program_id == 0) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
auto& system = EmulationSession::GetInstance().System(); 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( const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
program_id, user_id->AsU128(), 0); 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, 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 = const auto user_save_data_root =
FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture); 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, void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
jstring jpath) { 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) { void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) {

View File

@ -2,13 +2,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include "common/android/applets/software_keyboard.h"
#include "common/detached_tasks.h" #include "common/detached_tasks.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/registered_cache.h" #include "core/file_sys/registered_cache.h"
#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/acc/profile_manager.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "frontend_common/content_manager.h" #include "frontend_common/content_manager.h"
#include "jni/applets/software_keyboard.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
@ -54,7 +54,7 @@ public:
void SetDeviceType([[maybe_unused]] int index, int type); void SetDeviceType([[maybe_unused]] int index, int type);
void OnGamepadConnectEvent([[maybe_unused]] int index); void OnGamepadConnectEvent([[maybe_unused]] int index);
void OnGamepadDisconnectEvent([[maybe_unused]] int index); void OnGamepadDisconnectEvent([[maybe_unused]] int index);
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
static void OnEmulationStarted(); static void OnEmulationStarted();
@ -79,7 +79,7 @@ private:
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
std::atomic<bool> m_is_running = false; std::atomic<bool> m_is_running = false;
std::atomic<bool> m_is_paused = 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; std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
int m_applet_id{1}; int m_applet_id{1};

View File

@ -8,11 +8,11 @@
#include "android_config.h" #include "android_config.h"
#include "android_settings.h" #include "android_settings.h"
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h" #include "common/settings.h"
#include "frontend_common/config.h" #include "frontend_common/config.h"
#include "jni/android_common/android_common.h"
#include "jni/id_cache.h"
#include "native.h" #include "native.h"
std::unique_ptr<AndroidConfig> global_config; std::unique_ptr<AndroidConfig> global_config;
@ -20,7 +20,7 @@ std::unique_ptr<AndroidConfig> per_game_config;
template <typename T> template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { 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]; auto basic_setting = Settings::values.linkage.by_key[key];
if (basic_setting != 0) { if (basic_setting != 0) {
return static_cast<Settings::Setting<T>*>(basic_setting); 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 jprogramId,
jstring jfileName) { jstring jfileName) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId); 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); const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
per_game_config = per_game_config =
std::make_unique<AndroidConfig>(config_file_name, Config::ConfigType::PerGameConfig); 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) { jboolean needGlobal) {
auto setting = getSetting<std::string>(env, jkey); auto setting = getSetting<std::string>(env, jkey);
if (setting == nullptr) { 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, 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; 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, 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) { jstring jkey) {
auto setting = getSetting<std::string>(env, jkey); auto setting = getSetting<std::string>(env, jkey);
if (setting == nullptr) { if (setting == nullptr) {
return ToJString(env, ""); return Common::Android::ToJString(env, "");
} }
if (setting->PairedSetting() == nullptr) { 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, 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) { jstring jkey) {
auto setting = getSetting<std::string>(env, jkey); auto setting = getSetting<std::string>(env, jkey);
if (setting != nullptr) { 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) { jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getGameDirs(JNIEnv* env, jobject obj) {
jclass gameDirClass = IDCache::GetGameDirClass(); jclass gameDirClass = Common::Android::GetGameDirClass();
jmethodID gameDirConstructor = IDCache::GetGameDirConstructor(); jmethodID gameDirConstructor = Common::Android::GetGameDirConstructor();
jobjectArray jgameDirArray = jobjectArray jgameDirArray =
env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr); env->NewObjectArray(AndroidSettings::values.game_dirs.size(), gameDirClass, nullptr);
for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
jobject jgameDir = jobject jgameDir = env->NewObject(
env->NewObject(gameDirClass, gameDirConstructor, gameDirClass, gameDirConstructor,
ToJString(env, AndroidSettings::values.game_dirs[i].path), Common::Android::ToJString(env, AndroidSettings::values.game_dirs[i].path),
static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan)); static_cast<jboolean>(AndroidSettings::values.game_dirs[i].deep_scan));
env->SetObjectArrayElement(jgameDirArray, i, jgameDir); env->SetObjectArrayElement(jgameDirArray, i, jgameDir);
} }
return jgameDirArray; return jgameDirArray;
@ -292,14 +292,14 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGameDirs(JNIEnv* env, jobject
} }
jobject dir = env->GetObjectArrayElement(gameDirs, 0); 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 uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
dir = env->GetObjectArrayElement(gameDirs, i); dir = env->GetObjectArrayElement(gameDirs, i);
jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField)); jstring juriString = static_cast<jstring>(env->GetObjectField(dir, uriStringField));
jboolean jdeepScanBoolean = env->GetBooleanField(dir, deepScanBooleanField); 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::values.game_dirs.push_back(
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); 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, void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject obj,
jobject gameDir) { jobject gameDir) {
jclass gameDirClass = IDCache::GetGameDirClass(); jclass gameDirClass = Common::Android::GetGameDirClass();
jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;"); jfieldID uriStringField = env->GetFieldID(gameDirClass, "uriString", "Ljava/lang/String;");
jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z"); jfieldID deepScanBooleanField = env->GetFieldID(gameDirClass, "deepScan", "Z");
jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField)); jstring juriString = static_cast<jstring>(env->GetObjectField(gameDir, uriStringField));
jboolean jdeepScanBoolean = env->GetBooleanField(gameDir, deepScanBooleanField); 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::values.game_dirs.push_back(
AndroidSettings::GameDir{uriString, static_cast<bool>(jdeepScanBoolean)}); 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 program_id = EmulationSession::GetProgramId(env, jprogramId);
auto& disabledAddons = Settings::values.disabled_addons[program_id]; auto& disabledAddons = Settings::values.disabled_addons[program_id];
jobjectArray jdisabledAddonsArray = 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) { 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; return jdisabledAddonsArray;
} }
@ -339,7 +341,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
const int size = env->GetArrayLength(jdisabledAddons); const int size = env->GetArrayLength(jdisabledAddons);
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
auto jaddon = static_cast<jstring>(env->GetObjectArrayElement(jdisabledAddons, 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; Settings::values.disabled_addons[program_id] = disabled_addons;
} }
@ -348,26 +350,27 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JN
jobject obj) { jobject obj) {
jobjectArray joverlayControlDataArray = jobjectArray joverlayControlDataArray =
env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(), 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) { for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
const auto& control_data = AndroidSettings::values.overlay_control_data[i]; const auto& control_data = AndroidSettings::values.overlay_control_data[i];
jobject jlandscapePosition = jobject jlandscapePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
ToJDouble(env, control_data.landscape_position.first), Common::Android::ToJDouble(env, control_data.landscape_position.first),
ToJDouble(env, control_data.landscape_position.second)); Common::Android::ToJDouble(env, control_data.landscape_position.second));
jobject jportraitPosition = jobject jportraitPosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
ToJDouble(env, control_data.portrait_position.first), Common::Android::ToJDouble(env, control_data.portrait_position.first),
ToJDouble(env, control_data.portrait_position.second)); Common::Android::ToJDouble(env, control_data.portrait_position.second));
jobject jfoldablePosition = jobject jfoldablePosition =
env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(), env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(),
ToJDouble(env, control_data.foldable_position.first), Common::Android::ToJDouble(env, control_data.foldable_position.first),
ToJDouble(env, control_data.foldable_position.second)); Common::Android::ToJDouble(env, control_data.foldable_position.second));
jobject jcontrolData = env->NewObject( jobject jcontrolData =
IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(), env->NewObject(Common::Android::GetOverlayControlDataClass(),
ToJString(env, control_data.id), control_data.enabled, jlandscapePosition, Common::Android::GetOverlayControlDataConstructor(),
jportraitPosition, jfoldablePosition); Common::Android::ToJString(env, control_data.id), control_data.enabled,
jlandscapePosition, jportraitPosition, jfoldablePosition);
env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData); env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
} }
return joverlayControlDataArray; return joverlayControlDataArray;
@ -384,33 +387,41 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i); jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
jstring jidString = static_cast<jstring>( jstring jidString = static_cast<jstring>(env->GetObjectField(
env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField())); joverlayControlData, Common::Android::GetOverlayControlDataIdField()));
bool enabled = static_cast<bool>(env->GetBooleanField( bool enabled = static_cast<bool>(env->GetBooleanField(
joverlayControlData, IDCache::GetOverlayControlDataEnabledField())); joverlayControlData, Common::Android::GetOverlayControlDataEnabledField()));
jobject jlandscapePosition = env->GetObjectField( jobject jlandscapePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField()); joverlayControlData, Common::Android::GetOverlayControlDataLandscapePositionField());
std::pair<double, double> landscape_position = std::make_pair( std::pair<double, double> landscape_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())), Common::Android::GetJDouble(
GetJDouble(env, env, env->GetObjectField(jlandscapePosition, Common::Android::GetPairFirstField())),
env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField()))); Common::Android::GetJDouble(
env,
env->GetObjectField(jlandscapePosition, Common::Android::GetPairSecondField())));
jobject jportraitPosition = env->GetObjectField( jobject jportraitPosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField()); joverlayControlData, Common::Android::GetOverlayControlDataPortraitPositionField());
std::pair<double, double> portrait_position = std::make_pair( std::pair<double, double> portrait_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())), Common::Android::GetJDouble(
GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField()))); env, env->GetObjectField(jportraitPosition, Common::Android::GetPairFirstField())),
Common::Android::GetJDouble(
env,
env->GetObjectField(jportraitPosition, Common::Android::GetPairSecondField())));
jobject jfoldablePosition = env->GetObjectField( jobject jfoldablePosition = env->GetObjectField(
joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField()); joverlayControlData, Common::Android::GetOverlayControlDataFoldablePositionField());
std::pair<double, double> foldable_position = std::make_pair( std::pair<double, double> foldable_position = std::make_pair(
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())), Common::Android::GetJDouble(
GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField()))); 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{ AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
GetJString(env, jidString), enabled, landscape_position, portrait_position, Common::Android::GetJString(env, jidString), enabled, landscape_position,
foldable_position}); portrait_position, foldable_position});
} }
} }

View File

@ -1,31 +1,30 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <common/android/android_common.h>
#include <common/logging/log.h> #include <common/logging/log.h>
#include <jni.h> #include <jni.h>
#include "android_common/android_common.h"
extern "C" { extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_Log_debug(JNIEnv* env, jobject obj, jstring jmessage) { 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) { 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) { 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) { 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) { 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" } // extern "C"

View File

@ -107,6 +107,8 @@ add_library(common STATIC
quaternion.h quaternion.h
range_map.h range_map.h
range_mutex.h range_mutex.h
range_sets.h
range_sets.inc
reader_writer_queue.h reader_writer_queue.h
ring_buffer.h ring_buffer.h
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
@ -121,6 +123,7 @@ add_library(common STATIC
settings_input.cpp settings_input.cpp
settings_input.h settings_input.h
settings_setting.h settings_setting.h
slot_vector.h
socket_types.h socket_types.h
spin_lock.cpp spin_lock.cpp
spin_lock.h spin_lock.h
@ -179,9 +182,15 @@ endif()
if(ANDROID) if(ANDROID)
target_sources(common target_sources(common
PRIVATE PUBLIC
fs/fs_android.cpp fs/fs_android.cpp
fs/fs_android.h 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() endif()

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/android_common/android_common.h" #include "android_common.h"
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -9,7 +9,9 @@
#include <jni.h> #include <jni.h>
#include "common/string_util.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) { std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) { if (!jstr) {
@ -18,7 +20,8 @@ std::string GetJString(JNIEnv* env, jstring jstr) {
const jchar* jchars = env->GetStringChars(jstr, nullptr); const jchar* jchars = env->GetStringChars(jstr, nullptr);
const jsize length = env->GetStringLength(jstr); 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); const std::string converted_string = Common::UTF16ToUTF8(string_view);
env->ReleaseStringChars(jstr, jchars); env->ReleaseStringChars(jstr, jchars);
@ -36,25 +39,27 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
} }
double GetJDouble(JNIEnv* env, jobject jdouble) { double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField()); return env->GetDoubleField(jdouble, GetDoubleValueField());
} }
jobject ToJDouble(JNIEnv* env, double value) { 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) { s32 GetJInteger(JNIEnv* env, jobject jinteger) {
return env->GetIntField(jinteger, IDCache::GetIntegerValueField()); return env->GetIntField(jinteger, GetIntegerValueField());
} }
jobject ToJInteger(JNIEnv* env, s32 value) { 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) { bool GetJBoolean(JNIEnv* env, jobject jboolean) {
return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField()); return env->GetBooleanField(jboolean, GetBooleanValueField());
} }
jobject ToJBoolean(JNIEnv* env, bool value) { jobject ToJBoolean(JNIEnv* env, bool value) {
return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value); return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value);
} }
} // namespace Common::Android

View File

@ -8,6 +8,8 @@
#include <jni.h> #include <jni.h>
#include "common/common_types.h" #include "common/common_types.h"
namespace Common::Android {
std::string GetJString(JNIEnv* env, jstring jstr); std::string GetJString(JNIEnv* env, jstring jstr);
jstring ToJString(JNIEnv* env, std::string_view str); jstring ToJString(JNIEnv* env, std::string_view str);
jstring ToJString(JNIEnv* env, std::u16string_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); bool GetJBoolean(JNIEnv* env, jobject jboolean);
jobject ToJBoolean(JNIEnv* env, bool value); jobject ToJBoolean(JNIEnv* env, bool value);
} // namespace Common::Android

View File

@ -6,12 +6,12 @@
#include <jni.h> #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/logging/log.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.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_software_keyboard_class;
static jclass s_keyboard_config_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_normal;
static jmethodID s_swkbd_execute_inline; static jmethodID s_swkbd_execute_inline;
namespace SoftwareKeyboard { namespace Common::Android::SoftwareKeyboard {
static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) { static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = GetEnvForThread();
jobject object = env->AllocObject(s_keyboard_config_class); jobject object = env->AllocObject(s_keyboard_config_class);
env->SetObjectField(object, env->SetObjectField(object,
@ -78,7 +78,7 @@ static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParamet
} }
AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) { AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) {
JNIEnv* env = IDCache::GetEnvForThread(); JNIEnv* env = GetEnvForThread();
const jstring string = reinterpret_cast<jstring>(env->GetObjectField( const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
return ResultData{GetJString(env, 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. // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
std::thread([&] { std::thread([&] {
data = ResultData::CreateFromFrontend(IDCache::GetEnvForThread()->CallStaticObjectMethod( data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod(
s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters))); s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters)));
}).join(); }).join();
@ -183,8 +183,8 @@ void AndroidKeyboard::ShowInlineKeyboard(
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber. // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
m_is_inline_active = true; m_is_inline_active = true;
std::thread([&] { std::thread([&] {
IDCache::GetEnvForThread()->CallStaticVoidMethod( GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline,
s_software_keyboard_class, s_swkbd_execute_inline, ToJKeyboardParams(parameters)); ToJKeyboardParams(parameters));
}).join(); }).join();
} }
@ -220,7 +220,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
m_current_text += submitted_text; m_current_text += submitted_text;
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_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) { void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
@ -242,7 +242,7 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
case KEYCODE_DEL: case KEYCODE_DEL:
m_current_text.pop_back(); m_current_text.pop_back();
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
m_current_text.size()); static_cast<int>(m_current_text.size()));
break; break;
} }
} }
@ -274,4 +274,4 @@ void CleanupJNI(JNIEnv* env) {
env->DeleteGlobalRef(s_keyboard_data_class); env->DeleteGlobalRef(s_keyboard_data_class);
} }
} // namespace SoftwareKeyboard } // namespace Common::Android::SoftwareKeyboard

View File

@ -7,7 +7,7 @@
#include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/software_keyboard.h"
namespace SoftwareKeyboard { namespace Common::Android::SoftwareKeyboard {
class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet { class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet {
public: public:
@ -66,7 +66,7 @@ void InitJNI(JNIEnv* env);
// Should be called in JNI_Unload // Should be called in JNI_Unload
void CleanupJNI(JNIEnv* env); void CleanupJNI(JNIEnv* env);
} // namespace SoftwareKeyboard } // namespace Common::Android::SoftwareKeyboard
// Native function calls // Native function calls
extern "C" { extern "C" {

View File

@ -3,10 +3,10 @@
#include <jni.h> #include <jni.h>
#include "applets/software_keyboard.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/fs/fs_android.h" #include "common/fs/fs_android.h"
#include "jni/applets/software_keyboard.h" #include "id_cache.h"
#include "jni/id_cache.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
static JavaVM* s_java_vm; static JavaVM* s_java_vm;
@ -67,7 +67,7 @@ static jfieldID s_boolean_value_field;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6; static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace IDCache { namespace Common::Android {
JNIEnv* GetEnvForThread() { JNIEnv* GetEnvForThread() {
thread_local static struct OwnedEnv { thread_local static struct OwnedEnv {
@ -276,8 +276,6 @@ jfieldID GetBooleanValueField() {
return s_boolean_value_field; return s_boolean_value_field;
} }
} // namespace IDCache
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -393,7 +391,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
Common::FS::Android::RegisterCallbacks(env, s_native_library_class); Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets // Initialize applets
SoftwareKeyboard::InitJNI(env); Common::Android::SoftwareKeyboard::InitJNI(env);
return JNI_VERSION; return JNI_VERSION;
} }
@ -426,3 +424,5 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
} // namespace Common::Android

View File

@ -3,20 +3,40 @@
#pragma once #pragma once
#include <future>
#include <jni.h> #include <jni.h>
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
namespace IDCache { namespace Common::Android {
JNIEnv* GetEnvForThread(); 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 GetNativeLibraryClass();
jclass GetDiskCacheProgressClass(); jclass GetDiskCacheProgressClass();
jclass GetDiskCacheLoadCallbackStageClass(); jclass GetDiskCacheLoadCallbackStageClass();
jclass GetGameDirClass(); jclass GetGameDirClass();
jmethodID GetGameDirConstructor(); jmethodID GetGameDirConstructor();
jmethodID GetExitEmulationActivity();
jmethodID GetDiskCacheLoadProgress(); jmethodID GetDiskCacheLoadProgress();
jmethodID GetExitEmulationActivity();
jmethodID GetOnEmulationStarted(); jmethodID GetOnEmulationStarted();
jmethodID GetOnEmulationStopped(); jmethodID GetOnEmulationStopped();
jmethodID GetOnProgramChanged(); jmethodID GetOnProgramChanged();
@ -65,4 +85,4 @@ jclass GetBooleanClass();
jmethodID GetBooleanConstructor(); jmethodID GetBooleanConstructor();
jfieldID GetBooleanValueField(); jfieldID GetBooleanValueField();
} // namespace IDCache } // namespace Common::Android

View File

@ -1,63 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // 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/fs/fs_android.h"
#include "common/string_util.h" #include "common/string_util.h"
namespace Common::FS::Android { 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) { void RegisterCallbacks(JNIEnv* env, jclass clazz) {
env->GetJavaVM(&g_jvm); env->GetJavaVM(&g_jvm);
native_library = clazz; native_library = clazz;
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory",
F(JMethodID, JMethodName, Signature) "(Ljava/lang/String;)Ljava/lang/String;");
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ s_get_filename = env->GetStaticMethodID(native_library, "getFilename",
F(JMethodID, JMethodName, Signature) "(Ljava/lang/String;)Ljava/lang/String;");
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J");
F(JMethodID, JMethodName, Signature) s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z");
#define F(JMethodID, JMethodName, Signature) \ s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z");
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri",
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) "(Ljava/lang/String;Ljava/lang/String;)I");
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
} }
void UnRegisterCallbacks() { void UnRegisterCallbacks() {
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) s_get_parent_directory = nullptr;
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) s_get_filename = nullptr;
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
#define F(JMethodID) JMethodID = nullptr; s_get_size = nullptr;
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) s_is_directory = nullptr;
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) s_file_exists = nullptr;
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F s_open_content_uri = nullptr;
#undef FS
#undef FR
#undef FH
} }
bool IsContentUri(const std::string& path) { bool IsContentUri(const std::string& path) {
@ -70,7 +45,7 @@ bool IsContentUri(const std::string& path) {
} }
int OpenContentUri(const std::string& filepath, OpenMode openmode) { int OpenContentUri(const std::string& filepath, OpenMode openmode) {
if (open_content_uri == nullptr) if (s_open_content_uri == nullptr)
return -1; return -1;
const char* mode = ""; const char* mode = "";
@ -82,50 +57,66 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
UNIMPLEMENTED(); UNIMPLEMENTED();
return -1; return -1;
} }
auto env = GetEnvForThread(); auto env = Common::Android::GetEnvForThread();
jstring j_filepath = env->NewStringUTF(filepath.c_str()); jstring j_filepath = Common::Android::ToJString(env, filepath);
jstring j_mode = env->NewStringUTF(mode); jstring j_mode = Common::Android::ToJString(env, mode);
return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode);
} }
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ std::uint64_t GetSize(const std::string& filepath) {
F(FunctionName, ReturnValue, JMethodID, Caller) if (s_get_size == nullptr) {
#define F(FunctionName, ReturnValue, JMethodID, Caller) \ return 0;
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); \
} }
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) auto env = Common::Android::GetEnvForThread();
#undef F return static_cast<u64>(env->CallStaticLongMethod(
#undef FR native_library, s_get_size,
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)));
}
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ bool IsDirectory(const std::string& filepath) {
F(FunctionName, JMethodID, Caller) if (s_is_directory == nullptr) {
#define F(FunctionName, JMethodID, Caller) \ return 0;
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; \
} }
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) auto env = Common::Android::GetEnvForThread();
#undef F return env->CallStaticBooleanMethod(
#undef FH 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 } // namespace Common::FS::Android

View File

@ -7,38 +7,17 @@
#include <vector> #include <vector>
#include <jni.h> #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 { namespace Common::FS::Android {
static JavaVM* g_jvm = nullptr; static JavaVM* g_jvm = nullptr;
static jclass native_library = nullptr; static jclass native_library = nullptr;
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) static jmethodID s_get_parent_directory;
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) static jmethodID s_get_filename;
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) static jmethodID s_get_size;
#define F(JMethodID) static jmethodID JMethodID = nullptr; static jmethodID s_is_directory;
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) static jmethodID s_file_exists;
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) static jmethodID s_open_content_uri;
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
enum class OpenMode { enum class OpenMode {
Read, Read,
@ -57,24 +36,11 @@ void UnRegisterCallbacks();
bool IsContentUri(const std::string& path); bool IsContentUri(const std::string& path);
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ int OpenContentUri(const std::string& filepath, OpenMode openmode);
F(FunctionName, Parameters, ReturnValue) std::uint64_t GetSize(const std::string& filepath);
#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters; bool IsDirectory(const std::string& filepath);
ANDROID_STORAGE_FUNCTIONS(FS) bool Exists(const std::string& filepath);
#undef F std::string GetParentDirectory(const std::string& filepath);
#undef FS std::string GetFilename(const std::string& filepath);
#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
} // namespace Common::FS::Android } // namespace Common::FS::Android

73
src/common/range_sets.h Normal file
View 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
View 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

View File

@ -30,6 +30,7 @@ namespace Settings {
#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED> #define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED> #define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
SETTING(AppletMode, false);
SETTING(AudioEngine, false); SETTING(AudioEngine, false);
SETTING(bool, false); SETTING(bool, false);
SETTING(int, false); SETTING(int, false);
@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) {
return "Debugging"; return "Debugging";
case Category::GpuDriver: case Category::GpuDriver:
return "GpuDriver"; return "GpuDriver";
case Category::LibraryApplet:
return "LibraryApplet";
case Category::Miscellaneous: case Category::Miscellaneous:
return "Miscellaneous"; return "Miscellaneous";
case Category::Network: case Category::Network:

View File

@ -133,6 +133,38 @@ struct TouchFromButtonMap {
struct Values { struct Values {
Linkage linkage{}; 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 // Audio
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
Category::Audio, Specialization::RuntimeList}; Category::Audio, Specialization::RuntimeList};

View File

@ -44,6 +44,7 @@ enum class Category : u32 {
Services, Services,
Paths, Paths,
Linux, Linux,
LibraryApplet,
MaxEnum, MaxEnum,
}; };

View File

@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked); ENUM(ConsoleMode, Handheld, Docked);
ENUM(AppletMode, HLE, LLE);
template <typename Type> template <typename Type>
inline std::string CanonicalizeEnum(Type id) { inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations(); const auto group = EnumMetadata<Type>::Canonicalizations();

View File

@ -14,7 +14,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/polyfill_ranges.h" #include "common/polyfill_ranges.h"
namespace VideoCommon { namespace Common {
struct SlotId { struct SlotId {
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max(); static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
@ -217,11 +217,11 @@ private:
std::vector<u32> free_list; std::vector<u32> free_list;
}; };
} // namespace VideoCommon } // namespace Common
template <> template <>
struct std::hash<VideoCommon::SlotId> { struct std::hash<Common::SlotId> {
size_t operator()(const VideoCommon::SlotId& id) const noexcept { size_t operator()(const Common::SlotId& id) const noexcept {
return std::hash<u32>{}(id.index); return std::hash<u32>{}(id.index);
} }
}; };

View File

@ -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<VirtualAddress>);
static_assert(std::is_trivially_destructible_v<ProcessAddress>); 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<PhysicalAddress> == Null<uint64_t>);
static_assert(Null<VirtualAddress> == Null<uint64_t>); static_assert(Null<VirtualAddress> == Null<uint64_t>);
static_assert(Null<ProcessAddress> == Null<uint64_t>); static_assert(Null<ProcessAddress> == Null<uint64_t>);
// Constructor/assignment validations. // Constructor/assignment validations.
static_assert([] { static_assert([] {
const PhysicalAddress a(5); const PhysicalAddress a(5U);
PhysicalAddress b(a); PhysicalAddress b(a);
return b; return b;
}() == PhysicalAddress(5)); }() == PhysicalAddress(5U));
static_assert([] { static_assert([] {
const PhysicalAddress a(5); const PhysicalAddress a(5U);
PhysicalAddress b(10); PhysicalAddress b(10U);
b = a; b = a;
return b; return b;
}() == PhysicalAddress(5)); }() == PhysicalAddress(5U));
// Arithmetic validations. // Arithmetic validations.
static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15)); static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U));
static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5)); static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U));
static_assert([] { static_assert([] {
PhysicalAddress v(10); PhysicalAddress v(10U);
v += 5; v += 5U;
return v; return v;
}() == PhysicalAddress(15)); }() == PhysicalAddress(15U));
static_assert([] { static_assert([] {
PhysicalAddress v(10); PhysicalAddress v(10U);
v -= 5; v -= 5U;
return v; return v;
}() == PhysicalAddress(5)); }() == PhysicalAddress(5U));
static_assert(PhysicalAddress(10)++ == PhysicalAddress(10)); static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U));
static_assert(++PhysicalAddress(10) == PhysicalAddress(11)); static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U));
static_assert(PhysicalAddress(10)-- == PhysicalAddress(10)); static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U));
static_assert(--PhysicalAddress(10) == PhysicalAddress(9)); static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U));
// Logical validations. // Logical validations.
static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111); static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U);
static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101); static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110); static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U);
static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010); static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U);
static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101); static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010); static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U);
static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000); static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U);
static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101); static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U);
static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111); static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U);
static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111); static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U);
// Comparisons. // Comparisons.
static_assert(PhysicalAddress(0) == PhysicalAddress(0)); static_assert(PhysicalAddress(0U) == PhysicalAddress(0U));
static_assert(PhysicalAddress(0) != PhysicalAddress(1)); static_assert(PhysicalAddress(0U) != PhysicalAddress(1U));
static_assert(PhysicalAddress(0) < PhysicalAddress(1)); static_assert(PhysicalAddress(0U) < PhysicalAddress(1U));
static_assert(PhysicalAddress(0) <= PhysicalAddress(1)); static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U));
static_assert(PhysicalAddress(1) > PhysicalAddress(0)); static_assert(PhysicalAddress(1U) > PhysicalAddress(0U));
static_assert(PhysicalAddress(1) >= PhysicalAddress(0)); static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U));
static_assert(!(PhysicalAddress(0) == PhysicalAddress(1))); static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0) != PhysicalAddress(0))); static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1) < PhysicalAddress(0))); static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0))); static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U)));
static_assert(!(PhysicalAddress(0) > PhysicalAddress(1))); static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U)));
static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1))); static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U)));
} // namespace Common } // namespace Common

View File

@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const
} else if (id == CPSR_REGISTER) { } else if (id == CPSR_REGISTER) {
return ValueToHex(context.pstate); return ValueToHex(context.pstate);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) { } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
return ValueToHex(fprs[id - D0_REGISTER][0]); return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
return ValueToHex(fprs[id - Q0_REGISTER]); return ValueToHex(fprs[id - Q0_REGISTER]);
} else if (id == FPSCR_REGISTER) { } else if (id == FPSCR_REGISTER) {
@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
} else if (id == CPSR_REGISTER) { } else if (id == CPSR_REGISTER) {
context.pstate = HexToValue<u32>(value); context.pstate = HexToValue<u32>(value);
} else if (id >= D0_REGISTER && id < Q0_REGISTER) { } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0}; fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value);
} else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
fprs[id - Q0_REGISTER] = HexToValue<u128>(value); fprs[id - Q0_REGISTER] = HexToValue<u128>(value);
} else if (id == FPSCR_REGISTER) { } else if (id == FPSCR_REGISTER) {

View File

@ -43,6 +43,8 @@ public:
DeviceMemoryManager(const DeviceMemory& device_memory); DeviceMemoryManager(const DeviceMemory& device_memory);
~DeviceMemoryManager(); ~DeviceMemoryManager();
static constexpr bool HAS_FLUSH_INVALIDATION = true;
void BindInterface(DeviceInterface* device_inter); void BindInterface(DeviceInterface* device_inter);
DAddr Allocate(size_t size); DAddr Allocate(size_t size);

View File

@ -44,15 +44,32 @@ public:
GuestMemory() = delete; GuestMemory() = delete;
explicit GuestMemory(M& memory, u64 addr, std::size_t size, explicit GuestMemory(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) 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); 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); Read(addr, size, backup);
} }
} }
~GuestMemory() = default; ~GuestMemory() = default;
GuestMemory(GuestMemory&& rhs) = default;
GuestMemory& operator=(GuestMemory&& rhs) = default;
T* data() noexcept { T* data() noexcept {
return m_data_span.data(); return m_data_span.data();
} }
@ -109,8 +126,8 @@ public:
} }
if (this->TrySetSpan()) { if (this->TrySetSpan()) {
if constexpr (FLAGS & GuestMemoryFlags::Safe) { if constexpr (FLAGS & GuestMemoryFlags::Safe && M::HAS_FLUSH_INVALIDATION) {
m_memory.FlushRegion(m_addr, this->size_bytes()); m_memory->FlushRegion(m_addr, this->size_bytes());
} }
} else { } else {
if (backup) { if (backup) {
@ -123,9 +140,9 @@ public:
m_is_data_copy = true; m_is_data_copy = true;
m_span_valid = true; m_span_valid = true;
if constexpr (FLAGS & GuestMemoryFlags::Safe) { 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 { } 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; return m_data_span;
@ -133,18 +150,19 @@ public:
void Write(std::span<T> write_data) noexcept { void Write(std::span<T> write_data) noexcept {
if constexpr (FLAGS & GuestMemoryFlags::Cached) { 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) { } 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 { } 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 { 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_data_span = {reinterpret_cast<T*>(ptr), this->size()};
m_span_valid = true; m_span_valid = true;
m_is_data_copy = false;
return true; return true;
} }
return false; return false;
@ -159,7 +177,7 @@ protected:
return m_addr_changed; return m_addr_changed;
} }
M& m_memory; M* m_memory;
u64 m_addr{}; u64 m_addr{};
size_t m_size{}; size_t m_size{};
std::span<T> m_data_span{}; std::span<T> m_data_span{};
@ -175,17 +193,7 @@ public:
GuestMemoryScoped() = delete; GuestMemoryScoped() = delete;
explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size, explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
Common::ScratchBuffer<T>* backup = nullptr) Common::ScratchBuffer<T>* backup = nullptr)
: GuestMemory<M, T, FLAGS>(memory, addr, size, backup) { : 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;
}
}
}
}
~GuestMemoryScoped() { ~GuestMemoryScoped() {
if constexpr (FLAGS & GuestMemoryFlags::Write) { if constexpr (FLAGS & GuestMemoryFlags::Write) {
@ -196,15 +204,17 @@ public:
if (this->AddressChanged() || this->IsDataCopy()) { if (this->AddressChanged() || this->IsDataCopy()) {
ASSERT(this->m_span_valid); ASSERT(this->m_span_valid);
if constexpr (FLAGS & GuestMemoryFlags::Cached) { 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) { } 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 { } 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) || } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
(FLAGS & GuestMemoryFlags::Cached)) { (FLAGS & GuestMemoryFlags::Cached)) {
this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); this->m_memory->InvalidateRegion(this->m_addr, this->size_bytes());
} }
} }
} }

View File

@ -4,8 +4,9 @@
#include <random> #include <random>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.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/core.h"
#include "core/gpu_dirty_memory_manager.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
@ -1258,6 +1259,10 @@ void KProcess::InitializeInterfaces() {
#ifdef HAS_NCE #ifdef HAS_NCE
if (this->IsApplication() && Settings::IsNceEnabled()) { 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++) { 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); m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
} }

View File

@ -130,9 +130,9 @@ enum class AppletProgramId : u64 {
enum class LibraryAppletMode : u32 { enum class LibraryAppletMode : u32 {
AllForeground = 0, AllForeground = 0,
Background = 1, PartialForeground = 1,
NoUI = 2, NoUi = 2,
BackgroundIndirectDisplay = 3, PartialForegroundIndirectDisplay = 3,
AllForegroundInitiallyHidden = 4, AllForegroundInitiallyHidden = 4,
}; };

View File

@ -68,9 +68,9 @@ void SoftwareKeyboard::Initialize() {
case LibraryAppletMode::AllForeground: case LibraryAppletMode::AllForeground:
InitializeForeground(); InitializeForeground();
break; break;
case LibraryAppletMode::Background: case LibraryAppletMode::PartialForeground:
case LibraryAppletMode::BackgroundIndirectDisplay: case LibraryAppletMode::PartialForegroundIndirectDisplay:
InitializeBackground(applet_mode); InitializePartialForeground(applet_mode);
break; break;
default: default:
ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
InitializeFrontendNormalKeyboard(); InitializeFrontendNormalKeyboard();
} }
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { void SoftwareKeyboard::InitializePartialForeground(LibraryAppletMode library_applet_mode) {
LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
is_background = true; is_background = true;
@ -258,9 +258,9 @@ void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mod
swkbd_inline_initialize_arg.size()); swkbd_inline_initialize_arg.size());
if (swkbd_initialize_arg.library_applet_mode_flag) { if (swkbd_initialize_arg.library_applet_mode_flag) {
ASSERT(library_applet_mode == LibraryAppletMode::Background); ASSERT(library_applet_mode == LibraryAppletMode::PartialForeground);
} else { } else {
ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); ASSERT(library_applet_mode == LibraryAppletMode::PartialForegroundIndirectDisplay);
} }
} }

View File

@ -62,7 +62,7 @@ private:
void InitializeForeground(); void InitializeForeground();
/// Initializes the inline software keyboard. /// 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. /// Processes the text check sent by the application.
void ProcessTextCheck(); void ProcessTextCheck();

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/am/applet_data_broker.h" #include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/applet_manager.h"
@ -16,6 +17,34 @@ namespace Service::AM {
namespace { 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) { AppletProgramId AppletIdToProgramId(AppletId applet_id) {
switch (applet_id) { switch (applet_id) {
case AppletId::OverlayDisplay: case AppletId::OverlayDisplay:
@ -63,9 +92,10 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
} }
} }
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet( std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, std::shared_ptr<Applet> caller_applet,
LibraryAppletMode mode) { AppletId applet_id,
LibraryAppletMode mode) {
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
if (program_id == 0) { if (program_id == 0) {
// Unknown applet // Unknown applet
@ -87,24 +117,18 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
// Set focus state // Set focus state
switch (mode) { switch (mode) {
case LibraryAppletMode::AllForeground: case LibraryAppletMode::AllForeground:
case LibraryAppletMode::NoUI: case LibraryAppletMode::NoUi:
applet->focus_state = FocusState::InFocus; case LibraryAppletMode::PartialForeground:
case LibraryAppletMode::PartialForegroundIndirectDisplay:
applet->hid_registration.EnableAppletToGetInput(true); applet->hid_registration.EnableAppletToGetInput(true);
applet->focus_state = FocusState::InFocus;
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break; break;
case LibraryAppletMode::AllForegroundInitiallyHidden: case LibraryAppletMode::AllForegroundInitiallyHidden:
applet->system_buffer_manager.SetWindowVisibility(false);
applet->focus_state = FocusState::NotInFocus;
applet->hid_registration.EnableAppletToGetInput(false); applet->hid_registration.EnableAppletToGetInput(false);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); applet->focus_state = FocusState::NotInFocus;
break; applet->system_buffer_manager.SetWindowVisibility(false);
case LibraryAppletMode::Background: applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
case LibraryAppletMode::BackgroundIndirectDisplay:
default:
applet->focus_state = FocusState::Background;
applet->hid_registration.EnableAppletToGetInput(true);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break; break;
} }
@ -117,9 +141,10 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
} }
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet( std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id, std::shared_ptr<Applet> caller_applet,
LibraryAppletMode mode) { AppletId applet_id,
LibraryAppletMode mode) {
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id)); const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
auto process = std::make_unique<Process>(system); 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, LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
applet_mode); 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) { if (!library_applet) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);

View File

@ -1,10 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // 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/am_results.h"
#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/self_controller.h" #include "core/hle/service/am/self_controller.h"
#include "core/hle/service/caps/caps_su.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/ipc_helpers.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
#include "core/hle/service/nvnflinger/nvnflinger.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"}, {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
{61, nullptr, "SetMediaPlaybackState"}, {61, &ISelfController::SetMediaPlaybackState, "SetMediaPlaybackState"},
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
{64, nullptr, "SetInputDetectionSourceSet"}, {64, nullptr, "SetInputDetectionSourceSet"},
@ -288,7 +291,8 @@ void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
} }
Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { 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; return ResultSuccess;
} }
@ -323,6 +327,16 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
rb.Push(ResultSuccess); 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) { void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@ -38,6 +39,7 @@ private:
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
void SetHandlesRequestToDisplay(HLERequestContext& ctx); void SetHandlesRequestToDisplay(HLERequestContext& ctx);
void ApproveToDisplay(HLERequestContext& ctx); void ApproveToDisplay(HLERequestContext& ctx);
void SetMediaPlaybackState(HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(HLERequestContext& ctx); void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(HLERequestContext& ctx); void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
void ReportUserIsActive(HLERequestContext& ctx); void ReportUserIsActive(HLERequestContext& ctx);

View File

@ -17,11 +17,12 @@ SystemBufferManager::~SystemBufferManager() {
// Clean up shared layers. // Clean up shared layers.
if (m_buffer_sharing_enabled) { if (m_buffer_sharing_enabled) {
m_nvnflinger->GetSystemBufferManager().Finalize(m_process);
} }
} }
bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process,
AppletId applet_id) { AppletId applet_id, LibraryAppletMode mode) {
if (m_nvnflinger) { if (m_nvnflinger) {
return m_buffer_sharing_enabled; return m_buffer_sharing_enabled;
} }
@ -36,9 +37,15 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel:
return false; 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 display_id = m_nvnflinger->OpenDisplay("Default").value();
const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( 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()) { if (res.IsSuccess()) {
m_buffer_sharing_enabled = true; m_buffer_sharing_enabled = true;
@ -62,8 +69,12 @@ void SystemBufferManager::SetWindowVisibility(bool visible) {
Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, Result SystemBufferManager::WriteAppletCaptureBuffer(bool* out_was_written,
s32* out_fbshare_layer_index) { s32* out_fbshare_layer_index) {
// TODO if (!m_buffer_sharing_enabled) {
R_SUCCEED(); return VI::ResultPermissionDenied;
}
return m_nvnflinger->GetSystemBufferManager().WriteAppletCaptureBuffer(out_was_written,
out_fbshare_layer_index);
} }
} // namespace Service::AM } // namespace Service::AM

View File

@ -27,7 +27,8 @@ public:
SystemBufferManager(); SystemBufferManager();
~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, void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
u64* out_system_shared_layer_id) { u64* out_system_shared_layer_id) {

View File

@ -62,12 +62,12 @@ void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) {
applet->hid_registration.EnableAppletToGetInput(visible); applet->hid_registration.EnableAppletToGetInput(visible);
if (visible) { if (visible) {
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->focus_state = FocusState::InFocus; applet->focus_state = FocusState::InFocus;
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
} else { } else {
applet->focus_state = FocusState::NotInFocus; applet->focus_state = FocusState::NotInFocus;
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground);
} }
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);

View File

@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hid_debug_server.h" #include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
@ -11,7 +12,6 @@
#include "hid_core/resources/touch_screen/gesture.h" #include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h" #include "hid_core/resources/touch_screen/touch_screen.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Service::HID { namespace Service::HID {
@ -24,14 +24,14 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{0, nullptr, "DeactivateDebugPad"}, {0, nullptr, "DeactivateDebugPad"},
{1, nullptr, "SetDebugPadAutoPilotState"}, {1, nullptr, "SetDebugPadAutoPilotState"},
{2, nullptr, "UnsetDebugPadAutoPilotState"}, {2, nullptr, "UnsetDebugPadAutoPilotState"},
{10, &IHidDebugServer::DeactivateTouchScreen, "DeactivateTouchScreen"}, {10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"},
{11, &IHidDebugServer::SetTouchScreenAutoPilotState, "SetTouchScreenAutoPilotState"}, {11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"},
{12, &IHidDebugServer::UnsetTouchScreenAutoPilotState, "UnsetTouchScreenAutoPilotState"}, {12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"},
{13, &IHidDebugServer::GetTouchScreenConfiguration, "GetTouchScreenConfiguration"}, {13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"},
{14, &IHidDebugServer::ProcessTouchScreenAutoTune, "ProcessTouchScreenAutoTune"}, {14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"},
{15, &IHidDebugServer::ForceStopTouchScreenManagement, "ForceStopTouchScreenManagement"}, {15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"},
{16, &IHidDebugServer::ForceRestartTouchScreenManagement, "ForceRestartTouchScreenManagement"}, {16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"},
{17, &IHidDebugServer::IsTouchScreenManaged, "IsTouchScreenManaged"}, {17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"},
{20, nullptr, "DeactivateMouse"}, {20, nullptr, "DeactivateMouse"},
{21, nullptr, "SetMouseAutoPilotState"}, {21, nullptr, "SetMouseAutoPilotState"},
{22, nullptr, "UnsetMouseAutoPilotState"}, {22, nullptr, "UnsetMouseAutoPilotState"},
@ -47,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
{60, nullptr, "ClearNpadSystemCommonPolicy"}, {60, nullptr, "ClearNpadSystemCommonPolicy"},
{61, nullptr, "DeactivateNpad"}, {61, nullptr, "DeactivateNpad"},
{62, nullptr, "ForceDisconnectNpad"}, {62, nullptr, "ForceDisconnectNpad"},
{91, &IHidDebugServer::DeactivateGesture, "DeactivateGesture"}, {91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"},
{110, nullptr, "DeactivateHomeButton"}, {110, nullptr, "DeactivateHomeButton"},
{111, nullptr, "SetHomeButtonAutoPilotState"}, {111, nullptr, "SetHomeButtonAutoPilotState"},
{112, nullptr, "UnsetHomeButtonAutoPilotState"}, {112, nullptr, "UnsetHomeButtonAutoPilotState"},
@ -160,169 +160,122 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource
} }
IHidDebugServer::~IHidDebugServer() = default; IHidDebugServer::~IHidDebugServer() = default;
void IHidDebugServer::DeactivateTouchScreen(HLERequestContext& ctx) {
Result IHidDebugServer::DeactivateTouchScreen() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetTouchScreen()->Deactivate(); R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate());
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::SetTouchScreenAutoPilotState(HLERequestContext& ctx) { Result IHidDebugServer::SetTouchScreenAutoPilotState(
InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) {
AutoPilotState auto_pilot{}; AutoPilotState auto_pilot{};
auto_pilot.count = ctx.GetReadBufferNumElements<TouchState>();
const auto buffer = ctx.ReadBuffer();
auto_pilot.count = std::min(auto_pilot.count, static_cast<u64>(auto_pilot.state.size())); auto_pilot.count =
memcpy(auto_pilot.state.data(), buffer.data(), auto_pilot.count * sizeof(TouchState)); static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size()));
memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(),
auto_pilot.count * sizeof(TouchState));
LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count); LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);
const Result result = R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot));
GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::UnsetTouchScreenAutoPilotState(HLERequestContext& ctx) { Result IHidDebugServer::UnsetTouchScreenAutoPilotState() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState());
const Result result = GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::GetTouchScreenConfiguration(HLERequestContext& ctx) { Result IHidDebugServer::GetTouchScreenConfiguration(
IPC::RequestParser rp{ctx}; Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
const auto applet_resource_user_id{rp.Pop<u64>()}; ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid);
LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
*out_touchscreen_config, aruid.pid));
Core::HID::TouchScreenConfigurationForNx touchscreen_config{}; if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 &&
const Result result = GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration( out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config, applet_resource_user_id); out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
} }
IPC::ResponseBuilder rb{ctx, 6}; R_SUCCEED();
rb.Push(result);
rb.PushRaw(touchscreen_config);
} }
void IHidDebugServer::ProcessTouchScreenAutoTune(HLERequestContext& ctx) { Result IHidDebugServer::ProcessTouchScreenAutoTune() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune());
Result result = GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IHidDebugServer::ForceStopTouchScreenManagement(HLERequestContext& ctx) { Result IHidDebugServer::ForceStopTouchScreenManagement() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(ResultSuccess);
return;
} }
Result result = ResultSuccess;
bool is_touch_active{};
bool is_gesture_active{};
auto touch_screen = GetResourceManager()->GetTouchScreen(); auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture(); auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsTouchI2cManaged()) { if (firmware_settings->IsTouchI2cManaged()) {
result = touch_screen->IsActive(is_touch_active); bool is_touch_active{};
if (result.IsSuccess()) { bool is_gesture_active{};
result = gesture->IsActive(is_gesture_active); R_TRY(touch_screen->IsActive(is_touch_active));
R_TRY(gesture->IsActive(is_gesture_active));
if (is_touch_active) {
R_TRY(touch_screen->Deactivate());
} }
if (result.IsSuccess() && is_touch_active) { if (is_gesture_active) {
result = touch_screen->Deactivate(); R_TRY(gesture->Deactivate());
}
if (result.IsSuccess() && is_gesture_active) {
result = gesture->Deactivate();
} }
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::ForceRestartTouchScreenManagement(HLERequestContext& ctx) { Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
u32 basic_gesture_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
parameters.basic_gesture_id, parameters.applet_resource_user_id); basic_gesture_id, aruid.pid);
Result result = ResultSuccess;
auto touch_screen = GetResourceManager()->GetTouchScreen(); auto touch_screen = GetResourceManager()->GetTouchScreen();
auto gesture = GetResourceManager()->GetGesture(); auto gesture = GetResourceManager()->GetGesture();
if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) { if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
result = gesture->Activate(); R_TRY(gesture->Activate());
if (result.IsSuccess()) { R_TRY(gesture->Activate(aruid.pid, basic_gesture_id));
result = R_TRY(touch_screen->Activate());
gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id); R_TRY(touch_screen->Activate(aruid.pid));
}
if (result.IsSuccess()) {
result = touch_screen->Activate();
}
if (result.IsSuccess()) {
result = touch_screen->Activate(parameters.applet_resource_user_id);
}
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IHidDebugServer::IsTouchScreenManaged(HLERequestContext& ctx) { Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
bool is_touch_active{}; bool is_touch_active{};
bool is_gesture_active{}; bool is_gesture_active{};
R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active));
R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active));
Result result = GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active); *out_is_managed = is_touch_active || is_gesture_active;
if (result.IsSuccess()) { R_SUCCEED();
result = GetResourceManager()->GetGesture()->IsActive(is_gesture_active);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(result);
rb.Push(is_touch_active | is_gesture_active);
} }
void IHidDebugServer::DeactivateGesture(HLERequestContext& ctx) { Result IHidDebugServer::DeactivateGesture() {
LOG_INFO(Service_HID, "called"); LOG_INFO(Service_HID, "called");
Result result = ResultSuccess;
if (!firmware_settings->IsDeviceManaged()) { if (!firmware_settings->IsDeviceManaged()) {
result = GetResourceManager()->GetGesture()->Deactivate(); R_RETURN(GetResourceManager()->GetGesture()->Deactivate());
} }
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {

View File

@ -3,7 +3,9 @@
#pragma once #pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/resources/touch_screen/touch_types.h"
namespace Core { namespace Core {
class System; class System;
@ -20,15 +22,19 @@ public:
~IHidDebugServer() override; ~IHidDebugServer() override;
private: private:
void DeactivateTouchScreen(HLERequestContext& ctx); Result DeactivateTouchScreen();
void SetTouchScreenAutoPilotState(HLERequestContext& ctx); Result SetTouchScreenAutoPilotState(
void UnsetTouchScreenAutoPilotState(HLERequestContext& ctx); InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer);
void GetTouchScreenConfiguration(HLERequestContext& ctx); Result UnsetTouchScreenAutoPilotState();
void ProcessTouchScreenAutoTune(HLERequestContext& ctx); Result GetTouchScreenConfiguration(
void ForceStopTouchScreenManagement(HLERequestContext& ctx); Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
void ForceRestartTouchScreenManagement(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void IsTouchScreenManaged(HLERequestContext& ctx); Result ProcessTouchScreenAutoTune();
void DeactivateGesture(HLERequestContext& ctx); Result ForceStopTouchScreenManagement();
Result ForceRestartTouchScreenManagement(u32 basic_gesture_id,
ClientAppletResourceUserId aruid);
Result IsTouchScreenManaged(Out<bool> out_is_managed);
Result DeactivateGesture();
std::shared_ptr<ResourceManager> GetResourceManager(); std::shared_ptr<ResourceManager> GetResourceManager();

View File

@ -9,6 +9,7 @@
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/irs.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/memory.h" #include "core/memory.h"
@ -28,24 +29,24 @@ namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, {302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"},
{303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"}, {303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"},
{304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"}, {304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"},
{305, &IRS::StopImageProcessor, "StopImageProcessor"}, {305, C<&IRS::StopImageProcessor>, "StopImageProcessor"},
{306, &IRS::RunMomentProcessor, "RunMomentProcessor"}, {306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"},
{307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"}, {307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"},
{308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"}, {308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"},
{309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"}, {309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"},
{310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"}, {310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"},
{311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"}, {311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"},
{312, &IRS::RunPointingProcessor, "RunPointingProcessor"}, {312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"},
{313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"}, {313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"},
{314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"}, {314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"},
{315, &IRS::SetFunctionLevel, "SetFunctionLevel"}, {315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"},
{316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"}, {316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"},
{317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"}, {317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"},
{318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"}, {318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"},
{319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"}, {319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"},
}; };
// clang-format on // clang-format on
@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
} }
IRS::~IRS() = default; IRS::~IRS() = default;
void IRS::ActivateIrsensor(HLERequestContext& ctx) { Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
const auto applet_resource_user_id{rp.Pop<u64>()}; R_SUCCEED();
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IRS::DeactivateIrsensor(HLERequestContext& ctx) { Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) {
IPC::RequestParser rp{ctx}; LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
const auto applet_resource_user_id{rp.Pop<u64>()}; R_SUCCEED();
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) { Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
const auto applet_resource_user_id{rp.Pop<u64>()}; LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid);
LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); *out_shared_memory = &system.Kernel().GetIrsSharedMem();
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem());
} }
void IRS::StopImageProcessor(HLERequestContext& ctx) { Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Stop Image processor
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Stop Image processor
rb.Push(result); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
} }
void IRS::RunMomentProcessor(HLERequestContext& ctx) { Result IRS::RunMomentProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedMomentProcessorConfig& processor_config) {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedMomentProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle);
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetConfig(parameters.processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR);
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::RunClusteringProcessor(HLERequestContext& ctx) { Result IRS::RunClusteringProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ClusteringProcessor>(parameters.camera_handle); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
image_transfer_processor.SetConfig(parameters.processor_config); Common::Input::PollingMode::IR);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::RunImageTransferProcessor(HLERequestContext& ctx) { Result IRS::RunImageTransferProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
Core::IrSensor::IrCameraHandle camera_handle; u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
u32 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()}; ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
if (t_mem.IsNull()) {
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
LOG_INFO(Service_IRS, LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
"applet_resource_user_id={}", "applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(),
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); aruid.pid);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ImageTransferProcessor>(parameters.camera_handle); image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
image_transfer_processor.SetConfig(parameters.processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); Common::Input::PollingMode::IR);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) { Result IRS::GetImageTransferProcessorState(
IPC::RequestParser rp{ctx}; Out<Core::IrSensor::ImageTransferProcessorState> out_state,
struct Parameters { Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
Core::IrSensor::IrCameraHandle camera_handle; OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { R_TRY(IsIrCameraHandleValid(camera_handle));
IPC::ResponseBuilder rb{ctx, 2}; R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor,
rb.Push(InvalidProcessorState); InvalidProcessorState);
return;
}
std::vector<u8> data{}; *out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data);
const auto& image_transfer_processor =
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
const auto& state = image_transfer_processor.GetState(data);
ctx.WriteBuffer(data); R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(state);
} }
void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) { Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
struct Parameters { ClientAppletResourceUserId aruid) {
Core::IrSensor::IrCameraHandle camera_handle; LOG_WARNING(Service_IRS,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
INSERT_PADDING_WORDS_NOINIT(1); "applet_resource_user_id={}",
u64 applet_resource_user_id; camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
}; processor_config.required_mcu_version.major,
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); processor_config.required_mcu_version.minor, aruid.pid);
const auto parameters{rp.PopRaw<Parameters>()}; R_TRY(IsIrCameraHandleValid(camera_handle));
LOG_WARNING( auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
Service_IRS, MakeProcessor<TeraPluginProcessor>(camera_handle, device);
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle);
"applet_resource_user_id={}", image_transfer_processor.SetConfig(processor_config);
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, Common::Input::PollingMode::IR);
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
const auto result = IsIrCameraHandleValid(parameters.camera_handle); R_SUCCEED();
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
} }
void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
IPC::RequestParser rp{ctx}; Core::HID::NpadIdType npad_id) {
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId);
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && *out_camera_handle = {
npad_id != Core::HID::NpadIdType::Handheld) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(Service::HID::ResultInvalidNpadId);
return;
}
Core::IrSensor::IrCameraHandle camera_handle{
.npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)),
.npad_type = Core::HID::NpadStyleIndex::None, .npad_type = Core::HID::NpadStyleIndex::None,
}; };
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
camera_handle.npad_id, camera_handle.npad_type); out_camera_handle->npad_id, out_camera_handle->npad_type);
IPC::ResponseBuilder rb{ctx, 3}; R_SUCCEED();
rb.Push(ResultSuccess);
rb.PushRaw(camera_handle);
} }
void IRS::RunPointingProcessor(HLERequestContext& ctx) { Result IRS::RunPointingProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; ClientAppletResourceUserId aruid) {
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id); processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); MakeProcessor<PointingProcessor>(camera_handle, device);
MakeProcessor<PointingProcessor>(camera_handle, device); auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetConfig(processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR);
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::SuspendImageProcessor(HLERequestContext& ctx) { Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Suspend image processor
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Suspend image processor
rb.Push(result);
R_SUCCEED();
} }
void IRS::CheckFirmwareVersion(HLERequestContext& ctx) { Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedMcuVersion mcu_version,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; ClientAppletResourceUserId aruid) {
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major,
mcu_version.minor); mcu_version.minor);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Check firmware version
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Check firmware version
rb.Push(result);
R_SUCCEED();
} }
void IRS::SetFunctionLevel(HLERequestContext& ctx) { Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedFunctionLevel function_level,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; ClientAppletResourceUserId aruid) {
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING( LOG_WARNING(
Service_IRS, Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid);
applet_resource_user_id);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Set Function level
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Set Function level
rb.Push(result);
R_SUCCEED();
} }
void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) { Result IRS::RunImageTransferExProcessor(
IPC::RequestParser rp{ctx}; Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
struct Parameters { const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
Core::IrSensor::IrCameraHandle camera_handle; u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) {
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
u64 transfer_memory_size;
};
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()}; ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size");
const auto t_mem_handle{ctx.GetCopyHandle(0)};
auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
LOG_INFO(Service_IRS, LOG_INFO(Service_IRS,
"called, npad_type={}, npad_id={}, transfer_memory_size={}, " "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
"applet_resource_user_id={}", "applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid);
parameters.transfer_memory_size, parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
auto& image_transfer_processor = image_transfer_processor.SetConfig(processor_config);
GetProcessor<ImageTransferProcessor>(parameters.camera_handle); image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
image_transfer_processor.SetConfig(parameters.processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); Common::Input::PollingMode::IR);
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::RunIrLedProcessor(HLERequestContext& ctx) { Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; Core::IrSensor::PackedIrLedProcessorConfig processor_config,
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; ClientAppletResourceUserId aruid) {
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
"applet_resource_user_id={}", "applet_resource_user_id={}",
camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
processor_config.required_mcu_version.major, processor_config.required_mcu_version.major,
processor_config.required_mcu_version.minor, applet_resource_user_id); processor_config.required_mcu_version.minor, aruid.pid);
auto result = IsIrCameraHandleValid(camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) { auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); MakeProcessor<IrLedProcessor>(camera_handle, device);
MakeProcessor<IrLedProcessor>(camera_handle, device); auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); image_transfer_processor.SetConfig(processor_config);
image_transfer_processor.SetConfig(processor_config); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR);
Common::Input::PollingMode::IR);
}
IPC::ResponseBuilder rb{ctx, 2}; R_SUCCEED();
rb.Push(result);
} }
void IRS::StopImageProcessorAsync(HLERequestContext& ctx) { Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::IrCameraHandle camera_handle;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, LOG_WARNING(Service_IRS,
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, camera_handle.npad_type, camera_handle.npad_id, aruid.pid);
parameters.applet_resource_user_id);
auto result = IsIrCameraHandleValid(parameters.camera_handle); R_TRY(IsIrCameraHandleValid(camera_handle));
if (result.IsSuccess()) {
// TODO: Stop image processor async
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
result = ResultSuccess;
}
IPC::ResponseBuilder rb{ctx, 2}; // TODO: Stop image processor async
rb.Push(result); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
R_SUCCEED();
} }
void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) { Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
IPC::RequestParser rp{ctx}; ClientAppletResourceUserId aruid) {
struct Parameters {
Core::IrSensor::PackedFunctionLevel function_level;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
parameters.function_level.function_level, parameters.applet_resource_user_id); function_level.function_level, aruid.pid);
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/irs_types.h"
@ -35,26 +36,73 @@ private:
}; };
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
void ActivateIrsensor(HLERequestContext& ctx); Result ActivateIrsensor(ClientAppletResourceUserId aruid);
void DeactivateIrsensor(HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx); Result DeactivateIrsensor(ClientAppletResourceUserId aruid);
void StopImageProcessor(HLERequestContext& ctx);
void RunMomentProcessor(HLERequestContext& ctx); Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory,
void RunClusteringProcessor(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void RunImageTransferProcessor(HLERequestContext& ctx); Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
void GetImageTransferProcessorState(HLERequestContext& ctx); ClientAppletResourceUserId aruid);
void RunTeraPluginProcessor(HLERequestContext& ctx);
void GetNpadIrCameraHandle(HLERequestContext& ctx); Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle,
void RunPointingProcessor(HLERequestContext& ctx); ClientAppletResourceUserId aruid,
void SuspendImageProcessor(HLERequestContext& ctx); const Core::IrSensor::PackedMomentProcessorConfig& processor_config);
void CheckFirmwareVersion(HLERequestContext& ctx);
void SetFunctionLevel(HLERequestContext& ctx); Result RunClusteringProcessor(
void RunImageTransferExProcessor(HLERequestContext& ctx); Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
void RunIrLedProcessor(HLERequestContext& ctx); const Core::IrSensor::PackedClusteringProcessorConfig& processor_config);
void StopImageProcessorAsync(HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx); Result RunImageTransferProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result GetImageTransferProcessorState(
Out<Core::IrSensor::ImageTransferProcessorState> out_state,
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data);
Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle,
Core::HID::NpadIdType npad_id);
Result RunPointingProcessor(
Core::IrSensor::IrCameraHandle camera_handle,
const Core::IrSensor::PackedPointingProcessorConfig& processor_config,
ClientAppletResourceUserId aruid);
Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedMcuVersion mcu_version,
ClientAppletResourceUserId aruid);
Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result RunImageTransferExProcessor(
Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid,
const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config,
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem);
Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle,
Core::IrSensor::PackedIrLedProcessorConfig processor_config,
ClientAppletResourceUserId aruid);
Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle,
ClientAppletResourceUserId aruid);
Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level,
ClientAppletResourceUserId aruid);
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
const Core::IrSensor::IrCameraHandle& camera_handle); const Core::IrSensor::IrCameraHandle& camera_handle);

View File

@ -49,6 +49,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
continue; continue;
} }
if (session.process == process) { if (session.process == process) {
session.ref_count++;
return session.id; return session.id;
} }
} }
@ -66,6 +67,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
} }
auto& session = impl->sessions[new_id]; auto& session = impl->sessions[new_id];
session.is_active = true; session.is_active = true;
session.ref_count = 1;
// Optimization // Optimization
if (process->IsApplication()) { if (process->IsApplication()) {
auto& page_table = process->GetPageTable().GetBasePageTable(); auto& page_table = process->GetPageTable().GetBasePageTable();
@ -114,8 +116,11 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
void Container::CloseSession(SessionId session_id) { void Container::CloseSession(SessionId session_id) {
std::scoped_lock lk(impl->session_guard); std::scoped_lock lk(impl->session_guard);
impl->file.UnmapAllHandles(session_id);
auto& session = impl->sessions[session_id.id]; auto& session = impl->sessions[session_id.id];
if (--session.ref_count > 0) {
return;
}
impl->file.UnmapAllHandles(session_id);
auto& smmu = impl->host1x.MemoryManager(); auto& smmu = impl->host1x.MemoryManager();
if (session.has_preallocated_area) { if (session.has_preallocated_area) {
const DAddr region_start = session.mapper->GetRegionStart(); const DAddr region_start = session.mapper->GetRegionStart();

View File

@ -46,6 +46,7 @@ struct Session {
bool has_preallocated_area{}; bool has_preallocated_area{};
std::unique_ptr<HeapMapper> mapper{}; std::unique_ptr<HeapMapper> mapper{};
bool is_active{}; bool is_active{};
s32 ref_count{};
}; };
class Container { class Container {
@ -67,10 +68,7 @@ public:
const SyncpointManager& GetSyncpointManager() const; const SyncpointManager& GetSyncpointManager() const;
struct Host1xDeviceFileData { struct Host1xDeviceFileData {
std::unordered_map<DeviceFD, u32> fd_to_id{};
std::deque<u32> syncpts_accumulated{}; std::deque<u32> syncpts_accumulated{};
u32 nvdec_next_id{};
u32 vic_next_id{};
}; };
Host1xDeviceFileData& Host1xDeviceFile(); Host1xDeviceFileData& Host1xDeviceFile();

View File

@ -3,110 +3,21 @@
#include <mutex> #include <mutex>
#include <boost/container/small_vector.hpp> #include "common/range_sets.h"
#define BOOST_NO_MT #include "common/range_sets.inc"
#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 "core/hle/service/nvdrv/core/heap_mapper.h" #include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.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 { 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 { struct HeapMapper::HeapMapperInternal {
HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
~HeapMapperInternal() = default; ~HeapMapperInternal() = default;
template <typename Func> Common::RangeSet<VAddr> m_temporary_set;
void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, Common::OverlapRangeSet<VAddr> m_mapped_ranges;
Func&& func) { Tegra::MaxwellDeviceMemoryManager& m_device_memory;
const DAddr start_address = cpu_addr; std::mutex m_guard;
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;
}; };
HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid, 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() { 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) { DAddr HeapMapper::Map(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard); std::scoped_lock lk(m_internal->m_guard);
m_internal->base_set.clear(); // Add the mapping range to a temporary range set.
const IntervalType interval{start, start + size}; m_internal->m_temporary_set.Clear();
m_internal->base_set.insert(interval); m_internal->m_temporary_set.Add(start, size);
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
[this](VAddr start_addr, VAddr end_addr, int) { // Remove anything that's already mapped from the temporary range set.
const IntervalType other{start_addr, end_addr}; m_internal->m_mapped_ranges.ForEachInRange(
m_internal->base_set.subtract(other); start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
}); m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
if (!m_internal->base_set.empty()) { });
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end(); // Map anything that has not been mapped yet.
for (; it != end_it; it++) { m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
const VAddr inter_addr_end = it->upper(); const size_t sub_size = end_addr - start_addr;
const VAddr inter_addr = it->lower(); const size_t offset = start_addr - m_vaddress;
const size_t offset = inter_addr - m_vaddress; m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
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); // 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();
m_internal->mapping_overlaps += std::make_pair(interval, 1); return m_daddress + static_cast<DAddr>(start - m_vaddress);
m_internal->base_set.clear();
return m_daddress + (start - m_vaddress);
} }
void HeapMapper::Unmap(VAddr start, size_t size) { void HeapMapper::Unmap(VAddr start, size_t size) {
std::scoped_lock lk(m_internal->guard); std::scoped_lock lk(m_internal->m_guard);
m_internal->base_set.clear();
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, // Just subtract the range and whatever is deleted, unmap it.
[this](VAddr start_addr, VAddr end_addr, int value) { m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
if (value <= 1) { const size_t sub_size = end_addr - start_addr;
const IntervalType other{start_addr, end_addr}; const size_t offset = start_addr - m_vaddress;
m_internal->base_set.insert(other); m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
} });
});
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();
} }
} // namespace Service::Nvidia::NvCore } // namespace Service::Nvidia::NvCore

View File

@ -333,9 +333,13 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
}(); }();
for (auto& [id, handle] : handles_copy) { 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);
} }
} }

View File

@ -15,6 +15,22 @@
namespace Service::Nvidia::Devices { 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) nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {} : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
nvdisp_disp0::~nvdisp_disp0() = default; nvdisp_disp0::~nvdisp_disp0() = default;
@ -56,6 +72,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
.pixel_format = layer.format, .pixel_format = layer.format,
.transform_flags = layer.transform, .transform_flags = layer.transform,
.crop_rect = layer.crop_rect, .crop_rect = layer.crop_rect,
.blending = ConvertBlending(layer.blending),
}); });
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {

View File

@ -123,6 +123,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
vm.va_range_end = params.va_range_end; vm.va_range_end = params.va_range_end;
} }
const u64 max_big_page_bits = Common::Log2Ceil64(vm.va_range_end);
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
@ -132,8 +134,8 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits, gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split,
VM::PAGE_SIZE_BITS); vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
system.GPU().InitAddressSpace(*gmmu); system.GPU().InitAddressSpace(*gmmu);
vm.initialised = true; vm.initialised = true;

View File

@ -8,6 +8,7 @@
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices { namespace Service::Nvidia::Devices {
@ -21,13 +22,8 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
switch (command.group) { switch (command.group) {
case 0x0: case 0x0:
switch (command.cmd) { switch (command.cmd) {
case 0x1: { 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++;
}
return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd); return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
}
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output); return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
case 0x3: case 0x3:
@ -72,15 +68,12 @@ void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream started"); LOG_INFO(Service_NVDRV, "NVDEC video stream started");
system.SetNVDECActive(true); system.SetNVDECActive(true);
sessions[fd] = session_id; sessions[fd] = session_id;
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
} }
void nvhost_nvdec::OnClose(DeviceFD fd) { void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
auto& host1x_file = core.Host1xDeviceFile(); host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
const auto iter = host1x_file.fd_to_id.find(fd);
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
system.SetNVDECActive(false); system.SetNVDECActive(false);
auto it = sessions.find(fd); auto it = sessions.find(fd);
if (it != sessions.end()) { if (it != sessions.end()) {

View File

@ -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_, nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_,
NvCore::ChannelType channel_type_) NvCore::ChannelType channel_type_)
: nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, : nvdevice{system_}, host1x{system_.Host1x()}, core{core_},
nvmap{core.GetNvMapFile()}, channel_type{channel_type_} { syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()},
channel_type{channel_type_} {
auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated; auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated;
if (syncpts_accumulated.empty()) { if (syncpts_accumulated.empty()) {
channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); 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, syncpt_increments, params.syncpoint_count, offset);
offset += SliceVectors(data, fence_thresholds, params.fence_count, offset); offset += SliceVectors(data, fence_thresholds, params.fence_count, offset);
auto& gpu = system.GPU();
auto* session = core.GetSession(sessions[fd]); auto* session = core.GetSession(sessions[fd]);
if (gpu.UseNvdec()) { for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
for (std::size_t i = 0; i < syncpt_increments.size(); i++) { const SyncptIncr& syncpt_incr = syncpt_increments[i];
const SyncptIncr& syncpt_incr = syncpt_increments[i]; fence_thresholds[i] =
fence_thresholds[i] = syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
}
} }
for (const auto& cmd_buffer : command_buffers) { for (const auto& cmd_buffer : command_buffers) {
const auto object = nvmap.GetHandle(cmd_buffer.memory_id); const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader,
session->process->GetMemory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(), Core::Memory::GuestMemoryFlags::SafeRead>
cmdlist.size() * sizeof(u32)); cmdlist(session->process->GetMemory(), object->address + cmd_buffer.offset,
gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist); cmd_buffer.word_count);
host1x.PushEntries(fd, std::move(cmdlist));
} }
// Some games expect command_buffers to be written back // Some games expect command_buffers to be written back
offset = 0; offset = 0;
offset += WriteVectors(data, command_buffers, offset); offset += WriteVectors(data, command_buffers, offset);

View File

@ -119,6 +119,7 @@ protected:
Kernel::KEvent* QueryEvent(u32 event_id) override; Kernel::KEvent* QueryEvent(u32 event_id) override;
Tegra::Host1x::Host1x& host1x;
u32 channel_syncpoint; u32 channel_syncpoint;
s32_le nvmap_fd{}; s32_le nvmap_fd{};
u32_le submit_timeout{}; u32_le submit_timeout{};

View File

@ -7,6 +7,7 @@
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices { namespace Service::Nvidia::Devices {
@ -21,13 +22,8 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
switch (command.group) { switch (command.group) {
case 0x0: case 0x0:
switch (command.cmd) { switch (command.cmd) {
case 0x1: { 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++;
}
return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd); return WrapFixedVariable(this, &nvhost_vic::Submit, input, output, fd);
}
case 0x2: case 0x2:
return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output); return WrapFixed(this, &nvhost_vic::GetSyncpoint, input, output);
case 0x3: 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) { void nvhost_vic::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
sessions[fd] = session_id; sessions[fd] = session_id;
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::VIC, channel_syncpoint);
} }
void nvhost_vic::OnClose(DeviceFD fd) { void nvhost_vic::OnClose(DeviceFD fd) {
auto& host1x_file = core.Host1xDeviceFile(); host1x.StopDevice(fd, Tegra::Host1x::ChannelType::VIC);
const auto iter = host1x_file.fd_to_id.find(fd);
if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
sessions.erase(fd); sessions.erase(fd);
} }

View File

@ -14,24 +14,20 @@
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" #include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
#include "core/hle/service/vi/layer/vi_layer.h" #include "core/hle/service/vi/layer/vi_layer.h"
#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_results.h"
#include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"
namespace Service::Nvnflinger { namespace Service::Nvnflinger {
namespace { namespace {
Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address, Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_group,
std::unique_ptr<Kernel::KPageGroup>* out_page_group, Core::System& system, u32 size) {
Core::System& system, u32 size) {
using Core::Memory::YUZU_PAGESIZE; using Core::Memory::YUZU_PAGESIZE;
// Allocate memory for the system shared buffer. // 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. // FIXME: This memory belongs to vi's .data section.
auto& kernel = system.Kernel(); 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. // Hold a temporary page group reference while we try to map it.
auto pg = std::make_unique<Kernel::KPageGroup>( 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::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
Kernel::KMemoryManager::Direction::FromBack))); 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. // Get bounds of where mapping is possible.
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart()); const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE; 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 // Return failure, if necessary
R_UNLESS(i < 64, res); R_UNLESS(i < 64, res);
// Return the mapped page group.
*out_page_group = std::move(pg);
// We succeeded. // We succeeded.
R_SUCCEED(); 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)); 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 auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
constexpr u32 SharedBufferBlockLinearBpp = 4; constexpr u32 SharedBufferBlockLinearBpp = 4;
@ -186,53 +210,97 @@ FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& fli
FbShareBufferManager::~FbShareBufferManager() = default; 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}; std::scoped_lock lk{m_guard};
// Ensure we have not already created a buffer. // Ensure we haven't already created.
R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed); const u64 aruid = owner_process->GetProcessId();
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
// Allocate memory and space for the shared buffer. // Allocate memory for the shared buffer if needed.
Common::ProcessAddress map_address; if (!m_buffer_page_group) {
R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address), R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
std::addressof(m_buffer_page_group), m_system, SharedBufferSize));
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(); auto& container = m_nvdrv->GetContainer();
m_session_id = container.OpenSession(m_system.ApplicationProcess()); session.session_id = container.OpenSession(owner_process);
m_nvmap_fd = m_nvdrv->Open("/dev/nvmap", m_session_id); session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
// Create an nvmap handle for the buffer and assign the memory to it. // 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, R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
map_address, SharedBufferSize)); session.nvmap_fd, map_address, SharedBufferSize));
// Record the display id.
m_display_id = display_id;
// Create and open a layer for the display. // Create and open a layer for the display.
m_layer_id = m_flinger.CreateLayer(m_display_id).value(); session.layer_id = m_flinger.CreateLayer(m_display_id, blending).value();
m_flinger.OpenLayer(m_layer_id); m_flinger.OpenLayer(session.layer_id);
// Set up the buffer.
m_buffer_id = m_next_buffer_id++;
// Get the layer. // 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); ASSERT(layer != nullptr);
// Get the producer and set preallocated buffers. // Get the producer and set preallocated buffers.
auto& producer = layer->GetBufferQueue(); auto& producer = layer->GetBufferQueue();
MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle); MakeGraphicBuffer(producer, 0, session.buffer_nvmap_handle);
MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle); MakeGraphicBuffer(producer, 1, session.buffer_nvmap_handle);
// Assign outputs. // Assign outputs.
*out_buffer_id = m_buffer_id; *out_buffer_id = m_buffer_id;
*out_layer_id = m_layer_id; *out_layer_handle = session.layer_id;
// We succeeded. // We succeeded.
R_SUCCEED(); 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, Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
s32* out_nvmap_handle, s32* out_nvmap_handle,
SharedMemoryPoolLayout* out_pool_layout, 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(m_buffer_id > 0, VI::ResultNotFound);
R_UNLESS(buffer_id == m_buffer_id, 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_pool_layout = SharedBufferPoolLayout;
*out_buffer_size = SharedBufferSize; *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(); R_SUCCEED();
} }
Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) { Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
// Ensure the layer id is valid. // 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. // Get the layer.
VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id); VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
@ -309,6 +378,10 @@ Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
android::Status::NoError, android::Status::NoError,
VI::ResultOperationFailed); VI::ResultOperationFailed);
ON_RESULT_FAILURE {
producer.CancelBuffer(static_cast<s32>(slot), fence);
};
// Queue the buffer to the producer. // Queue the buffer to the producer.
android::QueueBufferInput input{}; android::QueueBufferInput input{};
android::QueueBufferOutput output{}; android::QueueBufferOutput output{};
@ -342,4 +415,33 @@ Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadab
R_SUCCEED(); 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 } // namespace Service::Nvnflinger

View File

@ -3,9 +3,12 @@
#pragma once #pragma once
#include <map>
#include "common/math_util.h" #include "common/math_util.h"
#include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.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/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/fence.h" #include "core/hle/service/nvnflinger/ui/fence.h"
@ -29,13 +32,18 @@ struct SharedMemoryPoolLayout {
}; };
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size"); static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
struct FbShareSession;
class FbShareBufferManager final { class FbShareBufferManager final {
public: public:
explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger, explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
std::shared_ptr<Nvidia::Module> nvdrv); std::shared_ptr<Nvidia::Module> nvdrv);
~FbShareBufferManager(); ~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, Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id, SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
u64 applet_resource_user_id); u64 applet_resource_user_id);
@ -45,6 +53,8 @@ public:
u32 transform, s32 swap_interval, u64 layer_id, s64 slot); u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
private: private:
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id); Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
@ -52,11 +62,8 @@ private:
u64 m_next_buffer_id = 1; u64 m_next_buffer_id = 1;
u64 m_display_id = 0; u64 m_display_id = 0;
u64 m_buffer_id = 0; u64 m_buffer_id = 0;
u64 m_layer_id = 0;
u32 m_buffer_nvmap_handle = 0;
SharedMemoryPoolLayout m_pool_layout = {}; SharedMemoryPoolLayout m_pool_layout = {};
Nvidia::DeviceFD m_nvmap_fd = {}; std::map<u64, FbShareSession> m_sessions;
Nvidia::NvCore::SessionId m_session_id = {};
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group; std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
std::mutex m_guard; std::mutex m_guard;
@ -65,4 +72,11 @@ private:
std::shared_ptr<Nvidia::Module> m_nvdrv; 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 } // namespace Service::Nvnflinger

View File

@ -7,7 +7,6 @@
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.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.h"
#include "core/hle/service/nvnflinger/buffer_item_consumer.h" #include "core/hle/service/nvnflinger/buffer_item_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
#include "core/hle/service/nvnflinger/hardware_composer.h" #include "core/hle/service/nvnflinger/hardware_composer.h"
#include "core/hle/service/nvnflinger/hwc_layer.h" #include "core/hle/service/nvnflinger/hwc_layer.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" #include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
@ -46,31 +45,9 @@ HardwareComposer::HardwareComposer() = default;
HardwareComposer::~HardwareComposer() = default; HardwareComposer::~HardwareComposer() = default;
u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) { Nvidia::Devices::nvdisp_disp0& nvdisp) {
boost::container::small_vector<HwcLayer, 2> composition_stack; boost::container::small_vector<HwcLayer, 2> composition_stack;
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;
}
}
// Set default speed limit to 100%. // Set default speed limit to 100%.
*out_speed_scale = 1.0f; *out_speed_scale = 1.0f;
@ -109,6 +86,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
.height = igbp_buffer.Height(), .height = igbp_buffer.Height(),
.stride = igbp_buffer.Stride(), .stride = igbp_buffer.Stride(),
.z_index = 0, .z_index = 0,
.blending = layer.GetBlending(),
.transform = static_cast<android::BufferTransformFlags>(item.transform), .transform = static_cast<android::BufferTransformFlags>(item.transform),
.crop_rect = item.crop, .crop_rect = item.crop,
.acquire_fence = item.fence, .acquire_fence = item.fence,
@ -142,7 +120,30 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
MicroProfileFlip(); MicroProfileFlip();
// Advance by at least one frame. // Advance by at least one frame.
return swap_interval.value_or(1); 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) { void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) {

View File

@ -27,7 +27,7 @@ public:
~HardwareComposer(); ~HardwareComposer();
u32 ComposeLocked(f32* out_speed_scale, VI::Display& display, u32 ComposeLocked(f32* out_speed_scale, VI::Display& display,
Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance); Nvidia::Devices::nvdisp_disp0& nvdisp);
void RemoveLayerLocked(VI::Display& display, LayerId layer_id); void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
private: private:

View File

@ -11,6 +11,18 @@
namespace Service::Nvnflinger { 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 { struct HwcLayer {
u32 buffer_handle; u32 buffer_handle;
u32 offset; u32 offset;
@ -19,6 +31,7 @@ struct HwcLayer {
u32 height; u32 height;
u32 stride; u32 stride;
s32 z_index; s32 z_index;
LayerBlending blending;
android::BufferTransformFlags transform; android::BufferTransformFlags transform;
Common::Rectangle<int> crop_rect; Common::Rectangle<int> crop_rect;
android::Fence acquire_fence; android::Fence acquire_fence;

View File

@ -157,7 +157,7 @@ bool Nvnflinger::CloseDisplay(u64 display_id) {
return true; 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(); const auto lock_guard = Lock();
auto* const display = FindDisplay(display_id); auto* const display = FindDisplay(display_id);
@ -166,13 +166,14 @@ std::optional<u64> Nvnflinger::CreateLayer(u64 display_id) {
} }
const u64 layer_id = next_layer_id++; const u64 layer_id = next_layer_id++;
CreateLayerAtId(*display, layer_id); CreateLayerAtId(*display, layer_id, blending);
return layer_id; 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++; const auto buffer_id = next_buffer_queue_id++;
display.CreateLayer(layer_id, buffer_id, nvdrv->container); display.CreateLayer(layer_id, buffer_id, nvdrv->container);
display.FindLayer(layer_id)->SetBlending(blending);
} }
bool Nvnflinger::OpenLayer(u64 layer_id) { bool Nvnflinger::OpenLayer(u64 layer_id) {
@ -291,8 +292,7 @@ void Nvnflinger::Compose() {
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
ASSERT(nvdisp); ASSERT(nvdisp);
swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp, swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp);
swap_interval);
} }
} }

View File

@ -15,6 +15,7 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/hle/result.h" #include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvnflinger/hwc_layer.h"
namespace Common { namespace Common {
class Event; class Event;
@ -72,7 +73,8 @@ public:
/// Creates a layer on the specified display and returns the layer ID. /// 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. /// 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. /// Opens a layer on all displays for the given layer ID.
bool OpenLayer(u64 layer_id); bool OpenLayer(u64 layer_id);
@ -128,7 +130,7 @@ private:
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
/// Creates a layer with the specified layer ID in the desired display. /// 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); void SplitVSync(std::stop_token stop_token);

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // 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" #include "core/hle/service/vi/layer/vi_layer.h"
namespace Service::VI { namespace Service::VI {
@ -8,8 +9,9 @@ namespace Service::VI {
Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
android::BufferQueueProducer& binder_, android::BufferQueueProducer& binder_,
std::shared_ptr<android::BufferItemConsumer>&& consumer_) std::shared_ptr<android::BufferItemConsumer>&& consumer_)
: layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
consumer{std::move(consumer_)}, open{false}, visible{true} {} consumer_)},
blending{Nvnflinger::LayerBlending::None}, open{false}, visible{true} {}
Layer::~Layer() = default; Layer::~Layer() = default;

View File

@ -14,6 +14,10 @@ class BufferQueueCore;
class BufferQueueProducer; class BufferQueueProducer;
} // namespace Service::android } // namespace Service::android
namespace Service::Nvnflinger {
enum class LayerBlending : u32;
}
namespace Service::VI { namespace Service::VI {
/// Represents a single display layer. /// Represents a single display layer.
@ -92,12 +96,21 @@ public:
return !std::exchange(open, true); return !std::exchange(open, true);
} }
Nvnflinger::LayerBlending GetBlending() {
return blending;
}
void SetBlending(Nvnflinger::LayerBlending b) {
blending = b;
}
private: private:
const u64 layer_id; const u64 layer_id;
const u32 binder_id; const u32 binder_id;
android::BufferQueueCore& core; android::BufferQueueCore& core;
android::BufferQueueProducer& binder; android::BufferQueueProducer& binder;
std::shared_ptr<android::BufferItemConsumer> consumer; std::shared_ptr<android::BufferItemConsumer> consumer;
Service::Nvnflinger::LayerBlending blending;
bool open; bool open;
bool visible; bool visible;
}; };

View File

@ -64,6 +64,8 @@ public:
Memory(Memory&&) = default; Memory(Memory&&) = default;
Memory& operator=(Memory&&) = delete; Memory& operator=(Memory&&) = delete;
static constexpr bool HAS_FLUSH_INVALIDATION = false;
/** /**
* Resets the state of the Memory system. * Resets the state of the Memory system.
*/ */

View File

@ -9,6 +9,7 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/service/hid/hid_server.h" #include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#include "core/memory.h" #include "core/memory.h"
@ -46,12 +47,23 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
StandardVmCallbacks::~StandardVmCallbacks() = default; StandardVmCallbacks::~StandardVmCallbacks() = default;
void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size); // Return zero on invalid address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
std::memset(data, 0, size);
return;
}
system.ApplicationMemory().ReadBlock(address, data, size);
} }
void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size); // Skip invalid memory write address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
return;
}
system.ApplicationMemory().WriteBlock(address, data, size);
} }
u64 StandardVmCallbacks::HidKeysDown() { u64 StandardVmCallbacks::HidKeysDown() {
@ -81,21 +93,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) {
data.back() == '\n' ? data.substr(0, data.size() - 1) : data); data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
} }
VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const { bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
if ((in < metadata.main_nso_extents.base || if ((in < metadata.main_nso_extents.base ||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
(in < metadata.heap_extents.base || (in < metadata.heap_extents.base ||
in >= metadata.heap_extents.base + metadata.heap_extents.size)) { in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
LOG_ERROR(CheatEngine, (in < metadata.alias_extents.base ||
in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
(in < metadata.aslr_extents.base ||
in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
LOG_DEBUG(CheatEngine,
"Cheat attempting to access memory at invalid address={:016X}, if this " "Cheat attempting to access memory at invalid address={:016X}, if this "
"persists, " "persists, "
"the cheat may be incorrect. However, this may be normal early in execution if " "the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.", "the game has not properly set up yet.",
in); in);
return 0; ///< Invalid addresses will hard crash return false; ///< Invalid addresses will hard crash
} }
return in; return true;
} }
CheatParser::~CheatParser() = default; CheatParser::~CheatParser() = default;
@ -211,16 +227,14 @@ void CheatEngine::Initialize() {
.base = GetInteger(page_table.GetHeapRegionStart()), .base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(), .size = page_table.GetHeapRegionSize(),
}; };
metadata.aslr_extents = {
metadata.address_space_extents = {
.base = GetInteger(page_table.GetAddressSpaceStart()),
.size = page_table.GetAddressSpaceSize(),
};
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()), .base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(), .size = page_table.GetAliasCodeRegionSize(),
}; };
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
is_pending_reload.exchange(true); is_pending_reload.exchange(true);
} }

View File

@ -27,17 +27,17 @@ public:
StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
~StandardVmCallbacks() override; ~StandardVmCallbacks() override;
void MemoryRead(VAddr address, void* data, u64 size) override; void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
void MemoryWrite(VAddr address, const void* data, u64 size) override; void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
u64 HidKeysDown() override; u64 HidKeysDown() override;
void DebugLog(u8 id, u64 value) override; void DebugLog(u8 id, u64 value) override;
void CommandLog(std::string_view data) override; void CommandLog(std::string_view data) override;
private: private:
VAddr SanitizeAddress(VAddr address) const; bool IsAddressInRange(VAddr address) const;
const CheatProcessMetadata& metadata; const CheatProcessMetadata& metadata;
System& system; Core::System& system;
}; };
// Intermediary class that parses a text file or other disk format for storing cheats into a // Intermediary class that parses a text file or other disk format for storing cheats into a

View File

@ -18,7 +18,7 @@ struct CheatProcessMetadata {
MemoryRegionExtents main_nso_extents{}; MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{}; MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{}; MemoryRegionExtents alias_extents{};
MemoryRegionExtents address_space_extents{}; MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{}; std::array<u8, 0x20> main_nso_build_id{};
}; };

View File

@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
} break; } break;
case CheatVmOpcodeType::EndConditionalBlock: { case CheatVmOpcodeType::EndConditionalBlock: {
// 20000000 // 20000000
// There's actually nothing left to process here! opcode.opcode = EndConditionalOpcode{
opcode.opcode = EndConditionalOpcode{}; .is_else = ((first_dword >> 24) & 0xf) == 1,
};
} break; } break;
case CheatVmOpcodeType::ControlLoop: { case CheatVmOpcodeType::ControlLoop: {
// 300R0000 VVVVVVVV // 300R0000 VVVVVVVV
@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
.idx = first_dword & 0xF, .idx = first_dword & 0xF,
}; };
} break; } break;
case CheatVmOpcodeType::PauseProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = PauseProcessOpcode{};
} break;
case CheatVmOpcodeType::ResumeProcess: {
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Pauses the current process. */
opcode.opcode = ResumeProcessOpcode{};
} break;
case CheatVmOpcodeType::DebugLog: { case CheatVmOpcodeType::DebugLog: {
// FFFTIX## // FFFTIX##
// FFFTI0Ma aaaaaaaa // FFFTI0Ma aaaaaaaa
@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
return valid; return valid;
} }
void DmntCheatVm::SkipConditionalBlock() { void DmntCheatVm::SkipConditionalBlock(bool is_if) {
if (condition_depth > 0) { if (condition_depth > 0) {
// We want to continue until we're out of the current block. // We want to continue until we're out of the current block.
const std::size_t desired_depth = condition_depth - 1; const std::size_t desired_depth = condition_depth - 1;
@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() {
// We also support nesting of conditional blocks, and Gateway does not. // We also support nesting of conditional blocks, and Gateway does not.
if (skip_opcode.begin_conditional_block) { if (skip_opcode.begin_conditional_block) {
condition_depth++; condition_depth++;
} else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) {
condition_depth--; if (!end_cond->is_else) {
condition_depth--;
} else if (is_if && condition_depth - 1 == desired_depth) {
break;
}
} }
} }
} else { } else {
@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
return metadata.main_nso_extents.base + rel_address; return metadata.main_nso_extents.base + rel_address;
case MemoryAccessType::Heap: case MemoryAccessType::Heap:
return metadata.heap_extents.base + rel_address; return metadata.heap_extents.base + rel_address;
case MemoryAccessType::Alias:
return metadata.alias_extents.base + rel_address;
case MemoryAccessType::Aslr:
return metadata.aslr_extents.base + rel_address;
} }
} }
@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() {
registers.fill(0); registers.fill(0);
saved_values.fill(0); saved_values.fill(0);
loop_tops.fill(0); loop_tops.fill(0);
static_registers.fill(0);
instruction_ptr = 0; instruction_ptr = 0;
condition_depth = 0; condition_depth = 0;
decode_success = true; decode_success = true;
@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
break; break;
} }
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
break; break;
} }
// Check against condition. // Check against condition.
@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
} }
// Skip conditional block if condition not met. // Skip conditional block if condition not met.
if (!cond_met) { if (!cond_met) {
SkipConditionalBlock(); SkipConditionalBlock(true);
} }
} else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) { } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
// Decrement the condition depth. if (end_cond->is_else) {
// We will assume, graciously, that mismatched conditional block ends are a nop. /* Skip to the end of the conditional block. */
if (condition_depth > 0) { this->SkipConditionalBlock(false);
condition_depth--; } else {
/* Decrement the condition depth. */
/* We will assume, graciously, that mismatched conditional block ends are a nop. */
if (condition_depth > 0) {
condition_depth--;
}
} }
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
if (ctrl_loop->start_loop) { if (ctrl_loop->start_loop) {
@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index], callbacks->MemoryReadUnsafe(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width); ldr_memory->bit_width);
break; break;
} }
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
break; break;
} }
// Increment register if relevant. // Increment register if relevant.
@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Check for keypress. // Check for keypress.
if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
// Keys not pressed. Skip conditional block. // Keys not pressed. Skip conditional block.
SkipConditionalBlock(); SkipConditionalBlock(true);
} }
} else if (auto perform_math_reg = } else if (auto perform_math_reg =
std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
break; break;
} }
@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); callbacks->MemoryReadUnsafe(cond_address, &cond_value,
begin_reg_cond->bit_width);
break; break;
} }
} }
@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Skip conditional block if condition not met. // Skip conditional block if condition not met.
if (!cond_met) { if (!cond_met) {
SkipConditionalBlock(); SkipConditionalBlock(true);
} }
} else if (auto save_restore_reg = } else if (auto save_restore_reg =
std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
// Store a register to a static register. // Store a register to a static register.
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
} }
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
// TODO: Pause cheat process
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
// TODO: Resume cheat process
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
// Read value from memory. // Read value from memory.
u64 log_value = 0; u64 log_value = 0;
@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2: case 2:
case 4: case 4:
case 8: case 8:
callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
break; break;
} }
} }

View File

@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 {
DoubleExtendedWidth = 0xF0, DoubleExtendedWidth = 0xF0,
// Double-extended width opcodes. // Double-extended width opcodes.
PauseProcess = 0xFF0,
ResumeProcess = 0xFF1,
DebugLog = 0xFFF, DebugLog = 0xFFF,
}; };
enum class MemoryAccessType : u32 { enum class MemoryAccessType : u32 {
MainNso = 0, MainNso = 0,
Heap = 1, Heap = 1,
Alias = 2,
Aslr = 3,
}; };
enum class ConditionalComparisonType : u32 { enum class ConditionalComparisonType : u32 {
@ -131,7 +135,9 @@ struct BeginConditionalOpcode {
VmInt value{}; VmInt value{};
}; };
struct EndConditionalOpcode {}; struct EndConditionalOpcode {
bool is_else;
};
struct ControlLoopOpcode { struct ControlLoopOpcode {
bool start_loop{}; bool start_loop{};
@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode {
u32 idx{}; u32 idx{};
}; };
struct PauseProcessOpcode {};
struct ResumeProcessOpcode {};
struct DebugLogOpcode { struct DebugLogOpcode {
u32 bit_width{}; u32 bit_width{};
u32 log_id{}; u32 log_id{};
@ -244,8 +254,8 @@ struct CheatVmOpcode {
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode, SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
UnrecognizedInstruction> ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
opcode{}; opcode{};
}; };
@ -256,8 +266,8 @@ public:
public: public:
virtual ~Callbacks(); virtual ~Callbacks();
virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
virtual u64 HidKeysDown() = 0; virtual u64 HidKeysDown() = 0;
@ -296,7 +306,7 @@ private:
std::array<std::size_t, NumRegisters> loop_tops{}; std::array<std::size_t, NumRegisters> loop_tops{};
bool DecodeNextOpcode(CheatVmOpcode& out); bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock(); void SkipConditionalBlock(bool is_if);
void ResetState(); void ResetState();
// For implementing the DebugLog opcode. // For implementing the DebugLog opcode.

View File

@ -401,6 +401,14 @@ void Config::ReadNetworkValues() {
EndGroup(); EndGroup();
} }
void Config::ReadLibraryAppletValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
ReadCategory(Settings::Category::LibraryApplet);
EndGroup();
}
void Config::ReadValues() { void Config::ReadValues() {
if (global) { if (global) {
ReadDataStorageValues(); ReadDataStorageValues();
@ -410,6 +418,7 @@ void Config::ReadValues() {
ReadServiceValues(); ReadServiceValues();
ReadWebServiceValues(); ReadWebServiceValues();
ReadMiscellaneousValues(); ReadMiscellaneousValues();
ReadLibraryAppletValues();
} }
ReadControlValues(); ReadControlValues();
ReadCoreValues(); ReadCoreValues();
@ -511,6 +520,7 @@ void Config::SaveValues() {
SaveNetworkValues(); SaveNetworkValues();
SaveWebServiceValues(); SaveWebServiceValues();
SaveMiscellaneousValues(); SaveMiscellaneousValues();
SaveLibraryAppletValues();
} else { } else {
LOG_DEBUG(Config, "Saving only generic configuration values"); LOG_DEBUG(Config, "Saving only generic configuration values");
} }
@ -691,6 +701,14 @@ void Config::SaveWebServiceValues() {
EndGroup(); EndGroup();
} }
void Config::SaveLibraryAppletValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet));
WriteCategory(Settings::Category::LibraryApplet);
EndGroup();
}
bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) {
std::string full_key = GetFullKey(key, false); std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) { if (!default_value.has_value()) {

View File

@ -88,6 +88,7 @@ protected:
void ReadSystemValues(); void ReadSystemValues();
void ReadWebServiceValues(); void ReadWebServiceValues();
void ReadNetworkValues(); void ReadNetworkValues();
void ReadLibraryAppletValues();
// Read platform specific sections // Read platform specific sections
virtual void ReadHidbusValues() = 0; virtual void ReadHidbusValues() = 0;
@ -121,6 +122,7 @@ protected:
void SaveScreenshotValues(); void SaveScreenshotValues();
void SaveSystemValues(); void SaveSystemValues();
void SaveWebServiceValues(); void SaveWebServiceValues();
void SaveLibraryAppletValues();
// Save platform specific sections // Save platform specific sections
virtual void SaveHidbusValues() = 0; virtual void SaveHidbusValues() = 0;

View File

@ -145,9 +145,8 @@ void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_m
} }
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
std::vector<u8>& data) const { std::span<u8> data) const {
const auto size = GetDataSize(current_config.trimming_format); const auto size = std::min(GetDataSize(current_config.trimming_format), data.size());
data.resize(size);
system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
return processor_state; return processor_state;
} }

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include <span>
#include "common/typed_address.h" #include "common/typed_address.h"
#include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/irs_types.h"
#include "hid_core/irsensor/processor_base.h" #include "hid_core/irsensor/processor_base.h"
@ -39,7 +41,7 @@ public:
// Transfer memory where the image data will be stored // Transfer memory where the image data will be stored
void SetTransferMemoryAddress(Common::ProcessAddress t_mem); void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const;
private: private:
// This is nn::irsensor::ImageTransferProcessorConfig // This is nn::irsensor::ImageTransferProcessorConfig

View File

@ -57,7 +57,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (data->flag.is_assigned) { if (data == nullptr || !data->flag.is_assigned) {
continue; continue;
} }
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];

View File

@ -131,7 +131,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
if (!data->flag.is_assigned) { if (data == nullptr || !data->flag.is_assigned) {
continue; continue;
} }
@ -463,13 +463,13 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock lock{*applet_resource_holder.shared_mutex}; std::scoped_lock lock{*applet_resource_holder.shared_mutex};
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
const auto aruid = data->aruid;
if (!data->flag.is_assigned) { if (data == nullptr || !data->flag.is_assigned) {
continue; continue;
} }
bool is_set{}; bool is_set{};
const auto aruid = data->aruid;
npad_resource.IsSupportedNpadStyleSet(is_set, aruid); npad_resource.IsSupportedNpadStyleSet(is_set, aruid);
// Wait until style is defined // Wait until style is defined
if (!is_set) { if (!is_set) {

View File

@ -28,142 +28,148 @@ void SixAxis::OnRelease() {}
void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
std::scoped_lock shared_lock{*shared_mutex}; std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
if (data == nullptr || !data->flag.is_assigned) { for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
return; const auto* data = applet_resource->GetAruidDataByIndex(aruid_index);
}
if (!IsControllerActivated()) { if (data == nullptr || !data->flag.is_assigned) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
auto& controller = controller_data[i];
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
continue; continue;
} }
const auto& motion_state = controller.device->GetMotions(); if (!IsControllerActivated()) {
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; return;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
} }
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, for (std::size_t i = 0; i < controller_data.size(); ++i) {
const Core::HID::ControllerMotion& hid_state) { NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
using namespace std::literals::chrono_literals; auto& controller = controller_data[i];
static constexpr Core::HID::SixAxisSensorState default_motion_state = { const auto& controller_type = controller.device->GetNpadStyleIndex();
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f}, if (!data->flag.enable_six_axis_sensor) {
.orientation = continue;
{ }
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0}, if (controller_type == Core::HID::NpadStyleIndex::None ||
Common::Vec3f{0, 0, 1.0f}, !controller.device->IsConnected()) {
}, continue;
.attribute = {1}, }
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
// Clear previous state
sixaxis_fullkey_state = {};
sixaxis_handheld_state = {};
sixaxis_dual_left_state = {};
sixaxis_dual_right_state = {};
sixaxis_left_lifo_state = {};
sixaxis_right_lifo_state = {};
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
controller.sixaxis_at_rest =
controller.sixaxis_at_rest && motion_state[e].is_at_rest;
}
}
const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state,
const Core::HID::ControllerMotion& hid_state) {
using namespace std::literals::chrono_literals;
static constexpr Core::HID::SixAxisSensorState default_motion_state = {
.delta_time = std::chrono::nanoseconds(5ms).count(),
.accel = {0, 0, -1.0f},
.orientation =
{
Common::Vec3f{1.0f, 0, 0},
Common::Vec3f{0, 1.0f, 0},
Common::Vec3f{0, 0, 1.0f},
},
.attribute = {1},
};
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state;
return;
}
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state;
return;
}
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
}; };
if (!controller.sixaxis_sensor_enabled) {
state = default_motion_state; switch (controller_type) {
return; case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::Fullkey:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
} }
if (!Settings::values.motion_enabled.GetValue()) {
state = default_motion_state; sixaxis_fullkey_state.sampling_number =
return; sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
} }
state.attribute.is_connected.Assign(1);
state.delta_time = std::chrono::nanoseconds(5ms).count();
state.accel = hid_state.accel;
state.gyro = hid_state.gyro;
state.rotation = hid_state.rotation;
state.orientation = hid_state.orientation;
};
switch (controller_type) { sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
case Core::HID::NpadStyleIndex::None: sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
ASSERT(false); sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
break; sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
case Core::HID::NpadStyleIndex::Fullkey:
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::Pokeball:
using namespace std::literals::chrono_literals;
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
} }
sixaxis_fullkey_state.sampling_number =
sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
}
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
} }
} }

View File

@ -63,7 +63,7 @@ Result TouchResource::ActivateTouch(u64 aruid) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index]; TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {}; touch_data = {};
continue; continue;
} }
@ -124,7 +124,7 @@ Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) {
auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& touch_data = aruid_data[aruid_index]; TouchAruidData& touch_data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
touch_data = {}; touch_data = {};
continue; continue;
} }
@ -324,7 +324,7 @@ Result TouchResource::SetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
TouchAruidData& data = aruid_data[aruid_index]; TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue; continue;
} }
if (aruid != data.aruid) { if (aruid != data.aruid) {
@ -344,7 +344,7 @@ Result TouchResource::GetTouchScreenConfiguration(
const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index);
const TouchAruidData& data = aruid_data[aruid_index]; const TouchAruidData& data = aruid_data[aruid_index];
if (!applet_data->flag.is_assigned) { if (applet_data == nullptr || !applet_data->flag.is_assigned) {
continue; continue;
} }
if (aruid != data.aruid) { if (aruid != data.aruid) {

View File

@ -60,10 +60,11 @@ public:
Add(spv::ImageOperandsMask::ConstOffsets, offsets); Add(spv::ImageOperandsMask::ConstOffsets, offsets);
} }
explicit ImageOperands(Id lod, Id ms) { explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, Id lod, Id ms) {
if (Sirit::ValidId(lod)) { if (Sirit::ValidId(lod)) {
Add(spv::ImageOperandsMask::Lod, lod); Add(spv::ImageOperandsMask::Lod, lod);
} }
AddOffset(ctx, offset, ImageFetchOffsetAllowed);
if (Sirit::ValidId(ms)) { if (Sirit::ValidId(ms)) {
Add(spv::ImageOperandsMask::Sample, ms); Add(spv::ImageOperandsMask::Sample, ms);
} }
@ -311,37 +312,6 @@ Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info,
return coords; return coords;
} }
} }
void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, Id& coords,
Id offset) {
if (!Sirit::ValidId(offset)) {
return;
}
Id result_type{};
switch (info.type) {
case TextureType::Buffer:
case TextureType::Color1D:
case TextureType::ColorArray1D: {
result_type = ctx.U32[1];
break;
}
case TextureType::Color2D:
case TextureType::Color2DRect:
case TextureType::ColorArray2D: {
result_type = ctx.U32[2];
break;
}
case TextureType::Color3D: {
result_type = ctx.U32[3];
break;
}
case TextureType::ColorCube:
case TextureType::ColorArrayCube:
return;
}
coords = ctx.OpIAdd(result_type, coords, offset);
}
} // Anonymous namespace } // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) { Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@ -524,10 +494,9 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
operands.Span()); operands.Span());
} }
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, Id ms) { const IR::Value& offset, Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
lod = Id{}; lod = Id{};
} }
@ -535,7 +504,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
// This image is multisampled, lod must be implicit // This image is multisampled, lod must be implicit
lod = Id{}; lod = Id{};
} }
const ImageOperands operands(lod, ms); const ImageOperands operands(ctx, offset, lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
} }

View File

@ -537,8 +537,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
const IR::Value& offset, const IR::Value& offset2); const IR::Value& offset, const IR::Value& offset2);
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref); const IR::Value& offset, const IR::Value& offset2, Id dref);
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, Id ms); const IR::Value& offset, Id lod, Id ms);
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
const IR::Value& skip_mips); const IR::Value& skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);

View File

@ -18,6 +18,7 @@ add_library(video_core STATIC
buffer_cache/usage_tracker.h buffer_cache/usage_tracker.h
buffer_cache/word_manager.h buffer_cache/word_manager.h
cache_types.h cache_types.h
capture.h
cdma_pusher.cpp cdma_pusher.cpp
cdma_pusher.h cdma_pusher.h
compatible_formats.cpp compatible_formats.cpp
@ -59,8 +60,8 @@ add_library(video_core STATIC
framebuffer_config.h framebuffer_config.h
fsr.cpp fsr.cpp
fsr.h fsr.h
host1x/codecs/codec.cpp host1x/codecs/decoder.cpp
host1x/codecs/codec.h host1x/codecs/decoder.h
host1x/codecs/h264.cpp host1x/codecs/h264.cpp
host1x/codecs/h264.h host1x/codecs/h264.h
host1x/codecs/vp8.cpp host1x/codecs/vp8.cpp
@ -79,8 +80,6 @@ add_library(video_core STATIC
host1x/nvdec.cpp host1x/nvdec.cpp
host1x/nvdec.h host1x/nvdec.h
host1x/nvdec_common.h host1x/nvdec_common.h
host1x/sync_manager.cpp
host1x/sync_manager.h
host1x/syncpoint_manager.cpp host1x/syncpoint_manager.cpp
host1x/syncpoint_manager.h host1x/syncpoint_manager.h
host1x/vic.cpp host1x/vic.cpp
@ -101,6 +100,7 @@ add_library(video_core STATIC
memory_manager.cpp memory_manager.cpp
memory_manager.h memory_manager.h
precompiled_headers.h precompiled_headers.h
present.h
pte_kind.h pte_kind.h
query_cache/bank_base.h query_cache/bank_base.h
query_cache/query_base.h query_cache/query_base.h
@ -274,7 +274,6 @@ add_library(video_core STATIC
texture_cache/image_view_info.h texture_cache/image_view_info.h
texture_cache/render_targets.h texture_cache/render_targets.h
texture_cache/samples_helper.h texture_cache/samples_helper.h
texture_cache/slot_vector.h
texture_cache/texture_cache.cpp texture_cache/texture_cache.cpp
texture_cache/texture_cache.h texture_cache/texture_cache.h
texture_cache/texture_cache_base.h texture_cache/texture_cache_base.h

View File

@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include <numeric> #include <numeric>
#include "common/range_sets.inc"
#include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/guest_memory.h" #include "video_core/guest_memory.h"
#include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/host1x/gpu_device_memory_manager.h"
@ -20,7 +21,7 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
: runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} { : runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} {
// Ensure the first slot is used for the null buffer // Ensure the first slot is used for the null buffer
void(slot_buffers.insert(runtime, NullBufferParams{})); void(slot_buffers.insert(runtime, NullBufferParams{}));
common_ranges.clear(); gpu_modified_ranges.Clear();
inline_buffer_id = NULL_BUFFER_ID; inline_buffer_id = NULL_BUFFER_ID;
if (!runtime.CanReportMemoryUsage()) { if (!runtime.CanReportMemoryUsage()) {
@ -43,6 +44,9 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
DEFAULT_CRITICAL_MEMORY)); DEFAULT_CRITICAL_MEMORY));
} }
template <class P>
BufferCache<P>::~BufferCache() = default;
template <class P> template <class P>
void BufferCache<P>::RunGarbageCollector() { void BufferCache<P>::RunGarbageCollector() {
const bool aggressive_gc = total_used_memory >= critical_memory; const bool aggressive_gc = total_used_memory >= critical_memory;
@ -96,20 +100,17 @@ void BufferCache<P>::TickFrame() {
++frame_tick; ++frame_tick;
delayed_destruction_ring.Tick(); delayed_destruction_ring.Tick();
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { for (auto& buffer : async_buffers_death_ring) {
for (auto& buffer : async_buffers_death_ring) { runtime.FreeDeferredStagingBuffer(buffer);
runtime.FreeDeferredStagingBuffer(buffer);
}
async_buffers_death_ring.clear();
} }
async_buffers_death_ring.clear();
} }
template <class P> template <class P>
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) { void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
if (memory_tracker.IsRegionGpuModified(device_addr, size)) { if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
const IntervalType subtract_interval{device_addr, device_addr + size}; ClearDownload(device_addr, size);
ClearDownload(subtract_interval); gpu_modified_ranges.Subtract(device_addr, size);
common_ranges.subtract(subtract_interval);
} }
memory_tracker.MarkRegionAsCpuModified(device_addr, size); memory_tracker.MarkRegionAsCpuModified(device_addr, size);
} }
@ -174,11 +175,11 @@ void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
} }
template <class P> template <class P>
void BufferCache<P>::ClearDownload(IntervalType subtract_interval) { void BufferCache<P>::ClearDownload(DAddr device_addr, u64 size) {
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024); async_downloads.DeleteAll(device_addr, size);
uncommitted_ranges.subtract(subtract_interval); uncommitted_gpu_modified_ranges.Subtract(device_addr, size);
for (auto& interval_set : committed_ranges) { for (auto& interval_set : committed_gpu_modified_ranges) {
interval_set.subtract(subtract_interval); interval_set.Subtract(device_addr, size);
} }
} }
@ -195,8 +196,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
return false; return false;
} }
const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount}; ClearDownload(*cpu_dest_address, amount);
ClearDownload(subtract_interval);
BufferId buffer_a; BufferId buffer_a;
BufferId buffer_b; BufferId buffer_b;
@ -215,21 +215,20 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
.size = amount, .size = amount,
}}; }};
boost::container::small_vector<IntervalType, 4> tmp_intervals; boost::container::small_vector<std::pair<DAddr, size_t>, 4> tmp_intervals;
auto mirror = [&](DAddr base_address, DAddr base_address_end) { auto mirror = [&](DAddr base_address, DAddr base_address_end) {
const u64 size = base_address_end - base_address; const u64 size = base_address_end - base_address;
const DAddr diff = base_address - *cpu_src_address; const DAddr diff = base_address - *cpu_src_address;
const DAddr new_base_address = *cpu_dest_address + diff; const DAddr new_base_address = *cpu_dest_address + diff;
const IntervalType add_interval{new_base_address, new_base_address + size}; tmp_intervals.push_back({new_base_address, size});
tmp_intervals.push_back(add_interval); uncommitted_gpu_modified_ranges.Add(new_base_address, size);
uncommitted_ranges.add(add_interval);
}; };
ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror); gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror);
// This subtraction in this order is important for overlapping copies. // This subtraction in this order is important for overlapping copies.
common_ranges.subtract(subtract_interval); gpu_modified_ranges.Subtract(*cpu_dest_address, amount);
const bool has_new_downloads = tmp_intervals.size() != 0; const bool has_new_downloads = tmp_intervals.size() != 0;
for (const IntervalType& add_interval : tmp_intervals) { for (const auto& pair : tmp_intervals) {
common_ranges.add(add_interval); gpu_modified_ranges.Add(pair.first, pair.second);
} }
const auto& copy = copies[0]; const auto& copy = copies[0];
src_buffer.MarkUsage(copy.src_offset, copy.size); src_buffer.MarkUsage(copy.src_offset, copy.size);
@ -257,9 +256,8 @@ bool BufferCache<P>::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) {
} }
const size_t size = amount * sizeof(u32); const size_t size = amount * sizeof(u32);
const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size}; ClearDownload(*cpu_dst_address, size);
ClearDownload(subtract_interval); gpu_modified_ranges.Subtract(*cpu_dst_address, size);
common_ranges.subtract(subtract_interval);
const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size)); const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast<u32>(size));
Buffer& dest_buffer = slot_buffers[buffer]; Buffer& dest_buffer = slot_buffers[buffer];
@ -300,11 +298,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
MarkWrittenBuffer(buffer_id, device_addr, size); MarkWrittenBuffer(buffer_id, device_addr, size);
break; break;
case ObtainBufferOperation::DiscardWrite: { case ObtainBufferOperation::DiscardWrite: {
DAddr device_addr_start = Common::AlignDown(device_addr, 64); const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
DAddr device_addr_end = Common::AlignUp(device_addr + size, 64); const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
IntervalType interval{device_addr_start, device_addr_end}; const size_t new_size = device_addr_end - device_addr_start;
ClearDownload(interval); ClearDownload(device_addr_start, new_size);
common_ranges.subtract(interval); gpu_modified_ranges.Subtract(device_addr_start, new_size);
break; break;
} }
default: default:
@ -504,46 +502,40 @@ void BufferCache<P>::FlushCachedWrites() {
template <class P> template <class P>
bool BufferCache<P>::HasUncommittedFlushes() const noexcept { bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
return !uncommitted_ranges.empty() || !committed_ranges.empty(); return !uncommitted_gpu_modified_ranges.Empty() || !committed_gpu_modified_ranges.empty();
} }
template <class P> template <class P>
void BufferCache<P>::AccumulateFlushes() { void BufferCache<P>::AccumulateFlushes() {
if (uncommitted_ranges.empty()) { if (uncommitted_gpu_modified_ranges.Empty()) {
return; return;
} }
committed_ranges.emplace_back(std::move(uncommitted_ranges)); committed_gpu_modified_ranges.emplace_back(std::move(uncommitted_gpu_modified_ranges));
} }
template <class P> template <class P>
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { return (!async_buffers.empty() && async_buffers.front().has_value());
return (!async_buffers.empty() && async_buffers.front().has_value());
} else {
return false;
}
} }
template <class P> template <class P>
void BufferCache<P>::CommitAsyncFlushesHigh() { void BufferCache<P>::CommitAsyncFlushesHigh() {
AccumulateFlushes(); AccumulateFlushes();
if (committed_ranges.empty()) { if (committed_gpu_modified_ranges.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { async_buffers.emplace_back(std::optional<Async_Buffer>{});
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return; return;
} }
MICROPROFILE_SCOPE(GPU_DownloadMemory); MICROPROFILE_SCOPE(GPU_DownloadMemory);
auto it = committed_ranges.begin(); auto it = committed_gpu_modified_ranges.begin();
while (it != committed_ranges.end()) { while (it != committed_gpu_modified_ranges.end()) {
auto& current_intervals = *it; auto& current_intervals = *it;
auto next_it = std::next(it); auto next_it = std::next(it);
while (next_it != committed_ranges.end()) { while (next_it != committed_gpu_modified_ranges.end()) {
for (auto& interval : *next_it) { next_it->ForEach([&current_intervals](DAddr start, DAddr end) {
current_intervals.subtract(interval); current_intervals.Subtract(start, end - start);
} });
next_it++; next_it++;
} }
it++; it++;
@ -552,10 +544,10 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads; boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads;
u64 total_size_bytes = 0; u64 total_size_bytes = 0;
u64 largest_copy = 0; u64 largest_copy = 0;
for (const IntervalSet& intervals : committed_ranges) { for (const Common::RangeSet<DAddr>& range_set : committed_gpu_modified_ranges) {
for (auto& interval : intervals) { range_set.ForEach([&](DAddr interval_lower, DAddr interval_upper) {
const std::size_t size = interval.upper() - interval.lower(); const std::size_t size = interval_upper - interval_lower;
const DAddr device_addr = interval.lower(); const DAddr device_addr = interval_lower;
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) { ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
const DAddr buffer_start = buffer.CpuAddr(); const DAddr buffer_start = buffer.CpuAddr();
const DAddr buffer_end = buffer_start + buffer.SizeBytes(); const DAddr buffer_end = buffer_start + buffer.SizeBytes();
@ -583,77 +575,35 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
largest_copy = std::max(largest_copy, new_size); largest_copy = std::max(largest_copy, new_size);
}; };
ForEachInRangeSet(common_ranges, device_addr_out, range_size, add_download); gpu_modified_ranges.ForEachInRange(device_addr_out, range_size,
add_download);
}); });
}); });
} });
} }
committed_ranges.clear(); committed_gpu_modified_ranges.clear();
if (downloads.empty()) { if (downloads.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { async_buffers.emplace_back(std::optional<Async_Buffer>{});
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return; return;
} }
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true); boost::container::small_vector<BufferCopy, 4> normalized_copies;
boost::container::small_vector<BufferCopy, 4> normalized_copies; runtime.PreCopyBarrier();
IntervalSet new_async_range{}; for (auto& [copy, buffer_id] : downloads) {
runtime.PreCopyBarrier(); copy.dst_offset += download_staging.offset;
for (auto& [copy, buffer_id] : downloads) { const std::array copies{copy};
copy.dst_offset += download_staging.offset; BufferCopy second_copy{copy};
const std::array copies{copy}; Buffer& buffer = slot_buffers[buffer_id];
BufferCopy second_copy{copy}; second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
Buffer& buffer = slot_buffers[buffer_id]; const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset; async_downloads.Add(orig_device_addr, copy.size);
DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset); buffer.MarkUsage(copy.src_offset, copy.size);
const IntervalType base_interval{orig_device_addr, orig_device_addr + copy.size}; runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
async_downloads += std::make_pair(base_interval, 1); normalized_copies.push_back(second_copy);
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if (!Settings::IsGPULevelHigh()) {
committed_ranges.clear();
uncommitted_ranges.clear();
} else {
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
Buffer& buffer = slot_buffers[buffer_id];
buffer.MarkUsage(copy.src_offset, copy.size);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
device_memory.WriteBlockUnsafe(device_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset,
immediate_buffer.subspan(0, copy.size));
const DAddr device_addr = buffer.CpuAddr() + copy.src_offset;
device_memory.WriteBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
}
}
}
} }
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} }
template <class P> template <class P>
@ -676,37 +626,31 @@ void BufferCache<P>::PopAsyncBuffers() {
async_buffers.pop_front(); async_buffers.pop_front();
return; return;
} }
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) { auto& downloads = pending_downloads.front();
auto& downloads = pending_downloads.front(); auto& async_buffer = async_buffers.front();
auto& async_buffer = async_buffers.front(); u8* base = async_buffer->mapped_span.data();
u8* base = async_buffer->mapped_span.data(); const size_t base_offset = async_buffer->offset;
const size_t base_offset = async_buffer->offset; for (const auto& copy : downloads) {
for (const auto& copy : downloads) { const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const DAddr device_addr = static_cast<DAddr>(copy.src_offset); const u64 dst_offset = copy.dst_offset - base_offset;
const u64 dst_offset = copy.dst_offset - base_offset; const u8* read_mapped_memory = base + dst_offset;
const u8* read_mapped_memory = base + dst_offset; async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
ForEachInOverlapCounter( device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
async_downloads, device_addr, copy.size, [&](DAddr start, DAddr end, int count) { end - start);
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr], });
end - start); async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
if (count == 1) { gpu_modified_ranges.Subtract(start, end - start);
const IntervalType base_interval{start, end}; });
common_ranges.subtract(base_interval);
}
});
const IntervalType subtract_interval{device_addr, device_addr + copy.size};
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1);
}
async_buffers_death_ring.emplace_back(*async_buffer);
async_buffers.pop_front();
pending_downloads.pop_front();
} }
async_buffers_death_ring.emplace_back(*async_buffer);
async_buffers.pop_front();
pending_downloads.pop_front();
} }
template <class P> template <class P>
bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) { bool BufferCache<P>::IsRegionGpuModified(DAddr addr, size_t size) {
bool is_dirty = false; bool is_dirty = false;
ForEachInRangeSet(common_ranges, addr, size, [&](DAddr, DAddr) { is_dirty = true; }); gpu_modified_ranges.ForEachInRange(addr, size, [&](DAddr, DAddr) { is_dirty = true; });
return is_dirty; return is_dirty;
} }
@ -1320,10 +1264,8 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
template <class P> template <class P>
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) { void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
memory_tracker.MarkRegionAsGpuModified(device_addr, size); memory_tracker.MarkRegionAsGpuModified(device_addr, size);
gpu_modified_ranges.Add(device_addr, size);
const IntervalType base_interval{device_addr, device_addr + size}; uncommitted_gpu_modified_ranges.Add(device_addr, size);
common_ranges.add(base_interval);
uncommitted_ranges.add(base_interval);
} }
template <class P> template <class P>
@ -1546,7 +1488,10 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
std::span<const u8> upload_span; std::span<const u8> upload_span;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset; const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
if (IsRangeGranular(device_addr, copy.size)) { if (IsRangeGranular(device_addr, copy.size)) {
upload_span = std::span(device_memory.GetPointer<u8>(device_addr), copy.size); auto* const ptr = device_memory.GetPointer<u8>(device_addr);
if (ptr != nullptr) {
upload_span = std::span(ptr, copy.size);
}
} else { } else {
if (immediate_buffer.empty()) { if (immediate_buffer.empty()) {
immediate_buffer = ImmediateBuffer(largest_copy); immediate_buffer = ImmediateBuffer(largest_copy);
@ -1600,9 +1545,8 @@ bool BufferCache<P>::InlineMemory(DAddr dest_address, size_t copy_size,
template <class P> template <class P>
void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size, void BufferCache<P>::InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
std::span<const u8> inlined_buffer) { std::span<const u8> inlined_buffer) {
const IntervalType subtract_interval{dest_address, dest_address + copy_size}; ClearDownload(dest_address, copy_size);
ClearDownload(subtract_interval); gpu_modified_ranges.Subtract(dest_address, copy_size);
common_ranges.subtract(subtract_interval);
BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size)); BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size));
auto& buffer = slot_buffers[buffer_id]; auto& buffer = slot_buffers[buffer_id];
@ -1652,12 +1596,9 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
largest_copy = std::max(largest_copy, new_size); largest_copy = std::max(largest_copy, new_size);
}; };
const DAddr start_address = device_addr_out; gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download);
const DAddr end_address = start_address + range_size; ClearDownload(device_addr_out, range_size);
ForEachInRangeSet(common_ranges, start_address, range_size, add_download); gpu_modified_ranges.Subtract(device_addr_out, range_size);
const IntervalType subtract_interval{start_address, end_address};
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
}); });
if (total_size_bytes == 0) { if (total_size_bytes == 0) {
return; return;

View File

@ -13,25 +13,15 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#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/common_types.h" #include "common/common_types.h"
#include "common/div_ceil.h" #include "common/div_ceil.h"
#include "common/literals.h" #include "common/literals.h"
#include "common/lru_cache.h" #include "common/lru_cache.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/range_sets.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/slot_vector.h"
#include "video_core/buffer_cache/buffer_base.h" #include "video_core/buffer_cache/buffer_base.h"
#include "video_core/control/channel_state_cache.h" #include "video_core/control/channel_state_cache.h"
#include "video_core/delayed_destruction_ring.h" #include "video_core/delayed_destruction_ring.h"
@ -41,21 +31,15 @@
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/surface.h" #include "video_core/surface.h"
#include "video_core/texture_cache/slot_vector.h"
#include "video_core/texture_cache/types.h" #include "video_core/texture_cache/types.h"
namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}
namespace VideoCommon { namespace VideoCommon {
MICROPROFILE_DECLARE(GPU_PrepareBuffers); MICROPROFILE_DECLARE(GPU_PrepareBuffers);
MICROPROFILE_DECLARE(GPU_BindUploadBuffers); MICROPROFILE_DECLARE(GPU_BindUploadBuffers);
MICROPROFILE_DECLARE(GPU_DownloadMemory); MICROPROFILE_DECLARE(GPU_DownloadMemory);
using BufferId = SlotId; using BufferId = Common::SlotId;
using VideoCore::Surface::PixelFormat; using VideoCore::Surface::PixelFormat;
using namespace Common::Literals; using namespace Common::Literals;
@ -184,7 +168,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX; static constexpr bool NEEDS_BIND_STORAGE_INDEX = P::NEEDS_BIND_STORAGE_INDEX;
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS; static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS; static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS; static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB; static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
@ -202,34 +185,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
using Async_Buffer = typename P::Async_Buffer; using Async_Buffer = typename P::Async_Buffer;
using MemoryTracker = typename P::MemoryTracker; using MemoryTracker = typename P::MemoryTracker;
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 OverlapResult { struct OverlapResult {
boost::container::small_vector<BufferId, 16> ids; boost::container::small_vector<BufferId, 16> ids;
DAddr begin; DAddr begin;
@ -240,6 +195,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
public: public:
explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_); explicit BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_);
~BufferCache();
void TickFrame(); void TickFrame();
void WriteMemory(DAddr device_addr, u64 size); void WriteMemory(DAddr device_addr, u64 size);
@ -379,75 +336,6 @@ private:
} }
} }
template <typename Func>
void ForEachInRangeSet(IntervalSet& current_range, DAddr device_addr, u64 size, Func&& func) {
const DAddr start_address = device_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++) {
DAddr inter_addr_end = it->upper();
DAddr 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);
}
}
template <typename Func>
void ForEachInOverlapCounter(OverlapCounter& current_range, DAddr device_addr, u64 size,
Func&& func) {
const DAddr start_address = device_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);
}
static bool IsRangeGranular(DAddr device_addr, size_t size) { static bool IsRangeGranular(DAddr device_addr, size_t size) {
return (device_addr & ~Core::DEVICE_PAGEMASK) == return (device_addr & ~Core::DEVICE_PAGEMASK) ==
((device_addr + size) & ~Core::DEVICE_PAGEMASK); ((device_addr + size) & ~Core::DEVICE_PAGEMASK);
@ -552,14 +440,14 @@ private:
[[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept; [[nodiscard]] bool HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept;
void ClearDownload(IntervalType subtract_interval); void ClearDownload(DAddr base_addr, u64 size);
void InlineMemoryImplementation(DAddr dest_address, size_t copy_size, void InlineMemoryImplementation(DAddr dest_address, size_t copy_size,
std::span<const u8> inlined_buffer); std::span<const u8> inlined_buffer);
Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::MaxwellDeviceMemoryManager& device_memory;
SlotVector<Buffer> slot_buffers; Common::SlotVector<Buffer> slot_buffers;
DelayedDestructionRing<Buffer, 8> delayed_destruction_ring; DelayedDestructionRing<Buffer, 8> delayed_destruction_ring;
const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{}; const Tegra::Engines::DrawManager::IndirectParams* current_draw_indirect{};
@ -567,13 +455,12 @@ private:
u32 last_index_count = 0; u32 last_index_count = 0;
MemoryTracker memory_tracker; MemoryTracker memory_tracker;
IntervalSet uncommitted_ranges; Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
IntervalSet common_ranges; Common::RangeSet<DAddr> gpu_modified_ranges;
IntervalSet cached_ranges; std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
std::deque<IntervalSet> committed_ranges;
// Async Buffers // Async Buffers
OverlapCounter async_downloads; Common::OverlapRangeSet<DAddr> async_downloads;
std::deque<std::optional<Async_Buffer>> async_buffers; std::deque<std::optional<Async_Buffer>> async_buffers;
std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads; std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
std::optional<Async_Buffer> current_buffer; std::optional<Async_Buffer> current_buffer;

36
src/video_core/capture.h Normal file
View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/alignment.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "core/frontend/framebuffer_layout.h"
#include "video_core/surface.h"
namespace VideoCore::Capture {
constexpr u32 BlockHeight = 4;
constexpr u32 BlockDepth = 0;
constexpr u32 BppLog2 = 2;
constexpr auto PixelFormat = Surface::PixelFormat::B8G8R8A8_UNORM;
constexpr auto LinearWidth = Layout::ScreenUndocked::Width;
constexpr auto LinearHeight = Layout::ScreenUndocked::Height;
constexpr auto LinearDepth = 1U;
constexpr auto BytesPerPixel = 4U;
constexpr auto TiledWidth = LinearWidth;
constexpr auto TiledHeight = Common::AlignUpLog2(LinearHeight, BlockHeight + BlockDepth + BppLog2);
constexpr auto TiledSize = TiledWidth * TiledHeight * (1 << BppLog2);
constexpr Layout::FramebufferLayout Layout{
.width = LinearWidth,
.height = LinearHeight,
.screen = {0, 0, LinearWidth, LinearHeight},
.is_srgb = false,
};
} // namespace VideoCore::Capture

View File

@ -2,136 +2,130 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#include <bit> #include <bit>
#include "common/thread.h"
#include "core/core.h"
#include "video_core/cdma_pusher.h" #include "video_core/cdma_pusher.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/host1x/control.h" #include "video_core/host1x/control.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
#include "video_core/host1x/nvdec.h" #include "video_core/host1x/nvdec.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
#include "video_core/host1x/sync_manager.h"
#include "video_core/host1x/vic.h" #include "video_core/host1x/vic.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
namespace Tegra { namespace Tegra {
CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_)
: host1x{host1x_}, nvdec_processor(std::make_shared<Host1x::Nvdec>(host1x)), CDmaPusher::CDmaPusher(Host1x::Host1x& host1x_, s32 id)
vic_processor(std::make_unique<Host1x::Vic>(host1x, nvdec_processor)), : host1x{host1x_}, memory_manager{host1x.GMMU()},
host1x_processor(std::make_unique<Host1x::Control>(host1x)), host_processor{std::make_unique<Host1x::Control>(host1x_)}, current_class{
sync_manager(std::make_unique<Host1x::SyncptIncrManager>(host1x)) {} static_cast<ChClassId>(id)} {
thread = std::jthread([this](std::stop_token stop_token) { ProcessEntries(stop_token); });
}
CDmaPusher::~CDmaPusher() = default; CDmaPusher::~CDmaPusher() = default;
void CDmaPusher::ProcessEntries(ChCommandHeaderList&& entries) { void CDmaPusher::ProcessEntries(std::stop_token stop_token) {
for (const auto& value : entries) { Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
if (mask != 0) { ChCommandHeaderList command_list{host1x.System().ApplicationMemory(), 0, 0};
const auto lbs = static_cast<u32>(std::countr_zero(mask)); u32 count{};
mask &= ~(1U << lbs); u32 method_offset{};
ExecuteCommand(offset + lbs, value.raw); u32 mask{};
continue; bool incrementing{};
} else if (count != 0) {
--count; while (!stop_token.stop_requested()) {
ExecuteCommand(offset, value.raw); {
if (incrementing) { std::unique_lock l{command_mutex};
++offset; Common::CondvarWait(command_cv, l, stop_token,
[this]() { return command_lists.size() > 0; });
if (stop_token.stop_requested()) {
return;
} }
continue;
command_list = std::move(command_lists.front());
command_lists.pop_front();
} }
const auto mode = value.submission_mode.Value();
switch (mode) { size_t i = 0;
case ChSubmissionMode::SetClass: { for (const auto value : command_list) {
mask = value.value & 0x3f; i++;
offset = value.method_offset; if (mask != 0) {
current_class = static_cast<ChClassId>((value.value >> 6) & 0x3ff); const auto lbs = static_cast<u32>(std::countr_zero(mask));
break; mask &= ~(1U << lbs);
} ExecuteCommand(method_offset + lbs, value.raw);
case ChSubmissionMode::Incrementing: continue;
case ChSubmissionMode::NonIncrementing: } else if (count != 0) {
count = value.value; --count;
offset = value.method_offset; ExecuteCommand(method_offset, value.raw);
incrementing = mode == ChSubmissionMode::Incrementing; if (incrementing) {
break; ++method_offset;
case ChSubmissionMode::Mask: }
mask = value.value; continue;
offset = value.method_offset; }
break; const auto mode = value.submission_mode.Value();
case ChSubmissionMode::Immediate: { switch (mode) {
const u32 data = value.value & 0xfff; case ChSubmissionMode::SetClass: {
offset = value.method_offset; mask = value.value & 0x3f;
ExecuteCommand(offset, data); method_offset = value.method_offset;
break; current_class = static_cast<ChClassId>((value.value >> 6) & 0x3ff);
} break;
default: }
UNIMPLEMENTED_MSG("ChSubmission mode {} is not implemented!", static_cast<u32>(mode)); case ChSubmissionMode::Incrementing:
break; case ChSubmissionMode::NonIncrementing:
count = value.value;
method_offset = value.method_offset;
incrementing = mode == ChSubmissionMode::Incrementing;
break;
case ChSubmissionMode::Mask:
mask = value.value;
method_offset = value.method_offset;
break;
case ChSubmissionMode::Immediate: {
const u32 data = value.value & 0xfff;
method_offset = value.method_offset;
ExecuteCommand(method_offset, data);
break;
}
default:
LOG_ERROR(HW_GPU, "Bad command at index {} (bytes 0x{:X}), buffer size {}", i - 1,
(i - 1) * sizeof(u32), command_list.size());
UNIMPLEMENTED_MSG("ChSubmission mode {} is not implemented!",
static_cast<u32>(mode));
break;
}
} }
} }
} }
void CDmaPusher::ExecuteCommand(u32 state_offset, u32 data) { void CDmaPusher::ExecuteCommand(u32 method, u32 arg) {
switch (current_class) { switch (current_class) {
case ChClassId::NvDec:
ThiStateWrite(nvdec_thi_state, offset, data);
switch (static_cast<ThiMethod>(offset)) {
case ThiMethod::IncSyncpt: {
LOG_DEBUG(Service_NVDRV, "NVDEC Class IncSyncpt Method");
const auto syncpoint_id = static_cast<u32>(data & 0xFF);
const auto cond = static_cast<u32>((data >> 8) & 0xFF);
if (cond == 0) {
sync_manager->Increment(syncpoint_id);
} else {
sync_manager->SignalDone(
sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id));
}
break;
}
case ThiMethod::SetMethod1:
LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
static_cast<u32>(nvdec_thi_state.method_0));
nvdec_processor->ProcessMethod(nvdec_thi_state.method_0, data);
break;
default:
break;
}
break;
case ChClassId::GraphicsVic:
ThiStateWrite(vic_thi_state, static_cast<u32>(state_offset), {data});
switch (static_cast<ThiMethod>(state_offset)) {
case ThiMethod::IncSyncpt: {
LOG_DEBUG(Service_NVDRV, "VIC Class IncSyncpt Method");
const auto syncpoint_id = static_cast<u32>(data & 0xFF);
const auto cond = static_cast<u32>((data >> 8) & 0xFF);
if (cond == 0) {
sync_manager->Increment(syncpoint_id);
} else {
sync_manager->SignalDone(
sync_manager->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id));
}
break;
}
case ThiMethod::SetMethod1:
LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
static_cast<u32>(vic_thi_state.method_0), data);
vic_processor->ProcessMethod(static_cast<Host1x::Vic::Method>(vic_thi_state.method_0),
data);
break;
default:
break;
}
break;
case ChClassId::Control: case ChClassId::Control:
// This device is mainly for syncpoint synchronization LOG_TRACE(Service_NVDRV, "Class {} method 0x{:X} arg 0x{:X}",
LOG_DEBUG(Service_NVDRV, "Host1X Class Method"); static_cast<u32>(current_class), method, arg);
host1x_processor->ProcessMethod(static_cast<Host1x::Control::Method>(offset), data); host_processor->ProcessMethod(static_cast<Host1x::Control::Method>(method), arg);
break; break;
default: default:
UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class)); thi_regs.reg_array[method] = arg;
break; switch (static_cast<ThiMethod>(method)) {
case ThiMethod::IncSyncpt: {
const auto syncpoint_id = static_cast<u32>(arg & 0xFF);
[[maybe_unused]] const auto cond = static_cast<u32>((arg >> 8) & 0xFF);
LOG_TRACE(Service_NVDRV, "Class {} IncSyncpt Method, syncpt {} cond {}",
static_cast<u32>(current_class), syncpoint_id, cond);
auto& syncpoint_manager = host1x.GetSyncpointManager();
syncpoint_manager.IncrementGuest(syncpoint_id);
syncpoint_manager.IncrementHost(syncpoint_id);
break;
}
case ThiMethod::SetMethod1:
LOG_TRACE(Service_NVDRV, "Class {} method 0x{:X} arg 0x{:X}",
static_cast<u32>(current_class), static_cast<u32>(thi_regs.method_0), arg);
ProcessMethod(thi_regs.method_0, arg);
break;
default:
break;
}
} }
} }
void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 state_offset, u32 argument) {
u8* const offset_ptr = reinterpret_cast<u8*>(&state) + sizeof(u32) * state_offset;
std::memcpy(offset_ptr, &argument, sizeof(u32));
}
} // namespace Tegra } // namespace Tegra

View File

@ -3,12 +3,18 @@
#pragma once #pragma once
#include <condition_variable>
#include <deque>
#include <memory> #include <memory>
#include <mutex>
#include <thread>
#include <vector> #include <vector>
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/polyfill_thread.h"
#include "core/memory.h"
namespace Tegra { namespace Tegra {
@ -62,23 +68,31 @@ struct ChCommand {
std::vector<u32> arguments; std::vector<u32> arguments;
}; };
using ChCommandHeaderList = std::vector<ChCommandHeader>; using ChCommandHeaderList =
Core::Memory::CpuGuestMemory<Tegra::ChCommandHeader, Core::Memory::GuestMemoryFlags::SafeRead>;
struct ThiRegisters { struct ThiRegisters {
u32_le increment_syncpt{}; static constexpr std::size_t NUM_REGS = 0x20;
INSERT_PADDING_WORDS(1);
u32_le increment_syncpt_error{}; union {
u32_le ctx_switch_incremement_syncpt{}; struct {
INSERT_PADDING_WORDS(4); u32_le increment_syncpt;
u32_le ctx_switch{}; INSERT_PADDING_WORDS_NOINIT(1);
INSERT_PADDING_WORDS(1); u32_le increment_syncpt_error;
u32_le ctx_syncpt_eof{}; u32_le ctx_switch_incremement_syncpt;
INSERT_PADDING_WORDS(5); INSERT_PADDING_WORDS_NOINIT(4);
u32_le method_0{}; u32_le ctx_switch;
u32_le method_1{}; INSERT_PADDING_WORDS_NOINIT(1);
INSERT_PADDING_WORDS(12); u32_le ctx_syncpt_eof;
u32_le int_status{}; INSERT_PADDING_WORDS_NOINIT(5);
u32_le int_mask{}; u32_le method_0;
u32_le method_1;
INSERT_PADDING_WORDS_NOINIT(12);
u32_le int_status;
u32_le int_mask;
};
std::array<u32, NUM_REGS> reg_array;
};
}; };
enum class ThiMethod : u32 { enum class ThiMethod : u32 {
@ -89,32 +103,39 @@ enum class ThiMethod : u32 {
class CDmaPusher { class CDmaPusher {
public: public:
explicit CDmaPusher(Host1x::Host1x& host1x); CDmaPusher() = delete;
~CDmaPusher(); virtual ~CDmaPusher();
/// Process the command entry void PushEntries(ChCommandHeaderList&& entries) {
void ProcessEntries(ChCommandHeaderList&& entries); std::scoped_lock l{command_mutex};
command_lists.push_back(std::move(entries));
command_cv.notify_one();
}
protected:
explicit CDmaPusher(Host1x::Host1x& host1x, s32 id);
virtual void ProcessMethod(u32 method, u32 arg) = 0;
Host1x::Host1x& host1x;
Tegra::MemoryManager& memory_manager;
private: private:
/// Process the command entry
void ProcessEntries(std::stop_token stop_token);
/// Invoke command class devices to execute the command based on the current state /// Invoke command class devices to execute the command based on the current state
void ExecuteCommand(u32 state_offset, u32 data); void ExecuteCommand(u32 state_offset, u32 data);
/// Write arguments value to the ThiRegisters member at the specified offset std::unique_ptr<Host1x::Control> host_processor;
void ThiStateWrite(ThiRegisters& state, u32 offset, u32 argument);
Host1x::Host1x& host1x; std::mutex command_mutex;
std::shared_ptr<Tegra::Host1x::Nvdec> nvdec_processor; std::condition_variable_any command_cv;
std::unique_ptr<Tegra::Host1x::Vic> vic_processor; std::deque<ChCommandHeaderList> command_lists;
std::unique_ptr<Tegra::Host1x::Control> host1x_processor; std::jthread thread;
std::unique_ptr<Host1x::SyncptIncrManager> sync_manager;
ChClassId current_class{};
ThiRegisters vic_thi_state{};
ThiRegisters nvdec_thi_state{};
u32 count{}; ThiRegisters thi_regs{};
u32 offset{}; ChClassId current_class;
u32 mask{};
bool incrementing{};
}; };
} // namespace Tegra } // namespace Tegra

View File

@ -11,6 +11,12 @@
namespace Tegra { namespace Tegra {
enum class BlendMode {
Opaque,
Premultiplied,
Coverage,
};
/** /**
* Struct describing framebuffer configuration * Struct describing framebuffer configuration
*/ */
@ -23,6 +29,7 @@ struct FramebufferConfig {
Service::android::PixelFormat pixel_format{}; Service::android::PixelFormat pixel_format{};
Service::android::BufferTransformFlags transform_flags{}; Service::android::BufferTransformFlags transform_flags{};
Common::Rectangle<int> crop_rect{}; Common::Rectangle<int> crop_rect{};
BlendMode blending{};
}; };
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,

View File

@ -250,30 +250,6 @@ struct GPU::Impl {
gpu_thread.SubmitList(channel, std::move(entries)); gpu_thread.SubmitList(channel, std::move(entries));
} }
/// Push GPU command buffer entries to be processed
void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
if (!use_nvdec) {
return;
}
if (!cdma_pushers.contains(id)) {
cdma_pushers.insert_or_assign(id, std::make_unique<Tegra::CDmaPusher>(host1x));
}
// SubmitCommandBuffer would make the nvdec operations async, this is not currently working
// TODO(ameerj): RE proper async nvdec operation
// gpu_thread.SubmitCommandBuffer(std::move(entries));
cdma_pushers[id]->ProcessEntries(std::move(entries));
}
/// Frees the CDMAPusher instance to free up resources
void ClearCdmaInstance(u32 id) {
const auto iter = cdma_pushers.find(id);
if (iter != cdma_pushers.end()) {
cdma_pushers.erase(iter);
}
}
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
void FlushRegion(DAddr addr, u64 size) { void FlushRegion(DAddr addr, u64 size) {
gpu_thread.FlushRegion(addr, size); gpu_thread.FlushRegion(addr, size);
@ -347,11 +323,21 @@ struct GPU::Impl {
WaitForSyncOperation(wait_fence); WaitForSyncOperation(wait_fence);
} }
std::vector<u8> GetAppletCaptureBuffer() {
std::vector<u8> out;
const auto wait_fence =
RequestSyncOperation([&] { out = renderer->GetAppletCaptureBuffer(); });
gpu_thread.TickGPU();
WaitForSyncOperation(wait_fence);
return out;
}
GPU& gpu; GPU& gpu;
Core::System& system; Core::System& system;
Host1x::Host1x& host1x; Host1x::Host1x& host1x;
std::map<u32, std::unique_ptr<Tegra::CDmaPusher>> cdma_pushers;
std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<VideoCore::RendererBase> renderer;
VideoCore::RasterizerInterface* rasterizer = nullptr; VideoCore::RasterizerInterface* rasterizer = nullptr;
const bool use_nvdec; const bool use_nvdec;
@ -505,6 +491,10 @@ void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
impl->RequestComposite(std::move(layers), std::move(fences)); impl->RequestComposite(std::move(layers), std::move(fences));
} }
std::vector<u8> GPU::GetAppletCaptureBuffer() {
return impl->GetAppletCaptureBuffer();
}
u64 GPU::GetTicks() const { u64 GPU::GetTicks() const {
return impl->GetTicks(); return impl->GetTicks();
} }
@ -541,14 +531,6 @@ void GPU::PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
impl->PushGPUEntries(channel, std::move(entries)); impl->PushGPUEntries(channel, std::move(entries));
} }
void GPU::PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries) {
impl->PushCommandBuffer(id, entries);
}
void GPU::ClearCdmaInstance(u32 id) {
impl->ClearCdmaInstance(id);
}
VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) {
return impl->OnCPURead(addr, size); return impl->OnCPURead(addr, size);
} }

View File

@ -215,6 +215,8 @@ public:
void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers,
std::vector<Service::Nvidia::NvFence>&& fences); std::vector<Service::Nvidia::NvFence>&& fences);
std::vector<u8> GetAppletCaptureBuffer();
/// Performs any additional setup necessary in order to begin GPU emulation. /// Performs any additional setup necessary in order to begin GPU emulation.
/// This can be used to launch any necessary threads and register any necessary /// This can be used to launch any necessary threads and register any necessary
/// core timing events. /// core timing events.
@ -232,15 +234,6 @@ public:
/// Push GPU command entries to be processed /// Push GPU command entries to be processed
void PushGPUEntries(s32 channel, Tegra::CommandList&& entries); void PushGPUEntries(s32 channel, Tegra::CommandList&& entries);
/// Push GPU command buffer entries to be processed
void PushCommandBuffer(u32 id, Tegra::ChCommandHeaderList& entries);
/// Frees the CDMAPusher instance to free up resources
void ClearCdmaInstance(u32 id);
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
[[nodiscard]] VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size); [[nodiscard]] VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size);

View File

@ -12,6 +12,7 @@
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/gpu_thread.h" #include "video_core/gpu_thread.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
namespace VideoCommon::GPUThread { namespace VideoCommon::GPUThread {

View File

@ -1,113 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/codec.h"
#include "video_core/host1x/codecs/h264.h"
#include "video_core/host1x/codecs/vp8.h"
#include "video_core/host1x/codecs/vp9.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
namespace Tegra {
Codec::Codec(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs)
: host1x(host1x_), state{regs}, h264_decoder(std::make_unique<Decoder::H264>(host1x)),
vp8_decoder(std::make_unique<Decoder::VP8>(host1x)),
vp9_decoder(std::make_unique<Decoder::VP9>(host1x)) {}
Codec::~Codec() = default;
void Codec::Initialize() {
initialized = decode_api.Initialize(current_codec);
}
void Codec::SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec) {
if (current_codec != codec) {
current_codec = codec;
LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", GetCurrentCodecName());
}
}
void Codec::Decode() {
const bool is_first_frame = !initialized;
if (is_first_frame) {
Initialize();
}
if (!initialized) {
return;
}
// Assemble bitstream.
bool vp9_hidden_frame = false;
size_t configuration_size = 0;
const auto packet_data = [&]() {
switch (current_codec) {
case Tegra::Host1x::NvdecCommon::VideoCodec::H264:
return h264_decoder->ComposeFrame(state, &configuration_size, is_first_frame);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP8:
return vp8_decoder->ComposeFrame(state);
case Tegra::Host1x::NvdecCommon::VideoCodec::VP9:
vp9_decoder->ComposeFrame(state);
vp9_hidden_frame = vp9_decoder->WasFrameHidden();
return vp9_decoder->GetFrameBytes();
default:
ASSERT(false);
return std::span<const u8>{};
}
}();
// Send assembled bitstream to decoder.
if (!decode_api.SendPacket(packet_data, configuration_size)) {
return;
}
// Only receive/store visible frames.
if (vp9_hidden_frame) {
return;
}
// Receive output frames from decoder.
decode_api.ReceiveFrames(frames);
while (frames.size() > 10) {
LOG_DEBUG(HW_GPU, "ReceiveFrames overflow, dropped frame");
frames.pop();
}
}
std::unique_ptr<FFmpeg::Frame> Codec::GetCurrentFrame() {
// Sometimes VIC will request more frames than have been decoded.
// in this case, return a blank frame and don't overwrite previous data.
if (frames.empty()) {
return {};
}
auto frame = std::move(frames.front());
frames.pop();
return frame;
}
Host1x::NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
return current_codec;
}
std::string_view Codec::GetCurrentCodecName() const {
switch (current_codec) {
case Host1x::NvdecCommon::VideoCodec::None:
return "None";
case Host1x::NvdecCommon::VideoCodec::H264:
return "H264";
case Host1x::NvdecCommon::VideoCodec::VP8:
return "VP8";
case Host1x::NvdecCommon::VideoCodec::H265:
return "H265";
case Host1x::NvdecCommon::VideoCodec::VP9:
return "VP9";
default:
return "Unknown";
}
}
} // namespace Tegra

View File

@ -1,63 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <optional>
#include <string_view>
#include <queue>
#include "common/common_types.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
namespace Decoder {
class H264;
class VP8;
class VP9;
} // namespace Decoder
namespace Host1x {
class Host1x;
} // namespace Host1x
class Codec {
public:
explicit Codec(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs);
~Codec();
/// Initialize the codec, returning success or failure
void Initialize();
/// Sets NVDEC video stream codec
void SetTargetCodec(Host1x::NvdecCommon::VideoCodec codec);
/// Call decoders to construct headers, decode AVFrame with ffmpeg
void Decode();
/// Returns next decoded frame
[[nodiscard]] std::unique_ptr<FFmpeg::Frame> GetCurrentFrame();
/// Returns the value of current_codec
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const;
/// Return name of the current codec
[[nodiscard]] std::string_view GetCurrentCodecName() const;
private:
bool initialized{};
Host1x::NvdecCommon::VideoCodec current_codec{Host1x::NvdecCommon::VideoCodec::None};
FFmpeg::DecodeApi decode_api;
Host1x::Host1x& host1x;
const Host1x::NvdecCommon::NvdecRegisters& state;
std::unique_ptr<Decoder::H264> h264_decoder;
std::unique_ptr<Decoder::VP8> vp8_decoder;
std::unique_ptr<Decoder::VP9> vp9_decoder;
std::queue<std::unique_ptr<FFmpeg::Frame>> frames{};
};
} // namespace Tegra

View File

@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/settings.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
namespace Tegra {
Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::NvdecRegisters& regs_,
Host1x::FrameQueue& frame_queue_)
: host1x(host1x_), memory_manager{host1x.GMMU()}, regs{regs_}, id{id_}, frame_queue{
frame_queue_} {}
Decoder::~Decoder() = default;
void Decoder::Decode() {
if (!initialized) {
return;
}
const auto packet_data = ComposeFrame();
// Send assembled bitstream to decoder.
if (!decode_api.SendPacket(packet_data)) {
return;
}
// Only receive/store visible frames.
if (vp9_hidden_frame) {
return;
}
// Receive output frames from decoder.
auto frame = decode_api.ReceiveFrame();
if (IsInterlaced()) {
auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
auto frame_copy = frame;
if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode interlaced frame for top 0x{:X} bottom 0x{:X}",
luma_top, luma_bottom);
}
if (UsingDecodeOrder()) {
frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
} else {
frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
}
} else {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
if (!frame.get()) {
LOG_ERROR(HW_GPU, "Failed to decode progressive frame for luma 0x{:X}", luma_offset);
}
if (UsingDecodeOrder()) {
frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
} else {
frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
}
}
}
} // namespace Tegra

View File

@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <mutex>
#include <optional>
#include <string_view>
#include <unordered_map>
#include <queue>
#include "common/common_types.h"
#include "video_core/host1x/ffmpeg/ffmpeg.h"
#include "video_core/host1x/nvdec_common.h"
namespace Tegra {
namespace Host1x {
class Host1x;
class FrameQueue;
} // namespace Host1x
class Decoder {
public:
virtual ~Decoder();
/// Call decoders to construct headers, decode AVFrame with ffmpeg
void Decode();
bool UsingDecodeOrder() const {
return decode_api.UsingDecodeOrder();
}
/// Returns the value of current_codec
[[nodiscard]] Host1x::NvdecCommon::VideoCodec GetCurrentCodec() const {
return codec;
}
/// Return name of the current codec
[[nodiscard]] virtual std::string_view GetCurrentCodecName() const = 0;
protected:
explicit Decoder(Host1x::Host1x& host1x, s32 id,
const Host1x::NvdecCommon::NvdecRegisters& regs,
Host1x::FrameQueue& frame_queue);
virtual std::span<const u8> ComposeFrame() = 0;
virtual std::tuple<u64, u64> GetProgressiveOffsets() = 0;
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
virtual bool IsInterlaced() = 0;
Host1x::Host1x& host1x;
Tegra::MemoryManager& memory_manager;
const Host1x::NvdecCommon::NvdecRegisters& regs;
s32 id;
Host1x::FrameQueue& frame_queue;
Host1x::NvdecCommon::VideoCodec codec;
FFmpeg::DecodeApi decode_api;
bool initialized{};
bool vp9_hidden_frame{};
};
} // namespace Tegra

View File

@ -10,7 +10,7 @@
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
namespace Tegra::Decoder { namespace Tegra::Decoders {
namespace { namespace {
// ZigZag LUTs from libavcodec. // ZigZag LUTs from libavcodec.
constexpr std::array<u8, 64> zig_zag_direct{ constexpr std::array<u8, 64> zig_zag_direct{
@ -25,23 +25,56 @@ constexpr std::array<u8, 16> zig_zag_scan{
}; };
} // Anonymous namespace } // Anonymous namespace
H264::H264(Host1x::Host1x& host1x_) : host1x{host1x_} {} H264::H264(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
Host1x::FrameQueue& frame_queue_)
: Decoder{host1x_, id_, regs_, frame_queue_} {
codec = Host1x::NvdecCommon::VideoCodec::H264;
initialized = decode_api.Initialize(codec);
}
H264::~H264() = default; H264::~H264() = default;
std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state, std::tuple<u64, u64> H264::GetProgressiveOffsets() {
size_t* out_configuration_size, bool is_first_frame) { auto pic_idx{current_context.h264_parameter_set.curr_pic_idx};
H264DecoderContext context; auto luma{regs.surface_luma_offsets[pic_idx].Address() +
host1x.GMMU().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext)); current_context.h264_parameter_set.luma_frame_offset.Address()};
auto chroma{regs.surface_chroma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.chroma_frame_offset.Address()};
return {luma, chroma};
}
const s64 frame_number = context.h264_parameter_set.frame_number.Value(); std::tuple<u64, u64, u64, u64> H264::GetInterlacedOffsets() {
auto pic_idx{current_context.h264_parameter_set.curr_pic_idx};
auto luma_top{regs.surface_luma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.luma_top_offset.Address()};
auto luma_bottom{regs.surface_luma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.luma_bot_offset.Address()};
auto chroma_top{regs.surface_chroma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.chroma_top_offset.Address()};
auto chroma_bottom{regs.surface_chroma_offsets[pic_idx].Address() +
current_context.h264_parameter_set.chroma_bot_offset.Address()};
return {luma_top, luma_bottom, chroma_top, chroma_bottom};
}
bool H264::IsInterlaced() {
return current_context.h264_parameter_set.luma_top_offset.Address() != 0 ||
current_context.h264_parameter_set.luma_bot_offset.Address() != 0;
}
std::span<const u8> H264::ComposeFrame() {
memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
sizeof(H264DecoderContext));
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) { if (!is_first_frame && frame_number != 0) {
frame.resize_destructive(context.stream_len); frame_scratch.resize_destructive(current_context.stream_len);
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size()); memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(), frame_scratch.data(),
*out_configuration_size = 0; frame_scratch.size());
return frame; return frame_scratch;
} }
is_first_frame = false;
// Encode header // Encode header
H264BitWriter writer{}; H264BitWriter writer{};
writer.WriteU(1, 24); writer.WriteU(1, 24);
@ -53,7 +86,7 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
writer.WriteU(31, 8); writer.WriteU(31, 8);
writer.WriteUe(0); writer.WriteUe(0);
const u32 chroma_format_idc = const u32 chroma_format_idc =
static_cast<u32>(context.h264_parameter_set.chroma_format_idc.Value()); static_cast<u32>(current_context.h264_parameter_set.chroma_format_idc.Value());
writer.WriteUe(chroma_format_idc); writer.WriteUe(chroma_format_idc);
if (chroma_format_idc == 3) { if (chroma_format_idc == 3) {
writer.WriteBit(false); writer.WriteBit(false);
@ -61,42 +94,44 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
writer.WriteUe(0); writer.WriteUe(0);
writer.WriteUe(0); writer.WriteUe(0);
writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag writer.WriteBit(current_context.qpprime_y_zero_transform_bypass_flag.Value() != 0);
writer.WriteBit(false); // Scaling matrix present flag writer.WriteBit(false); // Scaling matrix present flag
writer.WriteUe(static_cast<u32>(context.h264_parameter_set.log2_max_frame_num_minus4.Value())); writer.WriteUe(
static_cast<u32>(current_context.h264_parameter_set.log2_max_frame_num_minus4.Value()));
const auto order_cnt_type = const auto order_cnt_type =
static_cast<u32>(context.h264_parameter_set.pic_order_cnt_type.Value()); static_cast<u32>(current_context.h264_parameter_set.pic_order_cnt_type.Value());
writer.WriteUe(order_cnt_type); writer.WriteUe(order_cnt_type);
if (order_cnt_type == 0) { if (order_cnt_type == 0) {
writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4); writer.WriteUe(current_context.h264_parameter_set.log2_max_pic_order_cnt_lsb_minus4);
} else if (order_cnt_type == 1) { } else if (order_cnt_type == 1) {
writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0); writer.WriteBit(current_context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
writer.WriteSe(0); writer.WriteSe(0);
writer.WriteSe(0); writer.WriteSe(0);
writer.WriteUe(0); writer.WriteUe(0);
} }
const s32 pic_height = context.h264_parameter_set.frame_height_in_map_units / const s32 pic_height = current_context.h264_parameter_set.frame_height_in_mbs /
(context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2); (current_context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
// TODO (ameerj): Where do we get this number, it seems to be particular for each stream u32 max_num_ref_frames =
const auto nvdec_decoding = Settings::values.nvdec_emulation.GetValue(); std::max(std::max(current_context.h264_parameter_set.num_refidx_l0_default_active,
const bool uses_gpu_decoding = nvdec_decoding == Settings::NvdecEmulation::Gpu; current_context.h264_parameter_set.num_refidx_l1_default_active) +
const u32 max_num_ref_frames = uses_gpu_decoding ? 6u : 16u; 1,
4);
writer.WriteUe(max_num_ref_frames); writer.WriteUe(max_num_ref_frames);
writer.WriteBit(false); writer.WriteBit(false);
writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1); writer.WriteUe(current_context.h264_parameter_set.pic_width_in_mbs - 1);
writer.WriteUe(pic_height - 1); writer.WriteUe(pic_height - 1);
writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0); writer.WriteBit(current_context.h264_parameter_set.frame_mbs_only_flag != 0);
if (!context.h264_parameter_set.frame_mbs_only_flag) { if (!current_context.h264_parameter_set.frame_mbs_only_flag) {
writer.WriteBit(context.h264_parameter_set.flags.mbaff_frame.Value() != 0); writer.WriteBit(current_context.h264_parameter_set.flags.mbaff_frame.Value() != 0);
} }
writer.WriteBit(context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0); writer.WriteBit(current_context.h264_parameter_set.flags.direct_8x8_inference.Value() != 0);
writer.WriteBit(false); // Frame cropping flag writer.WriteBit(false); // Frame cropping flag
writer.WriteBit(false); // VUI parameter present flag writer.WriteBit(false); // VUI parameter present flag
@ -111,57 +146,59 @@ std::span<const u8> H264::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters
writer.WriteUe(0); writer.WriteUe(0);
writer.WriteUe(0); writer.WriteUe(0);
writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0); writer.WriteBit(current_context.h264_parameter_set.entropy_coding_mode_flag != 0);
writer.WriteBit(context.h264_parameter_set.pic_order_present_flag != 0); writer.WriteBit(current_context.h264_parameter_set.pic_order_present_flag != 0);
writer.WriteUe(0); writer.WriteUe(0);
writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active); writer.WriteUe(current_context.h264_parameter_set.num_refidx_l0_default_active);
writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active); writer.WriteUe(current_context.h264_parameter_set.num_refidx_l1_default_active);
writer.WriteBit(context.h264_parameter_set.flags.weighted_pred.Value() != 0); writer.WriteBit(current_context.h264_parameter_set.flags.weighted_pred.Value() != 0);
writer.WriteU(static_cast<s32>(context.h264_parameter_set.weighted_bipred_idc.Value()), 2); writer.WriteU(static_cast<s32>(current_context.h264_parameter_set.weighted_bipred_idc.Value()),
s32 pic_init_qp = static_cast<s32>(context.h264_parameter_set.pic_init_qp_minus26.Value()); 2);
s32 pic_init_qp =
static_cast<s32>(current_context.h264_parameter_set.pic_init_qp_minus26.Value());
writer.WriteSe(pic_init_qp); writer.WriteSe(pic_init_qp);
writer.WriteSe(0); writer.WriteSe(0);
s32 chroma_qp_index_offset = s32 chroma_qp_index_offset =
static_cast<s32>(context.h264_parameter_set.chroma_qp_index_offset.Value()); static_cast<s32>(current_context.h264_parameter_set.chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset); writer.WriteSe(chroma_qp_index_offset);
writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_present_flag != 0); writer.WriteBit(current_context.h264_parameter_set.deblocking_filter_control_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0); writer.WriteBit(current_context.h264_parameter_set.flags.constrained_intra_pred.Value() != 0);
writer.WriteBit(context.h264_parameter_set.redundant_pic_cnt_present_flag != 0); writer.WriteBit(current_context.h264_parameter_set.redundant_pic_cnt_present_flag != 0);
writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0); writer.WriteBit(current_context.h264_parameter_set.transform_8x8_mode_flag != 0);
writer.WriteBit(true); // pic_scaling_matrix_present_flag writer.WriteBit(true); // pic_scaling_matrix_present_flag
for (s32 index = 0; index < 6; index++) { for (s32 index = 0; index < 6; index++) {
writer.WriteBit(true); writer.WriteBit(true);
std::span<const u8> matrix{context.weight_scale}; std::span<const u8> matrix{current_context.weight_scale_4x4};
writer.WriteScalingList(scan, matrix, index * 16, 16); writer.WriteScalingList(scan_scratch, matrix, index * 16, 16);
} }
if (context.h264_parameter_set.transform_8x8_mode_flag) { if (current_context.h264_parameter_set.transform_8x8_mode_flag) {
for (s32 index = 0; index < 2; index++) { for (s32 index = 0; index < 2; index++) {
writer.WriteBit(true); writer.WriteBit(true);
std::span<const u8> matrix{context.weight_scale_8x8}; std::span<const u8> matrix{current_context.weight_scale_8x8};
writer.WriteScalingList(scan, matrix, index * 64, 64); writer.WriteScalingList(scan_scratch, matrix, index * 64, 64);
} }
} }
s32 chroma_qp_index_offset2 = s32 chroma_qp_index_offset2 =
static_cast<s32>(context.h264_parameter_set.second_chroma_qp_index_offset.Value()); static_cast<s32>(current_context.h264_parameter_set.second_chroma_qp_index_offset.Value());
writer.WriteSe(chroma_qp_index_offset2); writer.WriteSe(chroma_qp_index_offset2);
writer.End(); writer.End();
const auto& encoded_header = writer.GetByteArray(); const auto& encoded_header = writer.GetByteArray();
frame.resize(encoded_header.size() + context.stream_len); frame_scratch.resize(encoded_header.size() + current_context.stream_len);
std::memcpy(frame.data(), encoded_header.data(), encoded_header.size()); std::memcpy(frame_scratch.data(), encoded_header.data(), encoded_header.size());
*out_configuration_size = encoded_header.size(); memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, frame.data() + encoded_header.size(), frame_scratch.data() + encoded_header.size(),
context.stream_len); current_context.stream_len);
return frame; return frame_scratch;
} }
H264BitWriter::H264BitWriter() = default; H264BitWriter::H264BitWriter() = default;
@ -278,4 +315,4 @@ void H264BitWriter::Flush() {
buffer = 0; buffer = 0;
buffer_pos = 0; buffer_pos = 0;
} }
} // namespace Tegra::Decoder } // namespace Tegra::Decoders

View File

@ -10,6 +10,7 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
namespace Tegra { namespace Tegra {
@ -18,7 +19,7 @@ namespace Host1x {
class Host1x; class Host1x;
} // namespace Host1x } // namespace Host1x
namespace Decoder { namespace Decoders {
class H264BitWriter { class H264BitWriter {
public: public:
@ -60,123 +61,213 @@ private:
std::vector<u8> byte_array; std::vector<u8> byte_array;
}; };
class H264 { struct Offset {
public: constexpr u32 Address() const noexcept {
explicit H264(Host1x::Host1x& host1x); return offset << 8;
~H264(); }
/// Compose the H264 frame for FFmpeg decoding
[[nodiscard]] std::span<const u8> ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state,
size_t* out_configuration_size,
bool is_first_frame = false);
private: private:
Common::ScratchBuffer<u8> frame; u32 offset;
Common::ScratchBuffer<u8> scan; };
Host1x::Host1x& host1x; static_assert(std::is_trivial_v<Offset>, "Offset must be trivial");
static_assert(sizeof(Offset) == 0x4, "Offset has the wrong size!");
struct H264ParameterSet { struct H264ParameterSet {
s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00 s32 log2_max_pic_order_cnt_lsb_minus4; ///< 0x00
s32 delta_pic_order_always_zero_flag; ///< 0x04 s32 delta_pic_order_always_zero_flag; ///< 0x04
s32 frame_mbs_only_flag; ///< 0x08 s32 frame_mbs_only_flag; ///< 0x08
u32 pic_width_in_mbs; ///< 0x0C u32 pic_width_in_mbs; ///< 0x0C
u32 frame_height_in_map_units; ///< 0x10 u32 frame_height_in_mbs; ///< 0x10
union { ///< 0x14 union { ///< 0x14
BitField<0, 2, u32> tile_format; BitField<0, 2, u32> tile_format;
BitField<2, 3, u32> gob_height; BitField<2, 3, u32> gob_height;
}; BitField<5, 27, u32> reserved_surface_format;
u32 entropy_coding_mode_flag; ///< 0x18
s32 pic_order_present_flag; ///< 0x1C
s32 num_refidx_l0_default_active; ///< 0x20
s32 num_refidx_l1_default_active; ///< 0x24
s32 deblocking_filter_control_present_flag; ///< 0x28
s32 redundant_pic_cnt_present_flag; ///< 0x2C
u32 transform_8x8_mode_flag; ///< 0x30
u32 pitch_luma; ///< 0x34
u32 pitch_chroma; ///< 0x38
u32 luma_top_offset; ///< 0x3C
u32 luma_bot_offset; ///< 0x40
u32 luma_frame_offset; ///< 0x44
u32 chroma_top_offset; ///< 0x48
u32 chroma_bot_offset; ///< 0x4C
u32 chroma_frame_offset; ///< 0x50
u32 hist_buffer_size; ///< 0x54
union { ///< 0x58
union {
BitField<0, 1, u64> mbaff_frame;
BitField<1, 1, u64> direct_8x8_inference;
BitField<2, 1, u64> weighted_pred;
BitField<3, 1, u64> constrained_intra_pred;
BitField<4, 1, u64> ref_pic;
BitField<5, 1, u64> field_pic;
BitField<6, 1, u64> bottom_field;
BitField<7, 1, u64> second_field;
} flags;
BitField<8, 4, u64> log2_max_frame_num_minus4;
BitField<12, 2, u64> chroma_format_idc;
BitField<14, 2, u64> pic_order_cnt_type;
BitField<16, 6, s64> pic_init_qp_minus26;
BitField<22, 5, s64> chroma_qp_index_offset;
BitField<27, 5, s64> second_chroma_qp_index_offset;
BitField<32, 2, u64> weighted_bipred_idc;
BitField<34, 7, u64> curr_pic_idx;
BitField<41, 5, u64> curr_col_idx;
BitField<46, 16, u64> frame_number;
BitField<62, 1, u64> frame_surfaces;
BitField<63, 1, u64> output_memory_layout;
};
}; };
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size"); u32 entropy_coding_mode_flag; ///< 0x18
s32 pic_order_present_flag; ///< 0x1C
struct H264DecoderContext { s32 num_refidx_l0_default_active; ///< 0x20
INSERT_PADDING_WORDS_NOINIT(18); ///< 0x0000 s32 num_refidx_l1_default_active; ///< 0x24
u32 stream_len; ///< 0x0048 s32 deblocking_filter_control_present_flag; ///< 0x28
INSERT_PADDING_WORDS_NOINIT(3); ///< 0x004C s32 redundant_pic_cnt_present_flag; ///< 0x2C
H264ParameterSet h264_parameter_set; ///< 0x0058 u32 transform_8x8_mode_flag; ///< 0x30
INSERT_PADDING_WORDS_NOINIT(66); ///< 0x00B8 u32 pitch_luma; ///< 0x34
std::array<u8, 0x60> weight_scale; ///< 0x01C0 u32 pitch_chroma; ///< 0x38
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220 Offset luma_top_offset; ///< 0x3C
Offset luma_bot_offset; ///< 0x40
Offset luma_frame_offset; ///< 0x44
Offset chroma_top_offset; ///< 0x48
Offset chroma_bot_offset; ///< 0x4C
Offset chroma_frame_offset; ///< 0x50
u32 hist_buffer_size; ///< 0x54
union { ///< 0x58
union {
BitField<0, 1, u64> mbaff_frame;
BitField<1, 1, u64> direct_8x8_inference;
BitField<2, 1, u64> weighted_pred;
BitField<3, 1, u64> constrained_intra_pred;
BitField<4, 1, u64> ref_pic;
BitField<5, 1, u64> field_pic;
BitField<6, 1, u64> bottom_field;
BitField<7, 1, u64> second_field;
} flags;
BitField<8, 4, u64> log2_max_frame_num_minus4;
BitField<12, 2, u64> chroma_format_idc;
BitField<14, 2, u64> pic_order_cnt_type;
BitField<16, 6, s64> pic_init_qp_minus26;
BitField<22, 5, s64> chroma_qp_index_offset;
BitField<27, 5, s64> second_chroma_qp_index_offset;
BitField<32, 2, u64> weighted_bipred_idc;
BitField<34, 7, u64> curr_pic_idx;
BitField<41, 5, u64> curr_col_idx;
BitField<46, 16, u64> frame_number;
BitField<62, 1, u64> frame_surfaces;
BitField<63, 1, u64> output_memory_layout;
}; };
static_assert(sizeof(H264DecoderContext) == 0x2A0, "H264DecoderContext is an invalid size"); };
static_assert(sizeof(H264ParameterSet) == 0x60, "H264ParameterSet is an invalid size");
#define ASSERT_POSITION(field_name, position) \ #define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264ParameterSet, field_name) == position, \ static_assert(offsetof(H264ParameterSet, field_name) == position, \
"Field " #field_name " has invalid position") "Field " #field_name " has invalid position")
ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00); ASSERT_POSITION(log2_max_pic_order_cnt_lsb_minus4, 0x00);
ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04); ASSERT_POSITION(delta_pic_order_always_zero_flag, 0x04);
ASSERT_POSITION(frame_mbs_only_flag, 0x08); ASSERT_POSITION(frame_mbs_only_flag, 0x08);
ASSERT_POSITION(pic_width_in_mbs, 0x0C); ASSERT_POSITION(pic_width_in_mbs, 0x0C);
ASSERT_POSITION(frame_height_in_map_units, 0x10); ASSERT_POSITION(frame_height_in_mbs, 0x10);
ASSERT_POSITION(tile_format, 0x14); ASSERT_POSITION(tile_format, 0x14);
ASSERT_POSITION(entropy_coding_mode_flag, 0x18); ASSERT_POSITION(entropy_coding_mode_flag, 0x18);
ASSERT_POSITION(pic_order_present_flag, 0x1C); ASSERT_POSITION(pic_order_present_flag, 0x1C);
ASSERT_POSITION(num_refidx_l0_default_active, 0x20); ASSERT_POSITION(num_refidx_l0_default_active, 0x20);
ASSERT_POSITION(num_refidx_l1_default_active, 0x24); ASSERT_POSITION(num_refidx_l1_default_active, 0x24);
ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28); ASSERT_POSITION(deblocking_filter_control_present_flag, 0x28);
ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C); ASSERT_POSITION(redundant_pic_cnt_present_flag, 0x2C);
ASSERT_POSITION(transform_8x8_mode_flag, 0x30); ASSERT_POSITION(transform_8x8_mode_flag, 0x30);
ASSERT_POSITION(pitch_luma, 0x34); ASSERT_POSITION(pitch_luma, 0x34);
ASSERT_POSITION(pitch_chroma, 0x38); ASSERT_POSITION(pitch_chroma, 0x38);
ASSERT_POSITION(luma_top_offset, 0x3C); ASSERT_POSITION(luma_top_offset, 0x3C);
ASSERT_POSITION(luma_bot_offset, 0x40); ASSERT_POSITION(luma_bot_offset, 0x40);
ASSERT_POSITION(luma_frame_offset, 0x44); ASSERT_POSITION(luma_frame_offset, 0x44);
ASSERT_POSITION(chroma_top_offset, 0x48); ASSERT_POSITION(chroma_top_offset, 0x48);
ASSERT_POSITION(chroma_bot_offset, 0x4C); ASSERT_POSITION(chroma_bot_offset, 0x4C);
ASSERT_POSITION(chroma_frame_offset, 0x50); ASSERT_POSITION(chroma_frame_offset, 0x50);
ASSERT_POSITION(hist_buffer_size, 0x54); ASSERT_POSITION(hist_buffer_size, 0x54);
ASSERT_POSITION(flags, 0x58); ASSERT_POSITION(flags, 0x58);
#undef ASSERT_POSITION #undef ASSERT_POSITION
struct DpbEntry {
union {
BitField<0, 7, u32> index;
BitField<7, 5, u32> col_idx;
BitField<12, 2, u32> state;
BitField<14, 1, u32> is_long_term;
BitField<15, 1, u32> non_existing;
BitField<16, 1, u32> is_field;
BitField<17, 4, u32> top_field_marking;
BitField<21, 4, u32> bottom_field_marking;
BitField<25, 1, u32> output_memory_layout;
BitField<26, 6, u32> reserved;
} flags;
std::array<u32, 2> field_order_cnt;
u32 frame_idx;
};
static_assert(sizeof(DpbEntry) == 0x10, "DpbEntry has the wrong size!");
struct DisplayParam {
union {
BitField<0, 1, u32> enable_tf_output;
BitField<1, 1, u32> vc1_map_y_flag;
BitField<2, 3, u32> map_y_value;
BitField<5, 1, u32> vc1_map_uv_flag;
BitField<6, 3, u32> map_uv_value;
BitField<9, 8, u32> out_stride;
BitField<17, 3, u32> tiling_format;
BitField<20, 1, u32> output_structure; // 0=frame, 1=field
BitField<21, 11, u32> reserved0;
};
std::array<s32, 2> output_top;
std::array<s32, 2> output_bottom;
union {
BitField<0, 1, u32> enable_histogram;
BitField<1, 12, u32> histogram_start_x;
BitField<13, 12, u32> histogram_start_y;
BitField<25, 7, u32> reserved1;
};
union {
BitField<0, 12, u32> histogram_end_x;
BitField<12, 12, u32> histogram_end_y;
BitField<24, 8, u32> reserved2;
};
};
static_assert(sizeof(DisplayParam) == 0x1C, "DisplayParam has the wrong size!");
struct H264DecoderContext {
INSERT_PADDING_WORDS_NOINIT(13); ///< 0x0000
std::array<u8, 16> eos; ///< 0x0034
u8 explicit_eos_present_flag; ///< 0x0044
u8 hint_dump_en; ///< 0x0045
INSERT_PADDING_BYTES_NOINIT(2); ///< 0x0046
u32 stream_len; ///< 0x0048
u32 slice_count; ///< 0x004C
u32 mbhist_buffer_size; ///< 0x0050
u32 gptimer_timeout_value; ///< 0x0054
H264ParameterSet h264_parameter_set; ///< 0x0058
std::array<s32, 2> curr_field_order_cnt; ///< 0x00B8
std::array<DpbEntry, 16> dpb; ///< 0x00C0
std::array<u8, 0x60> weight_scale_4x4; ///< 0x01C0
std::array<u8, 0x80> weight_scale_8x8; ///< 0x0220
std::array<u8, 2> num_inter_view_refs_lX; ///< 0x02A0
std::array<u8, 14> reserved2; ///< 0x02A2
std::array<std::array<s8, 16>, 2> inter_view_refidx_lX; ///< 0x02B0
union { ///< 0x02D0
BitField<0, 1, u32> lossless_ipred8x8_filter_enable;
BitField<1, 1, u32> qpprime_y_zero_transform_bypass_flag;
BitField<2, 30, u32> reserved3;
};
DisplayParam display_param; ///< 0x02D4
std::array<u32, 3> reserved4; ///< 0x02F0
};
static_assert(sizeof(H264DecoderContext) == 0x2FC, "H264DecoderContext is an invalid size");
#define ASSERT_POSITION(field_name, position) \ #define ASSERT_POSITION(field_name, position) \
static_assert(offsetof(H264DecoderContext, field_name) == position, \ static_assert(offsetof(H264DecoderContext, field_name) == position, \
"Field " #field_name " has invalid position") "Field " #field_name " has invalid position")
ASSERT_POSITION(stream_len, 0x48); ASSERT_POSITION(stream_len, 0x48);
ASSERT_POSITION(h264_parameter_set, 0x58); ASSERT_POSITION(h264_parameter_set, 0x58);
ASSERT_POSITION(weight_scale, 0x1C0); ASSERT_POSITION(dpb, 0xC0);
ASSERT_POSITION(weight_scale_4x4, 0x1C0);
#undef ASSERT_POSITION #undef ASSERT_POSITION
class H264 final : public Decoder {
public:
explicit H264(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
Host1x::FrameQueue& frame_queue);
~H264() override;
H264(const H264&) = delete;
H264& operator=(const H264&) = delete;
H264(H264&&) = delete;
H264& operator=(H264&&) = delete;
/// Compose the H264 frame for FFmpeg decoding
[[nodiscard]] std::span<const u8> ComposeFrame() override;
std::tuple<u64, u64> GetProgressiveOffsets() override;
std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
bool IsInterlaced() override;
std::string_view GetCurrentCodecName() const override {
return "H264";
}
private:
bool is_first_frame{true};
Common::ScratchBuffer<u8> frame_scratch;
Common::ScratchBuffer<u8> scan_scratch;
H264DecoderContext current_context{};
}; };
} // namespace Decoder } // namespace Decoders
} // namespace Tegra } // namespace Tegra

View File

@ -7,47 +7,70 @@
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
namespace Tegra::Decoder { namespace Tegra::Decoders {
VP8::VP8(Host1x::Host1x& host1x_) : host1x{host1x_} {} VP8::VP8(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
Host1x::FrameQueue& frame_queue_)
: Decoder{host1x_, id_, regs_, frame_queue_} {
codec = Host1x::NvdecCommon::VideoCodec::VP8;
initialized = decode_api.Initialize(codec);
}
VP8::~VP8() = default; VP8::~VP8() = default;
std::span<const u8> VP8::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { std::tuple<u64, u64> VP8::GetProgressiveOffsets() {
VP8PictureInfo info; auto luma{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
host1x.GMMU().ReadBlock(state.picture_info_offset, &info, sizeof(VP8PictureInfo)); auto chroma{regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
return {luma, chroma};
}
const bool is_key_frame = info.key_frame == 1u; std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
const auto bitstream_size = static_cast<size_t>(info.vld_buffer_size); auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto luma_bottom{
regs.surface_luma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto chroma_top{
regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
auto chroma_bottom{
regs.surface_chroma_offsets[static_cast<u32>(Vp8SurfaceIndex::Current)].Address()};
return {luma_top, luma_bottom, chroma_top, chroma_bottom};
}
std::span<const u8> VP8::ComposeFrame() {
memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context,
sizeof(VP8PictureInfo));
const bool is_key_frame = current_context.key_frame == 1u;
const auto bitstream_size = static_cast<size_t>(current_context.vld_buffer_size);
const size_t header_size = is_key_frame ? 10u : 3u; const size_t header_size = is_key_frame ? 10u : 3u;
frame.resize(header_size + bitstream_size); frame_scratch.resize(header_size + bitstream_size);
// Based on page 30 of the VP8 specification. // Based on page 30 of the VP8 specification.
// https://datatracker.ietf.org/doc/rfc6386/ // https://datatracker.ietf.org/doc/rfc6386/
frame[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes). frame_scratch[0] = is_key_frame ? 0u : 1u; // 1-bit frame type (0: keyframe, 1: interframes).
frame[0] |= static_cast<u8>((info.version & 7u) << 1u); // 3-bit version number frame_scratch[0] |=
frame[0] |= static_cast<u8>(1u << 4u); // 1-bit show_frame flag static_cast<u8>((current_context.version & 7u) << 1u); // 3-bit version number
frame_scratch[0] |= static_cast<u8>(1u << 4u); // 1-bit show_frame flag
// The next 19-bits are the first partition size // The next 19-bits are the first partition size
frame[0] |= static_cast<u8>((info.first_part_size & 7u) << 5u); frame_scratch[0] |= static_cast<u8>((current_context.first_part_size & 7u) << 5u);
frame[1] = static_cast<u8>((info.first_part_size & 0x7f8u) >> 3u); frame_scratch[1] = static_cast<u8>((current_context.first_part_size & 0x7f8u) >> 3u);
frame[2] = static_cast<u8>((info.first_part_size & 0x7f800u) >> 11u); frame_scratch[2] = static_cast<u8>((current_context.first_part_size & 0x7f800u) >> 11u);
if (is_key_frame) { if (is_key_frame) {
frame[3] = 0x9du; frame_scratch[3] = 0x9du;
frame[4] = 0x01u; frame_scratch[4] = 0x01u;
frame[5] = 0x2au; frame_scratch[5] = 0x2au;
// TODO(ameerj): Horizontal/Vertical Scale // TODO(ameerj): Horizontal/Vertical Scale
// 16 bits: (2 bits Horizontal Scale << 14) | Width (14 bits) // 16 bits: (2 bits Horizontal Scale << 14) | Width (14 bits)
frame[6] = static_cast<u8>(info.frame_width & 0xff); frame_scratch[6] = static_cast<u8>(current_context.frame_width & 0xff);
frame[7] = static_cast<u8>(((info.frame_width >> 8) & 0x3f)); frame_scratch[7] = static_cast<u8>(((current_context.frame_width >> 8) & 0x3f));
// 16 bits:(2 bits Vertical Scale << 14) | Height (14 bits) // 16 bits:(2 bits Vertical Scale << 14) | Height (14 bits)
frame[8] = static_cast<u8>(info.frame_height & 0xff); frame_scratch[8] = static_cast<u8>(current_context.frame_height & 0xff);
frame[9] = static_cast<u8>(((info.frame_height >> 8) & 0x3f)); frame_scratch[9] = static_cast<u8>(((current_context.frame_height >> 8) & 0x3f));
} }
const u64 bitstream_offset = state.frame_bitstream_offset; const u64 bitstream_offset = regs.frame_bitstream_offset.Address();
host1x.GMMU().ReadBlock(bitstream_offset, frame.data() + header_size, bitstream_size); memory_manager.ReadBlock(bitstream_offset, frame_scratch.data() + header_size, bitstream_size);
return frame; return frame_scratch;
} }
} // namespace Tegra::Decoder } // namespace Tegra::Decoders

View File

@ -9,6 +9,7 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
namespace Tegra { namespace Tegra {
@ -17,20 +18,41 @@ namespace Host1x {
class Host1x; class Host1x;
} // namespace Host1x } // namespace Host1x
namespace Decoder { namespace Decoders {
enum class Vp8SurfaceIndex : u32 {
Last = 0,
Golden = 1,
AltRef = 2,
Current = 3,
};
class VP8 { class VP8 final : public Decoder {
public: public:
explicit VP8(Host1x::Host1x& host1x); explicit VP8(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
~VP8(); Host1x::FrameQueue& frame_queue);
~VP8() override;
/// Compose the VP8 frame for FFmpeg decoding VP8(const VP8&) = delete;
[[nodiscard]] std::span<const u8> ComposeFrame( VP8& operator=(const VP8&) = delete;
const Host1x::NvdecCommon::NvdecRegisters& state);
VP8(VP8&&) = delete;
VP8& operator=(VP8&&) = delete;
[[nodiscard]] std::span<const u8> ComposeFrame() override;
std::tuple<u64, u64> GetProgressiveOffsets() override;
std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
bool IsInterlaced() override {
return false;
}
std::string_view GetCurrentCodecName() const override {
return "VP8";
}
private: private:
Common::ScratchBuffer<u8> frame; Common::ScratchBuffer<u8> frame_scratch;
Host1x::Host1x& host1x;
struct VP8PictureInfo { struct VP8PictureInfo {
INSERT_PADDING_WORDS_NOINIT(14); INSERT_PADDING_WORDS_NOINIT(14);
@ -73,7 +95,9 @@ private:
INSERT_PADDING_WORDS_NOINIT(3); INSERT_PADDING_WORDS_NOINIT(3);
}; };
static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size"); static_assert(sizeof(VP8PictureInfo) == 0xc0, "PictureInfo is an invalid size");
VP8PictureInfo current_context{};
}; };
} // namespace Decoder } // namespace Decoders
} // namespace Tegra } // namespace Tegra

View File

@ -4,12 +4,13 @@
#include <algorithm> // for std::copy #include <algorithm> // for std::copy
#include <numeric> #include <numeric>
#include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "video_core/host1x/codecs/vp9.h" #include "video_core/host1x/codecs/vp9.h"
#include "video_core/host1x/host1x.h" #include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
namespace Tegra::Decoder { namespace Tegra::Decoders {
namespace { namespace {
constexpr u32 diff_update_probability = 252; constexpr u32 diff_update_probability = 252;
constexpr u32 frame_sync_code = 0x498342; constexpr u32 frame_sync_code = 0x498342;
@ -237,7 +238,12 @@ constexpr std::array<u8, 254> map_lut{
} }
} // Anonymous namespace } // Anonymous namespace
VP9::VP9(Host1x::Host1x& host1x_) : host1x{host1x_} {} VP9::VP9(Host1x::Host1x& host1x_, const Host1x::NvdecCommon::NvdecRegisters& regs_, s32 id_,
Host1x::FrameQueue& frame_queue_)
: Decoder{host1x_, id_, regs_, frame_queue_} {
codec = Host1x::NvdecCommon::VideoCodec::VP9;
initialized = decode_api.Initialize(codec);
}
VP9::~VP9() = default; VP9::~VP9() = default;
@ -356,35 +362,113 @@ void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_
} }
} }
Vp9PictureInfo VP9::GetVp9PictureInfo(const Host1x::NvdecCommon::NvdecRegisters& state) { void VP9::WriteSegmentation(VpxBitStreamWriter& writer) {
PictureInfo picture_info; bool enabled = current_picture_info.segmentation.enabled != 0;
host1x.GMMU().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo)); writer.WriteBit(enabled);
Vp9PictureInfo vp9_info = picture_info.Convert(); if (!enabled) {
return;
}
InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy); auto update_map = current_picture_info.segmentation.update_map != 0;
writer.WriteBit(update_map);
if (update_map) {
EntropyProbs entropy_probs{};
memory_manager.ReadBlock(regs.vp9_prob_tab_buffer_offset.Address(), &entropy_probs,
sizeof(entropy_probs));
auto WriteProb = [&](u8 prob) {
bool coded = prob != 255;
writer.WriteBit(coded);
if (coded) {
writer.WriteU(prob, 8);
}
};
for (size_t i = 0; i < entropy_probs.mb_segment_tree_probs.size(); i++) {
WriteProb(entropy_probs.mb_segment_tree_probs[i]);
}
auto temporal_update = current_picture_info.segmentation.temporal_update != 0;
writer.WriteBit(temporal_update);
if (temporal_update) {
for (s32 i = 0; i < 3; i++) {
WriteProb(entropy_probs.segment_pred_probs[i]);
}
}
}
if (last_segmentation == current_picture_info.segmentation) {
writer.WriteBit(false);
return;
}
last_segmentation = current_picture_info.segmentation;
writer.WriteBit(true);
writer.WriteBit(current_picture_info.segmentation.abs_delta != 0);
constexpr s32 MAX_SEGMENTS = 8;
constexpr std::array SegmentationFeatureBits = {8, 6, 2, 0};
for (s32 i = 0; i < MAX_SEGMENTS; i++) {
auto q_enabled = current_picture_info.segmentation.feature_enabled[i][0] != 0;
writer.WriteBit(q_enabled);
if (q_enabled) {
writer.WriteS(current_picture_info.segmentation.feature_data[i][0],
SegmentationFeatureBits[0]);
}
auto lf_enabled = current_picture_info.segmentation.feature_enabled[i][1] != 0;
writer.WriteBit(lf_enabled);
if (lf_enabled) {
writer.WriteS(current_picture_info.segmentation.feature_data[i][1],
SegmentationFeatureBits[1]);
}
auto ref_enabled = current_picture_info.segmentation.feature_enabled[i][2] != 0;
writer.WriteBit(ref_enabled);
if (ref_enabled) {
writer.WriteU(current_picture_info.segmentation.feature_data[i][2],
SegmentationFeatureBits[2]);
}
auto skip_enabled = current_picture_info.segmentation.feature_enabled[i][3] != 0;
writer.WriteBit(skip_enabled);
}
}
Vp9PictureInfo VP9::GetVp9PictureInfo() {
memory_manager.ReadBlock(regs.picture_info_offset.Address(), &current_picture_info,
sizeof(PictureInfo));
Vp9PictureInfo vp9_info = current_picture_info.Convert();
InsertEntropy(regs.vp9_prob_tab_buffer_offset.Address(), vp9_info.entropy);
// surface_luma_offset[0:3] contains the address of the reference frame offsets in the following // surface_luma_offset[0:3] contains the address of the reference frame offsets in the following
// order: last, golden, altref, current. // order: last, golden, altref, current.
std::copy(state.surface_luma_offset.begin(), state.surface_luma_offset.begin() + 4, for (size_t i = 0; i < 4; i++) {
vp9_info.frame_offsets.begin()); vp9_info.frame_offsets[i] = regs.surface_luma_offsets[i].Address();
}
return vp9_info; return vp9_info;
} }
void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) { void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
EntropyProbs entropy; EntropyProbs entropy;
host1x.GMMU().ReadBlock(offset, &entropy, sizeof(EntropyProbs)); memory_manager.ReadBlock(offset, &entropy, sizeof(EntropyProbs));
entropy.Convert(dst); entropy.Convert(dst);
} }
Vp9FrameContainer VP9::GetCurrentFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { Vp9FrameContainer VP9::GetCurrentFrame() {
Vp9FrameContainer current_frame{}; Vp9FrameContainer current_frame{};
{ {
// gpu.SyncGuestHost(); epic, why? // gpu.SyncGuestHost(); epic, why?
current_frame.info = GetVp9PictureInfo(state); current_frame.info = GetVp9PictureInfo();
current_frame.bit_stream.resize(current_frame.info.bitstream_size); current_frame.bit_stream.resize(current_frame.info.bitstream_size);
host1x.GMMU().ReadBlock(state.frame_bitstream_offset, current_frame.bit_stream.data(), memory_manager.ReadBlock(regs.frame_bitstream_offset.Address(),
current_frame.info.bitstream_size); current_frame.bit_stream.data(),
current_frame.info.bitstream_size);
} }
if (!next_frame.bit_stream.empty()) { if (!next_frame.bit_stream.empty()) {
Vp9FrameContainer temp{ Vp9FrameContainer temp{
@ -742,8 +826,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q); uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q); uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
ASSERT(!current_frame_info.segment_enabled); WriteSegmentation(uncomp_writer);
uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width); const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
const s32 max_tile_cols_log2 = CalcMaxLog2TileCols(current_frame_info.frame_size.width); const s32 max_tile_cols_log2 = CalcMaxLog2TileCols(current_frame_info.frame_size.width);
@ -770,10 +853,29 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
return uncomp_writer; return uncomp_writer;
} }
void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) { std::tuple<u64, u64> VP9::GetProgressiveOffsets() {
auto luma{regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto chroma{regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
return {luma, chroma};
}
std::tuple<u64, u64, u64, u64> VP9::GetInterlacedOffsets() {
auto luma_top{regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto luma_bottom{
regs.surface_luma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto chroma_top{
regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
auto chroma_bottom{
regs.surface_chroma_offsets[static_cast<u32>(Vp9SurfaceIndex::Current)].Address()};
return {luma_top, luma_bottom, chroma_top, chroma_bottom};
}
std::span<const u8> VP9::ComposeFrame() {
vp9_hidden_frame = false;
std::vector<u8> bitstream; std::vector<u8> bitstream;
{ {
Vp9FrameContainer curr_frame = GetCurrentFrame(state); Vp9FrameContainer curr_frame = GetCurrentFrame();
current_frame_info = curr_frame.info; current_frame_info = curr_frame.info;
bitstream = std::move(curr_frame.bit_stream); bitstream = std::move(curr_frame.bit_stream);
} }
@ -786,12 +888,16 @@ void VP9::ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state) {
std::vector<u8> uncompressed_header = uncomp_writer.GetByteArray(); std::vector<u8> uncompressed_header = uncomp_writer.GetByteArray();
// Write headers and frame to buffer // Write headers and frame to buffer
frame.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size()); frame_scratch.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame.begin()); std::copy(uncompressed_header.begin(), uncompressed_header.end(), frame_scratch.begin());
std::copy(compressed_header.begin(), compressed_header.end(), std::copy(compressed_header.begin(), compressed_header.end(),
frame.begin() + uncompressed_header.size()); frame_scratch.begin() + uncompressed_header.size());
std::copy(bitstream.begin(), bitstream.end(), std::copy(bitstream.begin(), bitstream.end(),
frame.begin() + uncompressed_header.size() + compressed_header.size()); frame_scratch.begin() + uncompressed_header.size() + compressed_header.size());
vp9_hidden_frame = WasFrameHidden();
return GetFrameBytes();
} }
VpxRangeEncoder::VpxRangeEncoder() { VpxRangeEncoder::VpxRangeEncoder() {
@ -944,4 +1050,4 @@ const std::vector<u8>& VpxBitStreamWriter::GetByteArray() const {
return byte_array; return byte_array;
} }
} // namespace Tegra::Decoder } // namespace Tegra::Decoders

View File

@ -10,6 +10,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/stream.h" #include "common/stream.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codecs/vp9_types.h" #include "video_core/host1x/codecs/vp9_types.h"
#include "video_core/host1x/nvdec_common.h" #include "video_core/host1x/nvdec_common.h"
@ -19,7 +20,7 @@ namespace Host1x {
class Host1x; class Host1x;
} // namespace Host1x } // namespace Host1x
namespace Decoder { namespace Decoders {
/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the /// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
/// VP9 header bitstreams. /// VP9 header bitstreams.
@ -110,21 +111,32 @@ private:
std::vector<u8> byte_array; std::vector<u8> byte_array;
}; };
class VP9 { class VP9 final : public Decoder {
public: public:
explicit VP9(Host1x::Host1x& host1x); explicit VP9(Host1x::Host1x& host1x, const Host1x::NvdecCommon::NvdecRegisters& regs, s32 id,
~VP9(); Host1x::FrameQueue& frame_queue);
~VP9() override;
VP9(const VP9&) = delete; VP9(const VP9&) = delete;
VP9& operator=(const VP9&) = delete; VP9& operator=(const VP9&) = delete;
VP9(VP9&&) = default; VP9(VP9&&) = delete;
VP9& operator=(VP9&&) = delete; VP9& operator=(VP9&&) = delete;
/// Composes the VP9 frame from the GPU state information. [[nodiscard]] std::span<const u8> ComposeFrame() override;
/// Based on the official VP9 spec documentation
void ComposeFrame(const Host1x::NvdecCommon::NvdecRegisters& state);
std::tuple<u64, u64> GetProgressiveOffsets() override;
std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() override;
bool IsInterlaced() override {
return false;
}
std::string_view GetCurrentCodecName() const override {
return "VP9";
}
private:
/// Returns true if the most recent frame was a hidden frame. /// Returns true if the most recent frame was a hidden frame.
[[nodiscard]] bool WasFrameHidden() const { [[nodiscard]] bool WasFrameHidden() const {
return !current_frame_info.show_frame; return !current_frame_info.show_frame;
@ -132,10 +144,9 @@ public:
/// Returns a const span to the composed frame data. /// Returns a const span to the composed frame data.
[[nodiscard]] std::span<const u8> GetFrameBytes() const { [[nodiscard]] std::span<const u8> GetFrameBytes() const {
return frame; return frame_scratch;
} }
private:
/// Generates compressed header probability updates in the bitstream writer /// Generates compressed header probability updates in the bitstream writer
template <typename T, std::size_t N> template <typename T, std::size_t N>
void WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob, void WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
@ -167,23 +178,22 @@ private:
/// Write motion vector probability updates. 6.3.17 in the spec /// Write motion vector probability updates. 6.3.17 in the spec
void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob); void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
void WriteSegmentation(VpxBitStreamWriter& writer);
/// Returns VP9 information from NVDEC provided offset and size /// Returns VP9 information from NVDEC provided offset and size
[[nodiscard]] Vp9PictureInfo GetVp9PictureInfo( [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo();
const Host1x::NvdecCommon::NvdecRegisters& state);
/// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct
void InsertEntropy(u64 offset, Vp9EntropyProbs& dst); void InsertEntropy(u64 offset, Vp9EntropyProbs& dst);
/// Returns frame to be decoded after buffering /// Returns frame to be decoded after buffering
[[nodiscard]] Vp9FrameContainer GetCurrentFrame( [[nodiscard]] Vp9FrameContainer GetCurrentFrame();
const Host1x::NvdecCommon::NvdecRegisters& state);
/// Use NVDEC providied information to compose the headers for the current frame /// Use NVDEC providied information to compose the headers for the current frame
[[nodiscard]] std::vector<u8> ComposeCompressedHeader(); [[nodiscard]] std::vector<u8> ComposeCompressedHeader();
[[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader(); [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
Host1x::Host1x& host1x; Common::ScratchBuffer<u8> frame_scratch;
Common::ScratchBuffer<u8> frame;
std::array<s8, 4> loop_filter_ref_deltas{}; std::array<s8, 4> loop_filter_ref_deltas{};
std::array<s8, 2> loop_filter_mode_deltas{}; std::array<s8, 2> loop_filter_mode_deltas{};
@ -192,9 +202,11 @@ private:
std::array<Vp9EntropyProbs, 4> frame_ctxs{}; std::array<Vp9EntropyProbs, 4> frame_ctxs{};
bool swap_ref_indices{}; bool swap_ref_indices{};
Segmentation last_segmentation{};
PictureInfo current_picture_info{};
Vp9PictureInfo current_frame_info{}; Vp9PictureInfo current_frame_info{};
Vp9EntropyProbs prev_frame_probs{}; Vp9EntropyProbs prev_frame_probs{};
}; };
} // namespace Decoder } // namespace Decoders
} // namespace Tegra } // namespace Tegra

View File

@ -11,7 +11,14 @@
namespace Tegra { namespace Tegra {
namespace Decoder { namespace Decoders {
enum class Vp9SurfaceIndex : u32 {
Last = 0,
Golden = 1,
AltRef = 2,
Current = 3,
};
struct Vp9FrameDimensions { struct Vp9FrameDimensions {
s16 width; s16 width;
s16 height; s16 height;
@ -48,11 +55,13 @@ enum class TxMode {
}; };
struct Segmentation { struct Segmentation {
constexpr bool operator==(const Segmentation& rhs) const = default;
u8 enabled; u8 enabled;
u8 update_map; u8 update_map;
u8 temporal_update; u8 temporal_update;
u8 abs_delta; u8 abs_delta;
std::array<u32, 8> feature_mask; std::array<std::array<u8, 4>, 8> feature_enabled;
std::array<std::array<s16, 4>, 8> feature_data; std::array<std::array<s16, 4>, 8> feature_data;
}; };
static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size"); static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
@ -190,7 +199,17 @@ struct PictureInfo {
static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size"); static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
struct EntropyProbs { struct EntropyProbs {
INSERT_PADDING_BYTES_NOINIT(1024); ///< 0x0000 std::array<u8, 10 * 10 * 8> kf_bmode_prob; ///< 0x0000
std::array<u8, 10 * 10 * 1> kf_bmode_probB; ///< 0x0320
std::array<u8, 3> ref_pred_probs; ///< 0x0384
std::array<u8, 7> mb_segment_tree_probs; ///< 0x0387
std::array<u8, 3> segment_pred_probs; ///< 0x038E
std::array<u8, 4> ref_scores; ///< 0x0391
std::array<u8, 2> prob_comppred; ///< 0x0395
INSERT_PADDING_BYTES_NOINIT(9); ///< 0x0397
std::array<u8, 10 * 8> kf_uv_mode_prob; ///< 0x03A0
std::array<u8, 10 * 1> kf_uv_mode_probB; ///< 0x03F0
INSERT_PADDING_BYTES_NOINIT(6); ///< 0x03FA
std::array<u8, 28> inter_mode_prob; ///< 0x0400 std::array<u8, 28> inter_mode_prob; ///< 0x0400
std::array<u8, 4> intra_inter_prob; ///< 0x041C std::array<u8, 4> intra_inter_prob; ///< 0x041C
INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420 INSERT_PADDING_BYTES_NOINIT(80); ///< 0x0420
@ -302,5 +321,5 @@ ASSERT_POSITION(class_0_fr, 0x560);
ASSERT_POSITION(coef_probs, 0x5A0); ASSERT_POSITION(coef_probs, 0x5A0);
#undef ASSERT_POSITION #undef ASSERT_POSITION
}; // namespace Decoder }; // namespace Decoders
}; // namespace Tegra }; // namespace Tegra

Some files were not shown because too many files have changed in this diff Show More