Merge remote-tracking branch 'origin/master' into ssl
This commit is contained in:
commit
98685d48e3
|
@ -12,7 +12,7 @@
|
||||||
url = https://github.com/mozilla/cubeb.git
|
url = https://github.com/mozilla/cubeb.git
|
||||||
[submodule "dynarmic"]
|
[submodule "dynarmic"]
|
||||||
path = externals/dynarmic
|
path = externals/dynarmic
|
||||||
url = https://github.com/MerryMage/dynarmic.git
|
url = https://github.com/merryhime/dynarmic.git
|
||||||
[submodule "libusb"]
|
[submodule "libusb"]
|
||||||
path = externals/libusb/libusb
|
path = externals/libusb/libusb
|
||||||
url = https://github.com/libusb/libusb.git
|
url = https://github.com/libusb/libusb.git
|
||||||
|
@ -24,10 +24,10 @@
|
||||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||||
[submodule "sirit"]
|
[submodule "sirit"]
|
||||||
path = externals/sirit
|
path = externals/sirit
|
||||||
url = https://github.com/yuzu-emu/sirit
|
url = https://github.com/yuzu-emu/sirit.git
|
||||||
[submodule "mbedtls"]
|
[submodule "mbedtls"]
|
||||||
path = externals/mbedtls
|
path = externals/mbedtls
|
||||||
url = https://github.com/yuzu-emu/mbedtls
|
url = https://github.com/yuzu-emu/mbedtls.git
|
||||||
[submodule "xbyak"]
|
[submodule "xbyak"]
|
||||||
path = externals/xbyak
|
path = externals/xbyak
|
||||||
url = https://github.com/herumi/xbyak.git
|
url = https://github.com/herumi/xbyak.git
|
||||||
|
@ -45,13 +45,16 @@
|
||||||
url = https://github.com/FFmpeg/FFmpeg.git
|
url = https://github.com/FFmpeg/FFmpeg.git
|
||||||
[submodule "vcpkg"]
|
[submodule "vcpkg"]
|
||||||
path = externals/vcpkg
|
path = externals/vcpkg
|
||||||
url = https://github.com/Microsoft/vcpkg.git
|
url = https://github.com/microsoft/vcpkg.git
|
||||||
[submodule "cpp-jwt"]
|
[submodule "cpp-jwt"]
|
||||||
path = externals/cpp-jwt
|
path = externals/cpp-jwt
|
||||||
url = https://github.com/arun11299/cpp-jwt.git
|
url = https://github.com/arun11299/cpp-jwt.git
|
||||||
[submodule "libadrenotools"]
|
[submodule "libadrenotools"]
|
||||||
path = externals/libadrenotools
|
path = externals/libadrenotools
|
||||||
url = https://github.com/bylaws/libadrenotools
|
url = https://github.com/bylaws/libadrenotools.git
|
||||||
[submodule "tzdb_to_nx"]
|
[submodule "tzdb_to_nx"]
|
||||||
path = externals/nx_tzdb/tzdb_to_nx
|
path = externals/nx_tzdb/tzdb_to_nx
|
||||||
url = https://github.com/lat9nq/tzdb_to_nx.git
|
url = https://github.com/lat9nq/tzdb_to_nx.git
|
||||||
|
[submodule "VulkanMemoryAllocator"]
|
||||||
|
path = externals/vma/VulkanMemoryAllocator
|
||||||
|
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||||
|
|
|
@ -504,7 +504,7 @@ if (ENABLE_SDL2)
|
||||||
if (YUZU_USE_BUNDLED_SDL2)
|
if (YUZU_USE_BUNDLED_SDL2)
|
||||||
# Detect toolchain and platform
|
# Detect toolchain and platform
|
||||||
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
||||||
set(SDL2_VER "SDL2-2.0.18")
|
set(SDL2_VER "SDL2-2.28.0")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||||
endif()
|
endif()
|
||||||
|
@ -524,7 +524,7 @@ if (ENABLE_SDL2)
|
||||||
elseif (YUZU_USE_EXTERNAL_SDL2)
|
elseif (YUZU_USE_EXTERNAL_SDL2)
|
||||||
message(STATUS "Using SDL2 from externals.")
|
message(STATUS "Using SDL2 from externals.")
|
||||||
else()
|
else()
|
||||||
find_package(SDL2 2.0.18 REQUIRED)
|
find_package(SDL2 2.26.4 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -143,6 +143,11 @@ endif()
|
||||||
# TZDB (Time Zone Database)
|
# TZDB (Time Zone Database)
|
||||||
add_subdirectory(nx_tzdb)
|
add_subdirectory(nx_tzdb)
|
||||||
|
|
||||||
|
# VMA
|
||||||
|
add_library(vma vma/vma.cpp)
|
||||||
|
target_include_directories(vma PUBLIC ./vma/VulkanMemoryAllocator/include)
|
||||||
|
target_link_libraries(vma PRIVATE Vulkan::Headers)
|
||||||
|
|
||||||
if (NOT TARGET LLVM::Demangle)
|
if (NOT TARGET LLVM::Demangle)
|
||||||
add_library(demangle demangle/ItaniumDemangle.cpp)
|
add_library(demangle demangle/ItaniumDemangle.cpp)
|
||||||
target_include_directories(demangle PUBLIC ./demangle)
|
target_include_directories(demangle PUBLIC ./demangle)
|
||||||
|
@ -152,6 +157,9 @@ endif()
|
||||||
add_library(stb stb/stb_dxt.cpp)
|
add_library(stb stb/stb_dxt.cpp)
|
||||||
target_include_directories(stb PUBLIC ./stb)
|
target_include_directories(stb PUBLIC ./stb)
|
||||||
|
|
||||||
|
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||||
|
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
if (ARCHITECTURE_arm64)
|
if (ARCHITECTURE_arm64)
|
||||||
add_subdirectory(libadrenotools)
|
add_subdirectory(libadrenotools)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit f17058b562c8a1090c0c996b42982721ace90903
|
Subproject commit 491fba1d06a4810645092b2559b9cc94abeb23bb
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace bcn {
|
||||||
|
/**
|
||||||
|
* @brief Decodes a BC1 encoded image to R8G8B8A8
|
||||||
|
*/
|
||||||
|
void DecodeBc1(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes a BC2 encoded image to R8G8B8A8
|
||||||
|
*/
|
||||||
|
void DecodeBc2(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes a BC3 encoded image to R8G8B8A8
|
||||||
|
*/
|
||||||
|
void DecodeBc3(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes a BC4 encoded image to R8
|
||||||
|
*/
|
||||||
|
void DecodeBc4(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes a BC5 encoded image to R8G8
|
||||||
|
*/
|
||||||
|
void DecodeBc5(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes a BC6 encoded image to R16G16B16A16
|
||||||
|
*/
|
||||||
|
void DecodeBc6(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decodes a BC7 encoded image to R8G8B8A8
|
||||||
|
*/
|
||||||
|
void DecodeBc7(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ add_library(nx_tzdb INTERFACE)
|
||||||
|
|
||||||
find_program(GIT git)
|
find_program(GIT git)
|
||||||
find_program(GNU_MAKE make)
|
find_program(GNU_MAKE make)
|
||||||
find_program(GNU_DATE date)
|
find_program(DATE_PROG date)
|
||||||
|
|
||||||
set(CAN_BUILD_NX_TZDB true)
|
set(CAN_BUILD_NX_TZDB true)
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ endif()
|
||||||
if (NOT GNU_MAKE)
|
if (NOT GNU_MAKE)
|
||||||
set(CAN_BUILD_NX_TZDB false)
|
set(CAN_BUILD_NX_TZDB false)
|
||||||
endif()
|
endif()
|
||||||
if (NOT GNU_DATE)
|
if (NOT DATE_PROG)
|
||||||
set(CAN_BUILD_NX_TZDB false)
|
set(CAN_BUILD_NX_TZDB false)
|
||||||
endif()
|
endif()
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
|
if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 34df65eff295c2bd9ee9e6a077d662486d5cabb3
|
Subproject commit 212afa2394a74226dcf1b7996a570aae17debb69
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 0aa3989b8f382f185fdf646cc83a1d16fa31d6ab
|
|
@ -0,0 +1,8 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#define VMA_IMPLEMENTATION
|
||||||
|
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
||||||
|
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
||||||
|
|
||||||
|
#include <vk_mem_alloc.h>
|
|
@ -26,7 +26,7 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn
|
||||||
android {
|
android {
|
||||||
namespace = "org.yuzu.yuzu_emu"
|
namespace = "org.yuzu.yuzu_emu"
|
||||||
|
|
||||||
compileSdkVersion = "android-33"
|
compileSdkVersion = "android-34"
|
||||||
ndkVersion = "25.2.9519653"
|
ndkVersion = "25.2.9519653"
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
|
@ -51,7 +51,7 @@ android {
|
||||||
// TODO If this is ever modified, change application_id in strings.xml
|
// TODO If this is ever modified, change application_id in strings.xml
|
||||||
applicationId = "org.yuzu.yuzu_emu"
|
applicationId = "org.yuzu.yuzu_emu"
|
||||||
minSdk = 30
|
minSdk = 30
|
||||||
targetSdk = 33
|
targetSdk = 34
|
||||||
versionName = getGitVersion()
|
versionName = getGitVersion()
|
||||||
|
|
||||||
// If you want to use autoVersion for the versionCode, create a property in local.properties
|
// If you want to use autoVersion for the versionCode, create a property in local.properties
|
||||||
|
|
|
@ -13,6 +13,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
|
@ -69,7 +70,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
android:resource="@xml/nfc_tech_filter" />
|
android:resource="@xml/nfc_tech_filter" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService"/>
|
<service android:name="org.yuzu.yuzu_emu.utils.ForegroundService" android:foregroundServiceType="specialUse">
|
||||||
|
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="Keep emulation running in background"/>
|
||||||
|
</service>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".features.DocumentProvider"
|
android:name=".features.DocumentProvider"
|
||||||
|
|
|
@ -286,7 +286,7 @@ object NativeLibrary {
|
||||||
/**
|
/**
|
||||||
* Unpauses emulation from a paused state.
|
* Unpauses emulation from a paused state.
|
||||||
*/
|
*/
|
||||||
external fun unPauseEmulation()
|
external fun unpauseEmulation()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses emulation.
|
* Pauses emulation.
|
||||||
|
@ -313,6 +313,21 @@ object NativeLibrary {
|
||||||
*/
|
*/
|
||||||
external fun isPaused(): Boolean
|
external fun isPaused(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutes emulation sound
|
||||||
|
*/
|
||||||
|
external fun muteAudio(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmutes emulation sound
|
||||||
|
*/
|
||||||
|
external fun unmuteAudio(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if emulation audio is muted.
|
||||||
|
*/
|
||||||
|
external fun isMuted(): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the performance stats for the current game
|
* Returns the performance stats for the current game
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,13 +27,13 @@ import android.view.MotionEvent
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
|
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
|
||||||
|
@ -44,8 +44,10 @@ import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
|
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.ForegroundService
|
import org.yuzu.yuzu_emu.utils.ForegroundService
|
||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
|
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||||
import org.yuzu.yuzu_emu.utils.NfcReader
|
import org.yuzu.yuzu_emu.utils.NfcReader
|
||||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
private lateinit var binding: ActivityEmulationBinding
|
private lateinit var binding: ActivityEmulationBinding
|
||||||
|
@ -63,6 +65,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
|
|
||||||
private val actionPause = "ACTION_EMULATOR_PAUSE"
|
private val actionPause = "ACTION_EMULATOR_PAUSE"
|
||||||
private val actionPlay = "ACTION_EMULATOR_PLAY"
|
private val actionPlay = "ACTION_EMULATOR_PLAY"
|
||||||
|
private val actionMute = "ACTION_EMULATOR_MUTE"
|
||||||
|
private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
|
||||||
|
|
||||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||||
|
|
||||||
|
@ -102,6 +106,19 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
inputHandler = InputHandler()
|
inputHandler = InputHandler()
|
||||||
inputHandler.initialize()
|
inputHandler.initialize()
|
||||||
|
|
||||||
|
val memoryUtil = MemoryUtil(this)
|
||||||
|
if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) {
|
||||||
|
Toast.makeText(
|
||||||
|
this,
|
||||||
|
getString(
|
||||||
|
R.string.device_memory_inadequate,
|
||||||
|
memoryUtil.getDeviceRAM(),
|
||||||
|
"8 ${getString(R.string.memory_gigabyte)}"
|
||||||
|
),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
// Start a foreground service to prevent the app from getting killed in the background
|
// Start a foreground service to prevent the app from getting killed in the background
|
||||||
val startIntent = Intent(this, ForegroundService::class.java)
|
val startIntent = Intent(this, ForegroundService::class.java)
|
||||||
startForegroundService(startIntent)
|
startForegroundService(startIntent)
|
||||||
|
@ -305,6 +322,41 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
pictureInPictureActions.add(pauseRemoteAction)
|
pictureInPictureActions.add(pauseRemoteAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NativeLibrary.isMuted()) {
|
||||||
|
val unmuteIcon = Icon.createWithResource(
|
||||||
|
this@EmulationActivity,
|
||||||
|
R.drawable.ic_pip_unmute
|
||||||
|
)
|
||||||
|
val unmutePendingIntent = PendingIntent.getBroadcast(
|
||||||
|
this@EmulationActivity,
|
||||||
|
R.drawable.ic_pip_unmute,
|
||||||
|
Intent(actionUnmute),
|
||||||
|
pendingFlags
|
||||||
|
)
|
||||||
|
val unmuteRemoteAction = RemoteAction(
|
||||||
|
unmuteIcon,
|
||||||
|
getString(R.string.unmute),
|
||||||
|
getString(R.string.unmute),
|
||||||
|
unmutePendingIntent
|
||||||
|
)
|
||||||
|
pictureInPictureActions.add(unmuteRemoteAction)
|
||||||
|
} else {
|
||||||
|
val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute)
|
||||||
|
val mutePendingIntent = PendingIntent.getBroadcast(
|
||||||
|
this@EmulationActivity,
|
||||||
|
R.drawable.ic_pip_mute,
|
||||||
|
Intent(actionMute),
|
||||||
|
pendingFlags
|
||||||
|
)
|
||||||
|
val muteRemoteAction = RemoteAction(
|
||||||
|
muteIcon,
|
||||||
|
getString(R.string.mute),
|
||||||
|
getString(R.string.mute),
|
||||||
|
mutePendingIntent
|
||||||
|
)
|
||||||
|
pictureInPictureActions.add(muteRemoteAction)
|
||||||
|
}
|
||||||
|
|
||||||
return this.apply { setActions(pictureInPictureActions) }
|
return this.apply { setActions(pictureInPictureActions) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,10 +374,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
private var pictureInPictureReceiver = object : BroadcastReceiver() {
|
private var pictureInPictureReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent) {
|
override fun onReceive(context: Context?, intent: Intent) {
|
||||||
if (intent.action == actionPlay) {
|
if (intent.action == actionPlay) {
|
||||||
if (NativeLibrary.isPaused()) NativeLibrary.unPauseEmulation()
|
if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation()
|
||||||
} else if (intent.action == actionPause) {
|
} else if (intent.action == actionPause) {
|
||||||
if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
|
if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
|
||||||
}
|
}
|
||||||
|
if (intent.action == actionUnmute) {
|
||||||
|
if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
|
||||||
|
} else if (intent.action == actionMute) {
|
||||||
|
if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio()
|
||||||
|
}
|
||||||
buildPictureInPictureParams()
|
buildPictureInPictureParams()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,6 +396,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
IntentFilter().apply {
|
IntentFilter().apply {
|
||||||
addAction(actionPause)
|
addAction(actionPause)
|
||||||
addAction(actionPlay)
|
addAction(actionPlay)
|
||||||
|
addAction(actionMute)
|
||||||
|
addAction(actionUnmute)
|
||||||
}.also {
|
}.also {
|
||||||
registerReceiver(pictureInPictureReceiver, it)
|
registerReceiver(pictureInPictureReceiver, it)
|
||||||
}
|
}
|
||||||
|
@ -347,6 +406,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
unregisterReceiver(pictureInPictureReceiver)
|
unregisterReceiver(pictureInPictureReceiver)
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
}
|
}
|
||||||
|
// Always resume audio, since there is no UI button
|
||||||
|
if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -714,7 +714,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
State.PAUSED -> {
|
State.PAUSED -> {
|
||||||
Log.debug("[EmulationFragment] Resuming emulation.")
|
Log.debug("[EmulationFragment] Resuming emulation.")
|
||||||
NativeLibrary.surfaceChanged(surface)
|
NativeLibrary.surfaceChanged(surface)
|
||||||
NativeLibrary.unPauseEmulation()
|
NativeLibrary.unpauseEmulation()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Log.debug("[EmulationFragment] Bug, run called while already running.")
|
else -> Log.debug("[EmulationFragment] Bug, run called while already running.")
|
||||||
|
|
|
@ -68,44 +68,65 @@ class HomeSettingsFragment : Fragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
mainActivity = requireActivity() as MainActivity
|
mainActivity = requireActivity() as MainActivity
|
||||||
|
|
||||||
val optionsList: MutableList<HomeSetting> = mutableListOf(
|
val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.advanced_settings,
|
R.string.advanced_settings,
|
||||||
R.string.settings_description,
|
R.string.settings_description,
|
||||||
R.drawable.ic_settings
|
R.drawable.ic_settings
|
||||||
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") },
|
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.open_user_folder,
|
R.string.open_user_folder,
|
||||||
R.string.open_user_folder_description,
|
R.string.open_user_folder_description,
|
||||||
R.drawable.ic_folder_open
|
R.drawable.ic_folder_open
|
||||||
) { openFileManager() },
|
) { openFileManager() }
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.preferences_theme,
|
R.string.preferences_theme,
|
||||||
R.string.theme_and_color_description,
|
R.string.theme_and_color_description,
|
||||||
R.drawable.ic_palette
|
R.drawable.ic_palette
|
||||||
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") },
|
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (GpuDriverHelper.supportsCustomDriverLoading()) {
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.install_gpu_driver,
|
R.string.install_gpu_driver,
|
||||||
R.string.install_gpu_driver_description,
|
R.string.install_gpu_driver_description,
|
||||||
R.drawable.ic_exit
|
R.drawable.ic_exit
|
||||||
) { driverInstaller() },
|
) { driverInstaller() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.install_amiibo_keys,
|
R.string.install_amiibo_keys,
|
||||||
R.string.install_amiibo_keys_description,
|
R.string.install_amiibo_keys_description,
|
||||||
R.drawable.ic_nfc
|
R.drawable.ic_nfc
|
||||||
) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) },
|
) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.install_game_content,
|
R.string.install_game_content,
|
||||||
R.string.install_game_content_description,
|
R.string.install_game_content_description,
|
||||||
R.drawable.ic_system_update_alt
|
R.drawable.ic_system_update_alt
|
||||||
) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) },
|
) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.select_games_folder,
|
R.string.select_games_folder,
|
||||||
R.string.select_games_folder_description,
|
R.string.select_games_folder_description,
|
||||||
R.drawable.ic_add
|
R.drawable.ic_add
|
||||||
) {
|
) {
|
||||||
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
mainActivity.getGamesDirectory.launch(
|
||||||
},
|
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.manage_save_data,
|
R.string.manage_save_data,
|
||||||
R.string.import_export_saves_description,
|
R.string.import_export_saves_description,
|
||||||
|
@ -115,22 +136,30 @@ class HomeSettingsFragment : Fragment() {
|
||||||
parentFragmentManager,
|
parentFragmentManager,
|
||||||
ImportExportSavesFragment.TAG
|
ImportExportSavesFragment.TAG
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.install_prod_keys,
|
R.string.install_prod_keys,
|
||||||
R.string.install_prod_keys_description,
|
R.string.install_prod_keys_description,
|
||||||
R.drawable.ic_unlock
|
R.drawable.ic_unlock
|
||||||
) { mainActivity.getProdKey.launch(arrayOf("*/*")) },
|
) { mainActivity.getProdKey.launch(arrayOf("*/*")) }
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.install_firmware,
|
R.string.install_firmware,
|
||||||
R.string.install_firmware_description,
|
R.string.install_firmware_description,
|
||||||
R.drawable.ic_firmware
|
R.drawable.ic_firmware
|
||||||
) { mainActivity.getFirmware.launch(arrayOf("application/zip")) },
|
) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.share_log,
|
R.string.share_log,
|
||||||
R.string.share_log_description,
|
R.string.share_log_description,
|
||||||
R.drawable.ic_log
|
R.drawable.ic_log
|
||||||
) { shareLog() },
|
) { shareLog() }
|
||||||
|
)
|
||||||
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.about,
|
R.string.about,
|
||||||
R.string.about_description,
|
R.string.about_description,
|
||||||
|
@ -141,6 +170,7 @@ class HomeSettingsFragment : Fragment() {
|
||||||
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
|
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!BuildConfig.PREMIUM) {
|
if (!BuildConfig.PREMIUM) {
|
||||||
optionsList.add(
|
optionsList.add(
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
|
||||||
|
class LongMessageDialogFragment : DialogFragment() {
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val titleId = requireArguments().getInt(TITLE)
|
||||||
|
val description = requireArguments().getString(DESCRIPTION)
|
||||||
|
val helpLinkId = requireArguments().getInt(HELP_LINK)
|
||||||
|
|
||||||
|
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setPositiveButton(R.string.close, null)
|
||||||
|
.setTitle(titleId)
|
||||||
|
.setMessage(description)
|
||||||
|
|
||||||
|
if (helpLinkId != 0) {
|
||||||
|
dialog.setNeutralButton(R.string.learn_more) { _, _ ->
|
||||||
|
openLink(getString(helpLinkId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openLink(link: String) {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "LongMessageDialogFragment"
|
||||||
|
|
||||||
|
private const val TITLE = "Title"
|
||||||
|
private const val DESCRIPTION = "Description"
|
||||||
|
private const val HELP_LINK = "Link"
|
||||||
|
|
||||||
|
fun newInstance(
|
||||||
|
titleId: Int,
|
||||||
|
description: String,
|
||||||
|
helpLinkId: Int = 0
|
||||||
|
): LongMessageDialogFragment {
|
||||||
|
val dialog = LongMessageDialogFragment()
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.apply {
|
||||||
|
putInt(TITLE, titleId)
|
||||||
|
putString(DESCRIPTION, description)
|
||||||
|
putInt(HELP_LINK, helpLinkId)
|
||||||
|
}
|
||||||
|
dialog.arguments = bundle
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
|
||||||
|
|
||||||
class SearchFragment : Fragment() {
|
class SearchFragment : Fragment() {
|
||||||
private var _binding: FragmentSearchBinding? = null
|
private var _binding: FragmentSearchBinding? = null
|
||||||
|
@ -128,10 +127,7 @@ class SearchFragment : Fragment() {
|
||||||
|
|
||||||
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
|
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
|
||||||
|
|
||||||
R.id.chip_retail -> baseList.filter {
|
R.id.chip_retail -> baseList.filter { !it.isHomebrew }
|
||||||
FileUtil.hasExtension(it.path, "xci") ||
|
|
||||||
FileUtil.hasExtension(it.path, "nsp")
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> baseList
|
else -> baseList
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Game(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val extensions: Set<String> = HashSet(
|
val extensions: Set<String> = HashSet(
|
||||||
listOf(".xci", ".nsp", ".nca", ".nro")
|
listOf("xci", "nsp", "nca", "nro")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package org.yuzu.yuzu_emu.ui.main
|
package org.yuzu.yuzu_emu.ui.main
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
|
@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
|
||||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||||
|
import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
|
||||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
|
@ -294,7 +296,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileUtil.hasExtension(result, "keys")) {
|
if (FileUtil.getExtension(result) != "keys") {
|
||||||
MessageDialogFragment.newInstance(
|
MessageDialogFragment.newInstance(
|
||||||
R.string.reading_keys_failure,
|
R.string.reading_keys_failure,
|
||||||
R.string.install_prod_keys_failure_extension_description
|
R.string.install_prod_keys_failure_extension_description
|
||||||
|
@ -391,7 +393,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileUtil.hasExtension(result, "bin")) {
|
if (FileUtil.getExtension(result) != "bin") {
|
||||||
MessageDialogFragment.newInstance(
|
MessageDialogFragment.newInstance(
|
||||||
R.string.reading_keys_failure,
|
R.string.reading_keys_failure,
|
||||||
R.string.install_amiibo_keys_failure_extension_description
|
R.string.install_amiibo_keys_failure_extension_description
|
||||||
|
@ -481,62 +483,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val installGameUpdate =
|
val installGameUpdate = registerForActivityResult(
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
ActivityResultContracts.OpenMultipleDocuments()
|
||||||
if (it == null) {
|
) { documents: List<Uri> ->
|
||||||
return@registerForActivityResult
|
if (documents.isNotEmpty()) {
|
||||||
}
|
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
IndeterminateProgressDialogFragment.newInstance(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
R.string.install_game_content
|
R.string.install_game_content
|
||||||
) {
|
) {
|
||||||
val result = NativeLibrary.installFileToNand(it.toString())
|
var installSuccess = 0
|
||||||
|
var installOverwrite = 0
|
||||||
|
var errorBaseGame = 0
|
||||||
|
var errorExtension = 0
|
||||||
|
var errorOther = 0
|
||||||
|
var errorTotal = 0
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.Main) {
|
documents.forEach {
|
||||||
when (result) {
|
when (NativeLibrary.installFileToNand(it.toString())) {
|
||||||
NativeLibrary.InstallFileToNandResult.Success -> {
|
NativeLibrary.InstallFileToNandResult.Success -> {
|
||||||
Toast.makeText(
|
installSuccess += 1
|
||||||
applicationContext,
|
|
||||||
R.string.install_game_content_success,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
|
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
|
||||||
Toast.makeText(
|
installOverwrite += 1
|
||||||
applicationContext,
|
|
||||||
R.string.install_game_content_success_overwrite,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
|
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
|
||||||
MessageDialogFragment.newInstance(
|
errorBaseGame += 1
|
||||||
R.string.install_game_content_failure,
|
|
||||||
R.string.install_game_content_failure_base
|
|
||||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
|
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
|
||||||
MessageDialogFragment.newInstance(
|
errorExtension += 1
|
||||||
R.string.install_game_content_failure,
|
|
||||||
R.string.install_game_content_failure_file_extension,
|
|
||||||
R.string.install_game_content_help_link
|
|
||||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
MessageDialogFragment.newInstance(
|
errorOther += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
val separator = System.getProperty("line.separator") ?: "\n"
|
||||||
|
val installResult = StringBuilder()
|
||||||
|
if (installSuccess > 0) {
|
||||||
|
installResult.append(
|
||||||
|
getString(
|
||||||
|
R.string.install_game_content_success_install,
|
||||||
|
installSuccess
|
||||||
|
)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
if (installOverwrite > 0) {
|
||||||
|
installResult.append(
|
||||||
|
getString(
|
||||||
|
R.string.install_game_content_success_overwrite,
|
||||||
|
installOverwrite
|
||||||
|
)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
errorTotal = errorBaseGame + errorExtension + errorOther
|
||||||
|
if (errorTotal > 0) {
|
||||||
|
installResult.append(separator)
|
||||||
|
installResult.append(
|
||||||
|
getString(
|
||||||
|
R.string.install_game_content_failed_count,
|
||||||
|
errorTotal
|
||||||
|
)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
if (errorBaseGame > 0) {
|
||||||
|
installResult.append(separator)
|
||||||
|
installResult.append(
|
||||||
|
getString(R.string.install_game_content_failure_base)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
if (errorExtension > 0) {
|
||||||
|
installResult.append(separator)
|
||||||
|
installResult.append(
|
||||||
|
getString(R.string.install_game_content_failure_file_extension)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
if (errorOther > 0) {
|
||||||
|
installResult.append(
|
||||||
|
getString(R.string.install_game_content_failure_description)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
LongMessageDialogFragment.newInstance(
|
||||||
R.string.install_game_content_failure,
|
R.string.install_game_content_failure,
|
||||||
R.string.install_game_content_failure_description,
|
installResult.toString().trim(),
|
||||||
R.string.install_game_content_help_link
|
R.string.install_game_content_help_link
|
||||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
).show(supportFragmentManager, LongMessageDialogFragment.TAG)
|
||||||
|
} else {
|
||||||
|
LongMessageDialogFragment.newInstance(
|
||||||
|
R.string.install_game_content_success,
|
||||||
|
installResult.toString().trim()
|
||||||
|
).show(supportFragmentManager, LongMessageDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return@newInstance installSuccess + installOverwrite + errorTotal
|
||||||
return@newInstance result
|
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.provider.OpenableColumns
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -185,19 +184,18 @@ object FileUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get file display name from given path
|
* Get file display name from given path
|
||||||
* @param path content uri path
|
* @param uri content uri
|
||||||
* @return String display name
|
* @return String display name
|
||||||
*/
|
*/
|
||||||
fun getFilename(context: Context, path: String): String {
|
fun getFilename(uri: Uri): String {
|
||||||
val resolver = context.contentResolver
|
val resolver = YuzuApplication.appContext.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
||||||
)
|
)
|
||||||
var filename = ""
|
var filename = ""
|
||||||
var c: Cursor? = null
|
var c: Cursor? = null
|
||||||
try {
|
try {
|
||||||
val mUri = Uri.parse(path)
|
c = resolver.query(uri, columns, null, null, null)
|
||||||
c = resolver.query(mUri, columns, null, null, null)
|
|
||||||
c!!.moveToNext()
|
c!!.moveToNext()
|
||||||
filename = c.getString(0)
|
filename = c.getString(0)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -326,25 +324,9 @@ object FileUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasExtension(path: String, extension: String): Boolean =
|
fun getExtension(uri: Uri): String {
|
||||||
path.substring(path.lastIndexOf(".") + 1).contains(extension)
|
val fileName = getFilename(uri)
|
||||||
|
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
||||||
fun hasExtension(uri: Uri, extension: String): Boolean {
|
.lowercase()
|
||||||
val fileName: String?
|
|
||||||
val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null)
|
|
||||||
val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
||||||
cursor?.moveToFirst()
|
|
||||||
|
|
||||||
if (nameIndex == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fileName = cursor.getString(nameIndex)
|
|
||||||
cursor.close()
|
|
||||||
|
|
||||||
if (fileName == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import java.util.*
|
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
|
@ -33,15 +32,9 @@ object GameHelper {
|
||||||
val children = FileUtil.listFiles(context, gamesUri)
|
val children = FileUtil.listFiles(context, gamesUri)
|
||||||
for (file in children) {
|
for (file in children) {
|
||||||
if (!file.isDirectory) {
|
if (!file.isDirectory) {
|
||||||
val filename = file.uri.toString()
|
|
||||||
val extensionStart = filename.lastIndexOf('.')
|
|
||||||
if (extensionStart > 0) {
|
|
||||||
val fileExtension = filename.substring(extensionStart)
|
|
||||||
|
|
||||||
// Check that the file has an extension we care about before trying to read out of it.
|
// Check that the file has an extension we care about before trying to read out of it.
|
||||||
if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) {
|
if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
|
||||||
games.add(getGame(filename))
|
games.add(getGame(file.uri))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,21 +52,19 @@ object GameHelper {
|
||||||
return games.toList()
|
return games.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGame(filePath: String): Game {
|
private fun getGame(uri: Uri): Game {
|
||||||
|
val filePath = uri.toString()
|
||||||
var name = NativeLibrary.getTitle(filePath)
|
var name = NativeLibrary.getTitle(filePath)
|
||||||
|
|
||||||
// If the game's title field is empty, use the filename.
|
// If the game's title field is empty, use the filename.
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
name = filePath.substring(filePath.lastIndexOf("/") + 1)
|
name = FileUtil.getFilename(uri)
|
||||||
}
|
}
|
||||||
var gameId = NativeLibrary.getGameId(filePath)
|
var gameId = NativeLibrary.getGameId(filePath)
|
||||||
|
|
||||||
// If the game's ID field is empty, use the filename without extension.
|
// If the game's ID field is empty, use the filename without extension.
|
||||||
if (gameId.isEmpty()) {
|
if (gameId.isEmpty()) {
|
||||||
gameId = filePath.substring(
|
gameId = name.substring(0, name.lastIndexOf("."))
|
||||||
filePath.lastIndexOf("/") + 1,
|
|
||||||
filePath.lastIndexOf(".")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val newGame = Game(
|
val newGame = Game(
|
||||||
|
|
|
@ -113,6 +113,8 @@ object GpuDriverHelper {
|
||||||
initializeDriverParameters(context)
|
initializeDriverParameters(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
external fun supportsCustomDriverLoading(): Boolean
|
||||||
|
|
||||||
// Parse the custom driver metadata to retrieve the name.
|
// Parse the custom driver metadata to retrieve the name.
|
||||||
val customDriverName: String?
|
val customDriverName: String?
|
||||||
get() {
|
get() {
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class MemoryUtil(val context: Context) {
|
||||||
|
|
||||||
|
private val Long.floatForm: String
|
||||||
|
get() = String.format(Locale.ROOT, "%.2f", this.toDouble())
|
||||||
|
|
||||||
|
private fun bytesToSizeUnit(size: Long): String {
|
||||||
|
return when {
|
||||||
|
size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}"
|
||||||
|
size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}"
|
||||||
|
size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}"
|
||||||
|
size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}"
|
||||||
|
size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}"
|
||||||
|
size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}"
|
||||||
|
else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val totalMemory =
|
||||||
|
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
|
||||||
|
val memInfo = ActivityManager.MemoryInfo()
|
||||||
|
getMemoryInfo(memInfo)
|
||||||
|
memInfo.totalMem
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isLessThan(minimum: Int, size: Long): Boolean {
|
||||||
|
return when (size) {
|
||||||
|
Kb -> totalMemory < Mb && totalMemory < minimum
|
||||||
|
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
|
||||||
|
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
|
||||||
|
Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
|
||||||
|
Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
|
||||||
|
Eb -> totalMemory / Eb < minimum
|
||||||
|
else -> totalMemory < Kb && totalMemory < minimum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDeviceRAM(): String {
|
||||||
|
return bytesToSizeUnit(totalMemory)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val Kb: Long = 1024
|
||||||
|
const val Mb = Kb * 1024
|
||||||
|
const val Gb = Mb * 1024
|
||||||
|
const val Tb = Gb * 1024
|
||||||
|
const val Pb = Tb * 1024
|
||||||
|
const val Eb = Pb * 1024
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,6 @@ add_library(yuzu-android SHARED
|
||||||
id_cache.cpp
|
id_cache.cpp
|
||||||
id_cache.h
|
id_cache.h
|
||||||
native.cpp
|
native.cpp
|
||||||
native.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <android/api-level.h>
|
#include <android/api-level.h>
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include <core/loader/nro.h>
|
#include <core/loader/nro.h>
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
#include "common/detached_tasks.h"
|
#include "common/detached_tasks.h"
|
||||||
#include "common/dynamic_library.h"
|
#include "common/dynamic_library.h"
|
||||||
|
@ -59,6 +60,9 @@
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
|
|
||||||
|
#define jconst [[maybe_unused]] const auto
|
||||||
|
#define jauto [[maybe_unused]] auto
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class EmulationSession final {
|
class EmulationSession final {
|
||||||
|
@ -98,7 +102,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
int InstallFileToNand(std::string filename) {
|
int InstallFileToNand(std::string filename) {
|
||||||
const auto copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
||||||
std::size_t block_size) {
|
std::size_t block_size) {
|
||||||
if (src == nullptr || dest == nullptr) {
|
if (src == nullptr || dest == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -108,10 +112,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
std::vector<u8> buffer(1_MiB);
|
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
||||||
const auto read = src->Read(buffer.data(), buffer.size(), i);
|
jconst read = src->Read(buffer.data(), buffer.size(), i);
|
||||||
dest->Write(buffer.data(), read, i);
|
dest->Write(buffer.data(), read, i);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -128,14 +132,14 @@ public:
|
||||||
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||||
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
||||||
|
|
||||||
std::shared_ptr<FileSys::NSP> nsp;
|
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
|
||||||
if (filename.ends_with("nsp")) {
|
if (filename.ends_with("nsp")) {
|
||||||
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
||||||
if (nsp->IsExtractedType()) {
|
if (nsp->IsExtractedType()) {
|
||||||
return InstallError;
|
return InstallError;
|
||||||
}
|
}
|
||||||
} else if (filename.ends_with("xci")) {
|
} else if (filename.ends_with("xci")) {
|
||||||
const auto xci =
|
jconst xci =
|
||||||
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
||||||
nsp = xci->GetSecurePartitionNSP();
|
nsp = xci->GetSecurePartitionNSP();
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,7 +154,7 @@ public:
|
||||||
return InstallError;
|
return InstallError;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
|
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
|
||||||
*nsp, true, copy_func);
|
*nsp, true, copy_func);
|
||||||
|
|
||||||
switch (res) {
|
switch (res) {
|
||||||
|
@ -233,10 +237,11 @@ public:
|
||||||
m_system.SetFilesystem(m_vfs);
|
m_system.SetFilesystem(m_vfs);
|
||||||
|
|
||||||
// Initialize system.
|
// Initialize system.
|
||||||
auto android_keyboard = std::make_unique<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();
|
||||||
|
Settings::LogSettings();
|
||||||
m_system.HIDCore().ReloadInputDevices();
|
m_system.HIDCore().ReloadInputDevices();
|
||||||
m_system.SetAppletFrontendSet({
|
m_system.SetAppletFrontendSet({
|
||||||
nullptr, // Amiibo Settings
|
nullptr, // Amiibo Settings
|
||||||
|
@ -330,7 +335,7 @@ public:
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
{
|
{
|
||||||
std::unique_lock lock(m_mutex);
|
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
||||||
[&]() { return !m_is_running; })) {
|
[&]() { return !m_is_running; })) {
|
||||||
// Emulation halted.
|
// Emulation halted.
|
||||||
|
@ -362,7 +367,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsHandheldOnly() {
|
bool IsHandheldOnly() {
|
||||||
const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
||||||
|
|
||||||
if (npad_style_set.fullkey == 1) {
|
if (npad_style_set.fullkey == 1) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -375,17 +380,17 @@ public:
|
||||||
return !Settings::values.use_docked_mode.GetValue();
|
return !Settings::values.use_docked_mode.GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDeviceType(int index, int type) {
|
void SetDeviceType([[maybe_unused]] int index, int type) {
|
||||||
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnGamepadConnectEvent(int index) {
|
void OnGamepadConnectEvent([[maybe_unused]] int index) {
|
||||||
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
// Ensure that player1 is configured correctly and handheld disconnected
|
// Ensure that player1 is configured correctly and handheld disconnected
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
||||||
auto handheld =
|
jauto handheld =
|
||||||
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
||||||
|
@ -397,7 +402,8 @@ public:
|
||||||
|
|
||||||
// Ensure that handheld is configured correctly and player 1 disconnected
|
// Ensure that handheld is configured correctly and player 1 disconnected
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
||||||
auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
jauto player1 =
|
||||||
|
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
||||||
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||||
|
@ -411,8 +417,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnGamepadDisconnectEvent(int index) {
|
void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
||||||
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
controller->Disconnect();
|
controller->Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +434,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
RomMetadata GetRomMetadata(const std::string& path) {
|
RomMetadata GetRomMetadata(const std::string& path) {
|
||||||
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
||||||
return search->second;
|
return search->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,14 +442,14 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||||
const auto file = Core::GetGameFileFromPath(m_vfs, path);
|
jconst file = Core::GetGameFileFromPath(m_vfs, path);
|
||||||
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||||
|
|
||||||
RomMetadata entry;
|
RomMetadata entry;
|
||||||
loader->ReadTitle(entry.title);
|
loader->ReadTitle(entry.title);
|
||||||
loader->ReadIcon(entry.icon);
|
loader->ReadIcon(entry.icon);
|
||||||
if (loader->GetFileType() == Loader::FileType::NRO) {
|
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||||
auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
|
jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||||
entry.isHomebrew = loader_nro->IsHomebrew();
|
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||||
} else {
|
} else {
|
||||||
entry.isHomebrew = false;
|
entry.isHomebrew = false;
|
||||||
|
@ -514,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||||
|
|
||||||
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
|
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
|
||||||
|
|
||||||
const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath);
|
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
|
||||||
if (result != Core::SystemResultStatus::Success) {
|
if (result != Core::SystemResultStatus::Success) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -526,83 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
|
||||||
[[maybe_unused]] jclass clazz,
|
[[maybe_unused]] jobject surf) {
|
||||||
jobject surf) {
|
|
||||||
EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
|
EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
|
||||||
EmulationSession::GetInstance().SurfaceChanged();
|
EmulationSession::GetInstance().SurfaceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
|
ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
|
||||||
EmulationSession::GetInstance().SetNativeWindow(nullptr);
|
EmulationSession::GetInstance().SetNativeWindow(nullptr);
|
||||||
EmulationSession::GetInstance().SurfaceChanged();
|
EmulationSession::GetInstance().SurfaceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
|
||||||
[[maybe_unused]] jclass clazz,
|
[[maybe_unused]] jstring j_directory) {
|
||||||
jstring j_directory) {
|
|
||||||
Common::FS::SetAppDirectory(GetJString(env, j_directory));
|
Common::FS::SetAppDirectory(GetJString(env, j_directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env,
|
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
|
||||||
[[maybe_unused]] jclass clazz,
|
[[maybe_unused]] jstring j_file) {
|
||||||
jstring j_file) {
|
|
||||||
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
|
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(
|
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
|
||||||
JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
|
jstring hook_lib_dir,
|
||||||
jstring custom_driver_name, jstring file_redirect_dir) {
|
jstring custom_driver_dir,
|
||||||
|
jstring custom_driver_name,
|
||||||
|
jstring file_redirect_dir) {
|
||||||
EmulationSession::GetInstance().InitializeGpuDriver(
|
EmulationSession::GetInstance().InitializeGpuDriver(
|
||||||
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
|
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
|
||||||
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
|
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env,
|
[[maybe_unused]] static bool CheckKgslPresent() {
|
||||||
[[maybe_unused]] jclass clazz) {
|
constexpr auto KgslPath{"/dev/kgsl-3d0"};
|
||||||
|
|
||||||
|
return access(KgslPath, F_OK) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] bool SupportsCustomDriver() {
|
||||||
|
return android_get_device_api_level() >= 28 && CheckKgslPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
|
||||||
|
JNIEnv* env, jobject instance) {
|
||||||
|
#ifdef ARCHITECTURE_arm64
|
||||||
|
// If the KGSL device exists custom drivers can be loaded using adrenotools
|
||||||
|
return SupportsCustomDriver();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
|
||||||
Core::Crypto::KeyManager::Instance().ReloadKeys();
|
Core::Crypto::KeyManager::Instance().ReloadKeys();
|
||||||
return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
|
return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
EmulationSession::GetInstance().UnPauseEmulation();
|
EmulationSession::GetInstance().UnPauseEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
EmulationSession::GetInstance().PauseEmulation();
|
EmulationSession::GetInstance().PauseEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
EmulationSession::GetInstance().HaltEmulation();
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
EmulationSession::GetInstance().ResetRomMetadata();
|
EmulationSession::GetInstance().ResetRomMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
|
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
|
return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
Settings::values.audio_muted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
|
||||||
|
Settings::values.audio_muted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
|
||||||
|
return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
|
||||||
return EmulationSession::GetInstance().IsHandheldOnly();
|
return EmulationSession::GetInstance().IsHandheldOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jint j_device, jint j_type) {
|
jint j_device, jint j_type) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
|
EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
|
||||||
|
@ -610,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jint j_device) {
|
jint j_device) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
|
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
|
||||||
|
@ -619,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) {
|
jint j_device) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
|
EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
|
||||||
}
|
}
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
jint j_device, jint j_button,
|
||||||
[[maybe_unused]] jint j_device,
|
jint action) {
|
||||||
jint j_button, jint action) {
|
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
// Ensure gamepad is connected
|
// Ensure gamepad is connected
|
||||||
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
|
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
|
||||||
|
@ -639,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jint j_device, jint stick_id,
|
jint j_device, jint stick_id,
|
||||||
jfloat x, jfloat y) {
|
jfloat x, jfloat y) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
|
@ -650,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
|
||||||
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device,
|
JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
|
||||||
jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x,
|
jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
|
||||||
jfloat accel_y, jfloat accel_z) {
|
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
|
EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
|
||||||
j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
|
j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
|
||||||
|
@ -660,8 +683,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jbyteArray j_data) {
|
jbyteArray j_data) {
|
||||||
jboolean isCopy{false};
|
jboolean isCopy{false};
|
||||||
std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
|
std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
|
||||||
|
@ -673,108 +695,92 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNI
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().Window().OnRemoveNfcTag();
|
EmulationSession::GetInstance().Window().OnRemoveNfcTag();
|
||||||
}
|
}
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
|
||||||
[[maybe_unused]] jclass clazz, jint id,
|
|
||||||
jfloat x, jfloat y) {
|
jfloat x, jfloat y) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
|
EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
|
||||||
[[maybe_unused]] jclass clazz, jint id,
|
|
||||||
jfloat x, jfloat y) {
|
jfloat x, jfloat y) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
|
EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
|
||||||
[[maybe_unused]] jclass clazz, jint id) {
|
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
EmulationSession::GetInstance().Window().OnTouchReleased(id);
|
EmulationSession::GetInstance().Window().OnTouchReleased(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env,
|
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
jstring j_filename) {
|
||||||
[[maybe_unused]] jstring j_filename) {
|
jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
|
||||||
auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
|
|
||||||
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()));
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
jstring j_filename) {
|
||||||
[[maybe_unused]] jstring j_filename) {
|
jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
|
||||||
auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
|
|
||||||
return env->NewStringUTF(title.c_str());
|
return env->NewStringUTF(title.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jstring j_filename) {
|
jstring j_filename) {
|
||||||
return j_filename;
|
return j_filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jstring j_filename) {
|
jstring j_filename) {
|
||||||
return j_filename;
|
return j_filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
jstring j_filename) {
|
||||||
[[maybe_unused]] jstring j_filename) {
|
|
||||||
return env->NewStringUTF("");
|
return env->NewStringUTF("");
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
jstring j_filename) {
|
||||||
[[maybe_unused]] jstring j_filename) {
|
|
||||||
return env->NewStringUTF("");
|
return env->NewStringUTF("");
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
jstring j_filename) {
|
||||||
[[maybe_unused]] jstring j_filename) {
|
|
||||||
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
|
|
||||||
// Create the default config.ini.
|
// Create the default config.ini.
|
||||||
Config{};
|
Config{};
|
||||||
// Initialize the emulated system.
|
// Initialize the emulated system.
|
||||||
EmulationSession::GetInstance().System().Initialize();
|
EmulationSession::GetInstance().System().Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env,
|
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
||||||
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file,
|
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
|
||||||
[[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
Config{};
|
Config{};
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jstring j_game_id, jstring j_section,
|
jstring j_game_id, jstring j_section,
|
||||||
jstring j_key) {
|
jstring j_key) {
|
||||||
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
||||||
|
@ -788,8 +794,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JN
|
||||||
return env->NewStringUTF("");
|
return env->NewStringUTF("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jstring j_game_id, jstring j_section,
|
jstring j_game_id, jstring j_section,
|
||||||
jstring j_key, jstring j_value) {
|
jstring j_key, jstring j_value) {
|
||||||
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
||||||
|
@ -803,20 +808,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEn
|
||||||
env->ReleaseStringUTFChars(j_value, value.data());
|
env->ReleaseStringUTFChars(j_value, value.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jstring j_game_id) {
|
jstring j_game_id) {
|
||||||
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
|
||||||
|
|
||||||
env->ReleaseStringUTFChars(j_game_id, game_id.data());
|
env->ReleaseStringUTFChars(j_game_id, game_id.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env,
|
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
jdoubleArray j_stats = env->NewDoubleArray(4);
|
jdoubleArray j_stats = env->NewDoubleArray(4);
|
||||||
|
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
const auto results = EmulationSession::GetInstance().PerfStats();
|
jconst results = EmulationSession::GetInstance().PerfStats();
|
||||||
|
|
||||||
// Converting the structure into an array makes it easier to pass it to the frontend
|
// Converting the structure into an array makes it easier to pass it to the frontend
|
||||||
double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
|
double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
|
||||||
|
@ -828,11 +831,11 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]]
|
||||||
return j_stats;
|
return j_stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(
|
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
|
||||||
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {}
|
jclass clazz,
|
||||||
|
jstring j_path) {}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jclass clazz,
|
|
||||||
jstring j_path) {
|
jstring j_path) {
|
||||||
const std::string path = GetJString(env, j_path);
|
const std::string path = GetJString(env, j_path);
|
||||||
|
|
||||||
|
@ -843,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] jclass clazz) {
|
|
||||||
LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc);
|
LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc);
|
||||||
LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
|
LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
// Function calls from the Java side
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jstring j_device,
|
|
||||||
jstring j_type);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_device);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_device);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jbyteArray j_data);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jfloat x, jfloat y,
|
|
||||||
jboolean pressed);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz,
|
|
||||||
jfloat x, jfloat y);
|
|
||||||
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jstring j_file);
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename);
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jstring j_filename);
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename);
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jstring j_filename);
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jstring j_filename);
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jstring j_directory);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver(
|
|
||||||
JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
|
|
||||||
jstring custom_driver_name, jstring file_redirect_dir);
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory(
|
|
||||||
JNIEnv* env, jclass clazz, jstring path_);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jstring path);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz,
|
|
||||||
jboolean enable);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(
|
|
||||||
JNIEnv* env, jclass clazz, jint layout_option, jint rotation);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_path);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
|
||||||
jclass clazz,
|
|
||||||
jobject surf);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_game_id);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key,
|
|
||||||
jstring j_value);
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting(
|
|
||||||
JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
|
|
||||||
|
|
||||||
JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env,
|
|
||||||
jclass clazz);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText(
|
|
||||||
JNIEnv* env, jclass clazz, jstring j_text);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput(
|
|
||||||
JNIEnv* env, jclass clazz, jint j_key_code);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
|
||||||
|
</vector>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
|
||||||
|
</vector>
|
|
@ -104,12 +104,14 @@
|
||||||
<string name="share_log_missing">No log file found</string>
|
<string name="share_log_missing">No log file found</string>
|
||||||
<string name="install_game_content">Install game content</string>
|
<string name="install_game_content">Install game content</string>
|
||||||
<string name="install_game_content_description">Install game updates or DLC</string>
|
<string name="install_game_content_description">Install game updates or DLC</string>
|
||||||
<string name="install_game_content_failure">Error installing file to NAND</string>
|
<string name="install_game_content_failure">Error installing file(s) to NAND</string>
|
||||||
<string name="install_game_content_failure_description">Game content installation failed. Please ensure content is valid and that the prod.keys file is installed.</string>
|
<string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string>
|
||||||
<string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts. Please select an update or DLC instead.</string>
|
<string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string>
|
||||||
<string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string>
|
<string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string>
|
||||||
<string name="install_game_content_success">Game content installed successfully</string>
|
<string name="install_game_content_failed_count">%1$d installation error(s)</string>
|
||||||
<string name="install_game_content_success_overwrite">Game content was overwritten successfully</string>
|
<string name="install_game_content_success">Game content(s) installed successfully</string>
|
||||||
|
<string name="install_game_content_success_install">%1$d installed successfully</string>
|
||||||
|
<string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
|
||||||
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
|
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
|
||||||
|
|
||||||
<!-- About screen strings -->
|
<!-- About screen strings -->
|
||||||
|
@ -270,6 +272,7 @@
|
||||||
<string name="fatal_error">Fatal Error</string>
|
<string name="fatal_error">Fatal Error</string>
|
||||||
<string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
|
<string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
|
||||||
<string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
|
<string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
|
||||||
|
<string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string>
|
||||||
|
|
||||||
<!-- Region Names -->
|
<!-- Region Names -->
|
||||||
<string name="region_japan">Japan</string>
|
<string name="region_japan">Japan</string>
|
||||||
|
@ -300,6 +303,15 @@
|
||||||
<string name="language_traditional_chinese">Traditional Chinese (正體中文)</string>
|
<string name="language_traditional_chinese">Traditional Chinese (正體中文)</string>
|
||||||
<string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string>
|
<string name="language_brazilian_portuguese">Brazilian Portuguese (Português do Brasil)</string>
|
||||||
|
|
||||||
|
<!-- Memory Sizes -->
|
||||||
|
<string name="memory_byte">Byte</string>
|
||||||
|
<string name="memory_kilobyte">KB</string>
|
||||||
|
<string name="memory_megabyte">MB</string>
|
||||||
|
<string name="memory_gigabyte">GB</string>
|
||||||
|
<string name="memory_terabyte">TB</string>
|
||||||
|
<string name="memory_petabyte">PB</string>
|
||||||
|
<string name="memory_exabyte">EB</string>
|
||||||
|
|
||||||
<!-- Renderer APIs -->
|
<!-- Renderer APIs -->
|
||||||
<string name="renderer_vulkan">Vulkan</string>
|
<string name="renderer_vulkan">Vulkan</string>
|
||||||
<string name="renderer_none">None</string>
|
<string name="renderer_none">None</string>
|
||||||
|
@ -387,6 +399,8 @@
|
||||||
<string name="picture_in_picture_description">Minimize window when placed in the background</string>
|
<string name="picture_in_picture_description">Minimize window when placed in the background</string>
|
||||||
<string name="pause">Pause</string>
|
<string name="pause">Pause</string>
|
||||||
<string name="play">Play</string>
|
<string name="play">Play</string>
|
||||||
|
<string name="mute">Mute</string>
|
||||||
|
<string name="unmute">Unmute</string>
|
||||||
|
|
||||||
<!-- Licenses screen strings -->
|
<!-- Licenses screen strings -->
|
||||||
<string name="licenses">Licenses</string>
|
<string name="licenses">Licenses</string>
|
||||||
|
|
|
@ -15,3 +15,6 @@ android.useAndroidX=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
kotlin.parallel.tasks.in.project=true
|
kotlin.parallel.tasks.in.project=true
|
||||||
android.defaults.buildfeatures.buildconfig=true
|
android.defaults.buildfeatures.buildconfig=true
|
||||||
|
|
||||||
|
# Android Gradle plugin 8.0.2
|
||||||
|
android.suppressUnsupportedCompileSdk=34
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <boost/container/static_vector.hpp>
|
||||||
|
|
||||||
#include "audio_buffer.h"
|
#include "audio_buffer.h"
|
||||||
#include "audio_core/device/device_session.h"
|
#include "audio_core/device/device_session.h"
|
||||||
|
@ -48,7 +49,7 @@ public:
|
||||||
*
|
*
|
||||||
* @param out_buffers - The buffers which were registered.
|
* @param out_buffers - The buffers which were registered.
|
||||||
*/
|
*/
|
||||||
void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) {
|
void RegisterBuffers(boost::container::static_vector<AudioBuffer, N>& out_buffers) {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit),
|
const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit),
|
||||||
BufferAppendLimit - registered_count)};
|
BufferAppendLimit - registered_count)};
|
||||||
|
@ -162,7 +163,8 @@ public:
|
||||||
* @param max_buffers - Maximum number of buffers to released.
|
* @param max_buffers - Maximum number of buffers to released.
|
||||||
* @return The number of buffers released.
|
* @return The number of buffers released.
|
||||||
*/
|
*/
|
||||||
u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) {
|
u32 GetRegisteredAppendedBuffers(
|
||||||
|
boost::container::static_vector<AudioBuffer, N>& buffers_flushed, u32 max_buffers) {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
if (registered_count + appended_count == 0) {
|
if (registered_count + appended_count == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -270,7 +272,7 @@ public:
|
||||||
*/
|
*/
|
||||||
bool FlushBuffers(u32& buffers_released) {
|
bool FlushBuffers(u32& buffers_released) {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
std::vector<AudioBuffer> buffers_flushed{};
|
boost::container::static_vector<AudioBuffer, N> buffers_flushed{};
|
||||||
|
|
||||||
buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit);
|
buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit);
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ void DeviceSession::ClearBuffers() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
|
void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) {
|
||||||
for (const auto& buffer : buffers) {
|
for (const auto& buffer : buffers) {
|
||||||
Sink::SinkBuffer new_buffer{
|
Sink::SinkBuffer new_buffer{
|
||||||
.frames = buffer.size / (channel_count * sizeof(s16)),
|
.frames = buffer.size / (channel_count * sizeof(s16)),
|
||||||
|
@ -88,13 +88,13 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const {
|
||||||
.consumed = false,
|
.consumed = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tmp_samples.resize_destructive(buffer.size / sizeof(s16));
|
||||||
if (type == Sink::StreamType::In) {
|
if (type == Sink::StreamType::In) {
|
||||||
std::vector<s16> samples{};
|
stream->AppendBuffer(new_buffer, tmp_samples);
|
||||||
stream->AppendBuffer(new_buffer, samples);
|
|
||||||
} else {
|
} else {
|
||||||
std::vector<s16> samples(buffer.size / sizeof(s16));
|
system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(),
|
||||||
system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, samples.data(), buffer.size);
|
buffer.size);
|
||||||
stream->AppendBuffer(new_buffer, samples);
|
stream->AppendBuffer(new_buffer, tmp_samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "audio_core/common/common.h"
|
#include "audio_core/common/common.h"
|
||||||
#include "audio_core/sink/sink.h"
|
#include "audio_core/sink/sink.h"
|
||||||
|
#include "common/scratch_buffer.h"
|
||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -62,7 +63,7 @@ public:
|
||||||
*
|
*
|
||||||
* @param buffers - The buffers to play.
|
* @param buffers - The buffers to play.
|
||||||
*/
|
*/
|
||||||
void AppendBuffers(std::span<const AudioBuffer> buffers) const;
|
void AppendBuffers(std::span<const AudioBuffer> buffers);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Audio In only) Pop samples from the backend, and write them back to this buffer's address.
|
* (Audio In only) Pop samples from the backend, and write them back to this buffer's address.
|
||||||
|
@ -146,8 +147,8 @@ private:
|
||||||
std::shared_ptr<Core::Timing::EventType> thread_event;
|
std::shared_ptr<Core::Timing::EventType> thread_event;
|
||||||
/// Is this session initialised?
|
/// Is this session initialised?
|
||||||
bool initialized{};
|
bool initialized{};
|
||||||
/// Buffer queue
|
/// Temporary sample buffer
|
||||||
std::vector<AudioBuffer> buffer_queue{};
|
Common::ScratchBuffer<s16> tmp_samples{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "audio_core/audio_event.h"
|
#include "audio_core/audio_event.h"
|
||||||
#include "audio_core/audio_manager.h"
|
#include "audio_core/audio_manager.h"
|
||||||
#include "audio_core/in/audio_in_system.h"
|
#include "audio_core/in/audio_in_system.h"
|
||||||
|
@ -89,7 +90,7 @@ Result System::Start() {
|
||||||
session->Start();
|
session->Start();
|
||||||
state = State::Started;
|
state = State::Started;
|
||||||
|
|
||||||
std::vector<AudioBuffer> buffers_to_flush{};
|
boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
|
||||||
buffers.RegisterBuffers(buffers_to_flush);
|
buffers.RegisterBuffers(buffers_to_flush);
|
||||||
session->AppendBuffers(buffers_to_flush);
|
session->AppendBuffers(buffers_to_flush);
|
||||||
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
|
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
|
||||||
|
@ -134,7 +135,7 @@ bool System::AppendBuffer(const AudioInBuffer& buffer, const u64 tag) {
|
||||||
|
|
||||||
void System::RegisterBuffers() {
|
void System::RegisterBuffers() {
|
||||||
if (state == State::Started) {
|
if (state == State::Started) {
|
||||||
std::vector<AudioBuffer> registered_buffers{};
|
boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
|
||||||
buffers.RegisterBuffers(registered_buffers);
|
buffers.RegisterBuffers(registered_buffers);
|
||||||
session->AppendBuffers(registered_buffers);
|
session->AppendBuffers(registered_buffers);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ Result System::Start() {
|
||||||
session->Start();
|
session->Start();
|
||||||
state = State::Started;
|
state = State::Started;
|
||||||
|
|
||||||
std::vector<AudioBuffer> buffers_to_flush{};
|
boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
|
||||||
buffers.RegisterBuffers(buffers_to_flush);
|
buffers.RegisterBuffers(buffers_to_flush);
|
||||||
session->AppendBuffers(buffers_to_flush);
|
session->AppendBuffers(buffers_to_flush);
|
||||||
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
|
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
|
||||||
|
@ -134,7 +134,7 @@ bool System::AppendBuffer(const AudioOutBuffer& buffer, u64 tag) {
|
||||||
|
|
||||||
void System::RegisterBuffers() {
|
void System::RegisterBuffers() {
|
||||||
if (state == State::Started) {
|
if (state == State::Started) {
|
||||||
std::vector<AudioBuffer> registered_buffers{};
|
boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
|
||||||
buffers.RegisterBuffers(registered_buffers);
|
buffers.RegisterBuffers(registered_buffers);
|
||||||
session->AppendBuffers(registered_buffers);
|
session->AppendBuffers(registered_buffers);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace AudioCore::AudioRenderer::ADSP {
|
namespace AudioCore::AudioRenderer::ADSP {
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
|
||||||
|
|
||||||
MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
|
MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
|
||||||
|
|
||||||
|
@ -144,6 +143,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
|
||||||
|
|
||||||
mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK);
|
mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK);
|
||||||
|
|
||||||
|
// 0.12 seconds (2304000 / 19200000)
|
||||||
constexpr u64 max_process_time{2'304'000ULL};
|
constexpr u64 max_process_time{2'304'000ULL};
|
||||||
|
|
||||||
while (!stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
|
@ -184,8 +184,7 @@ void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
|
||||||
u64 max_time{max_process_time};
|
u64 max_time{max_process_time};
|
||||||
if (index == 1 && command_buffer.applet_resource_user_id ==
|
if (index == 1 && command_buffer.applet_resource_user_id ==
|
||||||
mailbox->GetCommandBuffer(0).applet_resource_user_id) {
|
mailbox->GetCommandBuffer(0).applet_resource_user_id) {
|
||||||
max_time = max_process_time -
|
max_time = max_process_time - render_times_taken[0];
|
||||||
Core::Timing::CyclesToNs(render_times_taken[0]).count();
|
|
||||||
if (render_times_taken[0] > max_process_time) {
|
if (render_times_taken[0] > max_process_time) {
|
||||||
max_time = 0;
|
max_time = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace AudioCore::AudioRenderer::ADSP {
|
namespace AudioCore::AudioRenderer::ADSP {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "audio_core/renderer/command/resample/resample.h"
|
#include "audio_core/renderer/command/resample/resample.h"
|
||||||
#include "common/fixed_point.h"
|
#include "common/fixed_point.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/scratch_buffer.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace AudioCore::AudioRenderer {
|
namespace AudioCore::AudioRenderer {
|
||||||
|
@ -27,6 +28,7 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||||
const DecodeArg& req) {
|
const DecodeArg& req) {
|
||||||
|
std::array<T, TempBufferSize> tmp_samples{};
|
||||||
constexpr s32 min{std::numeric_limits<s16>::min()};
|
constexpr s32 min{std::numeric_limits<s16>::min()};
|
||||||
constexpr s32 max{std::numeric_limits<s16>::max()};
|
constexpr s32 max{std::numeric_limits<s16>::max()};
|
||||||
|
|
||||||
|
@ -49,18 +51,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||||
const u64 size{channel_count * samples_to_decode};
|
const u64 size{channel_count * samples_to_decode};
|
||||||
const u64 size_bytes{size * sizeof(T)};
|
const u64 size_bytes{size * sizeof(T)};
|
||||||
|
|
||||||
std::vector<T> samples(size);
|
memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes);
|
||||||
memory.ReadBlockUnsafe(source, samples.data(), size_bytes);
|
|
||||||
|
|
||||||
if constexpr (std::is_floating_point_v<T>) {
|
if constexpr (std::is_floating_point_v<T>) {
|
||||||
for (u32 i = 0; i < samples_to_decode; i++) {
|
for (u32 i = 0; i < samples_to_decode; i++) {
|
||||||
auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
|
auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
|
||||||
std::numeric_limits<s16>::max())};
|
std::numeric_limits<s16>::max())};
|
||||||
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
|
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (u32 i = 0; i < samples_to_decode; i++) {
|
for (u32 i = 0; i < samples_to_decode; i++) {
|
||||||
out_buffer[i] = samples[i * channel_count + req.target_channel];
|
out_buffer[i] = tmp_samples[i * channel_count + req.target_channel];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
@ -73,17 +74,16 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
|
const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
|
||||||
std::vector<T> samples(samples_to_decode);
|
memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T));
|
||||||
memory.ReadBlockUnsafe(source, samples.data(), samples_to_decode * sizeof(T));
|
|
||||||
|
|
||||||
if constexpr (std::is_floating_point_v<T>) {
|
if constexpr (std::is_floating_point_v<T>) {
|
||||||
for (u32 i = 0; i < samples_to_decode; i++) {
|
for (u32 i = 0; i < samples_to_decode; i++) {
|
||||||
auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
|
auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] *
|
||||||
std::numeric_limits<s16>::max())};
|
std::numeric_limits<s16>::max())};
|
||||||
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
|
out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16));
|
std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,7 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||||
*/
|
*/
|
||||||
static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||||
const DecodeArg& req) {
|
const DecodeArg& req) {
|
||||||
|
std::array<u8, TempBufferSize> wavebuffer{};
|
||||||
constexpr u32 SamplesPerFrame{14};
|
constexpr u32 SamplesPerFrame{14};
|
||||||
constexpr u32 NibblesPerFrame{16};
|
constexpr u32 NibblesPerFrame{16};
|
||||||
|
|
||||||
|
@ -138,9 +139,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
|
const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
|
||||||
std::vector<u8> wavebuffer(size);
|
memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size);
|
||||||
memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(),
|
|
||||||
wavebuffer.size());
|
|
||||||
|
|
||||||
auto context{req.adpcm_context};
|
auto context{req.adpcm_context};
|
||||||
auto header{context->header};
|
auto header{context->header};
|
||||||
|
@ -258,7 +257,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
|
||||||
u32 offset{voice_state.offset};
|
u32 offset{voice_state.offset};
|
||||||
|
|
||||||
auto output_buffer{args.output};
|
auto output_buffer{args.output};
|
||||||
std::vector<s16> temp_buffer(TempBufferSize, 0);
|
std::array<s16, TempBufferSize> temp_buffer{};
|
||||||
|
|
||||||
while (remaining_sample_count > 0) {
|
while (remaining_sample_count > 0) {
|
||||||
const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)};
|
const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)};
|
||||||
|
|
|
@ -44,8 +44,8 @@ static void InitializeCompressorEffect(const CompressorInfo::ParameterVersion2&
|
||||||
|
|
||||||
static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params,
|
static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& params,
|
||||||
CompressorInfo::State& state, bool enabled,
|
CompressorInfo::State& state, bool enabled,
|
||||||
std::vector<std::span<const s32>> input_buffers,
|
std::span<std::span<const s32>> input_buffers,
|
||||||
std::vector<std::span<s32>> output_buffers, u32 sample_count) {
|
std::span<std::span<s32>> output_buffers, u32 sample_count) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
auto state_00{state.unk_00};
|
auto state_00{state.unk_00};
|
||||||
auto state_04{state.unk_04};
|
auto state_04{state.unk_04};
|
||||||
|
@ -124,8 +124,8 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
|
void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
std::array<std::span<const s32>, MaxChannels> input_buffers{};
|
||||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
std::array<std::span<s32>, MaxChannels> output_buffers{};
|
||||||
|
|
||||||
for (s16 i = 0; i < parameter.channel_count; i++) {
|
for (s16 i = 0; i < parameter.channel_count; i++) {
|
||||||
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||||
|
|
|
@ -51,7 +51,7 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
|
||||||
state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor();
|
state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor();
|
||||||
state.delay_lines[channel].sample_count = sample_count.to_int_floor();
|
state.delay_lines[channel].sample_count = sample_count.to_int_floor();
|
||||||
state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0);
|
state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0);
|
||||||
if (state.delay_lines[channel].buffer.size() == 0) {
|
if (state.delay_lines[channel].sample_count == 0) {
|
||||||
state.delay_lines[channel].buffer.push_back(0);
|
state.delay_lines[channel].buffer.push_back(0);
|
||||||
}
|
}
|
||||||
state.delay_lines[channel].buffer_pos = 0;
|
state.delay_lines[channel].buffer_pos = 0;
|
||||||
|
@ -74,8 +74,8 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
|
||||||
*/
|
*/
|
||||||
template <size_t NumChannels>
|
template <size_t NumChannels>
|
||||||
static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
|
static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
|
||||||
std::vector<std::span<const s32>>& inputs,
|
std::span<std::span<const s32>> inputs, std::span<std::span<s32>> outputs,
|
||||||
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
|
const u32 sample_count) {
|
||||||
for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
|
for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
|
||||||
std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};
|
std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};
|
||||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||||
|
@ -153,8 +153,8 @@ static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::St
|
||||||
* @param sample_count - Number of samples to process.
|
* @param sample_count - Number of samples to process.
|
||||||
*/
|
*/
|
||||||
static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
|
static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
|
||||||
const bool enabled, std::vector<std::span<const s32>>& inputs,
|
const bool enabled, std::span<std::span<const s32>> inputs,
|
||||||
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
|
std::span<std::span<s32>> outputs, const u32 sample_count) {
|
||||||
|
|
||||||
if (!IsChannelCountValid(params.channel_count)) {
|
if (!IsChannelCountValid(params.channel_count)) {
|
||||||
LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count);
|
LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count);
|
||||||
|
@ -208,8 +208,8 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce
|
||||||
}
|
}
|
||||||
|
|
||||||
void DelayCommand::Process(const ADSP::CommandListProcessor& processor) {
|
void DelayCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
std::array<std::span<const s32>, MaxChannels> input_buffers{};
|
||||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
std::array<std::span<s32>, MaxChannels> output_buffers{};
|
||||||
|
|
||||||
for (s16 i = 0; i < parameter.channel_count; i++) {
|
for (s16 i = 0; i < parameter.channel_count; i++) {
|
||||||
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||||
|
|
|
@ -408,8 +408,8 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
|
||||||
}
|
}
|
||||||
|
|
||||||
void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
|
void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
std::array<std::span<const s32>, MaxChannels> input_buffers{};
|
||||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
std::array<std::span<s32>, MaxChannels> output_buffers{};
|
||||||
|
|
||||||
for (u32 i = 0; i < parameter.channel_count; i++) {
|
for (u32 i = 0; i < parameter.channel_count; i++) {
|
||||||
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||||
|
|
|
@ -47,8 +47,8 @@ static void InitializeLightLimiterEffect(const LightLimiterInfo::ParameterVersio
|
||||||
*/
|
*/
|
||||||
static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,
|
static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,
|
||||||
LightLimiterInfo::State& state, const bool enabled,
|
LightLimiterInfo::State& state, const bool enabled,
|
||||||
std::vector<std::span<const s32>>& inputs,
|
std::span<std::span<const s32>> inputs,
|
||||||
std::vector<std::span<s32>>& outputs, const u32 sample_count,
|
std::span<std::span<s32>> outputs, const u32 sample_count,
|
||||||
LightLimiterInfo::StatisticsInternal* statistics) {
|
LightLimiterInfo::StatisticsInternal* statistics) {
|
||||||
constexpr s64 min{std::numeric_limits<s32>::min()};
|
constexpr s64 min{std::numeric_limits<s32>::min()};
|
||||||
constexpr s64 max{std::numeric_limits<s32>::max()};
|
constexpr s64 max{std::numeric_limits<s32>::max()};
|
||||||
|
@ -147,8 +147,8 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
|
void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
std::array<std::span<const s32>, MaxChannels> input_buffers{};
|
||||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
std::array<std::span<s32>, MaxChannels> output_buffers{};
|
||||||
|
|
||||||
for (u32 i = 0; i < parameter.channel_count; i++) {
|
for (u32 i = 0; i < parameter.channel_count; i++) {
|
||||||
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||||
|
@ -190,8 +190,8 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
|
void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
std::array<std::span<const s32>, MaxChannels> input_buffers{};
|
||||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
std::array<std::span<s32>, MaxChannels> output_buffers{};
|
||||||
|
|
||||||
for (u32 i = 0; i < parameter.channel_count; i++) {
|
for (u32 i = 0; i < parameter.channel_count; i++) {
|
||||||
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||||
|
|
|
@ -250,8 +250,8 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine&
|
||||||
*/
|
*/
|
||||||
template <size_t NumChannels>
|
template <size_t NumChannels>
|
||||||
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
|
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
|
||||||
std::vector<std::span<const s32>>& inputs,
|
std::span<std::span<const s32>> inputs,
|
||||||
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
|
std::span<std::span<s32>> outputs, const u32 sample_count) {
|
||||||
static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
|
static constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
};
|
};
|
||||||
|
@ -369,8 +369,8 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever
|
||||||
* @param sample_count - Number of samples to process.
|
* @param sample_count - Number of samples to process.
|
||||||
*/
|
*/
|
||||||
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
|
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
|
||||||
const bool enabled, std::vector<std::span<const s32>>& inputs,
|
const bool enabled, std::span<std::span<const s32>> inputs,
|
||||||
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
|
std::span<std::span<s32>> outputs, const u32 sample_count) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
switch (params.channel_count) {
|
switch (params.channel_count) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -412,8 +412,8 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
|
void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
std::array<std::span<const s32>, MaxChannels> input_buffers{};
|
||||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
std::array<std::span<s32>, MaxChannels> output_buffers{};
|
||||||
|
|
||||||
for (u32 i = 0; i < parameter.channel_count; i++) {
|
for (u32 i = 0; i < parameter.channel_count; i++) {
|
||||||
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "audio_core/renderer/command/performance/performance.h"
|
#include "audio_core/renderer/command/performance/performance.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
|
||||||
|
|
||||||
namespace AudioCore::AudioRenderer {
|
namespace AudioCore::AudioRenderer {
|
||||||
|
|
||||||
|
@ -18,20 +17,18 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
auto base{entry_address.translated_address};
|
auto base{entry_address.translated_address};
|
||||||
if (state == PerformanceState::Start) {
|
if (state == PerformanceState::Start) {
|
||||||
auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)};
|
auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)};
|
||||||
*start_time_ptr = static_cast<u32>(
|
*start_time_ptr =
|
||||||
Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() -
|
static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
|
||||||
processor.start_time - processor.current_processing_time)
|
processor.current_processing_time);
|
||||||
.count());
|
|
||||||
} else if (state == PerformanceState::Stop) {
|
} else if (state == PerformanceState::Stop) {
|
||||||
auto processed_time_ptr{
|
auto processed_time_ptr{
|
||||||
reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)};
|
reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)};
|
||||||
auto entry_count_ptr{
|
auto entry_count_ptr{
|
||||||
reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)};
|
reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)};
|
||||||
|
|
||||||
*processed_time_ptr = static_cast<u32>(
|
*processed_time_ptr =
|
||||||
Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() -
|
static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time -
|
||||||
processor.start_time - processor.current_processing_time)
|
processor.current_processing_time);
|
||||||
.count());
|
|
||||||
(*entry_count_ptr)++;
|
(*entry_count_ptr)++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
|
||||||
constexpr s32 min{std::numeric_limits<s16>::min()};
|
constexpr s32 min{std::numeric_limits<s16>::min()};
|
||||||
constexpr s32 max{std::numeric_limits<s16>::max()};
|
constexpr s32 max{std::numeric_limits<s16>::max()};
|
||||||
|
|
||||||
std::vector<s16> output(processor.sample_count);
|
std::array<s16, TargetSampleCount * MaxChannels> output{};
|
||||||
for (u32 channel = 0; channel < input_count; channel++) {
|
for (u32 channel = 0; channel < input_count; channel++) {
|
||||||
auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count,
|
auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count,
|
||||||
processor.sample_count)};
|
processor.sample_count)};
|
||||||
|
@ -33,7 +33,7 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
|
||||||
}
|
}
|
||||||
|
|
||||||
processor.memory->WriteBlockUnsafe(address + pos, output.data(),
|
processor.memory->WriteBlockUnsafe(address + pos, output.data(),
|
||||||
output.size() * sizeof(s16));
|
processor.sample_count * sizeof(s16));
|
||||||
pos += static_cast<u32>(processor.sample_count * sizeof(s16));
|
pos += static_cast<u32>(processor.sample_count * sizeof(s16));
|
||||||
if (pos >= size) {
|
if (pos >= size) {
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
|
|
@ -33,8 +33,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
.consumed{false},
|
.consumed{false},
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<s16> samples(out_buffer.frames * input_count);
|
std::array<s16, TargetSampleCount * MaxChannels> samples{};
|
||||||
|
|
||||||
for (u32 channel = 0; channel < input_count; channel++) {
|
for (u32 channel = 0; channel < input_count; channel++) {
|
||||||
const auto offset{inputs[channel] * out_buffer.frames};
|
const auto offset{inputs[channel] * out_buffer.frames};
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
out_buffer.tag = reinterpret_cast<u64>(samples.data());
|
out_buffer.tag = reinterpret_cast<u64>(samples.data());
|
||||||
stream->AppendBuffer(out_buffer, samples);
|
stream->AppendBuffer(out_buffer, {samples.data(), out_buffer.frames * input_count});
|
||||||
|
|
||||||
if (stream->IsPaused()) {
|
if (stream->IsPaused()) {
|
||||||
stream->Start();
|
stream->Start();
|
||||||
|
|
|
@ -125,10 +125,10 @@ bool MixContext::TSortInfo(const SplitterContext& splitter_context) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<s32> sorted_results{node_states.GetSortedResuls()};
|
auto sorted_results{node_states.GetSortedResuls()};
|
||||||
const auto result_size{std::min(count, static_cast<s32>(sorted_results.size()))};
|
const auto result_size{std::min(count, static_cast<s32>(sorted_results.second))};
|
||||||
for (s32 i = 0; i < result_size; i++) {
|
for (s32 i = 0; i < result_size; i++) {
|
||||||
sorted_mix_infos[i] = &mix_infos[sorted_results[i]];
|
sorted_mix_infos[i] = &mix_infos[sorted_results.first[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
CalcMixBufferOffset();
|
CalcMixBufferOffset();
|
||||||
|
|
|
@ -134,8 +134,8 @@ u32 NodeStates::GetNodeCount() const {
|
||||||
return node_count;
|
return node_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<s32> NodeStates::GetSortedResuls() const {
|
std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls() const {
|
||||||
return {results.rbegin(), results.rbegin() + result_pos};
|
return {results.rbegin(), result_pos};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::AudioRenderer
|
} // namespace AudioCore::AudioRenderer
|
||||||
|
|
|
@ -175,7 +175,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return Vector of nodes in reverse order.
|
* @return Vector of nodes in reverse order.
|
||||||
*/
|
*/
|
||||||
std::vector<s32> GetSortedResuls() const;
|
std::pair<std::span<u32>::reverse_iterator, size_t> GetSortedResuls() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Number of nodes in the graph
|
/// Number of nodes in the graph
|
||||||
|
|
|
@ -444,6 +444,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
|
|
||||||
const auto start_time{core.CoreTiming().GetClockTicks()};
|
const auto start_time{core.CoreTiming().GetClockTicks()};
|
||||||
|
std::memset(output.data(), 0, output.size());
|
||||||
|
|
||||||
InfoUpdater info_updater(input, output, process_handle, behavior);
|
InfoUpdater info_updater(input, output, process_handle, behavior);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
explicit NullSinkStreamImpl(Core::System& system_, StreamType type_)
|
explicit NullSinkStreamImpl(Core::System& system_, StreamType type_)
|
||||||
: SinkStream{system_, type_} {}
|
: SinkStream{system_, type_} {}
|
||||||
~NullSinkStreamImpl() override {}
|
~NullSinkStreamImpl() override {}
|
||||||
void AppendBuffer(SinkBuffer&, std::vector<s16>&) override {}
|
void AppendBuffer(SinkBuffer&, std::span<s16>) override {}
|
||||||
std::vector<s16> ReleaseBuffer(u64) override {
|
std::vector<s16> ReleaseBuffer(u64) override {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
|
||||||
|
|
||||||
namespace AudioCore::Sink {
|
namespace AudioCore::Sink {
|
||||||
|
|
||||||
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
|
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
|
||||||
if (type == StreamType::In) {
|
if (type == StreamType::In) {
|
||||||
queue.enqueue(buffer);
|
queue.enqueue(buffer);
|
||||||
queued_buffers++;
|
queued_buffers++;
|
||||||
|
@ -67,15 +66,16 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
|
||||||
static_cast<s16>(std::clamp(right_sample, min, max));
|
static_cast<s16>(std::clamp(right_sample, min, max));
|
||||||
}
|
}
|
||||||
|
|
||||||
samples.resize(samples.size() / system_channels * device_channels);
|
samples = samples.subspan(0, samples.size() / system_channels * device_channels);
|
||||||
|
|
||||||
} else if (system_channels == 2 && device_channels == 6) {
|
} else if (system_channels == 2 && device_channels == 6) {
|
||||||
// We need moar samples! Not all games will provide 6 channel audio.
|
// We need moar samples! Not all games will provide 6 channel audio.
|
||||||
// TODO: Implement some upmixing here. Currently just passthrough, with other
|
// TODO: Implement some upmixing here. Currently just passthrough, with other
|
||||||
// channels left as silence.
|
// channels left as silence.
|
||||||
std::vector<s16> new_samples(samples.size() / system_channels * device_channels, 0);
|
auto new_size = samples.size() / system_channels * device_channels;
|
||||||
|
tmp_samples.resize_destructive(new_size);
|
||||||
|
|
||||||
for (u32 read_index = 0, write_index = 0; read_index < samples.size();
|
for (u32 read_index = 0, write_index = 0; read_index < new_size;
|
||||||
read_index += system_channels, write_index += device_channels) {
|
read_index += system_channels, write_index += device_channels) {
|
||||||
const auto left_sample{static_cast<s16>(std::clamp(
|
const auto left_sample{static_cast<s16>(std::clamp(
|
||||||
static_cast<s32>(
|
static_cast<s32>(
|
||||||
|
@ -83,7 +83,7 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
|
||||||
volume),
|
volume),
|
||||||
min, max))};
|
min, max))};
|
||||||
|
|
||||||
new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
|
tmp_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
|
||||||
|
|
||||||
const auto right_sample{static_cast<s16>(std::clamp(
|
const auto right_sample{static_cast<s16>(std::clamp(
|
||||||
static_cast<s32>(
|
static_cast<s32>(
|
||||||
|
@ -91,9 +91,9 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples) {
|
||||||
volume),
|
volume),
|
||||||
min, max))};
|
min, max))};
|
||||||
|
|
||||||
new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
|
tmp_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
|
||||||
}
|
}
|
||||||
samples = std::move(new_samples);
|
samples = std::span<s16>(tmp_samples);
|
||||||
|
|
||||||
} else if (volume != 1.0f) {
|
} else if (volume != 1.0f) {
|
||||||
for (u32 i = 0; i < samples.size(); i++) {
|
for (u32 i = 0; i < samples.size(); i++) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/reader_writer_queue.h"
|
#include "common/reader_writer_queue.h"
|
||||||
#include "common/ring_buffer.h"
|
#include "common/ring_buffer.h"
|
||||||
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -170,7 +171,7 @@ public:
|
||||||
* @param buffer - Audio buffer information to be queued.
|
* @param buffer - Audio buffer information to be queued.
|
||||||
* @param samples - The s16 samples to be queue for playback.
|
* @param samples - The s16 samples to be queue for playback.
|
||||||
*/
|
*/
|
||||||
virtual void AppendBuffer(SinkBuffer& buffer, std::vector<s16>& samples);
|
virtual void AppendBuffer(SinkBuffer& buffer, std::span<s16> samples);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release a buffer. Audio In only, will fill a buffer with recorded samples.
|
* Release a buffer. Audio In only, will fill a buffer with recorded samples.
|
||||||
|
@ -255,6 +256,8 @@ private:
|
||||||
/// Signalled when ring buffer entries are consumed
|
/// Signalled when ring buffer entries are consumed
|
||||||
std::condition_variable_any release_cv;
|
std::condition_variable_any release_cv;
|
||||||
std::mutex release_mutex;
|
std::mutex release_mutex;
|
||||||
|
/// Temporary buffer for appending samples when upmixing
|
||||||
|
Common::ScratchBuffer<s16> tmp_samples{};
|
||||||
};
|
};
|
||||||
|
|
||||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
||||||
|
|
|
@ -172,6 +172,8 @@ if(ARCHITECTURE_x86_64)
|
||||||
x64/cpu_wait.h
|
x64/cpu_wait.h
|
||||||
x64/native_clock.cpp
|
x64/native_clock.cpp
|
||||||
x64/native_clock.h
|
x64/native_clock.h
|
||||||
|
x64/rdtsc.cpp
|
||||||
|
x64/rdtsc.h
|
||||||
x64/xbyak_abi.h
|
x64/xbyak_abi.h
|
||||||
x64/xbyak_util.h
|
x64/xbyak_util.h
|
||||||
)
|
)
|
||||||
|
|
|
@ -436,7 +436,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
|
||||||
|
|
||||||
if (True(filter & DirEntryFilter::File) &&
|
if (True(filter & DirEntryFilter::File) &&
|
||||||
entry.status().type() == fs::file_type::regular) {
|
entry.status().type() == fs::file_type::regular) {
|
||||||
if (!callback(entry.path())) {
|
if (!callback(entry)) {
|
||||||
callback_error = true;
|
callback_error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,7 @@ void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable
|
||||||
|
|
||||||
if (True(filter & DirEntryFilter::Directory) &&
|
if (True(filter & DirEntryFilter::Directory) &&
|
||||||
entry.status().type() == fs::file_type::directory) {
|
entry.status().type() == fs::file_type::directory) {
|
||||||
if (!callback(entry.path())) {
|
if (!callback(entry)) {
|
||||||
callback_error = true;
|
callback_error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -493,7 +493,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
||||||
|
|
||||||
if (True(filter & DirEntryFilter::File) &&
|
if (True(filter & DirEntryFilter::File) &&
|
||||||
entry.status().type() == fs::file_type::regular) {
|
entry.status().type() == fs::file_type::regular) {
|
||||||
if (!callback(entry.path())) {
|
if (!callback(entry)) {
|
||||||
callback_error = true;
|
callback_error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -501,7 +501,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
||||||
|
|
||||||
if (True(filter & DirEntryFilter::Directory) &&
|
if (True(filter & DirEntryFilter::Directory) &&
|
||||||
entry.status().type() == fs::file_type::directory) {
|
entry.status().type() == fs::file_type::directory) {
|
||||||
if (!callback(entry.path())) {
|
if (!callback(entry)) {
|
||||||
callback_error = true;
|
callback_error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -605,6 +605,12 @@ fs::file_type GetEntryType(const fs::path& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetSize(const fs::path& path) {
|
u64 GetSize(const fs::path& path) {
|
||||||
|
#ifdef ANDROID
|
||||||
|
if (Android::IsContentUri(path)) {
|
||||||
|
return Android::GetSize(path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
const auto file_size = fs::file_size(path, ec);
|
const auto file_size = fs::file_size(path, ec);
|
||||||
|
|
|
@ -66,6 +66,6 @@ DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);
|
||||||
* @returns A boolean value.
|
* @returns A boolean value.
|
||||||
* Return true to indicate whether the callback is successful, false otherwise.
|
* Return true to indicate whether the callback is successful, false otherwise.
|
||||||
*/
|
*/
|
||||||
using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>;
|
using DirEntryCallable = std::function<bool(const std::filesystem::directory_entry& entry)>;
|
||||||
|
|
||||||
} // namespace Common::FS
|
} // namespace Common::FS
|
||||||
|
|
|
@ -75,8 +75,10 @@ enum class DriverResult {
|
||||||
ErrorWritingData,
|
ErrorWritingData,
|
||||||
NoDeviceDetected,
|
NoDeviceDetected,
|
||||||
InvalidHandle,
|
InvalidHandle,
|
||||||
|
InvalidParameters,
|
||||||
NotSupported,
|
NotSupported,
|
||||||
Disabled,
|
Disabled,
|
||||||
|
Delayed,
|
||||||
Unknown,
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,7 +88,7 @@ enum class NfcState {
|
||||||
NewAmiibo,
|
NewAmiibo,
|
||||||
WaitingForAmiibo,
|
WaitingForAmiibo,
|
||||||
AmiiboRemoved,
|
AmiiboRemoved,
|
||||||
NotAnAmiibo,
|
InvalidTagType,
|
||||||
NotSupported,
|
NotSupported,
|
||||||
WrongDeviceState,
|
WrongDeviceState,
|
||||||
WriteFailed,
|
WriteFailed,
|
||||||
|
@ -218,8 +220,22 @@ struct CameraStatus {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NfcStatus {
|
struct NfcStatus {
|
||||||
NfcState state{};
|
NfcState state{NfcState::Unknown};
|
||||||
std::vector<u8> data{};
|
u8 uuid_length;
|
||||||
|
u8 protocol;
|
||||||
|
u8 tag_type;
|
||||||
|
std::array<u8, 10> uuid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MifareData {
|
||||||
|
u8 command;
|
||||||
|
u8 sector;
|
||||||
|
std::array<u8, 0x6> key;
|
||||||
|
std::array<u8, 0x10> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MifareRequest {
|
||||||
|
std::array<MifareData, 0x10> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// List of buttons to be passed to Qt that can be translated
|
// List of buttons to be passed to Qt that can be translated
|
||||||
|
@ -294,7 +310,7 @@ struct CallbackStatus {
|
||||||
BatteryStatus battery_status{};
|
BatteryStatus battery_status{};
|
||||||
VibrationStatus vibration_status{};
|
VibrationStatus vibration_status{};
|
||||||
CameraFormat camera_status{CameraFormat::None};
|
CameraFormat camera_status{CameraFormat::None};
|
||||||
NfcState nfc_status{NfcState::Unknown};
|
NfcStatus nfc_status{};
|
||||||
std::vector<u8> raw_data{};
|
std::vector<u8> raw_data{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -356,9 +372,30 @@ public:
|
||||||
return NfcState::NotSupported;
|
return NfcState::NotSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual NfcState StartNfcPolling() {
|
||||||
|
return NfcState::NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual NfcState StopNfcPolling() {
|
||||||
|
return NfcState::NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual NfcState ReadAmiiboData([[maybe_unused]] std::vector<u8>& out_data) {
|
||||||
|
return NfcState::NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
|
virtual NfcState WriteNfcData([[maybe_unused]] const std::vector<u8>& data) {
|
||||||
return NfcState::NotSupported;
|
return NfcState::NotSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual NfcState ReadMifareData([[maybe_unused]] const MifareRequest& request,
|
||||||
|
[[maybe_unused]] MifareRequest& out_data) {
|
||||||
|
return NfcState::NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual NfcState WriteMifareData([[maybe_unused]] const MifareRequest& request) {
|
||||||
|
return NfcState::NotSupported;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An abstract class template for a factory that can create input devices.
|
/// An abstract class template for a factory that can create input devices.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <span>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ public:
|
||||||
return push_count;
|
return push_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t Push(const std::vector<T>& input) {
|
std::size_t Push(const std::span<T> input) {
|
||||||
return Push(input.data(), input.size());
|
return Push(input.data(), input.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include "common/concepts.h"
|
||||||
#include "common/make_unique_for_overwrite.h"
|
#include "common/make_unique_for_overwrite.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
@ -16,6 +19,12 @@ namespace Common {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class ScratchBuffer {
|
class ScratchBuffer {
|
||||||
public:
|
public:
|
||||||
|
using iterator = T*;
|
||||||
|
using const_iterator = const T*;
|
||||||
|
using value_type = T;
|
||||||
|
using element_type = T;
|
||||||
|
using iterator_category = std::contiguous_iterator_tag;
|
||||||
|
|
||||||
ScratchBuffer() = default;
|
ScratchBuffer() = default;
|
||||||
|
|
||||||
explicit ScratchBuffer(size_t initial_capacity)
|
explicit ScratchBuffer(size_t initial_capacity)
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <version>
|
||||||
#if __cpp_lib_chrono >= 201907L
|
#if __cpp_lib_chrono >= 201907L
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
#endif
|
#endif
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
@ -25,9 +28,19 @@ std::string GetTimeZoneString() {
|
||||||
if (time_zone_index == 0) { // Auto
|
if (time_zone_index == 0) { // Auto
|
||||||
#if __cpp_lib_chrono >= 201907L
|
#if __cpp_lib_chrono >= 201907L
|
||||||
const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
|
const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb();
|
||||||
|
try {
|
||||||
const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
|
const std::chrono::time_zone* current_zone = time_zone_data.current_zone();
|
||||||
std::string_view current_zone_name = current_zone->name();
|
std::string_view current_zone_name = current_zone->name();
|
||||||
location_name = current_zone_name;
|
location_name = current_zone_name;
|
||||||
|
} catch (std::runtime_error& runtime_error) {
|
||||||
|
// VCRUNTIME will throw a runtime_error if the operating system's selected time zone
|
||||||
|
// cannot be found
|
||||||
|
location_name = Common::TimeZone::FindSystemTimeZone();
|
||||||
|
LOG_WARNING(Common,
|
||||||
|
"Error occurred when trying to determine system time zone:\n{}\nFalling "
|
||||||
|
"back to hour offset \"{}\"",
|
||||||
|
runtime_error.what(), location_name);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
location_name = Common::TimeZone::FindSystemTimeZone();
|
location_name = Common::TimeZone::FindSystemTimeZone();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -483,6 +483,7 @@ struct Values {
|
||||||
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
|
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
|
||||||
"astc_recompression"};
|
"astc_recompression"};
|
||||||
SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
|
SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
|
||||||
|
SwitchableSetting<bool> barrier_feedback_loops{true, "barrier_feedback_loops"};
|
||||||
|
|
||||||
SwitchableSetting<u8> bg_red{0, "bg_red"};
|
SwitchableSetting<u8> bg_red{0, "bg_red"};
|
||||||
SwitchableSetting<u8> bg_green{0, "bg_green"};
|
SwitchableSetting<u8> bg_green{0, "bg_green"};
|
||||||
|
@ -524,9 +525,16 @@ struct Values {
|
||||||
Setting<bool> tas_loop{false, "tas_loop"};
|
Setting<bool> tas_loop{false, "tas_loop"};
|
||||||
|
|
||||||
Setting<bool> mouse_panning{false, "mouse_panning"};
|
Setting<bool> mouse_panning{false, "mouse_panning"};
|
||||||
Setting<u8, true> mouse_panning_sensitivity{50, 1, 100, "mouse_panning_sensitivity"};
|
Setting<u8, true> mouse_panning_x_sensitivity{50, 1, 100, "mouse_panning_x_sensitivity"};
|
||||||
Setting<bool> mouse_enabled{false, "mouse_enabled"};
|
Setting<u8, true> mouse_panning_y_sensitivity{50, 1, 100, "mouse_panning_y_sensitivity"};
|
||||||
|
Setting<u8, true> mouse_panning_deadzone_x_counterweight{
|
||||||
|
0, 0, 100, "mouse_panning_deadzone_x_counterweight"};
|
||||||
|
Setting<u8, true> mouse_panning_deadzone_y_counterweight{
|
||||||
|
0, 0, 100, "mouse_panning_deadzone_y_counterweight"};
|
||||||
|
Setting<u8, true> mouse_panning_decay_strength{22, 0, 100, "mouse_panning_decay_strength"};
|
||||||
|
Setting<u8, true> mouse_panning_min_decay{5, 0, 100, "mouse_panning_min_decay"};
|
||||||
|
|
||||||
|
Setting<bool> mouse_enabled{false, "mouse_enabled"};
|
||||||
Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
|
Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
|
||||||
Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
|
Setting<bool> keyboard_enabled{false, "keyboard_enabled"};
|
||||||
|
|
||||||
|
|
|
@ -28,13 +28,12 @@ static s64 GetSystemTimeNS() {
|
||||||
// GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
|
// GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
|
||||||
static constexpr s64 Multiplier = 100;
|
static constexpr s64 Multiplier = 100;
|
||||||
// Convert Windows epoch to Unix epoch.
|
// Convert Windows epoch to Unix epoch.
|
||||||
static constexpr s64 WindowsEpochToUnixEpochNS = 0x19DB1DED53E8000LL;
|
static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL;
|
||||||
|
|
||||||
FILETIME filetime;
|
FILETIME filetime;
|
||||||
GetSystemTimePreciseAsFileTime(&filetime);
|
GetSystemTimePreciseAsFileTime(&filetime);
|
||||||
return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) +
|
return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) +
|
||||||
static_cast<s64>(filetime.dwLowDateTime)) -
|
static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch);
|
||||||
WindowsEpochToUnixEpochNS;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ void AppendCPUInfo(FieldCollection& fc) {
|
||||||
add_field("CPU_Extension_x64_GFNI", caps.gfni);
|
add_field("CPU_Extension_x64_GFNI", caps.gfni);
|
||||||
add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
|
add_field("CPU_Extension_x64_INVARIANT_TSC", caps.invariant_tsc);
|
||||||
add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
|
add_field("CPU_Extension_x64_LZCNT", caps.lzcnt);
|
||||||
|
add_field("CPU_Extension_x64_MONITORX", caps.monitorx);
|
||||||
add_field("CPU_Extension_x64_MOVBE", caps.movbe);
|
add_field("CPU_Extension_x64_MOVBE", caps.movbe);
|
||||||
add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
|
add_field("CPU_Extension_x64_PCLMULQDQ", caps.pclmulqdq);
|
||||||
add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
|
add_field("CPU_Extension_x64_POPCNT", caps.popcnt);
|
||||||
|
|
|
@ -55,7 +55,7 @@ public:
|
||||||
is_set = false;
|
is_set = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsSet() {
|
[[nodiscard]] bool IsSet() const {
|
||||||
return is_set;
|
return is_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,88 +2,75 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/steady_clock.h"
|
#include "common/steady_clock.h"
|
||||||
#include "common/uint128.h"
|
|
||||||
#include "common/wall_clock.h"
|
#include "common/wall_clock.h"
|
||||||
|
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
#include "common/x64/cpu_detect.h"
|
#include "common/x64/cpu_detect.h"
|
||||||
#include "common/x64/native_clock.h"
|
#include "common/x64/native_clock.h"
|
||||||
|
#include "common/x64/rdtsc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
class StandardWallClock final : public WallClock {
|
class StandardWallClock final : public WallClock {
|
||||||
public:
|
public:
|
||||||
explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_)
|
explicit StandardWallClock() : start_time{SteadyClock::Now()} {}
|
||||||
: WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false},
|
|
||||||
start_time{SteadyClock::Now()} {}
|
|
||||||
|
|
||||||
std::chrono::nanoseconds GetTimeNS() override {
|
std::chrono::nanoseconds GetTimeNS() const override {
|
||||||
return SteadyClock::Now() - start_time;
|
return SteadyClock::Now() - start_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::microseconds GetTimeUS() override {
|
std::chrono::microseconds GetTimeUS() const override {
|
||||||
return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS());
|
return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::milliseconds GetTimeMS() override {
|
std::chrono::milliseconds GetTimeMS() const override {
|
||||||
return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS());
|
return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetClockCycles() override {
|
u64 GetCNTPCT() const override {
|
||||||
const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency);
|
return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||||
return Common::Divide128On32(temp, NS_RATIO).first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetCPUCycles() override {
|
u64 GetGPUTick() const override {
|
||||||
const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency);
|
return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||||
return Common::Divide128On32(temp, NS_RATIO).first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pause([[maybe_unused]] bool is_paused) override {
|
u64 GetHostTicksNow() const override {
|
||||||
// Do nothing in this clock type.
|
return static_cast<u64>(SteadyClock::Now().time_since_epoch().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetHostTicksElapsed() const override {
|
||||||
|
return static_cast<u64>(GetTimeNS().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNative() const override {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SteadyClock::time_point start_time;
|
SteadyClock::time_point start_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
|
||||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
|
||||||
u64 emulated_clock_frequency) {
|
|
||||||
const auto& caps = GetCPUCaps();
|
const auto& caps = GetCPUCaps();
|
||||||
u64 rtsc_frequency = 0;
|
|
||||||
if (caps.invariant_tsc) {
|
|
||||||
rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
|
if (caps.invariant_tsc && caps.tsc_frequency >= WallClock::GPUTickFreq) {
|
||||||
// - A nanosecond
|
return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
|
||||||
// - The emulated CPU frequency
|
|
||||||
// - The emulated clock counter frequency (CNTFRQ)
|
|
||||||
if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
|
|
||||||
rtsc_frequency <= emulated_clock_frequency) {
|
|
||||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
|
|
||||||
emulated_clock_frequency);
|
|
||||||
} else {
|
} else {
|
||||||
return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency,
|
// Fallback to StandardWallClock if the hardware TSC
|
||||||
rtsc_frequency);
|
// - Is not invariant
|
||||||
|
// - Is not more precise than GPUTickFreq
|
||||||
|
return std::make_unique<StandardWallClock>();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
return std::make_unique<StandardWallClock>();
|
||||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
#endif
|
||||||
u64 emulated_clock_frequency) {
|
|
||||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
std::unique_ptr<WallClock> CreateStandardWallClock() {
|
||||||
|
return std::make_unique<StandardWallClock>();
|
||||||
std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
|
|
||||||
u64 emulated_clock_frequency) {
|
|
||||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <ratio>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
@ -12,50 +13,82 @@ namespace Common {
|
||||||
|
|
||||||
class WallClock {
|
class WallClock {
|
||||||
public:
|
public:
|
||||||
static constexpr u64 NS_RATIO = 1'000'000'000;
|
static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
|
||||||
static constexpr u64 US_RATIO = 1'000'000;
|
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
|
||||||
static constexpr u64 MS_RATIO = 1'000;
|
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
|
||||||
|
|
||||||
virtual ~WallClock() = default;
|
virtual ~WallClock() = default;
|
||||||
|
|
||||||
/// Returns current wall time in nanoseconds
|
/// @returns The time in nanoseconds since the construction of this clock.
|
||||||
[[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
|
virtual std::chrono::nanoseconds GetTimeNS() const = 0;
|
||||||
|
|
||||||
/// Returns current wall time in microseconds
|
/// @returns The time in microseconds since the construction of this clock.
|
||||||
[[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0;
|
virtual std::chrono::microseconds GetTimeUS() const = 0;
|
||||||
|
|
||||||
/// Returns current wall time in milliseconds
|
/// @returns The time in milliseconds since the construction of this clock.
|
||||||
[[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0;
|
virtual std::chrono::milliseconds GetTimeMS() const = 0;
|
||||||
|
|
||||||
/// Returns current wall time in emulated clock cycles
|
/// @returns The guest CNTPCT ticks since the construction of this clock.
|
||||||
[[nodiscard]] virtual u64 GetClockCycles() = 0;
|
virtual u64 GetCNTPCT() const = 0;
|
||||||
|
|
||||||
/// Returns current wall time in emulated cpu cycles
|
/// @returns The guest GPU ticks since the construction of this clock.
|
||||||
[[nodiscard]] virtual u64 GetCPUCycles() = 0;
|
virtual u64 GetGPUTick() const = 0;
|
||||||
|
|
||||||
virtual void Pause(bool is_paused) = 0;
|
/// @returns The raw host timer ticks since an indeterminate epoch.
|
||||||
|
virtual u64 GetHostTicksNow() const = 0;
|
||||||
|
|
||||||
/// Tells if the wall clock, uses the host CPU's hardware clock
|
/// @returns The raw host timer ticks since the construction of this clock.
|
||||||
[[nodiscard]] bool IsNative() const {
|
virtual u64 GetHostTicksElapsed() const = 0;
|
||||||
return is_native;
|
|
||||||
|
/// @returns Whether the clock directly uses the host's hardware clock.
|
||||||
|
virtual bool IsNative() const = 0;
|
||||||
|
|
||||||
|
static inline u64 NSToCNTPCT(u64 ns) {
|
||||||
|
return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 NSToGPUTick(u64 ns) {
|
||||||
|
return ns * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cycle Timing
|
||||||
|
|
||||||
|
static inline u64 CPUTickToNS(u64 cpu_tick) {
|
||||||
|
return cpu_tick * CPUTickToNsRatio::num / CPUTickToNsRatio::den;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 CPUTickToUS(u64 cpu_tick) {
|
||||||
|
return cpu_tick * CPUTickToUsRatio::num / CPUTickToUsRatio::den;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 CPUTickToCNTPCT(u64 cpu_tick) {
|
||||||
|
return cpu_tick * CPUTickToCNTPCTRatio::num / CPUTickToCNTPCTRatio::den;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 CPUTickToGPUTick(u64 cpu_tick) {
|
||||||
|
return cpu_tick * CPUTickToGPUTickRatio::num / CPUTickToGPUTickRatio::den;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_)
|
using NsRatio = std::nano;
|
||||||
: emulated_cpu_frequency{emulated_cpu_frequency_},
|
using UsRatio = std::micro;
|
||||||
emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {}
|
using MsRatio = std::milli;
|
||||||
|
|
||||||
u64 emulated_cpu_frequency;
|
using NsToUsRatio = std::ratio_divide<std::nano, std::micro>;
|
||||||
u64 emulated_clock_frequency;
|
using NsToMsRatio = std::ratio_divide<std::nano, std::milli>;
|
||||||
|
using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
|
||||||
|
using NsToGPUTickRatio = std::ratio<GPUTickFreq, std::nano::den>;
|
||||||
|
|
||||||
private:
|
// Cycle Timing
|
||||||
bool is_native;
|
|
||||||
|
using CPUTickToNsRatio = std::ratio<std::nano::den, CPUTickFreq>;
|
||||||
|
using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
|
||||||
|
using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
|
||||||
|
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
std::unique_ptr<WallClock> CreateOptimalClock();
|
||||||
u64 emulated_clock_frequency);
|
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
|
std::unique_ptr<WallClock> CreateStandardWallClock();
|
||||||
u64 emulated_clock_frequency);
|
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/x64/cpu_detect.h"
|
#include "common/x64/cpu_detect.h"
|
||||||
|
#include "common/x64/rdtsc.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -167,6 +168,7 @@ static CPUCaps Detect() {
|
||||||
__cpuid(cpu_id, 0x80000001);
|
__cpuid(cpu_id, 0x80000001);
|
||||||
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
|
caps.lzcnt = Common::Bit<5>(cpu_id[2]);
|
||||||
caps.fma4 = Common::Bit<16>(cpu_id[2]);
|
caps.fma4 = Common::Bit<16>(cpu_id[2]);
|
||||||
|
caps.monitorx = Common::Bit<29>(cpu_id[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max_ex_fn >= 0x80000007) {
|
if (max_ex_fn >= 0x80000007) {
|
||||||
|
@ -187,6 +189,8 @@ static CPUCaps Detect() {
|
||||||
caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
|
caps.tsc_frequency = static_cast<u64>(caps.crystal_frequency) *
|
||||||
caps.tsc_crystal_ratio_numerator /
|
caps.tsc_crystal_ratio_numerator /
|
||||||
caps.tsc_crystal_ratio_denominator;
|
caps.tsc_crystal_ratio_denominator;
|
||||||
|
} else {
|
||||||
|
caps.tsc_frequency = X64::EstimateRDTSCFrequency();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ struct CPUCaps {
|
||||||
bool gfni : 1;
|
bool gfni : 1;
|
||||||
bool invariant_tsc : 1;
|
bool invariant_tsc : 1;
|
||||||
bool lzcnt : 1;
|
bool lzcnt : 1;
|
||||||
|
bool monitorx : 1;
|
||||||
bool movbe : 1;
|
bool movbe : 1;
|
||||||
bool pclmulqdq : 1;
|
bool pclmulqdq : 1;
|
||||||
bool popcnt : 1;
|
bool popcnt : 1;
|
||||||
|
|
|
@ -9,58 +9,64 @@
|
||||||
|
|
||||||
#include "common/x64/cpu_detect.h"
|
#include "common/x64/cpu_detect.h"
|
||||||
#include "common/x64/cpu_wait.h"
|
#include "common/x64/cpu_wait.h"
|
||||||
|
#include "common/x64/rdtsc.h"
|
||||||
|
|
||||||
namespace Common::X64 {
|
namespace Common::X64 {
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
namespace {
|
||||||
__forceinline static u64 FencedRDTSC() {
|
|
||||||
_mm_lfence();
|
|
||||||
_ReadWriteBarrier();
|
|
||||||
const u64 result = __rdtsc();
|
|
||||||
_mm_lfence();
|
|
||||||
_ReadWriteBarrier();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
__forceinline static void TPAUSE() {
|
|
||||||
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
|
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
|
||||||
// For reference:
|
// For reference:
|
||||||
// At 1 GHz, 100K cycles is 100us
|
// At 1 GHz, 100K cycles is 100us
|
||||||
// At 2 GHz, 100K cycles is 50us
|
// At 2 GHz, 100K cycles is 50us
|
||||||
// At 4 GHz, 100K cycles is 25us
|
// At 4 GHz, 100K cycles is 25us
|
||||||
static constexpr auto PauseCycles = 100'000;
|
constexpr auto PauseCycles = 100'000U;
|
||||||
_tpause(0, FencedRDTSC() + PauseCycles);
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__forceinline static void TPAUSE() {
|
||||||
|
static constexpr auto RequestC02State = 0U;
|
||||||
|
_tpause(RequestC02State, FencedRDTSC() + PauseCycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline static void MWAITX() {
|
||||||
|
static constexpr auto EnableWaitTimeFlag = 1U << 1;
|
||||||
|
static constexpr auto RequestC1State = 0U;
|
||||||
|
|
||||||
|
// monitor_var should be aligned to a cache line.
|
||||||
|
alignas(64) u64 monitor_var{};
|
||||||
|
_mm_monitorx(&monitor_var, 0, 0);
|
||||||
|
_mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static u64 FencedRDTSC() {
|
|
||||||
u64 eax;
|
|
||||||
u64 edx;
|
|
||||||
asm volatile("lfence\n\t"
|
|
||||||
"rdtsc\n\t"
|
|
||||||
"lfence\n\t"
|
|
||||||
: "=a"(eax), "=d"(edx));
|
|
||||||
return (edx << 32) | eax;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void TPAUSE() {
|
static void TPAUSE() {
|
||||||
// 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
|
static constexpr auto RequestC02State = 0U;
|
||||||
// For reference:
|
|
||||||
// At 1 GHz, 100K cycles is 100us
|
|
||||||
// At 2 GHz, 100K cycles is 50us
|
|
||||||
// At 4 GHz, 100K cycles is 25us
|
|
||||||
static constexpr auto PauseCycles = 100'000;
|
|
||||||
const auto tsc = FencedRDTSC() + PauseCycles;
|
const auto tsc = FencedRDTSC() + PauseCycles;
|
||||||
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
|
const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
|
||||||
const auto edx = static_cast<u32>(tsc >> 32);
|
const auto edx = static_cast<u32>(tsc >> 32);
|
||||||
asm volatile("tpause %0" : : "r"(0), "d"(edx), "a"(eax));
|
asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MWAITX() {
|
||||||
|
static constexpr auto EnableWaitTimeFlag = 1U << 1;
|
||||||
|
static constexpr auto RequestC1State = 0U;
|
||||||
|
|
||||||
|
// monitor_var should be aligned to a cache line.
|
||||||
|
alignas(64) u64 monitor_var{};
|
||||||
|
asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0));
|
||||||
|
asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void MicroSleep() {
|
void MicroSleep() {
|
||||||
static const bool has_waitpkg = GetCPUCaps().waitpkg;
|
static const bool has_waitpkg = GetCPUCaps().waitpkg;
|
||||||
|
static const bool has_monitorx = GetCPUCaps().monitorx;
|
||||||
|
|
||||||
if (has_waitpkg) {
|
if (has_waitpkg) {
|
||||||
TPAUSE();
|
TPAUSE();
|
||||||
|
} else if (has_monitorx) {
|
||||||
|
MWAITX();
|
||||||
} else {
|
} else {
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,164 +1,50 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "common/atomic_ops.h"
|
|
||||||
#include "common/steady_clock.h"
|
|
||||||
#include "common/uint128.h"
|
#include "common/uint128.h"
|
||||||
#include "common/x64/native_clock.h"
|
#include "common/x64/native_clock.h"
|
||||||
|
#include "common/x64/rdtsc.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
namespace Common::X64 {
|
||||||
#include <intrin.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Common {
|
NativeClock::NativeClock(u64 rdtsc_frequency_)
|
||||||
|
: start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_},
|
||||||
|
ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)},
|
||||||
|
us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
|
||||||
|
ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
|
||||||
|
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
|
||||||
|
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||||
__forceinline static u64 FencedRDTSC() {
|
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)};
|
||||||
_mm_lfence();
|
|
||||||
_ReadWriteBarrier();
|
|
||||||
const u64 result = __rdtsc();
|
|
||||||
_mm_lfence();
|
|
||||||
_ReadWriteBarrier();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static u64 FencedRDTSC() {
|
|
||||||
u64 eax;
|
|
||||||
u64 edx;
|
|
||||||
asm volatile("lfence\n\t"
|
|
||||||
"rdtsc\n\t"
|
|
||||||
"lfence\n\t"
|
|
||||||
: "=a"(eax), "=d"(edx));
|
|
||||||
return (edx << 32) | eax;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <u64 Nearest>
|
|
||||||
static u64 RoundToNearest(u64 value) {
|
|
||||||
const auto mod = value % Nearest;
|
|
||||||
return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 EstimateRDTSCFrequency() {
|
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||||
// Discard the first result measuring the rdtsc.
|
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)};
|
||||||
FencedRDTSC();
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds{1});
|
|
||||||
FencedRDTSC();
|
|
||||||
|
|
||||||
// Get the current time.
|
|
||||||
const auto start_time = Common::RealTimeClock::Now();
|
|
||||||
const u64 tsc_start = FencedRDTSC();
|
|
||||||
// Wait for 250 milliseconds.
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds{250});
|
|
||||||
const auto end_time = Common::RealTimeClock::Now();
|
|
||||||
const u64 tsc_end = FencedRDTSC();
|
|
||||||
// Calculate differences.
|
|
||||||
const u64 timer_diff = static_cast<u64>(
|
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
|
|
||||||
const u64 tsc_diff = tsc_end - tsc_start;
|
|
||||||
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
|
|
||||||
return RoundToNearest<1000>(tsc_freq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace X64 {
|
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||||
NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_,
|
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)};
|
||||||
u64 rtsc_frequency_)
|
|
||||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
|
|
||||||
rtsc_frequency_} {
|
|
||||||
// Thread to re-adjust the RDTSC frequency after 10 seconds has elapsed.
|
|
||||||
time_sync_thread = std::jthread{[this](std::stop_token token) {
|
|
||||||
// Get the current time.
|
|
||||||
const auto start_time = Common::RealTimeClock::Now();
|
|
||||||
const u64 tsc_start = FencedRDTSC();
|
|
||||||
// Wait for 10 seconds.
|
|
||||||
if (!Common::StoppableTimedWait(token, std::chrono::seconds{10})) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto end_time = Common::RealTimeClock::Now();
|
|
||||||
const u64 tsc_end = FencedRDTSC();
|
|
||||||
// Calculate differences.
|
|
||||||
const u64 timer_diff = static_cast<u64>(
|
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
|
|
||||||
const u64 tsc_diff = tsc_end - tsc_start;
|
|
||||||
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
|
|
||||||
rtsc_frequency = tsc_freq;
|
|
||||||
CalculateAndSetFactors();
|
|
||||||
}};
|
|
||||||
|
|
||||||
time_point.inner.last_measure = FencedRDTSC();
|
|
||||||
time_point.inner.accumulated_ticks = 0U;
|
|
||||||
CalculateAndSetFactors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 NativeClock::GetRTSC() {
|
u64 NativeClock::GetCNTPCT() const {
|
||||||
TimePoint new_time_point{};
|
return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor);
|
||||||
TimePoint current_time_point{};
|
|
||||||
|
|
||||||
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
|
|
||||||
do {
|
|
||||||
const u64 current_measure = FencedRDTSC();
|
|
||||||
u64 diff = current_measure - current_time_point.inner.last_measure;
|
|
||||||
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
|
|
||||||
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
|
|
||||||
? current_measure
|
|
||||||
: current_time_point.inner.last_measure;
|
|
||||||
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
|
|
||||||
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
|
|
||||||
current_time_point.pack, current_time_point.pack));
|
|
||||||
return new_time_point.inner.accumulated_ticks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeClock::Pause(bool is_paused) {
|
u64 NativeClock::GetGPUTick() const {
|
||||||
if (!is_paused) {
|
return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor);
|
||||||
TimePoint current_time_point{};
|
|
||||||
TimePoint new_time_point{};
|
|
||||||
|
|
||||||
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
|
|
||||||
do {
|
|
||||||
new_time_point.pack = current_time_point.pack;
|
|
||||||
new_time_point.inner.last_measure = FencedRDTSC();
|
|
||||||
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
|
|
||||||
current_time_point.pack, current_time_point.pack));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::nanoseconds NativeClock::GetTimeNS() {
|
u64 NativeClock::GetHostTicksNow() const {
|
||||||
const u64 rtsc_value = GetRTSC();
|
return FencedRDTSC();
|
||||||
return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::microseconds NativeClock::GetTimeUS() {
|
u64 NativeClock::GetHostTicksElapsed() const {
|
||||||
const u64 rtsc_value = GetRTSC();
|
return FencedRDTSC() - start_ticks;
|
||||||
return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::milliseconds NativeClock::GetTimeMS() {
|
bool NativeClock::IsNative() const {
|
||||||
const u64 rtsc_value = GetRTSC();
|
return true;
|
||||||
return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 NativeClock::GetClockCycles() {
|
} // namespace Common::X64
|
||||||
const u64 rtsc_value = GetRTSC();
|
|
||||||
return MultiplyHigh(rtsc_value, clock_rtsc_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 NativeClock::GetCPUCycles() {
|
|
||||||
const u64 rtsc_value = GetRTSC();
|
|
||||||
return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeClock::CalculateAndSetFactors() {
|
|
||||||
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
|
|
||||||
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
|
|
||||||
ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
|
|
||||||
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
|
|
||||||
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace X64
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
|
|
|
@ -3,58 +3,39 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/polyfill_thread.h"
|
|
||||||
#include "common/wall_clock.h"
|
#include "common/wall_clock.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common::X64 {
|
||||||
|
|
||||||
namespace X64 {
|
|
||||||
class NativeClock final : public WallClock {
|
class NativeClock final : public WallClock {
|
||||||
public:
|
public:
|
||||||
explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_,
|
explicit NativeClock(u64 rdtsc_frequency_);
|
||||||
u64 rtsc_frequency_);
|
|
||||||
|
|
||||||
std::chrono::nanoseconds GetTimeNS() override;
|
std::chrono::nanoseconds GetTimeNS() const override;
|
||||||
|
|
||||||
std::chrono::microseconds GetTimeUS() override;
|
std::chrono::microseconds GetTimeUS() const override;
|
||||||
|
|
||||||
std::chrono::milliseconds GetTimeMS() override;
|
std::chrono::milliseconds GetTimeMS() const override;
|
||||||
|
|
||||||
u64 GetClockCycles() override;
|
u64 GetCNTPCT() const override;
|
||||||
|
|
||||||
u64 GetCPUCycles() override;
|
u64 GetGPUTick() const override;
|
||||||
|
|
||||||
void Pause(bool is_paused) override;
|
u64 GetHostTicksNow() const override;
|
||||||
|
|
||||||
|
u64 GetHostTicksElapsed() const override;
|
||||||
|
|
||||||
|
bool IsNative() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u64 GetRTSC();
|
u64 start_ticks;
|
||||||
|
u64 rdtsc_frequency;
|
||||||
|
|
||||||
void CalculateAndSetFactors();
|
u64 ns_rdtsc_factor;
|
||||||
|
u64 us_rdtsc_factor;
|
||||||
union alignas(16) TimePoint {
|
u64 ms_rdtsc_factor;
|
||||||
TimePoint() : pack{} {}
|
u64 cntpct_rdtsc_factor;
|
||||||
u128 pack{};
|
u64 gputick_rdtsc_factor;
|
||||||
struct Inner {
|
|
||||||
u64 last_measure{};
|
|
||||||
u64 accumulated_ticks{};
|
|
||||||
} inner;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TimePoint time_point;
|
} // namespace Common::X64
|
||||||
|
|
||||||
// factors
|
|
||||||
u64 clock_rtsc_factor{};
|
|
||||||
u64 cpu_rtsc_factor{};
|
|
||||||
u64 ns_rtsc_factor{};
|
|
||||||
u64 us_rtsc_factor{};
|
|
||||||
u64 ms_rtsc_factor{};
|
|
||||||
|
|
||||||
u64 rtsc_frequency;
|
|
||||||
|
|
||||||
std::jthread time_sync_thread;
|
|
||||||
};
|
|
||||||
} // namespace X64
|
|
||||||
|
|
||||||
u64 EstimateRDTSCFrequency();
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "common/steady_clock.h"
|
||||||
|
#include "common/uint128.h"
|
||||||
|
#include "common/x64/rdtsc.h"
|
||||||
|
|
||||||
|
namespace Common::X64 {
|
||||||
|
|
||||||
|
template <u64 Nearest>
|
||||||
|
static u64 RoundToNearest(u64 value) {
|
||||||
|
const auto mod = value % Nearest;
|
||||||
|
return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 EstimateRDTSCFrequency() {
|
||||||
|
// Discard the first result measuring the rdtsc.
|
||||||
|
FencedRDTSC();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds{1});
|
||||||
|
FencedRDTSC();
|
||||||
|
|
||||||
|
// Get the current time.
|
||||||
|
const auto start_time = RealTimeClock::Now();
|
||||||
|
const u64 tsc_start = FencedRDTSC();
|
||||||
|
// Wait for 100 milliseconds.
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds{100});
|
||||||
|
const auto end_time = RealTimeClock::Now();
|
||||||
|
const u64 tsc_end = FencedRDTSC();
|
||||||
|
// Calculate differences.
|
||||||
|
const u64 timer_diff = static_cast<u64>(
|
||||||
|
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
|
||||||
|
const u64 tsc_diff = tsc_end - tsc_start;
|
||||||
|
const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
|
||||||
|
return RoundToNearest<100'000>(tsc_freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common::X64
|
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common::X64 {
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__forceinline static u64 FencedRDTSC() {
|
||||||
|
_mm_lfence();
|
||||||
|
_ReadWriteBarrier();
|
||||||
|
const u64 result = __rdtsc();
|
||||||
|
_mm_lfence();
|
||||||
|
_ReadWriteBarrier();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline u64 FencedRDTSC() {
|
||||||
|
u64 eax;
|
||||||
|
u64 edx;
|
||||||
|
asm volatile("lfence\n\t"
|
||||||
|
"rdtsc\n\t"
|
||||||
|
"lfence\n\t"
|
||||||
|
: "=a"(eax), "=d"(edx));
|
||||||
|
return (edx << 32) | eax;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u64 EstimateRDTSCFrequency();
|
||||||
|
|
||||||
|
} // namespace Common::X64
|
|
@ -14,7 +14,6 @@ add_library(core STATIC
|
||||||
core.h
|
core.h
|
||||||
core_timing.cpp
|
core_timing.cpp
|
||||||
core_timing.h
|
core_timing.h
|
||||||
core_timing_util.h
|
|
||||||
cpu_manager.cpp
|
cpu_manager.cpp
|
||||||
cpu_manager.h
|
cpu_manager.h
|
||||||
crypto/aes_util.cpp
|
crypto/aes_util.cpp
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue