Compare commits

..

6 Commits

Author SHA1 Message Date
039a84338a Android 228 2024-02-04 01:00:37 +00:00
f0eb935051 Merge yuzu-emu#12903 2024-02-04 01:00:36 +00:00
db727765f1 Merge yuzu-emu#12892 2024-02-04 01:00:36 +00:00
60dda4ed9a Merge yuzu-emu#12756 2024-02-04 01:00:36 +00:00
dde2c301f3 Merge yuzu-emu#12749 2024-02-04 01:00:36 +00:00
0bf2470bdf Merge yuzu-emu#12461 2024-02-04 01:00:36 +00:00
50 changed files with 854 additions and 1135 deletions

View File

@ -2,13 +2,9 @@
|----|----|----|----|----| |----|----|----|----|----|
| [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 | | [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 | | [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 | | [12756](https://github.com/yuzu-emu/yuzu-android//pull/12756) | [`b19285d4f`](https://github.com/yuzu-emu/yuzu-android//pull/12756/files) | general: applet multiprocess | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12873](https://github.com/yuzu-emu/yuzu-android//pull/12873) | [`023c3aa65`](https://github.com/yuzu-emu/yuzu-android//pull/12873/files) | GPU: Implement channel scheduling. | [FernandoS27](https://github.com/FernandoS27/) | Yes | | [12892](https://github.com/yuzu-emu/yuzu-android//pull/12892) | [`78f72b3bf`](https://github.com/yuzu-emu/yuzu-android//pull/12892/files) | cmif_serialization: enforce const for references | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12903](https://github.com/yuzu-emu/yuzu-android//pull/12903) | [`5be8121af`](https://github.com/yuzu-emu/yuzu-android//pull/12903/files) | shader_recompiler: use only ConstOffset for OpImageFetch | [liamwhite](https://github.com/liamwhite/) | Yes | | [12903](https://github.com/yuzu-emu/yuzu-android//pull/12903) | [`5be8121af`](https://github.com/yuzu-emu/yuzu-android//pull/12903/files) | shader_recompiler: use only ConstOffset for OpImageFetch | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12905](https://github.com/yuzu-emu/yuzu-android//pull/12905) | [`5eb5c9675`](https://github.com/yuzu-emu/yuzu-android//pull/12905/files) | nvnflinger: release buffers before presentation sleep | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12914](https://github.com/yuzu-emu/yuzu-android//pull/12914) | [`3a6d8ae2c`](https://github.com/yuzu-emu/yuzu-android//pull/12914/files) | VideoCore Refactor Part 1. | [FernandoS27](https://github.com/FernandoS27/) | Yes |
| [12915](https://github.com/yuzu-emu/yuzu-android//pull/12915) | [`504abbd6e`](https://github.com/yuzu-emu/yuzu-android//pull/12915/files) | dmnt: cheats: Update cheat vm to latest version | [german77](https://github.com/german77/) | Yes |
| [12920](https://github.com/yuzu-emu/yuzu-android//pull/12920) | [`62fc6d5c3`](https://github.com/yuzu-emu/yuzu-android//pull/12920/files) | android: Move JNI setup and helpers to common | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -2,8 +2,14 @@
# 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

@ -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 "android_common.h" #include "jni/android_common/android_common.h"
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -9,9 +9,7 @@
#include <jni.h> #include <jni.h>
#include "common/string_util.h" #include "common/string_util.h"
#include "id_cache.h" #include "jni/id_cache.h"
namespace Common::Android {
std::string GetJString(JNIEnv* env, jstring jstr) { std::string GetJString(JNIEnv* env, jstring jstr) {
if (!jstr) { if (!jstr) {
@ -20,8 +18,7 @@ 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), const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length);
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);
@ -39,27 +36,25 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
} }
double GetJDouble(JNIEnv* env, jobject jdouble) { double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->GetDoubleField(jdouble, GetDoubleValueField()); return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
} }
jobject ToJDouble(JNIEnv* env, double value) { jobject ToJDouble(JNIEnv* env, double value) {
return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value); return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
} }
s32 GetJInteger(JNIEnv* env, jobject jinteger) { s32 GetJInteger(JNIEnv* env, jobject jinteger) {
return env->GetIntField(jinteger, GetIntegerValueField()); return env->GetIntField(jinteger, IDCache::GetIntegerValueField());
} }
jobject ToJInteger(JNIEnv* env, s32 value) { jobject ToJInteger(JNIEnv* env, s32 value) {
return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value); return env->NewObject(IDCache::GetIntegerClass(), IDCache::GetIntegerConstructor(), value);
} }
bool GetJBoolean(JNIEnv* env, jobject jboolean) { bool GetJBoolean(JNIEnv* env, jobject jboolean) {
return env->GetBooleanField(jboolean, GetBooleanValueField()); return env->GetBooleanField(jboolean, IDCache::GetBooleanValueField());
} }
jobject ToJBoolean(JNIEnv* env, bool value) { jobject ToJBoolean(JNIEnv* env, bool value) {
return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value); return env->NewObject(IDCache::GetBooleanClass(), IDCache::GetBooleanConstructor(), value);
} }
} // namespace Common::Android

View File

@ -8,8 +8,6 @@
#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);
@ -22,5 +20,3 @@ 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 Common::Android::SoftwareKeyboard { namespace SoftwareKeyboard {
static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) { static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) {
JNIEnv* env = GetEnvForThread(); JNIEnv* env = IDCache::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 = GetEnvForThread(); JNIEnv* env = IDCache::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(GetEnvForThread()->CallStaticObjectMethod( data = ResultData::CreateFromFrontend(IDCache::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([&] {
GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline, IDCache::GetEnvForThread()->CallStaticVoidMethod(
ToJKeyboardParams(parameters)); s_software_keyboard_class, s_swkbd_execute_inline, 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,
static_cast<int>(m_current_text.size())); 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,
static_cast<int>(m_current_text.size())); 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 Common::Android::SoftwareKeyboard } // namespace SoftwareKeyboard

View File

@ -7,7 +7,7 @@
#include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/software_keyboard.h"
namespace Common::Android::SoftwareKeyboard { namespace 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 Common::Android::SoftwareKeyboard } // namespace SoftwareKeyboard
// Native function calls // Native function calls
extern "C" { extern "C" {

View File

@ -3,7 +3,6 @@
#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"
@ -61,8 +60,7 @@ void EmuWindow_Android::OnRemoveNfcTag() {
void EmuWindow_Android::OnFrameDisplayed() { void EmuWindow_Android::OnFrameDisplayed() {
if (!m_first_frame) { if (!m_first_frame) {
Common::Android::RunJNIOnFiber<void>( EmulationSession::GetInstance().OnEmulationStarted();
[&](JNIEnv* env) { EmulationSession::GetInstance().OnEmulationStarted(); });
m_first_frame = true; m_first_frame = true;
} }
} }

View File

@ -1,12 +1,13 @@
// 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 {
@ -78,7 +79,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(
Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read); GetJString(env, jpath), FileSys::OpenMode::Read);
if (!file) { if (!file) {
return false; return false;
} }
@ -103,31 +104,27 @@ 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 Common::Android::ToJString( return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title);
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 Common::Android::ToJString( return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId));
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 Common::Android::ToJString( return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer);
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 Common::Android::ToJString( return ToJString(env, GetRomMetadata(GetJString(env, jpath), jreload).version);
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(Common::Android::GetJString(env, jpath)).icon; auto icon_data = GetRomMetadata(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()));
@ -136,8 +133,7 @@ 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>( return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew);
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

@ -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 "id_cache.h" #include "jni/applets/software_keyboard.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 Common::Android { namespace IDCache {
JNIEnv* GetEnvForThread() { JNIEnv* GetEnvForThread() {
thread_local static struct OwnedEnv { thread_local static struct OwnedEnv {
@ -276,6 +276,8 @@ jfieldID GetBooleanValueField() {
return s_boolean_value_field; return s_boolean_value_field;
} }
} // namespace IDCache
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -391,7 +393,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
Common::Android::SoftwareKeyboard::InitJNI(env); SoftwareKeyboard::InitJNI(env);
return JNI_VERSION; return JNI_VERSION;
} }
@ -424,5 +426,3 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
} // namespace Common::Android

View File

@ -3,40 +3,20 @@
#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 Common::Android { namespace IDCache {
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 GetDiskCacheLoadProgress();
jmethodID GetExitEmulationActivity(); jmethodID GetExitEmulationActivity();
jmethodID GetDiskCacheLoadProgress();
jmethodID GetOnEmulationStarted(); jmethodID GetOnEmulationStarted();
jmethodID GetOnEmulationStopped(); jmethodID GetOnEmulationStopped();
jmethodID GetOnProgramChanged(); jmethodID GetOnProgramChanged();
@ -85,4 +65,4 @@ jclass GetBooleanClass();
jmethodID GetBooleanConstructor(); jmethodID GetBooleanConstructor();
jfieldID GetBooleanValueField(); jfieldID GetBooleanValueField();
} // namespace Common::Android } // namespace IDCache

View File

@ -20,8 +20,6 @@
#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"
@ -59,6 +57,8 @@
#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<Common::Android::SoftwareKeyboard::AndroidKeyboard>(); jauto android_keyboard = std::make_unique<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,39 +411,37 @@ void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
controller->Disconnect(); controller->Disconnect();
} }
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { 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 = Common::Android::GetEnvForThread(); JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(Common::Android::GetDiskCacheProgressClass(), env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
Common::Android::GetDiskCacheLoadProgress(), static_cast<jint>(stage), IDCache::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 = Common::Android::GetEnvForThread(); JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(), env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted());
Common::Android::GetOnEmulationStarted());
} }
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) { void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
JNIEnv* env = Common::Android::GetEnvForThread(); JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(), env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(),
Common::Android::GetOnEmulationStopped(), static_cast<jint>(result)); static_cast<jint>(result));
} }
void EmulationSession::ChangeProgram(std::size_t program_index) { void EmulationSession::ChangeProgram(std::size_t program_index) {
JNIEnv* env = Common::Android::GetEnvForThread(); JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(Common::Android::GetNativeLibraryClass(), env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnProgramChanged(),
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 = Common::Android::GetJString(env, jprogramId); auto program_id_string = GetJString(env, jprogramId);
try { try {
return std::stoull(program_id_string); return std::stoull(program_id_string);
} catch (...) { } catch (...) {
@ -493,7 +491,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(Common::Android::GetJString(env, j_directory)); Common::FS::SetAppDirectory(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,
@ -503,22 +501,21 @@ 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,
Common::Android::ToJDouble(env, max), ToJDouble(env, max), ToJDouble(env, progress));
Common::Android::ToJDouble(env, progress)); return GetJBoolean(env, jwasCancelled);
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(),
Common::Android::GetJString(env, j_file), callback)); 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 = Common::Android::GetJString(env, jupdatePath); std::string updatePath = 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));
@ -541,10 +538,8 @@ 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(
Common::Android::GetJString(env, hook_lib_dir), GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
Common::Android::GetJString(env, custom_driver_dir), GetJString(env, custom_driver_name), GetJString(env, file_redirect_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() {
@ -571,7 +566,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 = Common::Android::GetJString(env, j_hook_lib_dir); std::string hook_lib_dir = 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);
@ -592,10 +587,9 @@ 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 = env->NewObjectArray( jobjectArray j_driver_info =
2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string)); env->NewObjectArray(2, IDCache::GetStringClass(), ToJString(env, version_string));
env->SetObjectArrayElement(j_driver_info, 1, env->SetObjectArrayElement(j_driver_info, 1, ToJString(env, device.GetDriverName()));
Common::Android::ToJString(env, device.GetDriverName()));
return j_driver_info; return j_driver_info;
} }
@ -748,15 +742,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 Common::Android::ToJString(env, "NCE"); return ToJString(env, "NCE");
} }
return Common::Android::ToJString(env, "JIT"); return 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 Common::Android::ToJString( return ToJString(env,
env, EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor()); 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) {
@ -770,14 +764,13 @@ 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 = Common::Android::GetJString(env, j_path); const std::string path = 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(Common::Android::GetNativeLibraryClass(), env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
Common::Android::GetExitEmulationActivity(), IDCache::GetExitEmulationActivity(), static_cast<int>(result));
static_cast<int>(result));
} }
} }
@ -788,7 +781,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(Common::Android::GetJString(env, j_text)); const std::u16string input = Common::UTF8ToUTF16(GetJString(env, j_text));
EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input); EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardText(input);
} }
@ -822,16 +815,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 Common::Android::ToJString(env, ""); return 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 Common::Android::ToJString(env, ""); return ToJString(env, "");
} }
return Common::Android::ToJString(env, applet_nca->GetFullPath()); return 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,
@ -864,7 +857,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 = Common::Android::GetJString(env, jpath); const auto path = 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) {
@ -882,15 +875,14 @@ 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(), Common::Android::GetPatchClass(), nullptr); env->NewObjectArray(patches.size(), IDCache::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(
Common::Android::GetPatchClass(), Common::Android::GetPatchConstructor(), patch.enabled, IDCache::GetPatchClass(), IDCache::GetPatchConstructor(), patch.enabled,
Common::Android::ToJString(env, patch.name), ToJString(env, patch.name), ToJString(env, patch.version),
Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type), static_cast<jint>(patch.type), ToJString(env, std::to_string(patch.program_id)),
Common::Android::ToJString(env, std::to_string(patch.program_id)), ToJString(env, std::to_string(patch.title_id)));
Common::Android::ToJString(env, std::to_string(patch.title_id)));
env->SetObjectArrayElement(jpatchArray, i, jpatch); env->SetObjectArrayElement(jpatchArray, i, jpatch);
++i; ++i;
} }
@ -914,7 +906,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, Common::Android::GetJString(env, jname)); program_id, GetJString(env, jname));
} }
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
@ -925,18 +917,17 @@ 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,
Common::Android::ToJDouble(env, max), ToJDouble(env, max), ToJDouble(env, progress));
Common::Android::ToJDouble(env, progress)); return GetJBoolean(env, jwasCancelled);
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 = env->NewObjectArray(result.size(), Common::Android::GetStringClass(), jobjectArray jresult =
Common::Android::ToJString(env, "")); env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
for (size_t i = 0; i < result.size(); ++i) { for (size_t i = 0; i < result.size(); ++i) {
env->SetObjectArrayElement(jresult, i, Common::Android::ToJString(env, result[i])); env->SetObjectArrayElement(jresult, i, ToJString(env, result[i]));
} }
return jresult; return jresult;
} }
@ -948,20 +939,19 @@ 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,
Common::Android::ToJDouble(env, max), ToJDouble(env, max), ToJDouble(env, progress));
Common::Android::ToJDouble(env, progress)); return GetJBoolean(env, jwasCancelled);
return Common::Android::GetJBoolean(env, jwasCancelled);
}; };
auto& session = EmulationSession::GetInstance(); auto& session = EmulationSession::GetInstance();
return static_cast<jint>(ContentManager::VerifyGameContents( return static_cast<jint>(
session.System(), Common::Android::GetJString(env, jpath), callback)); ContentManager::VerifyGameContents(session.System(), 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 Common::Android::ToJString(env, ""); return ToJString(env, "");
} }
auto& system = EmulationSession::GetInstance().System(); auto& system = EmulationSession::GetInstance().System();
@ -978,7 +968,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 Common::Android::ToJString(env, user_save_data_path); return 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,
@ -991,13 +981,12 @@ 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 Common::Android::ToJString(env, user_save_data_root); return 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( EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath));
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);
Common::Android::SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard(); 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;
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; 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 = Common::Android::GetJString(env, jkey); auto key = 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 = Common::Android::GetJString(env, jfileName); auto file_name = 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 Common::Android::ToJString(env, ""); return ToJString(env, "");
} }
return Common::Android::ToJString(env, setting->GetValue(static_cast<bool>(needGlobal))); return 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(Common::Android::GetJString(env, value)); setting->SetValue(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 Common::Android::ToJString(env, ""); return ToJString(env, "");
} }
if (setting->PairedSetting() == nullptr) { if (setting->PairedSetting() == nullptr) {
return Common::Android::ToJString(env, ""); return ToJString(env, "");
} }
return Common::Android::ToJString(env, setting->PairedSetting()->GetLabel()); return 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 Common::Android::ToJString(env, setting->DefaultToString()); return ToJString(env, setting->DefaultToString());
} }
return Common::Android::ToJString(env, ""); return 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 = Common::Android::GetGameDirClass(); jclass gameDirClass = IDCache::GetGameDirClass();
jmethodID gameDirConstructor = Common::Android::GetGameDirConstructor(); jmethodID gameDirConstructor = IDCache::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 = env->NewObject( jobject jgameDir =
gameDirClass, gameDirConstructor, env->NewObject(gameDirClass, gameDirConstructor,
Common::Android::ToJString(env, AndroidSettings::values.game_dirs[i].path), 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 = Common::Android::GetGameDirClass(); jclass gameDirClass = IDCache::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 = Common::Android::GetJString(env, juriString); std::string uriString = 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 = Common::Android::GetGameDirClass(); jclass gameDirClass = IDCache::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 = Common::Android::GetJString(env, juriString); std::string uriString = 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,11 +323,9 @@ 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(), Common::Android::GetStringClass(), env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, ""));
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, env->SetObjectArrayElement(jdisabledAddonsArray, i, ToJString(env, disabledAddons[i]));
Common::Android::ToJString(env, disabledAddons[i]));
} }
return jdisabledAddonsArray; return jdisabledAddonsArray;
} }
@ -341,7 +339,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(Common::Android::GetJString(env, jaddon)); disabled_addons.push_back(GetJString(env, jaddon));
} }
Settings::values.disabled_addons[program_id] = disabled_addons; Settings::values.disabled_addons[program_id] = disabled_addons;
} }
@ -350,27 +348,26 @@ 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(),
Common::Android::GetOverlayControlDataClass(), nullptr); IDCache::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(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(), env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
Common::Android::ToJDouble(env, control_data.landscape_position.first), ToJDouble(env, control_data.landscape_position.first),
Common::Android::ToJDouble(env, control_data.landscape_position.second)); ToJDouble(env, control_data.landscape_position.second));
jobject jportraitPosition = jobject jportraitPosition =
env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(), env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
Common::Android::ToJDouble(env, control_data.portrait_position.first), ToJDouble(env, control_data.portrait_position.first),
Common::Android::ToJDouble(env, control_data.portrait_position.second)); ToJDouble(env, control_data.portrait_position.second));
jobject jfoldablePosition = jobject jfoldablePosition =
env->NewObject(Common::Android::GetPairClass(), Common::Android::GetPairConstructor(), env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
Common::Android::ToJDouble(env, control_data.foldable_position.first), ToJDouble(env, control_data.foldable_position.first),
Common::Android::ToJDouble(env, control_data.foldable_position.second)); ToJDouble(env, control_data.foldable_position.second));
jobject jcontrolData = jobject jcontrolData = env->NewObject(
env->NewObject(Common::Android::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
Common::Android::GetOverlayControlDataConstructor(), ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
Common::Android::ToJString(env, control_data.id), control_data.enabled, jportraitPosition, jfoldablePosition);
jlandscapePosition, jportraitPosition, jfoldablePosition);
env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData); env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
} }
return joverlayControlDataArray; return joverlayControlDataArray;
@ -387,41 +384,33 @@ 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>(env->GetObjectField( jstring jidString = static_cast<jstring>(
joverlayControlData, Common::Android::GetOverlayControlDataIdField())); env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
bool enabled = static_cast<bool>(env->GetBooleanField( bool enabled = static_cast<bool>(env->GetBooleanField(
joverlayControlData, Common::Android::GetOverlayControlDataEnabledField())); joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
jobject jlandscapePosition = env->GetObjectField( jobject jlandscapePosition = env->GetObjectField(
joverlayControlData, Common::Android::GetOverlayControlDataLandscapePositionField()); joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
std::pair<double, double> landscape_position = std::make_pair( std::pair<double, double> landscape_position = std::make_pair(
Common::Android::GetJDouble( GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
env, env->GetObjectField(jlandscapePosition, Common::Android::GetPairFirstField())), GetJDouble(env,
Common::Android::GetJDouble( env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
env,
env->GetObjectField(jlandscapePosition, Common::Android::GetPairSecondField())));
jobject jportraitPosition = env->GetObjectField( jobject jportraitPosition = env->GetObjectField(
joverlayControlData, Common::Android::GetOverlayControlDataPortraitPositionField()); joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
std::pair<double, double> portrait_position = std::make_pair( std::pair<double, double> portrait_position = std::make_pair(
Common::Android::GetJDouble( GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
env, env->GetObjectField(jportraitPosition, Common::Android::GetPairFirstField())), GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
Common::Android::GetJDouble(
env,
env->GetObjectField(jportraitPosition, Common::Android::GetPairSecondField())));
jobject jfoldablePosition = env->GetObjectField( jobject jfoldablePosition = env->GetObjectField(
joverlayControlData, Common::Android::GetOverlayControlDataFoldablePositionField()); joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
std::pair<double, double> foldable_position = std::make_pair( std::pair<double, double> foldable_position = std::make_pair(
Common::Android::GetJDouble( GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
env, env->GetObjectField(jfoldablePosition, Common::Android::GetPairFirstField())), GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
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{
Common::Android::GetJString(env, jidString), enabled, landscape_position, GetJString(env, jidString), enabled, landscape_position, portrait_position,
portrait_position, foldable_position}); foldable_position});
} }
} }

View File

@ -1,30 +1,31 @@
// 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, "{}", Common::Android::GetJString(env, jmessage)); LOG_DEBUG(Frontend, "{}", 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, "{}", Common::Android::GetJString(env, jmessage)); LOG_WARNING(Frontend, "{}", 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, "{}", Common::Android::GetJString(env, jmessage)); LOG_INFO(Frontend, "{}", 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, "{}", Common::Android::GetJString(env, jmessage)); LOG_ERROR(Frontend, "{}", 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, "{}", Common::Android::GetJString(env, jmessage)); LOG_CRITICAL(Frontend, "{}", GetJString(env, jmessage));
} }
} // extern "C" } // extern "C"

View File

@ -107,8 +107,6 @@ 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
@ -123,7 +121,6 @@ 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
@ -182,15 +179,9 @@ endif()
if(ANDROID) if(ANDROID)
target_sources(common target_sources(common
PUBLIC PRIVATE
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,38 +1,63 @@
// 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;
s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory", #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
"(Ljava/lang/String;)Ljava/lang/String;"); F(JMethodID, JMethodName, Signature)
s_get_filename = env->GetStaticMethodID(native_library, "getFilename", #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
"(Ljava/lang/String;)Ljava/lang/String;"); F(JMethodID, JMethodName, Signature)
s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J"); #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z"); F(JMethodID, JMethodName, Signature)
s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z"); #define F(JMethodID, JMethodName, Signature) \
s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri", JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
"(Ljava/lang/String;Ljava/lang/String;)I"); ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
} }
void UnRegisterCallbacks() { void UnRegisterCallbacks() {
s_get_parent_directory = nullptr; #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
s_get_filename = nullptr; #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
s_get_size = nullptr; #define F(JMethodID) JMethodID = nullptr;
s_is_directory = nullptr; ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
s_file_exists = nullptr; ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
s_open_content_uri = nullptr; #undef F
#undef FS
#undef FR
#undef FH
} }
bool IsContentUri(const std::string& path) { bool IsContentUri(const std::string& path) {
@ -45,7 +70,7 @@ bool IsContentUri(const std::string& path) {
} }
int OpenContentUri(const std::string& filepath, OpenMode openmode) { int OpenContentUri(const std::string& filepath, OpenMode openmode) {
if (s_open_content_uri == nullptr) if (open_content_uri == nullptr)
return -1; return -1;
const char* mode = ""; const char* mode = "";
@ -57,66 +82,50 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
UNIMPLEMENTED(); UNIMPLEMENTED();
return -1; return -1;
} }
auto env = Common::Android::GetEnvForThread(); auto env = GetEnvForThread();
jstring j_filepath = Common::Android::ToJString(env, filepath); jstring j_filepath = env->NewStringUTF(filepath.c_str());
jstring j_mode = Common::Android::ToJString(env, mode); jstring j_mode = env->NewStringUTF(mode);
return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode); return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode);
} }
std::uint64_t GetSize(const std::string& filepath) { #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
if (s_get_size == nullptr) { F(FunctionName, ReturnValue, JMethodID, Caller)
return 0; #define F(FunctionName, ReturnValue, JMethodID, Caller) \
ReturnValue FunctionName(const std::string& filepath) { \
if (JMethodID == nullptr) { \
return 0; \
} \
auto env = GetEnvForThread(); \
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
return env->Caller(native_library, JMethodID, j_filepath); \
} }
auto env = Common::Android::GetEnvForThread(); ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
return static_cast<u64>(env->CallStaticLongMethod( #undef F
native_library, s_get_size, #undef FR
Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)));
}
bool IsDirectory(const std::string& filepath) { #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
if (s_is_directory == nullptr) { F(FunctionName, JMethodID, Caller)
return 0; #define F(FunctionName, JMethodID, Caller) \
std::string FunctionName(const std::string& filepath) { \
if (JMethodID == nullptr) { \
return 0; \
} \
auto env = GetEnvForThread(); \
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
jstring j_return = \
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
if (!j_return) { \
return {}; \
} \
const jchar* jchars = env->GetStringChars(j_return, nullptr); \
const jsize length = env->GetStringLength(j_return); \
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
const std::string converted_string = Common::UTF16ToUTF8(string_view); \
env->ReleaseStringChars(j_return, jchars); \
return converted_string; \
} }
auto env = Common::Android::GetEnvForThread(); ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
return env->CallStaticBooleanMethod( #undef F
native_library, s_is_directory, #undef FH
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,17 +7,38 @@
#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;
static jmethodID s_get_parent_directory; #define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
static jmethodID s_get_filename; #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
static jmethodID s_get_size; #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
static jmethodID s_is_directory; #define F(JMethodID) static jmethodID JMethodID = nullptr;
static jmethodID s_file_exists; ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
static jmethodID s_open_content_uri; ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
enum class OpenMode { enum class OpenMode {
Read, Read,
@ -36,11 +57,24 @@ void UnRegisterCallbacks();
bool IsContentUri(const std::string& path); bool IsContentUri(const std::string& path);
int OpenContentUri(const std::string& filepath, OpenMode openmode); #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
std::uint64_t GetSize(const std::string& filepath); F(FunctionName, Parameters, ReturnValue)
bool IsDirectory(const std::string& filepath); #define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters;
bool Exists(const std::string& filepath); ANDROID_STORAGE_FUNCTIONS(FS)
std::string GetParentDirectory(const std::string& filepath); #undef F
std::string GetFilename(const std::string& filepath); #undef FS
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
F(FunctionName, ReturnValue)
#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
#undef F
#undef FR
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
#undef F
#undef FH
} // namespace Common::FS::Android } // namespace Common::FS::Android

View File

@ -1,73 +0,0 @@
// 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

View File

@ -1,304 +0,0 @@
// 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

@ -118,17 +118,23 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
switch (mode) { switch (mode) {
case LibraryAppletMode::AllForeground: case LibraryAppletMode::AllForeground:
case LibraryAppletMode::NoUi: case LibraryAppletMode::NoUi:
case LibraryAppletMode::PartialForeground:
case LibraryAppletMode::PartialForegroundIndirectDisplay:
applet->hid_registration.EnableAppletToGetInput(true);
applet->focus_state = FocusState::InFocus; applet->focus_state = FocusState::InFocus;
applet->hid_registration.EnableAppletToGetInput(true);
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->hid_registration.EnableAppletToGetInput(false);
applet->focus_state = FocusState::NotInFocus;
applet->system_buffer_manager.SetWindowVisibility(false); applet->system_buffer_manager.SetWindowVisibility(false);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoBackground); applet->focus_state = FocusState::NotInFocus;
applet->hid_registration.EnableAppletToGetInput(false);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break;
case LibraryAppletMode::PartialForeground:
case LibraryAppletMode::PartialForegroundIndirectDisplay:
default:
applet->focus_state = FocusState::Background;
applet->hid_registration.EnableAppletToGetInput(true);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
break; break;
} }

View File

@ -38,8 +38,7 @@ bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel:
} }
Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None; Nvnflinger::LayerBlending blending = Nvnflinger::LayerBlending::None;
if (mode == LibraryAppletMode::PartialForeground || if (mode == LibraryAppletMode::PartialForeground) {
mode == LibraryAppletMode::PartialForegroundIndirectDisplay) {
blending = Nvnflinger::LayerBlending::Coverage; blending = Nvnflinger::LayerBlending::Coverage;
} }

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->focus_state = FocusState::InFocus;
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->focus_state = FocusState::InFocus;
} 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,21 +3,110 @@
#include <mutex> #include <mutex>
#include "common/range_sets.h" #include <boost/container/small_vector.hpp>
#include "common/range_sets.inc" #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 "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) : m_device_memory{host1x.MemoryManager()} {} HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {}
~HeapMapperInternal() = default; ~HeapMapperInternal() = default;
Common::RangeSet<VAddr> m_temporary_set; template <typename Func>
Common::OverlapRangeSet<VAddr> m_mapped_ranges; void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size,
Tegra::MaxwellDeviceMemoryManager& m_device_memory; Func&& func) {
std::mutex m_guard; const DAddr start_address = cpu_addr;
const DAddr end_address = start_address + size;
const IntervalType search_interval{start_address, end_address};
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
auto& inter = it->first;
DAddr inter_addr_end = inter.upper();
DAddr inter_addr = inter.lower();
if (inter_addr_end > end_address) {
inter_addr_end = end_address;
}
if (inter_addr < start_address) {
inter_addr = start_address;
}
func(inter_addr, inter_addr_end, it->second);
}
}
void RemoveEachInOverlapCounter(OverlapCounter& current_range,
const IntervalType search_interval, int subtract_value) {
bool any_removals = false;
current_range.add(std::make_pair(search_interval, subtract_value));
do {
any_removals = false;
auto it = current_range.lower_bound(search_interval);
if (it == current_range.end()) {
return;
}
auto end_it = current_range.upper_bound(search_interval);
for (; it != end_it; it++) {
if (it->second <= 0) {
any_removals = true;
current_range.erase(it);
break;
}
}
} while (any_removals);
}
IntervalSet base_set;
OverlapCounter mapping_overlaps;
Tegra::MaxwellDeviceMemoryManager& device_memory;
std::mutex guard;
}; };
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,
@ -27,48 +116,60 @@ HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size,
} }
HeapMapper::~HeapMapper() { HeapMapper::~HeapMapper() {
// Unmap whatever has been mapped. m_internal->device_memory.Unmap(m_daddress, m_size);
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->m_guard); std::scoped_lock lk(m_internal->guard);
// Add the mapping range to a temporary range set. m_internal->base_set.clear();
m_internal->m_temporary_set.Clear(); const IntervalType interval{start, start + size};
m_internal->m_temporary_set.Add(start, size); m_internal->base_set.insert(interval);
m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
// Remove anything that's already mapped from the temporary range set. [this](VAddr start_addr, VAddr end_addr, int) {
m_internal->m_mapped_ranges.ForEachInRange( const IntervalType other{start_addr, end_addr};
start, size, [this](VAddr start_addr, VAddr end_addr, s32) { m_internal->base_set.subtract(other);
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();
// Map anything that has not been mapped yet. auto end_it = m_internal->base_set.end();
m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) { for (; it != end_it; it++) {
const size_t sub_size = end_addr - start_addr; const VAddr inter_addr_end = it->upper();
const size_t offset = start_addr - m_vaddress; const VAddr inter_addr = it->lower();
m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid); const size_t offset = inter_addr - m_vaddress;
}); const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size,
// Add the mapping range to the split map, to register the map and overlaps. m_asid);
m_internal->m_mapped_ranges.Add(start, size); }
m_internal->m_temporary_set.Clear(); }
return m_daddress + static_cast<DAddr>(start - m_vaddress); m_internal->mapping_overlaps += std::make_pair(interval, 1);
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->m_guard); std::scoped_lock lk(m_internal->guard);
m_internal->base_set.clear();
// Just subtract the range and whatever is deleted, unmap it. m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size,
m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) { [this](VAddr start_addr, VAddr end_addr, int value) {
const size_t sub_size = end_addr - start_addr; if (value <= 1) {
const size_t offset = start_addr - m_vaddress; const IntervalType other{start_addr, end_addr};
m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size); m_internal->base_set.insert(other);
}); }
});
if (!m_internal->base_set.empty()) {
auto it = m_internal->base_set.begin();
auto end_it = m_internal->base_set.end();
for (; it != end_it; it++) {
const VAddr inter_addr_end = it->upper();
const VAddr inter_addr = it->lower();
const size_t offset = inter_addr - m_vaddress;
const size_t sub_size = inter_addr_end - inter_addr;
m_internal->device_memory.Unmap(m_daddress + offset, sub_size);
}
}
const IntervalType to_remove{start, start + size};
m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1);
m_internal->base_set.clear();
} }
} // namespace Service::Nvidia::NvCore } // namespace Service::Nvidia::NvCore

View File

@ -7,6 +7,7 @@
#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"
@ -45,9 +46,31 @@ 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) { Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance) {
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;
@ -120,30 +143,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display,
MicroProfileFlip(); MicroProfileFlip();
// Advance by at least one frame. // Advance by at least one frame.
const u32 frame_advance = swap_interval.value_or(1); return 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); Nvidia::Devices::nvdisp_disp0& nvdisp, u32 frame_advance);
void RemoveLayerLocked(VI::Display& display, LayerId layer_id); void RemoveLayerLocked(VI::Display& display, LayerId layer_id);
private: private:

View File

@ -292,7 +292,8 @@ 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

@ -9,7 +9,6 @@
#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"
@ -86,12 +85,8 @@ VAddr StandardVmCallbacks::SanitizeAddress(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)) {
(in < metadata.alias_extents.base || LOG_ERROR(CheatEngine,
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 "
@ -216,14 +211,16 @@ 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

@ -37,7 +37,7 @@ private:
VAddr SanitizeAddress(VAddr address) const; VAddr SanitizeAddress(VAddr address) const;
const CheatProcessMetadata& metadata; const CheatProcessMetadata& metadata;
Core::System& system; 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 aslr_extents{}; MemoryRegionExtents address_space_extents{};
std::array<u8, 0x20> main_nso_build_id{}; std::array<u8, 0x20> main_nso_build_id{};
}; };

View File

@ -322,9 +322,8 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
} break; } break;
case CheatVmOpcodeType::EndConditionalBlock: { case CheatVmOpcodeType::EndConditionalBlock: {
// 20000000 // 20000000
opcode.opcode = EndConditionalOpcode{ // There's actually nothing left to process here!
.is_else = ((first_dword >> 24) & 0xf) == 1, opcode.opcode = EndConditionalOpcode{};
};
} break; } break;
case CheatVmOpcodeType::ControlLoop: { case CheatVmOpcodeType::ControlLoop: {
// 300R0000 VVVVVVVV // 300R0000 VVVVVVVV
@ -556,18 +555,6 @@ 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
@ -634,7 +621,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
return valid; return valid;
} }
void DmntCheatVm::SkipConditionalBlock(bool is_if) { void DmntCheatVm::SkipConditionalBlock() {
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;
@ -650,12 +637,8 @@ void DmntCheatVm::SkipConditionalBlock(bool is_if) {
// 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 (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) { } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
if (!end_cond->is_else) { condition_depth--;
condition_depth--;
} else if (is_if && condition_depth - 1 == desired_depth) {
break;
}
} }
} }
} else { } else {
@ -692,10 +675,6 @@ 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;
} }
} }
@ -703,6 +682,7 @@ 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;
@ -814,18 +794,13 @@ 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(true); SkipConditionalBlock();
} }
} else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) { } else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) {
if (end_cond->is_else) { // Decrement the condition depth.
/* Skip to the end of the conditional block. */ // We will assume, graciously, that mismatched conditional block ends are a nop.
this->SkipConditionalBlock(false); if (condition_depth > 0) {
} else { condition_depth--;
/* 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) {
@ -933,7 +908,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(true); SkipConditionalBlock();
} }
} 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)) {
@ -1141,7 +1116,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(true); SkipConditionalBlock();
} }
} 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)) {
@ -1203,10 +1178,6 @@ 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;

View File

@ -42,16 +42,12 @@ 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 {
@ -135,9 +131,7 @@ struct BeginConditionalOpcode {
VmInt value{}; VmInt value{};
}; };
struct EndConditionalOpcode { struct EndConditionalOpcode {};
bool is_else;
};
struct ControlLoopOpcode { struct ControlLoopOpcode {
bool start_loop{}; bool start_loop{};
@ -228,10 +222,6 @@ 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{};
@ -254,8 +244,8 @@ struct CheatVmOpcode {
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode,
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction> UnrecognizedInstruction>
opcode{}; opcode{};
}; };
@ -306,7 +296,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(bool is_if); void SkipConditionalBlock();
void ResetState(); void ResetState();
// For implementing the DebugLog opcode. // For implementing the DebugLog opcode.

View File

@ -274,6 +274,7 @@ 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,7 +7,6 @@
#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"
@ -21,7 +20,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{}));
gpu_modified_ranges.Clear(); common_ranges.clear();
inline_buffer_id = NULL_BUFFER_ID; inline_buffer_id = NULL_BUFFER_ID;
if (!runtime.CanReportMemoryUsage()) { if (!runtime.CanReportMemoryUsage()) {
@ -44,9 +43,6 @@ 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;
@ -100,17 +96,20 @@ void BufferCache<P>::TickFrame() {
++frame_tick; ++frame_tick;
delayed_destruction_ring.Tick(); delayed_destruction_ring.Tick();
for (auto& buffer : async_buffers_death_ring) { if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
runtime.FreeDeferredStagingBuffer(buffer); for (auto& buffer : async_buffers_death_ring) {
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)) {
ClearDownload(device_addr, size); const IntervalType subtract_interval{device_addr, device_addr + size};
gpu_modified_ranges.Subtract(device_addr, size); ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
} }
memory_tracker.MarkRegionAsCpuModified(device_addr, size); memory_tracker.MarkRegionAsCpuModified(device_addr, size);
} }
@ -175,11 +174,11 @@ void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
} }
template <class P> template <class P>
void BufferCache<P>::ClearDownload(DAddr device_addr, u64 size) { void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
async_downloads.DeleteAll(device_addr, size); RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
uncommitted_gpu_modified_ranges.Subtract(device_addr, size); uncommitted_ranges.subtract(subtract_interval);
for (auto& interval_set : committed_gpu_modified_ranges) { for (auto& interval_set : committed_ranges) {
interval_set.Subtract(device_addr, size); interval_set.subtract(subtract_interval);
} }
} }
@ -196,7 +195,8 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
return false; return false;
} }
ClearDownload(*cpu_dest_address, amount); const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
ClearDownload(subtract_interval);
BufferId buffer_a; BufferId buffer_a;
BufferId buffer_b; BufferId buffer_b;
@ -215,20 +215,21 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
.size = amount, .size = amount,
}}; }};
boost::container::small_vector<std::pair<DAddr, size_t>, 4> tmp_intervals; boost::container::small_vector<IntervalType, 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;
tmp_intervals.push_back({new_base_address, size}); const IntervalType add_interval{new_base_address, new_base_address + size};
uncommitted_gpu_modified_ranges.Add(new_base_address, size); tmp_intervals.push_back(add_interval);
uncommitted_ranges.add(add_interval);
}; };
gpu_modified_ranges.ForEachInRange(*cpu_src_address, amount, mirror); ForEachInRangeSet(common_ranges, *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.
gpu_modified_ranges.Subtract(*cpu_dest_address, amount); common_ranges.subtract(subtract_interval);
const bool has_new_downloads = tmp_intervals.size() != 0; const bool has_new_downloads = tmp_intervals.size() != 0;
for (const auto& pair : tmp_intervals) { for (const IntervalType& add_interval : tmp_intervals) {
gpu_modified_ranges.Add(pair.first, pair.second); common_ranges.add(add_interval);
} }
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);
@ -256,8 +257,9 @@ 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);
ClearDownload(*cpu_dst_address, size); const IntervalType subtract_interval{*cpu_dst_address, *cpu_dst_address + size};
gpu_modified_ranges.Subtract(*cpu_dst_address, size); ClearDownload(subtract_interval);
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];
@ -298,11 +300,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: {
const DAddr device_addr_start = Common::AlignDown(device_addr, 64); DAddr device_addr_start = Common::AlignDown(device_addr, 64);
const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64); DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
const size_t new_size = device_addr_end - device_addr_start; IntervalType interval{device_addr_start, device_addr_end};
ClearDownload(device_addr_start, new_size); ClearDownload(interval);
gpu_modified_ranges.Subtract(device_addr_start, new_size); common_ranges.subtract(interval);
break; break;
} }
default: default:
@ -502,40 +504,46 @@ void BufferCache<P>::FlushCachedWrites() {
template <class P> template <class P>
bool BufferCache<P>::HasUncommittedFlushes() const noexcept { bool BufferCache<P>::HasUncommittedFlushes() const noexcept {
return !uncommitted_gpu_modified_ranges.Empty() || !committed_gpu_modified_ranges.empty(); return !uncommitted_ranges.empty() || !committed_ranges.empty();
} }
template <class P> template <class P>
void BufferCache<P>::AccumulateFlushes() { void BufferCache<P>::AccumulateFlushes() {
if (uncommitted_gpu_modified_ranges.Empty()) { if (uncommitted_ranges.empty()) {
return; return;
} }
committed_gpu_modified_ranges.emplace_back(std::move(uncommitted_gpu_modified_ranges)); committed_ranges.emplace_back(std::move(uncommitted_ranges));
} }
template <class P> template <class P>
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept { bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
return (!async_buffers.empty() && async_buffers.front().has_value()); if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
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_gpu_modified_ranges.empty()) { if (committed_ranges.empty()) {
async_buffers.emplace_back(std::optional<Async_Buffer>{}); if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return; return;
} }
MICROPROFILE_SCOPE(GPU_DownloadMemory); MICROPROFILE_SCOPE(GPU_DownloadMemory);
auto it = committed_gpu_modified_ranges.begin(); auto it = committed_ranges.begin();
while (it != committed_gpu_modified_ranges.end()) { while (it != committed_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_gpu_modified_ranges.end()) { while (next_it != committed_ranges.end()) {
next_it->ForEach([&current_intervals](DAddr start, DAddr end) { for (auto& interval : *next_it) {
current_intervals.Subtract(start, end - start); current_intervals.subtract(interval);
}); }
next_it++; next_it++;
} }
it++; it++;
@ -544,10 +552,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 Common::RangeSet<DAddr>& range_set : committed_gpu_modified_ranges) { for (const IntervalSet& intervals : committed_ranges) {
range_set.ForEach([&](DAddr interval_lower, DAddr interval_upper) { for (auto& interval : intervals) {
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();
@ -575,35 +583,77 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
largest_copy = std::max(largest_copy, new_size); largest_copy = std::max(largest_copy, new_size);
}; };
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, ForEachInRangeSet(common_ranges, device_addr_out, range_size, add_download);
add_download);
}); });
}); });
}); }
} }
committed_gpu_modified_ranges.clear(); committed_ranges.clear();
if (downloads.empty()) { if (downloads.empty()) {
async_buffers.emplace_back(std::optional<Async_Buffer>{}); if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return; return;
} }
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true); if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
boost::container::small_vector<BufferCopy, 4> normalized_copies; auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
runtime.PreCopyBarrier(); boost::container::small_vector<BufferCopy, 4> normalized_copies;
for (auto& [copy, buffer_id] : downloads) { IntervalSet new_async_range{};
copy.dst_offset += download_staging.offset; runtime.PreCopyBarrier();
const std::array copies{copy}; for (auto& [copy, buffer_id] : downloads) {
BufferCopy second_copy{copy}; copy.dst_offset += download_staging.offset;
Buffer& buffer = slot_buffers[buffer_id]; const std::array copies{copy};
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset; BufferCopy second_copy{copy};
const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset); Buffer& buffer = slot_buffers[buffer_id];
async_downloads.Add(orig_device_addr, copy.size); second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
buffer.MarkUsage(copy.src_offset, copy.size); DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); const IntervalType base_interval{orig_device_addr, orig_device_addr + copy.size};
normalized_copies.push_back(second_copy); async_downloads += std::make_pair(base_interval, 1);
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>
@ -626,31 +676,37 @@ void BufferCache<P>::PopAsyncBuffers() {
async_buffers.pop_front(); async_buffers.pop_front();
return; return;
} }
auto& downloads = pending_downloads.front(); if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto& async_buffer = async_buffers.front(); auto& downloads = pending_downloads.front();
u8* base = async_buffer->mapped_span.data(); auto& async_buffer = async_buffers.front();
const size_t base_offset = async_buffer->offset; u8* base = async_buffer->mapped_span.data();
for (const auto& copy : downloads) { const size_t base_offset = async_buffer->offset;
const DAddr device_addr = static_cast<DAddr>(copy.src_offset); for (const auto& copy : downloads) {
const u64 dst_offset = copy.dst_offset - base_offset; const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
const u8* read_mapped_memory = base + dst_offset; const u64 dst_offset = copy.dst_offset - base_offset;
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) { const u8* read_mapped_memory = base + dst_offset;
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr], ForEachInOverlapCounter(
end - start); async_downloads, device_addr, copy.size, [&](DAddr start, DAddr end, int count) {
}); device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) { end - start);
gpu_modified_ranges.Subtract(start, end - start); if (count == 1) {
}); 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;
gpu_modified_ranges.ForEachInRange(addr, size, [&](DAddr, DAddr) { is_dirty = true; }); ForEachInRangeSet(common_ranges, addr, size, [&](DAddr, DAddr) { is_dirty = true; });
return is_dirty; return is_dirty;
} }
@ -1264,8 +1320,10 @@ 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);
uncommitted_gpu_modified_ranges.Add(device_addr, size); const IntervalType base_interval{device_addr, device_addr + size};
common_ranges.add(base_interval);
uncommitted_ranges.add(base_interval);
} }
template <class P> template <class P>
@ -1545,8 +1603,9 @@ 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) {
ClearDownload(dest_address, copy_size); const IntervalType subtract_interval{dest_address, dest_address + copy_size};
gpu_modified_ranges.Subtract(dest_address, copy_size); ClearDownload(subtract_interval);
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];
@ -1596,9 +1655,12 @@ 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);
}; };
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download); const DAddr start_address = device_addr_out;
ClearDownload(device_addr_out, range_size); const DAddr end_address = start_address + range_size;
gpu_modified_ranges.Subtract(device_addr_out, range_size); ForEachInRangeSet(common_ranges, start_address, range_size, add_download);
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,15 +13,25 @@
#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"
@ -31,15 +41,21 @@
#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 = Common::SlotId; using BufferId = SlotId;
using VideoCore::Surface::PixelFormat; using VideoCore::Surface::PixelFormat;
using namespace Common::Literals; using namespace Common::Literals;
@ -168,6 +184,7 @@ 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;
@ -185,6 +202,34 @@ 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;
@ -195,8 +240,6 @@ 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);
@ -336,6 +379,75 @@ 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);
@ -440,14 +552,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(DAddr base_addr, u64 size); void ClearDownload(IntervalType subtract_interval);
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;
Common::SlotVector<Buffer> slot_buffers; 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{};
@ -455,12 +567,13 @@ private:
u32 last_index_count = 0; u32 last_index_count = 0;
MemoryTracker memory_tracker; MemoryTracker memory_tracker;
Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges; IntervalSet uncommitted_ranges;
Common::RangeSet<DAddr> gpu_modified_ranges; IntervalSet common_ranges;
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges; IntervalSet cached_ranges;
std::deque<IntervalSet> committed_ranges;
// Async Buffers // Async Buffers
Common::OverlapRangeSet<DAddr> async_downloads; OverlapCounter 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;

View File

@ -13,102 +13,20 @@ Scheduler::Scheduler(GPU& gpu_) : gpu{gpu_} {}
Scheduler::~Scheduler() = default; Scheduler::~Scheduler() = default;
void Scheduler::Init() {
master_control = Common::Fiber::ThreadToFiber();
}
void Scheduler::Resume() {
bool nothing_pending;
do {
nothing_pending = true;
current_fifo = nullptr;
{
std::unique_lock lk(scheduling_guard);
size_t num_iters = gpfifos.size();
for (size_t i = 0; i < num_iters; i++) {
size_t current_id = (current_fifo_rotation_id + i) % gpfifos.size();
auto& fifo = gpfifos[current_id];
if (!fifo.is_active) {
continue;
}
std::scoped_lock lk2(fifo.guard);
if (!fifo.pending_work.empty() || fifo.working.load(std::memory_order_acquire)) {
current_fifo = &fifo;
current_fifo_rotation_id = current_id;
nothing_pending = false;
break;
}
}
}
if (current_fifo) {
Common::Fiber::YieldTo(master_control, *current_fifo->context);
current_fifo = nullptr;
}
} while (!nothing_pending);
}
void Scheduler::Yield() {
ASSERT(current_fifo != nullptr);
Common::Fiber::YieldTo(current_fifo->context, *master_control);
gpu.BindChannel(current_fifo->bind_id);
}
void Scheduler::Push(s32 channel, CommandList&& entries) { void Scheduler::Push(s32 channel, CommandList&& entries) {
std::unique_lock lk(scheduling_guard); std::unique_lock lk(scheduling_guard);
auto it = channel_gpfifo_ids.find(channel); auto it = channels.find(channel);
ASSERT(it != channel_gpfifo_ids.end()); ASSERT(it != channels.end());
auto gpfifo_id = it->second; auto channel_state = it->second;
auto& fifo = gpfifos[gpfifo_id]; gpu.BindChannel(channel_state->bind_id);
{ channel_state->dma_pusher->Push(std::move(entries));
std::scoped_lock lk2(fifo.guard); channel_state->dma_pusher->DispatchCalls();
fifo.pending_work.emplace_back(std::move(entries));
}
}
void Scheduler::ChannelLoop(size_t gpfifo_id, s32 channel_id) {
gpu.BindChannel(channel_id);
auto& fifo = gpfifos[gpfifo_id];
while (true) {
auto* channel_state = channels[channel_id].get();
fifo.guard.lock();
while (!fifo.pending_work.empty()) {
{
fifo.working.store(true, std::memory_order_release);
CommandList&& entries = std::move(fifo.pending_work.front());
channel_state->dma_pusher->Push(std::move(entries));
fifo.pending_work.pop_front();
}
fifo.guard.unlock();
channel_state->dma_pusher->DispatchCalls();
fifo.guard.lock();
}
fifo.working.store(false, std::memory_order_relaxed);
fifo.guard.unlock();
Common::Fiber::YieldTo(fifo.context, *master_control);
gpu.BindChannel(channel_id);
}
} }
void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) { void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) {
s32 channel = new_channel->bind_id; s32 channel = new_channel->bind_id;
std::unique_lock lk(scheduling_guard); std::unique_lock lk(scheduling_guard);
channels.emplace(channel, new_channel); channels.emplace(channel, new_channel);
size_t new_fifo_id;
if (!free_fifos.empty()) {
new_fifo_id = free_fifos.front();
free_fifos.pop_front();
} else {
new_fifo_id = gpfifos.size();
gpfifos.emplace_back();
}
auto& new_fifo = gpfifos[new_fifo_id];
channel_gpfifo_ids[channel] = new_fifo_id;
new_fifo.is_active = true;
new_fifo.bind_id = channel;
new_fifo.pending_work.clear();
std::function<void()> callback = std::bind(&Scheduler::ChannelLoop, this, new_fifo_id, channel);
new_fifo.context = std::make_shared<Common::Fiber>(std::move(callback));
} }
} // namespace Tegra::Control } // namespace Tegra::Control

View File

@ -3,13 +3,10 @@
#pragma once #pragma once
#include <atomic>
#include <deque>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
#include "common/fiber.h"
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
namespace Tegra { namespace Tegra {
@ -25,36 +22,14 @@ public:
explicit Scheduler(GPU& gpu_); explicit Scheduler(GPU& gpu_);
~Scheduler(); ~Scheduler();
void Init();
void Resume();
void Yield();
void Push(s32 channel, CommandList&& entries); void Push(s32 channel, CommandList&& entries);
void DeclareChannel(std::shared_ptr<ChannelState> new_channel); void DeclareChannel(std::shared_ptr<ChannelState> new_channel);
private: private:
void ChannelLoop(size_t gpfifo_id, s32 channel_id);
std::unordered_map<s32, std::shared_ptr<ChannelState>> channels; std::unordered_map<s32, std::shared_ptr<ChannelState>> channels;
std::unordered_map<s32, size_t> channel_gpfifo_ids;
std::mutex scheduling_guard; std::mutex scheduling_guard;
std::shared_ptr<Common::Fiber> master_control;
struct GPFifoContext {
bool is_active;
std::shared_ptr<Common::Fiber> context;
std::deque<CommandList> pending_work;
std::atomic<bool> working{};
std::mutex guard;
s32 bind_id;
};
std::deque<GPFifoContext> gpfifos;
std::deque<size_t> free_fifos;
GPU& gpu; GPU& gpu;
size_t current_fifo_rotation_id{};
GPFifoContext* current_fifo{};
}; };
} // namespace Control } // namespace Control

View File

@ -6,7 +6,6 @@
#include "common/settings.h" #include "common/settings.h"
#include "core/core.h" #include "core/core.h"
#include "video_core/control/channel_state.h" #include "video_core/control/channel_state.h"
#include "video_core/control/scheduler.h"
#include "video_core/dma_pusher.h" #include "video_core/dma_pusher.h"
#include "video_core/engines/fermi_2d.h" #include "video_core/engines/fermi_2d.h"
#include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_compute.h"
@ -15,8 +14,6 @@
#include "video_core/engines/maxwell_dma.h" #include "video_core/engines/maxwell_dma.h"
#include "video_core/engines/puller.h" #include "video_core/engines/puller.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"
#include "video_core/host1x/syncpoint_manager.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
@ -63,14 +60,11 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
} }
void Puller::ProcessFenceActionMethod() { void Puller::ProcessFenceActionMethod() {
auto& syncpoint_manager = gpu.Host1x().GetSyncpointManager();
switch (regs.fence_action.op) { switch (regs.fence_action.op) {
case Puller::FenceOperation::Acquire: case Puller::FenceOperation::Acquire:
while (regs.fence_value > // UNIMPLEMENTED_MSG("Channel Scheduling pending.");
syncpoint_manager.GetGuestSyncpointValue(regs.fence_action.syncpoint_id)) { // WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
rasterizer->ReleaseFences(); rasterizer->ReleaseFences();
gpu.Scheduler().Yield();
}
break; break;
case Puller::FenceOperation::Increment: case Puller::FenceOperation::Increment:
rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id); rasterizer->SignalSyncPoint(regs.fence_action.syncpoint_id);

View File

@ -387,14 +387,6 @@ std::shared_ptr<Control::ChannelState> GPU::AllocateChannel() {
return impl->AllocateChannel(); return impl->AllocateChannel();
} }
Tegra::Control::Scheduler& GPU::Scheduler() {
return *impl->scheduler;
}
const Tegra::Control::Scheduler& GPU::Scheduler() const {
return *impl->scheduler;
}
void GPU::InitChannel(Control::ChannelState& to_init) { void GPU::InitChannel(Control::ChannelState& to_init) {
impl->InitChannel(to_init); impl->InitChannel(to_init);
} }

View File

@ -124,8 +124,7 @@ class KeplerCompute;
namespace Control { namespace Control {
struct ChannelState; struct ChannelState;
class Scheduler; }
} // namespace Control
namespace Host1x { namespace Host1x {
class Host1x; class Host1x;
@ -205,12 +204,6 @@ public:
/// Returns a const reference to the shader notifier. /// Returns a const reference to the shader notifier.
[[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const; [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const;
/// Returns GPU Channel Scheduler.
[[nodiscard]] Tegra::Control::Scheduler& Scheduler();
/// Returns GPU Channel Scheduler.
[[nodiscard]] const Tegra::Control::Scheduler& Scheduler() const;
[[nodiscard]] u64 GetTicks() const; [[nodiscard]] u64 GetTicks() const;
[[nodiscard]] bool IsAsync() const; [[nodiscard]] bool IsAsync() const;

View File

@ -34,15 +34,13 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
CommandDataContainer next; CommandDataContainer next;
scheduler.Init();
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
state.queue.PopWait(next, stop_token); state.queue.PopWait(next, stop_token);
if (stop_token.stop_requested()) { if (stop_token.stop_requested()) {
break; break;
} }
if (std::holds_alternative<SubmitListCommand>(next.data)) { if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
scheduler.Resume(); scheduler.Push(submit_list->channel, std::move(submit_list->entries));
} else if (std::holds_alternative<GPUTickCommand>(next.data)) { } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
system.GPU().TickWork(); system.GPU().TickWork();
} else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
@ -69,16 +67,14 @@ ThreadManager::~ThreadManager() = default;
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context, Core::Frontend::GraphicsContext& context,
Tegra::Control::Scheduler& scheduler_) { Tegra::Control::Scheduler& scheduler) {
rasterizer = renderer.ReadRasterizer(); rasterizer = renderer.ReadRasterizer();
scheduler = &scheduler_;
thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context), thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
std::ref(scheduler_), std::ref(state)); std::ref(scheduler), std::ref(state));
} }
void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) {
scheduler->Push(channel, std::move(entries)); PushCommand(SubmitListCommand(channel, std::move(entries)));
PushCommand(SubmitListCommand());
} }
void ThreadManager::FlushRegion(DAddr addr, u64 size) { void ThreadManager::FlushRegion(DAddr addr, u64 size) {

View File

@ -36,7 +36,13 @@ class RendererBase;
namespace VideoCommon::GPUThread { namespace VideoCommon::GPUThread {
/// Command to signal to the GPU thread that a command list is ready for processing /// Command to signal to the GPU thread that a command list is ready for processing
struct SubmitListCommand final {}; struct SubmitListCommand final {
explicit SubmitListCommand(s32 channel_, Tegra::CommandList&& entries_)
: channel{channel_}, entries{std::move(entries_)} {}
s32 channel;
Tegra::CommandList entries;
};
/// Command to signal to the GPU thread to flush a region /// Command to signal to the GPU thread to flush a region
struct FlushRegionCommand final { struct FlushRegionCommand final {
@ -118,7 +124,6 @@ public:
private: private:
/// Pushes a command to be executed by the GPU thread /// Pushes a command to be executed by the GPU thread
u64 PushCommand(CommandData&& command_data, bool block = false); u64 PushCommand(CommandData&& command_data, bool block = false);
Tegra::Control::Scheduler* scheduler;
Core::System& system; Core::System& system;
const bool is_async; const bool is_async;

View File

@ -18,12 +18,12 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/slot_vector.h"
#include "video_core/control/channel_state_cache.h" #include "video_core/control/channel_state_cache.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
#include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
#include "video_core/texture_cache/slot_vector.h"
namespace VideoCore { namespace VideoCore {
enum class QueryType { enum class QueryType {
@ -37,7 +37,7 @@ constexpr std::size_t NumQueryTypes = static_cast<size_t>(QueryType::Count);
namespace VideoCommon { namespace VideoCommon {
using AsyncJobId = Common::SlotId; using AsyncJobId = SlotId;
static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0}; static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
@ -341,7 +341,7 @@ private:
static constexpr std::uintptr_t YUZU_PAGESIZE = 4096; static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
static constexpr unsigned YUZU_PAGEBITS = 12; static constexpr unsigned YUZU_PAGEBITS = 12;
Common::SlotVector<AsyncJob> slot_async_jobs; SlotVector<AsyncJob> slot_async_jobs;
VideoCore::RasterizerInterface& rasterizer; VideoCore::RasterizerInterface& rasterizer;
Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::MaxwellDeviceMemoryManager& device_memory;

View File

@ -90,7 +90,7 @@ public:
void PostCopyBarrier(); void PostCopyBarrier();
void Finish(); void Finish();
void TickFrame(Common::SlotVector<Buffer>&) noexcept {} void TickFrame(VideoCommon::SlotVector<Buffer>&) noexcept {}
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
@ -251,6 +251,7 @@ struct BufferCacheParams {
static constexpr bool NEEDS_BIND_STORAGE_INDEX = true; static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
static constexpr bool USE_MEMORY_MAPS = true; static constexpr bool USE_MEMORY_MAPS = true;
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true; static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = true;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads // TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false; static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;

View File

@ -30,13 +30,13 @@ class Image;
class ImageView; class ImageView;
class Sampler; class Sampler;
using Common::SlotVector;
using VideoCommon::ImageId; using VideoCommon::ImageId;
using VideoCommon::ImageViewId; using VideoCommon::ImageViewId;
using VideoCommon::ImageViewType; using VideoCommon::ImageViewType;
using VideoCommon::NUM_RT; using VideoCommon::NUM_RT;
using VideoCommon::Region2D; using VideoCommon::Region2D;
using VideoCommon::RenderTargets; using VideoCommon::RenderTargets;
using VideoCommon::SlotVector;
struct FormatProperties { struct FormatProperties {
GLenum compatibility_class; GLenum compatibility_class;

View File

@ -368,7 +368,7 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
return static_cast<u32>(device.GetStorageBufferAlignment()); return static_cast<u32>(device.GetStorageBufferAlignment());
} }
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept { void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept {
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
it->ResetUsageTracking(); it->ResetUsageTracking();
} }

View File

@ -81,7 +81,7 @@ public:
ComputePassDescriptorQueue& compute_pass_descriptor_queue, ComputePassDescriptorQueue& compute_pass_descriptor_queue,
DescriptorPool& descriptor_pool); DescriptorPool& descriptor_pool);
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept; void TickFrame(VideoCommon::SlotVector<Buffer>& slot_buffers) noexcept;
void Finish(); void Finish();
@ -181,6 +181,7 @@ struct BufferCacheParams {
static constexpr bool NEEDS_BIND_STORAGE_INDEX = false; static constexpr bool NEEDS_BIND_STORAGE_INDEX = false;
static constexpr bool USE_MEMORY_MAPS = true; static constexpr bool USE_MEMORY_MAPS = true;
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false; static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true; static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
}; };

View File

@ -20,11 +20,11 @@ struct ResolutionScalingInfo;
namespace Vulkan { namespace Vulkan {
using Common::SlotVector;
using VideoCommon::ImageId; using VideoCommon::ImageId;
using VideoCommon::NUM_RT; using VideoCommon::NUM_RT;
using VideoCommon::Region2D; using VideoCommon::Region2D;
using VideoCommon::RenderTargets; using VideoCommon::RenderTargets;
using VideoCommon::SlotVector;
using VideoCore::Surface::PixelFormat; using VideoCore::Surface::PixelFormat;
class BlitImageHelper; class BlitImageHelper;

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 Common { namespace VideoCommon {
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 Common } // namespace VideoCommon
template <> template <>
struct std::hash<Common::SlotId> { struct std::hash<VideoCommon::SlotId> {
size_t operator()(const Common::SlotId& id) const noexcept { size_t operator()(const VideoCommon::SlotId& id) const noexcept {
return std::hash<u32>{}(id.index); return std::hash<u32>{}(id.index);
} }
}; };

View File

@ -21,7 +21,6 @@
#include "common/lru_cache.h" #include "common/lru_cache.h"
#include "common/polyfill_ranges.h" #include "common/polyfill_ranges.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/slot_vector.h"
#include "common/thread_worker.h" #include "common/thread_worker.h"
#include "video_core/compatible_formats.h" #include "video_core/compatible_formats.h"
#include "video_core/control/channel_state_cache.h" #include "video_core/control/channel_state_cache.h"
@ -33,6 +32,7 @@
#include "video_core/texture_cache/image_info.h" #include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view_base.h" #include "video_core/texture_cache/image_view_base.h"
#include "video_core/texture_cache/render_targets.h" #include "video_core/texture_cache/render_targets.h"
#include "video_core/texture_cache/slot_vector.h"
#include "video_core/texture_cache/types.h" #include "video_core/texture_cache/types.h"
#include "video_core/textures/texture.h" #include "video_core/textures/texture.h"
@ -451,16 +451,16 @@ private:
struct PendingDownload { struct PendingDownload {
bool is_swizzle; bool is_swizzle;
size_t async_buffer_id; size_t async_buffer_id;
Common::SlotId object_id; SlotId object_id;
}; };
Common::SlotVector<Image> slot_images; SlotVector<Image> slot_images;
Common::SlotVector<ImageMapView> slot_map_views; SlotVector<ImageMapView> slot_map_views;
Common::SlotVector<ImageView> slot_image_views; SlotVector<ImageView> slot_image_views;
Common::SlotVector<ImageAlloc> slot_image_allocs; SlotVector<ImageAlloc> slot_image_allocs;
Common::SlotVector<Sampler> slot_samplers; SlotVector<Sampler> slot_samplers;
Common::SlotVector<Framebuffer> slot_framebuffers; SlotVector<Framebuffer> slot_framebuffers;
Common::SlotVector<BufferDownload> slot_buffer_downloads; SlotVector<BufferDownload> slot_buffer_downloads;
// TODO: This data structure is not optimal and it should be reworked // TODO: This data structure is not optimal and it should be reworked

View File

@ -5,21 +5,21 @@
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/slot_vector.h" #include "video_core/texture_cache/slot_vector.h"
namespace VideoCommon { namespace VideoCommon {
constexpr size_t NUM_RT = 8; constexpr size_t NUM_RT = 8;
constexpr size_t MAX_MIP_LEVELS = 14; constexpr size_t MAX_MIP_LEVELS = 14;
constexpr Common::SlotId CORRUPT_ID{0xfffffffe}; constexpr SlotId CORRUPT_ID{0xfffffffe};
using ImageId = Common::SlotId; using ImageId = SlotId;
using ImageMapId = Common::SlotId; using ImageMapId = SlotId;
using ImageViewId = Common::SlotId; using ImageViewId = SlotId;
using ImageAllocId = Common::SlotId; using ImageAllocId = SlotId;
using SamplerId = Common::SlotId; using SamplerId = SlotId;
using FramebufferId = Common::SlotId; using FramebufferId = SlotId;
/// Fake image ID for null image views /// Fake image ID for null image views
constexpr ImageId NULL_IMAGE_ID{0}; constexpr ImageId NULL_IMAGE_ID{0};