Compare commits
63 Commits
android-47
...
android-52
Author | SHA1 | Date | |
---|---|---|---|
c5daaccf5f | |||
6c4abd23be | |||
84b384fbea | |||
3620533995 | |||
c5105b65d5 | |||
1ac2615adb | |||
d7a0b8c373 | |||
6bb02dcb8a | |||
32c453a5f1 | |||
91eb5afd0b | |||
bc4e58eb51 | |||
8674724ef0 | |||
a8edbb7019 | |||
d8c8fbe41f | |||
234cc45192 | |||
b923f5aa7e | |||
18ad55be0b | |||
4e71628097 | |||
92e6ff30a1 | |||
2e55459e03 | |||
8677d98a10 | |||
49df2b9715 | |||
2f2de400e1 | |||
9e134c3da2 | |||
59b3c30f94 | |||
a669e37ddb | |||
7d89f2c146 | |||
51ffc2c66c | |||
e41655960e | |||
1cdd11d9f5 | |||
ccd163ab2c | |||
182fb83556 | |||
39c8ddcda2 | |||
2c4ebeb51d | |||
00af46c356 | |||
ce0f1baf51 | |||
75f5b3177d | |||
3c45452fae | |||
ab862207d7 | |||
7f8335f4ae | |||
6ed5b581f0 | |||
387ede76d2 | |||
8a4cb3f902 | |||
35b77b9599 | |||
bc4ad5e62d | |||
0e443dcb05 | |||
ef61d129d3 | |||
b8bab551a4 | |||
a9f223cd9f | |||
87022a4833 | |||
1bc832c9b1 | |||
0cd9d51e06 | |||
c03f0b3c89 | |||
6af8cca2c1 | |||
e28b936950 | |||
6fe51b48e9 | |||
96c98d09cb | |||
76a03e99b6 | |||
95409c6859 | |||
227950ac99 | |||
bc5ec10498 | |||
d9275b7757 | |||
3e28e85468 |
@ -49,7 +49,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
|
||||
|
||||
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
|
||||
|
||||
cmake_dependent_option(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
|
||||
|
||||
@ -63,6 +63,8 @@ option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(USE_SYSTEM_MOLTENVK "Use the system MoltenVK lib (instead of the bundled one)" OFF "APPLE" OFF)
|
||||
|
||||
set(DEFAULT_ENABLE_OPENSSL ON)
|
||||
if (ANDROID OR WIN32 OR APPLE)
|
||||
# - Windows defaults to the Schannel backend.
|
||||
|
@ -36,3 +36,21 @@ endif()
|
||||
message(STATUS "Using bundled binaries at ${prefix}")
|
||||
set(${prefix_var} "${prefix}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(download_moltenvk_external platform version)
|
||||
set(MOLTENVK_DIR "${CMAKE_BINARY_DIR}/externals/MoltenVK")
|
||||
set(MOLTENVK_TAR "${CMAKE_BINARY_DIR}/externals/MoltenVK.tar")
|
||||
if (NOT EXISTS ${MOLTENVK_DIR})
|
||||
if (NOT EXISTS ${MOLTENVK_TAR})
|
||||
file(DOWNLOAD https://github.com/KhronosGroup/MoltenVK/releases/download/${version}/MoltenVK-${platform}.tar
|
||||
${MOLTENVK_TAR} SHOW_PROGRESS)
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${MOLTENVK_TAR}"
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
|
||||
endif()
|
||||
|
||||
# Add the MoltenVK library path to the prefix so find_library can locate it.
|
||||
list(APPEND CMAKE_PREFIX_PATH "${MOLTENVK_DIR}/MoltenVK/dylib/${platform}")
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
5
dist/qt_themes/default/style.qss
vendored
5
dist/qt_themes/default/style.qss
vendored
@ -78,6 +78,11 @@ QPushButton#buttonRefreshDevices {
|
||||
max-height: 21px;
|
||||
}
|
||||
|
||||
QPushButton#button_reset_defaults {
|
||||
min-width: 57px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
QWidget#bottomPerGameInput,
|
||||
QWidget#topControllerApplet,
|
||||
QWidget#bottomControllerApplet,
|
||||
|
@ -2228,6 +2228,10 @@ QPushButton#buttonRefreshDevices {
|
||||
padding: 0px 0px;
|
||||
}
|
||||
|
||||
QPushButton#button_reset_defaults {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
QSpinBox#spinboxLStickRange,
|
||||
QSpinBox#spinboxRStickRange,
|
||||
QSpinBox#vibrationSpinPlayer1,
|
||||
|
14
externals/CMakeLists.txt
vendored
14
externals/CMakeLists.txt
vendored
@ -42,6 +42,11 @@ endif()
|
||||
# mbedtls
|
||||
add_subdirectory(mbedtls)
|
||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(mbedcrypto PRIVATE
|
||||
-Wno-unused-but-set-variable
|
||||
-Wno-string-concatenation)
|
||||
endif()
|
||||
|
||||
# MicroProfile
|
||||
add_library(microprofile INTERFACE)
|
||||
@ -94,6 +99,12 @@ if (ENABLE_CUBEB AND NOT TARGET cubeb::cubeb)
|
||||
set(BUILD_TOOLS OFF)
|
||||
add_subdirectory(cubeb)
|
||||
add_library(cubeb::cubeb ALIAS cubeb)
|
||||
if (NOT MSVC)
|
||||
if (TARGET speex)
|
||||
target_compile_options(speex PRIVATE -Wno-sign-compare)
|
||||
endif()
|
||||
target_compile_options(cubeb PRIVATE -Wno-implicit-const-int-float-conversion)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# DiscordRPC
|
||||
@ -151,6 +162,9 @@ endif()
|
||||
if (NOT TARGET LLVM::Demangle)
|
||||
add_library(demangle demangle/ItaniumDemangle.cpp)
|
||||
target_include_directories(demangle PUBLIC ./demangle)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(demangle PRIVATE -Wno-deprecated-declarations) # std::is_pod
|
||||
endif()
|
||||
add_library(LLVM::Demangle ALIAS demangle)
|
||||
endif()
|
||||
|
||||
|
@ -114,16 +114,19 @@ else()
|
||||
-Wno-attributes
|
||||
-Wno-invalid-offsetof
|
||||
-Wno-unused-parameter
|
||||
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
|
||||
add_compile_options(
|
||||
-Wno-braced-scalar-init
|
||||
-Wno-unused-private-field
|
||||
-Wno-nullability-completeness
|
||||
-Werror=shadow-uncaptured-local
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=type-limits
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options("-mcx16")
|
||||
add_compile_options("-fwrapv")
|
||||
|
@ -95,6 +95,7 @@ android {
|
||||
// builds a release build that doesn't need signing
|
||||
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
|
||||
register("relWithDebInfo") {
|
||||
isDefault = true
|
||||
resValue("string", "app_name_suffixed", "yuzu Debug Release")
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
isMinifyEnabled = true
|
||||
@ -122,6 +123,7 @@ android {
|
||||
flavorDimensions.add("version")
|
||||
productFlavors {
|
||||
create("mainline") {
|
||||
isDefault = true
|
||||
dimension = "version"
|
||||
buildConfigField("Boolean", "PREMIUM", "false")
|
||||
}
|
||||
@ -160,6 +162,11 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.create<Delete>("ktlintReset") {
|
||||
delete(File(buildDir.path + File.separator + "intermediates/ktLint"))
|
||||
}
|
||||
|
||||
tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset")
|
||||
tasks.getByPath("preBuild").dependsOn("ktlintCheck")
|
||||
|
||||
ktlint {
|
||||
|
@ -25,6 +25,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
android:hasFragileUserData="false"
|
||||
android:supportsRtl="true"
|
||||
android:isGame="true"
|
||||
android:appCategory="game"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:banner="@drawable/tv_banner"
|
||||
android:extractNativeLibs="true"
|
||||
|
@ -11,6 +11,7 @@ import kotlinx.serialization.json.Json
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||
|
||||
object GameHelper {
|
||||
const val KEY_GAME_PATH = "game_path"
|
||||
@ -29,15 +30,7 @@ object GameHelper {
|
||||
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
||||
NativeLibrary.reloadKeys()
|
||||
|
||||
val children = FileUtil.listFiles(context, gamesUri)
|
||||
for (file in children) {
|
||||
if (!file.isDirectory) {
|
||||
// Check that the file has an extension we care about before trying to read out of it.
|
||||
if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
|
||||
games.add(getGame(file.uri))
|
||||
}
|
||||
}
|
||||
}
|
||||
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
|
||||
|
||||
// Cache list of games found on disk
|
||||
val serializedGames = mutableSetOf<String>()
|
||||
@ -52,6 +45,30 @@ object GameHelper {
|
||||
return games.toList()
|
||||
}
|
||||
|
||||
private fun addGamesRecursive(
|
||||
games: MutableList<Game>,
|
||||
files: Array<MinimalDocumentFile>,
|
||||
depth: Int
|
||||
) {
|
||||
if (depth <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
files.forEach {
|
||||
if (it.isDirectory) {
|
||||
addGamesRecursive(
|
||||
games,
|
||||
FileUtil.listFiles(YuzuApplication.appContext, it.uri),
|
||||
depth - 1
|
||||
)
|
||||
} else {
|
||||
if (Game.extensions.contains(FileUtil.getExtension(it.uri))) {
|
||||
games.add(getGame(it.uri))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGame(uri: Uri): Game {
|
||||
val filePath = uri.toString()
|
||||
var name = NativeLibrary.getTitle(filePath)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "input_common/main.h"
|
||||
#include "jni/config.h"
|
||||
@ -144,7 +145,9 @@ void Config::ReadValues() {
|
||||
Service::Account::MAX_USERS - 1);
|
||||
|
||||
// Disable docked mode by default on Android
|
||||
Settings::values.use_docked_mode = config->GetBoolean("System", "use_docked_mode", false);
|
||||
Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
|
||||
? Settings::ConsoleMode::Docked
|
||||
: Settings::ConsoleMode::Handheld);
|
||||
|
||||
const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
|
||||
if (rng_seed_enabled) {
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
@ -224,6 +225,42 @@ public:
|
||||
m_system.Renderer().NotifySurfaceChanged();
|
||||
}
|
||||
|
||||
void ConfigureFilesystemProvider(const std::string& filepath) {
|
||||
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(m_system, file);
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file_type = loader->GetFileType();
|
||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 program_id = 0;
|
||||
const auto res2 = loader->ReadProgramId(program_id);
|
||||
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
||||
m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
||||
program_id, file);
|
||||
} else if (res2 == Loader::ResultStatus::Success &&
|
||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||
const auto nsp = file_type == Loader::FileType::NSP
|
||||
? std::make_shared<FileSys::NSP>(file)
|
||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||
entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
@ -254,8 +291,14 @@ public:
|
||||
std::move(android_keyboard), // Software Keyboard
|
||||
nullptr, // Web Browser
|
||||
});
|
||||
|
||||
// Initialize filesystem.
|
||||
m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
|
||||
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||
m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
|
||||
m_manual_provider.get());
|
||||
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
||||
ConfigureFilesystemProvider(filepath);
|
||||
|
||||
// Initialize account manager
|
||||
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
||||
@ -377,7 +420,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
return !Settings::values.use_docked_mode.GetValue();
|
||||
return !Settings::IsDockedMode();
|
||||
}
|
||||
|
||||
void SetDeviceType([[maybe_unused]] int index, int type) {
|
||||
@ -489,6 +532,7 @@ private:
|
||||
bool m_is_paused{};
|
||||
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||
|
||||
// GPU driver parameters
|
||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "audio_core/sink/cubeb_sink.h"
|
||||
#include "audio_core/sink/sink_stream.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -332,25 +333,38 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetCubebLatency() {
|
||||
cubeb* ctx;
|
||||
namespace {
|
||||
static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) {
|
||||
return TargetSampleCount;
|
||||
}
|
||||
static void TmpStateCallback(cubeb_stream*, void*, cubeb_state) {}
|
||||
} // namespace
|
||||
|
||||
bool IsCubebSuitable() {
|
||||
#if !defined(HAVE_CUBEB)
|
||||
return false;
|
||||
#else
|
||||
cubeb* ctx{nullptr};
|
||||
|
||||
#ifdef _WIN32
|
||||
auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
#endif
|
||||
|
||||
// Init cubeb
|
||||
if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
// Return a large latency so we choose SDL instead.
|
||||
return 10000u;
|
||||
LOG_ERROR(Audio_Sink, "Cubeb failed to init, it is not suitable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
SCOPE_EXIT({ cubeb_destroy(ctx); });
|
||||
|
||||
#ifdef _WIN32
|
||||
if (SUCCEEDED(com_init_result)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test min latency
|
||||
cubeb_stream_params params{};
|
||||
params.rate = TargetSampleRate;
|
||||
params.channels = 2;
|
||||
@ -361,12 +375,32 @@ u32 GetCubebLatency() {
|
||||
u32 latency{0};
|
||||
const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency);
|
||||
if (latency_error != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
|
||||
latency = TargetSampleCount * 2;
|
||||
LOG_ERROR(Audio_Sink, "Cubeb could not get min latency, it is not suitable.");
|
||||
return false;
|
||||
}
|
||||
latency = std::max(latency, TargetSampleCount * 2);
|
||||
cubeb_destroy(ctx);
|
||||
return latency;
|
||||
|
||||
if (latency > TargetSampleCount * 3) {
|
||||
LOG_ERROR(Audio_Sink, "Cubeb latency is too high, it is not suitable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test opening a device with standard parameters
|
||||
cubeb_devid output_device{0};
|
||||
cubeb_devid input_device{0};
|
||||
std::string name{"Yuzu test"};
|
||||
cubeb_stream* stream{nullptr};
|
||||
|
||||
if (cubeb_stream_init(ctx, &stream, name.c_str(), input_device, nullptr, output_device, ¶ms,
|
||||
latency, &TmpDataCallback, &TmpStateCallback, nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Cubeb could not open a device, it is not suitable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
cubeb_stream_stop(stream);
|
||||
cubeb_stream_destroy(stream);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -97,10 +97,11 @@ private:
|
||||
std::vector<std::string> ListCubebSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
* Check if this backend is suitable for use.
|
||||
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
* @return True is this backend is suitable, false otherwise.
|
||||
*/
|
||||
u32 GetCubebLatency();
|
||||
bool IsCubebSuitable();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "audio_core/sink/sdl2_sink.h"
|
||||
#include "audio_core/sink/sink_stream.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore::Sink {
|
||||
@ -84,6 +85,7 @@ public:
|
||||
}
|
||||
|
||||
Stop();
|
||||
SDL_ClearQueuedAudio(device);
|
||||
SDL_CloseAudioDevice(device);
|
||||
}
|
||||
|
||||
@ -227,8 +229,42 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||
return device_list;
|
||||
}
|
||||
|
||||
u32 GetSDLLatency() {
|
||||
return TargetSampleCount * 2;
|
||||
bool IsSDLSuitable() {
|
||||
#if !defined(HAVE_SDL2)
|
||||
return false;
|
||||
#else
|
||||
// Check SDL can init
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We can set any latency frequency we want with SDL, so no need to check that.
|
||||
|
||||
// Check we can open a device with standard parameters
|
||||
SDL_AudioSpec spec;
|
||||
spec.freq = TargetSampleRate;
|
||||
spec.channels = 2u;
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.samples = TargetSampleCount * 2;
|
||||
spec.callback = nullptr;
|
||||
spec.userdata = nullptr;
|
||||
|
||||
SDL_AudioSpec obtained;
|
||||
auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false);
|
||||
|
||||
if (device == 0) {
|
||||
LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_CloseAudioDevice(device);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -88,10 +88,11 @@ private:
|
||||
std::vector<std::string> ListSDLSinkDevices(bool capture);
|
||||
|
||||
/**
|
||||
* Get the reported latency for this sink.
|
||||
* Check if this backend is suitable for use.
|
||||
* Checks if enabled, its latency, whether it opens successfully, etc.
|
||||
*
|
||||
* @return Minimum latency for this sink.
|
||||
* @return True is this backend is suitable, false otherwise.
|
||||
*/
|
||||
u32 GetSDLLatency();
|
||||
bool IsSDLSuitable();
|
||||
|
||||
} // namespace AudioCore::Sink
|
||||
|
@ -22,7 +22,7 @@ namespace {
|
||||
struct SinkDetails {
|
||||
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
|
||||
using ListDevicesFn = std::vector<std::string> (*)(bool);
|
||||
using LatencyFn = u32 (*)();
|
||||
using SuitableFn = bool (*)();
|
||||
|
||||
/// Name for this sink.
|
||||
Settings::AudioEngine id;
|
||||
@ -30,8 +30,8 @@ struct SinkDetails {
|
||||
FactoryFn factory;
|
||||
/// A method to call to list available devices.
|
||||
ListDevicesFn list_devices;
|
||||
/// Method to get the latency of this backend.
|
||||
LatencyFn latency;
|
||||
/// Check whether this backend is suitable to be used.
|
||||
SuitableFn is_suitable;
|
||||
};
|
||||
|
||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||
@ -43,7 +43,7 @@ constexpr SinkDetails sink_details[] = {
|
||||
return std::make_unique<CubebSink>(device_id);
|
||||
},
|
||||
&ListCubebSinkDevices,
|
||||
&GetCubebLatency,
|
||||
&IsCubebSuitable,
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
@ -53,14 +53,17 @@ constexpr SinkDetails sink_details[] = {
|
||||
return std::make_unique<SDLSink>(device_id);
|
||||
},
|
||||
&ListSDLSinkDevices,
|
||||
&GetSDLLatency,
|
||||
&IsSDLSuitable,
|
||||
},
|
||||
#endif
|
||||
SinkDetails{Settings::AudioEngine::Null,
|
||||
SinkDetails{
|
||||
Settings::AudioEngine::Null,
|
||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
||||
return std::make_unique<NullSink>(device_id);
|
||||
},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
|
||||
[](bool capture) { return std::vector<std::string>{"null"}; },
|
||||
[]() { return true; },
|
||||
},
|
||||
};
|
||||
|
||||
const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||
@ -72,18 +75,22 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
||||
auto iter = find_backend(sink_id);
|
||||
|
||||
if (sink_id == Settings::AudioEngine::Auto) {
|
||||
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||
// causes audio issues, in that case go with SDL.
|
||||
#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
|
||||
iter = find_backend(Settings::AudioEngine::Cubeb);
|
||||
if (iter->latency() > TargetSampleCount * 3) {
|
||||
iter = find_backend(Settings::AudioEngine::Sdl2);
|
||||
// Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking
|
||||
// that the backend is available and suitable to use.
|
||||
for (auto& details : sink_details) {
|
||||
if (details.is_suitable()) {
|
||||
iter = &details;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
iter = std::begin(sink_details);
|
||||
#endif
|
||||
LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
|
||||
}
|
||||
LOG_ERROR(Service_Audio, "Auto-selecting the {} backend",
|
||||
Settings::CanonicalizeEnum(iter->id));
|
||||
} else {
|
||||
if (iter != std::end(sink_details) && !iter->is_suitable()) {
|
||||
LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
|
||||
Settings::CanonicalizeEnum(iter->id));
|
||||
iter = find_backend(Settings::AudioEngine::Null);
|
||||
}
|
||||
}
|
||||
|
||||
if (iter == std::end(sink_details)) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <version>
|
||||
#include "common/settings_enums.h"
|
||||
#if __cpp_lib_chrono >= 201907L
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
@ -145,6 +146,10 @@ bool IsFastmemEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsDockedMode() {
|
||||
return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
|
||||
}
|
||||
|
||||
float Volume() {
|
||||
if (values.audio_muted) {
|
||||
return 0.0f;
|
||||
@ -207,9 +212,7 @@ const char* TranslateCategory(Category category) {
|
||||
return "Miscellaneous";
|
||||
}
|
||||
|
||||
void UpdateRescalingInfo() {
|
||||
const auto setup = values.resolution_setup.GetValue();
|
||||
auto& info = values.resolution_info;
|
||||
void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo& info) {
|
||||
info.downscale = false;
|
||||
switch (setup) {
|
||||
case ResolutionSetup::Res1_2X:
|
||||
@ -269,6 +272,12 @@ void UpdateRescalingInfo() {
|
||||
info.active = info.up_scale != 1 || info.down_shift != 0;
|
||||
}
|
||||
|
||||
void UpdateRescalingInfo() {
|
||||
const auto setup = values.resolution_setup.GetValue();
|
||||
auto& info = values.resolution_info;
|
||||
TranslateResolutionInfo(setup, info);
|
||||
}
|
||||
|
||||
void RestoreGlobalState(bool is_powered_on) {
|
||||
// If a game is running, DO NOT restore the global settings state
|
||||
if (is_powered_on) {
|
||||
|
@ -379,7 +379,13 @@ struct Values {
|
||||
|
||||
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
|
||||
|
||||
SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System};
|
||||
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
|
||||
ConsoleMode::Docked,
|
||||
"use_docked_mode",
|
||||
Category::System,
|
||||
Specialization::Radio,
|
||||
true,
|
||||
true};
|
||||
|
||||
// Controls
|
||||
InputSetting<std::array<PlayerInput, 10>> players;
|
||||
@ -519,12 +525,15 @@ bool IsGPULevelHigh();
|
||||
|
||||
bool IsFastmemEnabled();
|
||||
|
||||
bool IsDockedMode();
|
||||
|
||||
float Volume();
|
||||
|
||||
std::string GetTimeZoneString(TimeZone time_zone);
|
||||
|
||||
void LogSettings();
|
||||
|
||||
void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo& info);
|
||||
void UpdateRescalingInfo();
|
||||
|
||||
// Restore the global state of all applicable settings in the Values struct
|
||||
|
@ -56,6 +56,7 @@ enum Specialization : u8 {
|
||||
Scalar = 5, // Values are continuous
|
||||
Countable = 6, // Can be stepped through
|
||||
Paired = 7, // Another setting is associated with this setting
|
||||
Radio = 8, // Setting should be presented in a radio group
|
||||
|
||||
Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
|
||||
};
|
||||
|
@ -146,6 +146,8 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
|
||||
|
||||
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
||||
|
||||
ENUM(ConsoleMode, Handheld, Docked);
|
||||
|
||||
template <typename Type>
|
||||
inline std::string CanonicalizeEnum(Type id) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
|
@ -460,11 +460,6 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i & v.swap();
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator&(const swap_struct_t<T, F> v, const S& i) {
|
||||
return static_cast<S>(v.swap() & i);
|
||||
}
|
||||
|
||||
// Comparison
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator<(const S& p, const swap_struct_t<T, F> v) {
|
||||
|
@ -606,9 +606,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
const auto result = RemoveExistingEntry(title_id);
|
||||
|
||||
// Install Metadata File
|
||||
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id_data);
|
||||
if (res != InstallResult::Success) {
|
||||
return res;
|
||||
const auto meta_result = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id_data);
|
||||
if (meta_result != InstallResult::Success) {
|
||||
return meta_result;
|
||||
}
|
||||
|
||||
// Install all the other NCAs
|
||||
@ -621,9 +621,19 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
if (nca == nullptr) {
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
||||
if (res2 != InstallResult::Success) {
|
||||
return res2;
|
||||
if (nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
|
||||
nca->GetTitleId() != title_id) {
|
||||
// Create fake cnmt for patch to multiprogram application
|
||||
const auto sub_nca_result =
|
||||
InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy);
|
||||
if (sub_nca_result != InstallResult::Success) {
|
||||
return sub_nca_result;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto nca_result = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
||||
if (nca_result != InstallResult::Success) {
|
||||
return nca_result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,6 +673,8 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
|
||||
}
|
||||
|
||||
bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
|
||||
bool removed_data = false;
|
||||
|
||||
const auto delete_nca = [this](const NcaID& id) {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
@ -706,11 +718,18 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
|
||||
const auto deleted_html = delete_nca(html_id);
|
||||
const auto deleted_legal = delete_nca(legal_id);
|
||||
|
||||
return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
|
||||
deleted_control || deleted_html || deleted_legal);
|
||||
removed_data |= (deleted_meta || deleted_program || deleted_data || deleted_control ||
|
||||
deleted_html || deleted_legal);
|
||||
}
|
||||
|
||||
return false;
|
||||
// If patch entries for any program exist in yuzu meta, remove them
|
||||
for (u8 i = 0; i < 0x10; i++) {
|
||||
const auto meta_dir = dir->CreateDirectoryRelative("yuzu_meta");
|
||||
const auto filename = GetCNMTName(TitleType::Update, title_id + i);
|
||||
removed_data |= meta_dir->DeleteFile(filename);
|
||||
}
|
||||
|
||||
return removed_data;
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/frontend/applets/controller.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
@ -62,7 +64,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
||||
controller->Connect(true);
|
||||
}
|
||||
} else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
|
||||
!Settings::values.use_docked_mode.GetValue()) {
|
||||
!Settings::IsDockedMode()) {
|
||||
// We should *never* reach here under any normal circumstances.
|
||||
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||
controller->Connect(true);
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
|
||||
namespace Layout {
|
||||
@ -49,7 +50,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
}
|
||||
|
||||
FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) {
|
||||
const bool is_docked = Settings::values.use_docked_mode.GetValue();
|
||||
const bool is_docked = Settings::IsDockedMode();
|
||||
const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width;
|
||||
const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height;
|
||||
|
||||
|
@ -38,7 +38,7 @@ namespace {
|
||||
*/
|
||||
void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority,
|
||||
KProcessAddress stack_top) {
|
||||
const KProcessAddress entry_point = owner_process.GetPageTable().GetCodeRegionStart();
|
||||
const KProcessAddress entry_point = owner_process.GetEntryPoint();
|
||||
ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1));
|
||||
|
||||
KThread* thread = KThread::Create(system.Kernel());
|
||||
@ -358,6 +358,21 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
m_system_resource_size = metadata.GetSystemResourceSize();
|
||||
m_image_size = code_size;
|
||||
|
||||
if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit) {
|
||||
// For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large.
|
||||
// However, some (buggy) programs/libraries like skyline incorrectly depend on the
|
||||
// existence of ASLR pages before the entry point, so we will adjust the load address
|
||||
// to point to about 2GiB into the ASLR region.
|
||||
m_code_address = 0x8000'0000;
|
||||
} else {
|
||||
// All other processes can be mapped at the beginning of the code region.
|
||||
if (metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is36Bit) {
|
||||
m_code_address = 0x800'0000;
|
||||
} else {
|
||||
m_code_address = 0x20'0000;
|
||||
}
|
||||
}
|
||||
|
||||
KScopedResourceReservation memory_reservation(
|
||||
m_resource_limit, LimitableResource::PhysicalMemoryMax, code_size + m_system_resource_size);
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
@ -368,15 +383,15 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
// Initialize process address space
|
||||
if (const Result result{m_page_table.InitializeForProcess(
|
||||
metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
|
||||
0x8000000, code_size, std::addressof(m_kernel.GetAppSystemResource()), m_resource_limit,
|
||||
m_kernel.System().ApplicationMemory())};
|
||||
this->GetEntryPoint(), code_size, std::addressof(m_kernel.GetAppSystemResource()),
|
||||
m_resource_limit, m_kernel.System().ApplicationMemory())};
|
||||
result.IsError()) {
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
// Map process code region
|
||||
if (const Result result{m_page_table.MapProcessCode(m_page_table.GetCodeRegionStart(),
|
||||
code_size / PageSize, KMemoryState::Code,
|
||||
if (const Result result{m_page_table.MapProcessCode(this->GetEntryPoint(), code_size / PageSize,
|
||||
KMemoryState::Code,
|
||||
KMemoryPermission::None)};
|
||||
result.IsError()) {
|
||||
R_RETURN(result);
|
||||
|
@ -177,6 +177,10 @@ public:
|
||||
return m_program_id;
|
||||
}
|
||||
|
||||
KProcessAddress GetEntryPoint() const {
|
||||
return m_code_address;
|
||||
}
|
||||
|
||||
/// Gets the resource limit descriptor for this process
|
||||
KResourceLimit* GetResourceLimit() const;
|
||||
|
||||
@ -485,6 +489,9 @@ private:
|
||||
/// Address indicating the location of the process' dedicated TLS region.
|
||||
KProcessAddress m_plr_address = 0;
|
||||
|
||||
/// Address indicating the location of the process's entry point.
|
||||
KProcessAddress m_code_address = 0;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> m_random_entropy{};
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@ -833,7 +834,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
} else {
|
||||
@ -921,7 +922,7 @@ void IStorage::Open(HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
|
||||
const bool use_docked_mode{Settings::IsDockedMode()};
|
||||
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/apm/apm_controller.h"
|
||||
|
||||
@ -67,8 +68,7 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
|
||||
}
|
||||
|
||||
PerformanceMode Controller::GetCurrentPerformanceMode() const {
|
||||
return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
|
||||
: PerformanceMode::Normal;
|
||||
return Settings::IsDockedMode() ? PerformanceMode::Boost : PerformanceMode::Normal;
|
||||
}
|
||||
|
||||
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
@ -154,10 +155,18 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
|
||||
std::string src_path(Common::FS::SanitizePath(src_path_));
|
||||
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
||||
auto src = backing->GetFileRelative(src_path);
|
||||
auto dst = backing->GetFileRelative(dest_path);
|
||||
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
||||
// Use more-optimized vfs implementation rename.
|
||||
if (src == nullptr)
|
||||
if (src == nullptr) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (dst && Common::FS::Exists(dst->GetFullPath())) {
|
||||
LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath());
|
||||
return FileSys::ERROR_PATH_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
|
@ -331,7 +331,7 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties()
|
||||
};
|
||||
|
||||
// Hack: There is no touch in docked but games still allow it
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
gesture.points[id] = {
|
||||
.x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
|
||||
.y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
|
||||
|
@ -1518,7 +1518,7 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
|
||||
return false;
|
||||
}
|
||||
// Handheld shouldn't be supported in docked mode
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -204,9 +204,11 @@ void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
|
||||
if (!mapping->fixed) {
|
||||
auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
|
||||
u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
|
||||
u32 page_size{mapping->big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
|
||||
u64 aligned_size{Common::AlignUp(mapping->size, page_size)};
|
||||
|
||||
allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
|
||||
static_cast<u32>(mapping->size >> page_size_bits));
|
||||
static_cast<u32>(aligned_size >> page_size_bits));
|
||||
}
|
||||
|
||||
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
||||
|
@ -79,8 +79,8 @@ protected:
|
||||
using HandlerFnP = void (Self::*)(HLERequestContext&);
|
||||
|
||||
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
||||
[[nodiscard]] std::scoped_lock<std::mutex> LockService() {
|
||||
return std::scoped_lock{lock_service};
|
||||
[[nodiscard]] virtual std::unique_lock<std::mutex> LockService() {
|
||||
return std::unique_lock{lock_service};
|
||||
}
|
||||
|
||||
/// System context that the service operates under.
|
||||
|
@ -1029,6 +1029,11 @@ BSD::~BSD() {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> BSD::LockService() {
|
||||
// Do not lock socket IClient instances.
|
||||
return {};
|
||||
}
|
||||
|
||||
BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
|
@ -186,6 +186,9 @@ private:
|
||||
|
||||
// Callback identifier for the OnProxyPacketReceived event.
|
||||
Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
|
||||
|
||||
protected:
|
||||
virtual std::unique_lock<std::mutex> LockService() override;
|
||||
};
|
||||
|
||||
class BSDCFG final : public ServiceFramework<BSDCFG> {
|
||||
|
@ -139,7 +139,6 @@ private:
|
||||
bool do_not_close_socket = false;
|
||||
bool get_server_cert_chain = false;
|
||||
std::shared_ptr<Network::SocketBase> socket;
|
||||
bool did_set_host_name = false;
|
||||
bool did_handshake = false;
|
||||
|
||||
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
||||
@ -174,11 +173,7 @@ private:
|
||||
Result SetHostNameImpl(const std::string& hostname) {
|
||||
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||
ASSERT(!did_handshake);
|
||||
Result res = backend->SetHostName(hostname);
|
||||
if (res == ResultSuccess) {
|
||||
did_set_host_name = true;
|
||||
}
|
||||
return res;
|
||||
return backend->SetHostName(hostname);
|
||||
}
|
||||
|
||||
Result SetVerifyOptionImpl(u32 option) {
|
||||
@ -208,9 +203,6 @@ private:
|
||||
|
||||
Result DoHandshakeImpl() {
|
||||
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
did_set_host_name, { return ResultInternalError; },
|
||||
"Expected SetHostName before DoHandshake");
|
||||
Result res = backend->DoHandshake();
|
||||
did_handshake = res.IsSuccess();
|
||||
return res;
|
||||
|
@ -167,9 +167,8 @@ public:
|
||||
}
|
||||
|
||||
~SSLConnectionBackendOpenSSL() {
|
||||
// these are null-tolerant:
|
||||
// this is null-tolerant:
|
||||
SSL_free(ssl);
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
||||
static void KeyLogCallback(const SSL* ssl, const char* line) {
|
||||
|
@ -32,7 +32,7 @@ static void OneTimeInit() {
|
||||
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||
schannel_cred.dwFlags =
|
||||
SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols
|
||||
SCH_CRED_AUTO_CRED_VALIDATION | // validate certs
|
||||
SCH_CRED_NO_SERVERNAME_CHECK | // don't validate server names
|
||||
SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate
|
||||
// ^ I'm assuming that nobody would want to connect Yuzu to a
|
||||
// service that requires some OS-provided corporate client
|
||||
@ -227,10 +227,9 @@ public:
|
||||
ciphertext_read_buf.size());
|
||||
}
|
||||
|
||||
const SECURITY_STATUS ret =
|
||||
InitializeSecurityContextA(&cred_handle, initial_call_done ? &ctxt : nullptr,
|
||||
// Caller ensured we have set a hostname:
|
||||
const_cast<char*>(hostname.value().c_str()), req,
|
||||
char* hostname_ptr = hostname ? const_cast<char*>(hostname->c_str()) : nullptr;
|
||||
const SECURITY_STATUS ret = InitializeSecurityContextA(
|
||||
&cred_handle, initial_call_done ? &ctxt : nullptr, hostname_ptr, req,
|
||||
0, // Reserved1
|
||||
0, // TargetDataRep not used with Schannel
|
||||
initial_call_done ? &input_desc : nullptr,
|
||||
|
@ -217,7 +217,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
if (Settings::values.use_docked_mode.GetValue()) {
|
||||
if (Settings::IsDockedMode()) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
} else {
|
||||
|
@ -153,7 +153,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
|
||||
|
||||
// Load NSO modules
|
||||
modules.clear();
|
||||
const VAddr base_address{GetInteger(process.GetPageTable().GetCodeRegionStart())};
|
||||
const VAddr base_address{GetInteger(process.GetEntryPoint())};
|
||||
VAddr next_load_addr{base_address};
|
||||
const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
@ -96,7 +96,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
|
||||
}
|
||||
|
||||
codeset.memory = std::move(program_image);
|
||||
const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart());
|
||||
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||
process.LoadModule(std::move(codeset), base_address);
|
||||
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
|
||||
|
@ -203,7 +203,7 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data)
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::move(program_image);
|
||||
process.LoadModule(std::move(codeset), process.GetPageTable().GetCodeRegionStart());
|
||||
process.LoadModule(std::move(codeset), process.GetEntryPoint());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S
|
||||
modules.clear();
|
||||
|
||||
// Load module
|
||||
const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart());
|
||||
const VAddr base_address = GetInteger(process.GetEntryPoint());
|
||||
if (!LoadModule(process, system, *file, base_address, true, true)) {
|
||||
return {ResultStatus::ErrorLoadingNSO, {}};
|
||||
}
|
||||
|
@ -117,8 +117,8 @@ json GetProcessorStateDataAuto(Core::System& system) {
|
||||
arm.SaveContext(context);
|
||||
|
||||
return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
|
||||
GetInteger(process->GetPageTable().GetCodeRegionStart()),
|
||||
context.sp, context.pc, context.pstate, context.cpu_registers);
|
||||
GetInteger(process->GetEntryPoint()), context.sp, context.pc,
|
||||
context.pstate, context.cpu_registers);
|
||||
}
|
||||
|
||||
json GetBacktraceData(Core::System& system) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
@ -275,7 +276,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
|
||||
static_cast<u32>(Settings::values.shader_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_UseAsynchronousShaders",
|
||||
Settings::values.use_asynchronous_shaders.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode());
|
||||
}
|
||||
|
||||
bool TelemetrySession::SubmitTestcase() {
|
||||
|
@ -835,15 +835,15 @@ public:
|
||||
return input_engine->SupportsNfc(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState StartNfcPolling() {
|
||||
Common::Input::NfcState StartNfcPolling() override {
|
||||
return input_engine->StartNfcPolling(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState StopNfcPolling() {
|
||||
Common::Input::NfcState StopNfcPolling() override {
|
||||
return input_engine->StopNfcPolling(identifier);
|
||||
}
|
||||
|
||||
Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) {
|
||||
Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) override {
|
||||
return input_engine->ReadAmiiboData(identifier, out_data);
|
||||
}
|
||||
|
||||
@ -852,11 +852,11 @@ public:
|
||||
}
|
||||
|
||||
Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
|
||||
Common::Input::MifareRequest& out_data) {
|
||||
Common::Input::MifareRequest& out_data) override {
|
||||
return input_engine->ReadMifareData(identifier, request, out_data);
|
||||
}
|
||||
|
||||
Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) {
|
||||
Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) override {
|
||||
return input_engine->WriteMifareData(identifier, request);
|
||||
}
|
||||
|
||||
|
@ -558,12 +558,15 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const IR::Value& coord, const IR::Value& derivatives,
|
||||
const IR::Value& offset, const IR::Value& lod_clamp) {
|
||||
const auto info{inst.Flags<IR::TextureInstInfo>()};
|
||||
ScopedRegister dpdx, dpdy;
|
||||
ScopedRegister dpdx, dpdy, coords;
|
||||
const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp};
|
||||
if (multi_component) {
|
||||
// Allocate this early to avoid aliasing other registers
|
||||
dpdx = ScopedRegister{ctx.reg_alloc};
|
||||
dpdy = ScopedRegister{ctx.reg_alloc};
|
||||
if (info.num_derivates >= 3) {
|
||||
coords = ScopedRegister{ctx.reg_alloc};
|
||||
}
|
||||
}
|
||||
const auto sparse_inst{PrepareSparse(inst)};
|
||||
const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
|
||||
@ -580,15 +583,27 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
"MOV.F {}.y,{}.w;",
|
||||
dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
|
||||
dpdy.reg, derivatives_vec);
|
||||
Register final_coord;
|
||||
if (info.num_derivates >= 3) {
|
||||
ctx.Add("MOV.F {}.z,{}.x;"
|
||||
"MOV.F {}.z,{}.y;",
|
||||
dpdx.reg, coord_vec, dpdy.reg, coord_vec);
|
||||
ctx.Add("MOV.F {}.x,0;"
|
||||
"MOV.F {}.y,0;",
|
||||
"MOV.F {}.z,0;", coords.reg, coords.reg, coords.reg);
|
||||
final_coord = coords.reg;
|
||||
} else {
|
||||
final_coord = coord_vec;
|
||||
}
|
||||
if (info.has_lod_clamp) {
|
||||
const ScalarF32 lod_clamp_value{ctx.reg_alloc.Consume(lod_clamp)};
|
||||
ctx.Add("MOV.F {}.w,{};"
|
||||
"TXD.F.LODCLAMP{} {},{},{},{},{},{}{};",
|
||||
dpdy.reg, lod_clamp_value, sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
|
||||
dpdy.reg, lod_clamp_value, sparse_mod, ret, final_coord, dpdx.reg, dpdy.reg,
|
||||
texture, type, offset_vec);
|
||||
} else {
|
||||
ctx.Add("TXD.F{} {},{},{},{},{},{}{};", sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
|
||||
texture, type, offset_vec);
|
||||
ctx.Add("TXD.F{} {},{},{},{},{},{}{};", sparse_mod, ret, final_coord, dpdx.reg,
|
||||
dpdy.reg, texture, type, offset_vec);
|
||||
}
|
||||
} else {
|
||||
ctx.Add("TXD.F{} {},{},{}.x,{}.y,{},{}{};", sparse_mod, ret, coord_vec, derivatives_vec,
|
||||
|
@ -548,7 +548,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
if (sparse_inst) {
|
||||
throw NotImplementedException("EmitImageGradient Sparse");
|
||||
}
|
||||
if (!offset.IsEmpty()) {
|
||||
if (!offset.IsEmpty() && info.num_derivates <= 2) {
|
||||
throw NotImplementedException("EmitImageGradient offset");
|
||||
}
|
||||
const auto texture{Texture(ctx, info, index)};
|
||||
@ -556,6 +556,12 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
|
||||
const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp};
|
||||
const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)};
|
||||
if (multi_component) {
|
||||
if (info.num_derivates >= 3) {
|
||||
const auto offset_vec{ctx.var_alloc.Consume(offset)};
|
||||
ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yz, {}.y));", texel, texture,
|
||||
coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec);
|
||||
return;
|
||||
}
|
||||
ctx.Add("{}=textureGrad({},{},vec2({}.xz),vec2({}.yz));", texel, texture, coords,
|
||||
derivatives_vec, derivatives_vec);
|
||||
} else {
|
||||
|
@ -42,6 +42,7 @@ union TextureInstInfo {
|
||||
BitField<23, 2, u32> gather_component;
|
||||
BitField<25, 2, u32> num_derivates;
|
||||
BitField<27, 3, ImageFormat> image_format;
|
||||
BitField<30, 1, u32> ndv_is_active;
|
||||
};
|
||||
static_assert(sizeof(TextureInstInfo) <= sizeof(u32));
|
||||
|
||||
|
@ -19,7 +19,7 @@ void TranslatorVisitor::FSWZADD(u64 insn) {
|
||||
} const fswzadd{insn};
|
||||
|
||||
if (fswzadd.ndv != 0) {
|
||||
throw NotImplementedException("FSWZADD NDV");
|
||||
LOG_WARNING(Shader, "(STUBBED) FSWZADD - NDV mode");
|
||||
}
|
||||
|
||||
const IR::F32 src_a{GetFloatReg8(insn)};
|
||||
|
@ -16,8 +16,10 @@ void MOV(TranslatorVisitor& v, u64 insn, const IR::U32& src, bool is_mov32i = fa
|
||||
BitField<12, 4, u64> mov32i_mask;
|
||||
} const mov{insn};
|
||||
|
||||
if ((is_mov32i ? mov.mov32i_mask : mov.mask) != 0xf) {
|
||||
throw NotImplementedException("Non-full move mask");
|
||||
u64 mask = is_mov32i ? mov.mov32i_mask : mov.mask;
|
||||
if (mask != 0xf && mask != 0x1) {
|
||||
LOG_WARNING(Shader, "(STUBBED) Masked Mov");
|
||||
return;
|
||||
}
|
||||
v.X(mov.dest_reg, src);
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ void TranslatorVisitor::R2B(u64) {
|
||||
}
|
||||
|
||||
void TranslatorVisitor::RAM(u64) {
|
||||
ThrowNotImplemented(Opcode::RAM);
|
||||
LOG_WARNING(Shader, "(STUBBED) RAM Instruction");
|
||||
}
|
||||
|
||||
void TranslatorVisitor::RET(u64) {
|
||||
@ -221,7 +221,7 @@ void TranslatorVisitor::RTT(u64) {
|
||||
}
|
||||
|
||||
void TranslatorVisitor::SAM(u64) {
|
||||
ThrowNotImplemented(Opcode::SAM);
|
||||
LOG_WARNING(Shader, "(STUBBED) SAM Instruction");
|
||||
}
|
||||
|
||||
void TranslatorVisitor::SETCRSPTR(u64) {
|
||||
|
@ -172,6 +172,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool aoffi, Blod blod, bool lc,
|
||||
info.is_depth.Assign(tex.dc != 0 ? 1 : 0);
|
||||
info.has_bias.Assign(blod == Blod::LB || blod == Blod::LBA ? 1 : 0);
|
||||
info.has_lod_clamp.Assign(lc ? 1 : 0);
|
||||
info.ndv_is_active.Assign(tex.ndv != 0 ? 1 : 0);
|
||||
|
||||
const IR::Value sample{[&]() -> IR::Value {
|
||||
if (tex.dc == 0) {
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/frontend/ir/modifiers.h"
|
||||
#include "shader_recompiler/frontend/ir/value.h"
|
||||
#include "shader_recompiler/ir_opt/passes.h"
|
||||
|
||||
@ -410,7 +411,49 @@ void FoldSelect(IR::Inst& inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void FoldFPAdd32(IR::Inst& inst) {
|
||||
if (FoldWhenAllImmediates(inst, [](f32 a, f32 b) { return a + b; })) {
|
||||
return;
|
||||
}
|
||||
const IR::Value lhs_value{inst.Arg(0)};
|
||||
const IR::Value rhs_value{inst.Arg(1)};
|
||||
const auto check_neutral = [](const IR::Value& one_operand) {
|
||||
return one_operand.IsImmediate() && std::abs(one_operand.F32()) == 0.0f;
|
||||
};
|
||||
if (check_neutral(lhs_value)) {
|
||||
inst.ReplaceUsesWith(rhs_value);
|
||||
}
|
||||
if (check_neutral(rhs_value)) {
|
||||
inst.ReplaceUsesWith(lhs_value);
|
||||
}
|
||||
}
|
||||
|
||||
bool FoldDerivateYFromCorrection(IR::Inst& inst) {
|
||||
const IR::Value lhs_value{inst.Arg(0)};
|
||||
const IR::Value rhs_value{inst.Arg(1)};
|
||||
IR::Inst* const lhs_op{lhs_value.InstRecursive()};
|
||||
IR::Inst* const rhs_op{rhs_value.InstRecursive()};
|
||||
if (lhs_op->GetOpcode() == IR::Opcode::YDirection) {
|
||||
if (rhs_op->GetOpcode() != IR::Opcode::DPdyFine) {
|
||||
return false;
|
||||
}
|
||||
inst.ReplaceUsesWith(rhs_value);
|
||||
return true;
|
||||
}
|
||||
if (rhs_op->GetOpcode() != IR::Opcode::YDirection) {
|
||||
return false;
|
||||
}
|
||||
if (lhs_op->GetOpcode() != IR::Opcode::DPdyFine) {
|
||||
return false;
|
||||
}
|
||||
inst.ReplaceUsesWith(lhs_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FoldFPMul32(IR::Inst& inst) {
|
||||
if (FoldWhenAllImmediates(inst, [](f32 a, f32 b) { return a * b; })) {
|
||||
return;
|
||||
}
|
||||
const auto control{inst.Flags<IR::FpControl>()};
|
||||
if (control.no_contraction) {
|
||||
return;
|
||||
@ -421,6 +464,9 @@ void FoldFPMul32(IR::Inst& inst) {
|
||||
if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
if (FoldDerivateYFromCorrection(inst)) {
|
||||
return;
|
||||
}
|
||||
IR::Inst* const lhs_op{lhs_value.InstRecursive()};
|
||||
IR::Inst* const rhs_op{rhs_value.InstRecursive()};
|
||||
if (lhs_op->GetOpcode() != IR::Opcode::FPMul32 ||
|
||||
@ -622,8 +668,13 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
|
||||
}
|
||||
const IR::Value value_3{GetThroughCast(inst2->Arg(0).Resolve(), IR::Opcode::BitCastU32F32)};
|
||||
if (value_2 != value_3) {
|
||||
if (!value_2.IsImmediate() || !value_3.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
if (Common::BitCast<u32>(value_2.F32()) != value_3.U32()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const IR::Value index{inst2->Arg(1)};
|
||||
const IR::Value clamp{inst2->Arg(2)};
|
||||
const IR::Value segmentation_mask{inst2->Arg(3)};
|
||||
@ -648,6 +699,169 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
|
||||
}
|
||||
}
|
||||
|
||||
bool FindGradient3DDerivates(std::array<IR::Value, 3>& results, IR::Value coord) {
|
||||
if (coord.IsImmediate()) {
|
||||
return false;
|
||||
}
|
||||
const auto check_through_shuffle = [](IR::Value input, IR::Value& result) {
|
||||
const IR::Value value_1{GetThroughCast(input.Resolve(), IR::Opcode::BitCastF32U32)};
|
||||
IR::Inst* const inst2{value_1.InstRecursive()};
|
||||
if (inst2->GetOpcode() != IR::Opcode::ShuffleIndex) {
|
||||
return false;
|
||||
}
|
||||
const IR::Value index{inst2->Arg(1).Resolve()};
|
||||
const IR::Value clamp{inst2->Arg(2).Resolve()};
|
||||
const IR::Value segmentation_mask{inst2->Arg(3).Resolve()};
|
||||
if (!index.IsImmediate() || !clamp.IsImmediate() || !segmentation_mask.IsImmediate()) {
|
||||
return false;
|
||||
}
|
||||
if (index.U32() != 3 && clamp.U32() != 3) {
|
||||
return false;
|
||||
}
|
||||
result = GetThroughCast(inst2->Arg(0).Resolve(), IR::Opcode::BitCastU32F32);
|
||||
return true;
|
||||
};
|
||||
IR::Inst* const inst = coord.InstRecursive();
|
||||
if (inst->GetOpcode() != IR::Opcode::FSwizzleAdd) {
|
||||
return false;
|
||||
}
|
||||
std::array<IR::Value, 3> temporary_values;
|
||||
IR::Value value_1 = inst->Arg(0).Resolve();
|
||||
IR::Value value_2 = inst->Arg(1).Resolve();
|
||||
IR::Value value_3 = inst->Arg(2).Resolve();
|
||||
std::array<u32, 4> swizzles_mask_a{};
|
||||
std::array<u32, 4> swizzles_mask_b{};
|
||||
const auto resolve_mask = [](std::array<u32, 4>& mask_results, IR::Value mask) {
|
||||
u32 value = mask.U32();
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
mask_results[i] = (value >> (i * 2)) & 0x3;
|
||||
}
|
||||
};
|
||||
resolve_mask(swizzles_mask_a, value_3);
|
||||
size_t coordinate_index = 0;
|
||||
const auto resolve_pending = [&](IR::Value resolve_v) {
|
||||
IR::Inst* const inst_r = resolve_v.InstRecursive();
|
||||
if (inst_r->GetOpcode() != IR::Opcode::FSwizzleAdd) {
|
||||
return false;
|
||||
}
|
||||
if (!check_through_shuffle(inst_r->Arg(0).Resolve(), temporary_values[1])) {
|
||||
return false;
|
||||
}
|
||||
if (!check_through_shuffle(inst_r->Arg(1).Resolve(), temporary_values[2])) {
|
||||
return false;
|
||||
}
|
||||
resolve_mask(swizzles_mask_b, inst_r->Arg(2).Resolve());
|
||||
return true;
|
||||
};
|
||||
if (value_1.IsImmediate() || value_2.IsImmediate()) {
|
||||
return false;
|
||||
}
|
||||
bool should_continue = false;
|
||||
if (resolve_pending(value_1)) {
|
||||
should_continue = check_through_shuffle(value_2, temporary_values[0]);
|
||||
coordinate_index = 0;
|
||||
}
|
||||
if (resolve_pending(value_2)) {
|
||||
should_continue = check_through_shuffle(value_1, temporary_values[0]);
|
||||
coordinate_index = 2;
|
||||
}
|
||||
if (!should_continue) {
|
||||
return false;
|
||||
}
|
||||
// figure which is which
|
||||
size_t zero_mask_a = 0;
|
||||
size_t zero_mask_b = 0;
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
if (swizzles_mask_a[i] == 2 || swizzles_mask_b[i] == 2) {
|
||||
// last operand can be inversed, we cannot determine a result.
|
||||
return false;
|
||||
}
|
||||
zero_mask_a |= static_cast<size_t>(swizzles_mask_a[i] == 3 ? 1 : 0) << i;
|
||||
zero_mask_b |= static_cast<size_t>(swizzles_mask_b[i] == 3 ? 1 : 0) << i;
|
||||
}
|
||||
static constexpr size_t ddx_pattern = 0b1010;
|
||||
static constexpr size_t ddx_pattern_inv = ~ddx_pattern & 0b00001111;
|
||||
if (std::popcount(zero_mask_a) != 2) {
|
||||
return false;
|
||||
}
|
||||
if (std::popcount(zero_mask_b) != 2) {
|
||||
return false;
|
||||
}
|
||||
if (zero_mask_a == zero_mask_b) {
|
||||
return false;
|
||||
}
|
||||
results[0] = temporary_values[coordinate_index];
|
||||
|
||||
if (coordinate_index == 0) {
|
||||
if (zero_mask_b == ddx_pattern || zero_mask_b == ddx_pattern_inv) {
|
||||
results[1] = temporary_values[1];
|
||||
results[2] = temporary_values[2];
|
||||
return true;
|
||||
}
|
||||
results[2] = temporary_values[1];
|
||||
results[1] = temporary_values[2];
|
||||
} else {
|
||||
const auto assign_result = [&results](IR::Value temporary_value, size_t mask) {
|
||||
if (mask == ddx_pattern || mask == ddx_pattern_inv) {
|
||||
results[1] = temporary_value;
|
||||
return;
|
||||
}
|
||||
results[2] = temporary_value;
|
||||
};
|
||||
assign_result(temporary_values[1], zero_mask_b);
|
||||
assign_result(temporary_values[0], zero_mask_a);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
|
||||
IR::TextureInstInfo info = inst.Flags<IR::TextureInstInfo>();
|
||||
auto orig_opcode = inst.GetOpcode();
|
||||
if (info.ndv_is_active == 0) {
|
||||
return;
|
||||
}
|
||||
if (info.type != TextureType::Color3D) {
|
||||
return;
|
||||
}
|
||||
const IR::Value handle{inst.Arg(0)};
|
||||
const IR::Value coords{inst.Arg(1)};
|
||||
const IR::Value bias_lc{inst.Arg(2)};
|
||||
const IR::Value offset{inst.Arg(3)};
|
||||
if (!offset.IsImmediate()) {
|
||||
return;
|
||||
}
|
||||
IR::Inst* const inst2 = coords.InstRecursive();
|
||||
std::array<std::array<IR::Value, 3>, 3> results_matrix;
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
if (!FindGradient3DDerivates(results_matrix[i], inst2->Arg(i).Resolve())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
IR::F32 lod_clamp{};
|
||||
if (info.has_lod_clamp != 0) {
|
||||
if (!bias_lc.IsImmediate()) {
|
||||
lod_clamp = IR::F32{bias_lc.InstRecursive()->Arg(1).Resolve()};
|
||||
} else {
|
||||
lod_clamp = IR::F32{bias_lc};
|
||||
}
|
||||
}
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
IR::Value new_coords =
|
||||
ir.CompositeConstruct(results_matrix[0][0], results_matrix[1][0], results_matrix[2][0]);
|
||||
IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2],
|
||||
results_matrix[1][1], results_matrix[1][2]);
|
||||
IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]);
|
||||
info.num_derivates.Assign(3);
|
||||
IR::Value new_gradient_instruction =
|
||||
ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info);
|
||||
IR::Inst* const new_inst = new_gradient_instruction.InstRecursive();
|
||||
if (orig_opcode == IR::Opcode::ImageSampleImplicitLod) {
|
||||
new_inst->ReplaceOpcode(IR::Opcode::ImageGradient);
|
||||
}
|
||||
inst.ReplaceUsesWith(new_gradient_instruction);
|
||||
}
|
||||
|
||||
void FoldConstBuffer(Environment& env, IR::Block& block, IR::Inst& inst) {
|
||||
const IR::Value bank{inst.Arg(0)};
|
||||
const IR::Value offset{inst.Arg(1)};
|
||||
@ -743,6 +957,12 @@ void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
|
||||
case IR::Opcode::SelectF32:
|
||||
case IR::Opcode::SelectF64:
|
||||
return FoldSelect(inst);
|
||||
case IR::Opcode::FPNeg32:
|
||||
FoldWhenAllImmediates(inst, [](f32 a) { return -a; });
|
||||
return;
|
||||
case IR::Opcode::FPAdd32:
|
||||
FoldFPAdd32(inst);
|
||||
return;
|
||||
case IR::Opcode::FPMul32:
|
||||
return FoldFPMul32(inst);
|
||||
case IR::Opcode::LogicalAnd:
|
||||
@ -858,6 +1078,11 @@ void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) {
|
||||
FoldDriverConstBuffer(env, block, inst, 1);
|
||||
}
|
||||
break;
|
||||
case IR::Opcode::BindlessImageSampleImplicitLod:
|
||||
case IR::Opcode::BoundImageSampleImplicitLod:
|
||||
case IR::Opcode::ImageSampleImplicitLod:
|
||||
FoldImageSampleImplicitLod(block, inst);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -27,14 +27,24 @@ MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192))
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
static void Dump(u64 hash, std::span<const u32> code) {
|
||||
static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
|
||||
const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
|
||||
const auto macro_dir{base_dir / "macros"};
|
||||
if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
|
||||
return;
|
||||
}
|
||||
const auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
|
||||
auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
|
||||
|
||||
if (decompiled) {
|
||||
auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)};
|
||||
if (Common::FS::Exists(name)) {
|
||||
(void)Common::FS::RenameFile(name, new_name);
|
||||
return;
|
||||
}
|
||||
name = new_name;
|
||||
}
|
||||
|
||||
std::fstream macro_file(name, std::ios::out | std::ios::binary);
|
||||
if (!macro_file) {
|
||||
LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
|
||||
@ -90,9 +100,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
if (!mid_method.has_value()) {
|
||||
cache_info.lle_program = Compile(macro_code->second);
|
||||
cache_info.hash = Common::HashValue(macro_code->second);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second);
|
||||
}
|
||||
} else {
|
||||
const auto& macro_cached = uploaded_macro_code[mid_method.value()];
|
||||
const auto rebased_method = method - mid_method.value();
|
||||
@ -102,9 +109,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
code.size() * sizeof(u32));
|
||||
cache_info.hash = Common::HashValue(code);
|
||||
cache_info.lle_program = Compile(code);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, code);
|
||||
}
|
||||
}
|
||||
|
||||
auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
|
||||
@ -117,6 +121,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
MICROPROFILE_SCOPE(MacroHLE);
|
||||
cache_info.hle_program->Execute(parameters, method);
|
||||
}
|
||||
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second, cache_info.has_hle_program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,9 +611,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
|
||||
const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
|
||||
Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(hash, key.unique_hashes[index]);
|
||||
}
|
||||
if (!uses_vertex_a || index != 1) {
|
||||
// Normal path
|
||||
programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
|
||||
@ -624,6 +621,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||
programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
|
||||
}
|
||||
|
||||
if (Settings::values.dump_shaders) {
|
||||
env.Dump(hash, key.unique_hashes[index]);
|
||||
}
|
||||
|
||||
if (programs[index].info.requires_layer_emulation) {
|
||||
layer_source_program = &programs[index];
|
||||
}
|
||||
|
@ -41,9 +41,6 @@ namespace {
|
||||
bool enable_validation) {
|
||||
std::vector<const char*> extensions;
|
||||
extensions.reserve(6);
|
||||
#ifdef __APPLE__
|
||||
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||
#endif
|
||||
switch (window_type) {
|
||||
case Core::Frontend::WindowSystemType::Headless:
|
||||
break;
|
||||
@ -74,6 +71,11 @@ namespace {
|
||||
if (window_type != Core::Frontend::WindowSystemType::Headless) {
|
||||
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (AreExtensionsSupported(dld, std::array{VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME})) {
|
||||
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
if (enable_validation) {
|
||||
const bool debug_utils =
|
||||
AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME});
|
||||
|
@ -19,13 +19,17 @@ std::shared_ptr<Common::DynamicLibrary> OpenLibrary(
|
||||
#else
|
||||
auto library = std::make_shared<Common::DynamicLibrary>();
|
||||
#ifdef __APPLE__
|
||||
const auto libvulkan_filename =
|
||||
Common::FS::GetBundleDirectory() / "Contents/Frameworks/libvulkan.1.dylib";
|
||||
const auto libmoltenvk_filename =
|
||||
Common::FS::GetBundleDirectory() / "Contents/Frameworks/libMoltenVK.dylib";
|
||||
const char* library_paths[] = {std::getenv("LIBVULKAN_PATH"), libvulkan_filename.c_str(),
|
||||
libmoltenvk_filename.c_str()};
|
||||
// Check if a path to a specific Vulkan library has been specified.
|
||||
char* const libvulkan_env = std::getenv("LIBVULKAN_PATH");
|
||||
if (!libvulkan_env || !library->Open(libvulkan_env)) {
|
||||
// Use the libvulkan.dylib from the application bundle.
|
||||
const auto filename =
|
||||
Common::FS::GetBundleDirectory() / "Contents/Frameworks/libvulkan.dylib";
|
||||
void(library->Open(Common::FS::PathToUTF8String(filename).c_str()));
|
||||
for (const auto& library_path : library_paths) {
|
||||
if (library_path && library->Open(library_path)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // for deprecated OpenSSL functions
|
||||
#endif
|
||||
#include <jwt/jwt.hpp>
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
|
@ -313,6 +313,18 @@ if (APPLE)
|
||||
target_sources(yuzu PRIVATE ${MACOSX_ICON})
|
||||
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
|
||||
|
||||
if (NOT USE_SYSTEM_MOLTENVK)
|
||||
set(MOLTENVK_PLATFORM "macOS")
|
||||
set(MOLTENVK_VERSION "v1.2.5")
|
||||
download_moltenvk_external(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
|
||||
endif()
|
||||
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
|
||||
message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")
|
||||
set_source_files_properties(${MOLTENVK_LIBRARY} PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks
|
||||
XCODE_FILE_ATTRIBUTES "CodeSignOnCopy")
|
||||
target_sources(yuzu PRIVATE ${MOLTENVK_LIBRARY})
|
||||
|
||||
elseif(WIN32)
|
||||
# compile as a win32 gui application instead of a console application
|
||||
if (QT_VERSION VERSION_GREATER_EQUAL 6)
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <thread>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
@ -226,9 +228,11 @@ int QtControllerSelectorDialog::exec() {
|
||||
}
|
||||
|
||||
void QtControllerSelectorDialog::ApplyConfiguration() {
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
|
||||
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system);
|
||||
const bool pre_docked_mode = Settings::IsDockedMode();
|
||||
const bool docked_mode_selected = ui->radioDocked->isChecked();
|
||||
Settings::values.use_docked_mode.SetValue(
|
||||
docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
|
||||
OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
|
||||
|
||||
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
|
||||
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
|
||||
@ -616,8 +620,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
|
||||
ui->radioDocked->setEnabled(!is_handheld);
|
||||
ui->radioUndocked->setEnabled(!is_handheld);
|
||||
|
||||
ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioDocked->setChecked(Settings::IsDockedMode());
|
||||
ui->radioUndocked->setChecked(!Settings::IsDockedMode());
|
||||
|
||||
// Also force into undocked mode if the controller type is handheld.
|
||||
if (is_handheld) {
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include "common/settings_enums.h"
|
||||
#include "uisettings.h"
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
|
||||
#include <QCamera>
|
||||
#include <QCameraImageCapture>
|
||||
@ -916,7 +918,6 @@ void GRenderWindow::ReleaseRenderTarget() {
|
||||
|
||||
void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
|
||||
auto& renderer = system.Renderer();
|
||||
const f32 res_scale = Settings::values.resolution_info.up_factor;
|
||||
|
||||
if (renderer.IsScreenshotPending()) {
|
||||
LOG_WARNING(Render,
|
||||
@ -924,7 +925,18 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
|
||||
const Layout::FramebufferLayout layout{[]() {
|
||||
u32 height = UISettings::values.screenshot_height.GetValue();
|
||||
if (height == 0) {
|
||||
height = Settings::IsDockedMode() ? Layout::ScreenDocked::Height
|
||||
: Layout::ScreenUndocked::Height;
|
||||
height *= Settings::values.resolution_info.up_factor;
|
||||
}
|
||||
const u32 width =
|
||||
UISettings::CalculateWidth(height, Settings::values.aspect_ratio.GetValue());
|
||||
return Layout::DefaultFrameLayout(width, height);
|
||||
}()};
|
||||
|
||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||
renderer.RequestScreenshot(
|
||||
screenshot_image.bits(),
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_common.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
@ -85,9 +86,9 @@ const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_ma
|
||||
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
|
||||
};
|
||||
|
||||
const std::map<bool, QString> Config::use_docked_mode_texts_map = {
|
||||
{true, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
|
||||
{false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
|
||||
const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = {
|
||||
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
|
||||
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
|
||||
};
|
||||
|
||||
const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
|
||||
@ -376,7 +377,7 @@ void Config::ReadControlValues() {
|
||||
const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
|
||||
if (controller_type == Settings::ControllerType::Handheld) {
|
||||
Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
|
||||
Settings::values.use_docked_mode.SetValue(false);
|
||||
Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
|
||||
}
|
||||
|
||||
if (IsCustomConfig()) {
|
||||
@ -592,8 +593,7 @@ void Config::ReadRendererValues() {
|
||||
void Config::ReadScreenshotValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Screenshots"));
|
||||
|
||||
UISettings::values.enable_screenshot_save_as =
|
||||
ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool();
|
||||
ReadCategory(Settings::Category::Screenshots);
|
||||
FS::SetYuzuPath(
|
||||
FS::YuzuPath::ScreenshotsDir,
|
||||
qt_config
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
class QSettings;
|
||||
@ -51,7 +52,7 @@ public:
|
||||
|
||||
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
|
||||
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
|
||||
static const std::map<bool, QString> use_docked_mode_texts_map;
|
||||
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map;
|
||||
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
|
||||
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
|
||||
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <memory>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "ui_configure.h"
|
||||
#include "vk_device_info.h"
|
||||
@ -41,16 +42,19 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
|
||||
general_tab{std::make_unique<ConfigureGeneral>(system_, nullptr, *builder, this)},
|
||||
graphics_advanced_tab{
|
||||
std::make_unique<ConfigureGraphicsAdvanced>(system_, nullptr, *builder, this)},
|
||||
ui_tab{std::make_unique<ConfigureUi>(system_, this)},
|
||||
graphics_tab{std::make_unique<ConfigureGraphics>(
|
||||
system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
|
||||
[this](Settings::AspectRatio ratio, Settings::ResolutionSetup setup) {
|
||||
ui_tab->UpdateScreenshotInfo(ratio, setup);
|
||||
},
|
||||
nullptr, *builder, this)},
|
||||
hotkeys_tab{std::make_unique<ConfigureHotkeys>(system_.HIDCore(), this)},
|
||||
input_tab{std::make_unique<ConfigureInput>(system_, this)},
|
||||
network_tab{std::make_unique<ConfigureNetwork>(system_, this)},
|
||||
profile_tab{std::make_unique<ConfigureProfileManager>(system_, this)},
|
||||
system_tab{std::make_unique<ConfigureSystem>(system_, nullptr, *builder, this)},
|
||||
ui_tab{std::make_unique<ConfigureUi>(system_, this)}, web_tab{std::make_unique<ConfigureWeb>(
|
||||
this)} {
|
||||
web_tab{std::make_unique<ConfigureWeb>(this)} {
|
||||
Settings::SetConfiguringGlobal(true);
|
||||
|
||||
ui->setupUi(this);
|
||||
|
@ -81,12 +81,12 @@ private:
|
||||
std::unique_ptr<ConfigureFilesystem> filesystem_tab;
|
||||
std::unique_ptr<ConfigureGeneral> general_tab;
|
||||
std::unique_ptr<ConfigureGraphicsAdvanced> graphics_advanced_tab;
|
||||
std::unique_ptr<ConfigureUi> ui_tab;
|
||||
std::unique_ptr<ConfigureGraphics> graphics_tab;
|
||||
std::unique_ptr<ConfigureHotkeys> hotkeys_tab;
|
||||
std::unique_ptr<ConfigureInput> input_tab;
|
||||
std::unique_ptr<ConfigureNetwork> network_tab;
|
||||
std::unique_ptr<ConfigureProfileManager> profile_tab;
|
||||
std::unique_ptr<ConfigureSystem> system_tab;
|
||||
std::unique_ptr<ConfigureUi> ui_tab;
|
||||
std::unique_ptr<ConfigureWeb> web_tab;
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <QtCore/qobjectdefs.h>
|
||||
#include <qabstractbutton.h>
|
||||
#include <qboxlayout.h>
|
||||
#include <qcombobox.h>
|
||||
#include <qcoreevent.h>
|
||||
#include <qglobal.h>
|
||||
#include <qgridlayout.h>
|
||||
@ -77,13 +78,16 @@ static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode)
|
||||
}
|
||||
}
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
|
||||
std::vector<VkDeviceInfo::Record>& records_,
|
||||
ConfigureGraphics::ConfigureGraphics(
|
||||
const Core::System& system_, std::vector<VkDeviceInfo::Record>& records_,
|
||||
const std::function<void()>& expose_compute_option_,
|
||||
const std::function<void(Settings::AspectRatio, Settings::ResolutionSetup)>&
|
||||
update_aspect_ratio_,
|
||||
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
|
||||
const ConfigurationShared::Builder& builder, QWidget* parent)
|
||||
: ConfigurationShared::Tab(group_, parent), ui{std::make_unique<Ui::ConfigureGraphics>()},
|
||||
records{records_}, expose_compute_option{expose_compute_option_}, system{system_},
|
||||
records{records_}, expose_compute_option{expose_compute_option_},
|
||||
update_aspect_ratio{update_aspect_ratio_}, system{system_},
|
||||
combobox_translations{builder.ComboboxTranslations()},
|
||||
shader_mapping{
|
||||
combobox_translations.at(Settings::EnumMetadata<Settings::ShaderBackend>::Index())} {
|
||||
@ -140,6 +144,26 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_,
|
||||
UpdateBackgroundColorButton(new_bg_color);
|
||||
});
|
||||
|
||||
const auto& update_screenshot_info = [this, &builder]() {
|
||||
const auto& combobox_enumerations = builder.ComboboxTranslations().at(
|
||||
Settings::EnumMetadata<Settings::AspectRatio>::Index());
|
||||
const auto index = aspect_ratio_combobox->currentIndex();
|
||||
const auto ratio = static_cast<Settings::AspectRatio>(combobox_enumerations[index].first);
|
||||
|
||||
const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at(
|
||||
Settings::EnumMetadata<Settings::ResolutionSetup>::Index());
|
||||
const auto res_index = resolution_combobox->currentIndex();
|
||||
const auto setup = static_cast<Settings::ResolutionSetup>(
|
||||
combobox_enumerations_resolution[res_index].first);
|
||||
|
||||
update_aspect_ratio(ratio, setup);
|
||||
};
|
||||
|
||||
connect(aspect_ratio_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
update_screenshot_info);
|
||||
connect(resolution_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
update_screenshot_info);
|
||||
|
||||
api_combobox->setEnabled(!UISettings::values.has_broken_vulkan && api_combobox->isEnabled());
|
||||
ui->api_widget->setEnabled(
|
||||
(!UISettings::values.has_broken_vulkan || Settings::IsConfiguringGlobal()) &&
|
||||
@ -280,6 +304,14 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
|
||||
// Keep track of vsync_mode's combobox so we can populate it
|
||||
vsync_mode_combobox = widget->combobox;
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
} else if (setting->Id() == Settings::values.aspect_ratio.Id()) {
|
||||
// Keep track of the aspect ratio combobox to update other UI tabs that need it
|
||||
aspect_ratio_combobox = widget->combobox;
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
} else if (setting->Id() == Settings::values.resolution_setup.Id()) {
|
||||
// Keep track of the resolution combobox to update other UI tabs that need it
|
||||
resolution_combobox = widget->combobox;
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
} else {
|
||||
hold_graphics.emplace(setting->Id(), widget);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <qobjectdefs.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "configuration/shared_translation.h"
|
||||
#include "vk_device_info.h"
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
@ -43,12 +44,13 @@ class Builder;
|
||||
|
||||
class ConfigureGraphics : public ConfigurationShared::Tab {
|
||||
public:
|
||||
explicit ConfigureGraphics(const Core::System& system_,
|
||||
std::vector<VkDeviceInfo::Record>& records,
|
||||
const std::function<void()>& expose_compute_option_,
|
||||
explicit ConfigureGraphics(
|
||||
const Core::System& system_, std::vector<VkDeviceInfo::Record>& records,
|
||||
const std::function<void()>& expose_compute_option,
|
||||
const std::function<void(Settings::AspectRatio, Settings::ResolutionSetup)>&
|
||||
update_aspect_ratio,
|
||||
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
|
||||
const ConfigurationShared::Builder& builder,
|
||||
QWidget* parent = nullptr);
|
||||
const ConfigurationShared::Builder& builder, QWidget* parent = nullptr);
|
||||
~ConfigureGraphics() override;
|
||||
|
||||
void ApplyConfiguration() override;
|
||||
@ -91,6 +93,7 @@ private:
|
||||
u32 vulkan_device{};
|
||||
Settings::ShaderBackend shader_backend{};
|
||||
const std::function<void()>& expose_compute_option;
|
||||
const std::function<void(Settings::AspectRatio, Settings::ResolutionSetup)> update_aspect_ratio;
|
||||
|
||||
const Core::System& system;
|
||||
const ConfigurationShared::ComboboxTranslationMap& combobox_translations;
|
||||
@ -104,4 +107,6 @@ private:
|
||||
QWidget* vulkan_device_widget;
|
||||
QWidget* api_widget;
|
||||
QWidget* shader_backend_widget;
|
||||
QComboBox* aspect_ratio_combobox;
|
||||
QComboBox* resolution_combobox;
|
||||
};
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hid/emulated_controller.h"
|
||||
#include "core/hid/hid_core.h"
|
||||
@ -197,9 +199,11 @@ void ConfigureInput::ApplyConfiguration() {
|
||||
|
||||
advanced->ApplyConfiguration();
|
||||
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
|
||||
Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system);
|
||||
const bool pre_docked_mode = Settings::IsDockedMode();
|
||||
const bool docked_mode_selected = ui->radioDocked->isChecked();
|
||||
Settings::values.use_docked_mode.SetValue(
|
||||
docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
|
||||
OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
|
||||
|
||||
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
|
||||
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
|
||||
@ -267,8 +271,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
|
||||
ui->radioDocked->setEnabled(!is_handheld);
|
||||
ui->radioUndocked->setEnabled(!is_handheld);
|
||||
|
||||
ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
|
||||
ui->radioDocked->setChecked(Settings::IsDockedMode());
|
||||
ui->radioUndocked->setChecked(!Settings::IsDockedMode());
|
||||
|
||||
// Also force into undocked mode if the controller type is handheld.
|
||||
if (is_handheld) {
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include <QTimer>
|
||||
|
||||
#include "common/fs/fs_util.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "common/settings_input.h"
|
||||
#include "configuration/shared_widget.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@ -57,7 +59,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
|
||||
std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *builder, this);
|
||||
graphics_tab = std::make_unique<ConfigureGraphics>(
|
||||
system_, vk_device_records, [&]() { graphics_advanced_tab->ExposeComputeOption(); },
|
||||
tab_group, *builder, this);
|
||||
[](Settings::AspectRatio, Settings::ResolutionSetup) {}, tab_group, *builder, this);
|
||||
input_tab = std::make_unique<ConfigureInputPerGame>(system_, game_config.get(), this);
|
||||
system_tab = std::make_unique<ConfigureSystem>(system_, tab_group, *builder, this);
|
||||
|
||||
@ -97,6 +99,12 @@ void ConfigurePerGame::ApplyConfiguration() {
|
||||
addons_tab->ApplyConfiguration();
|
||||
input_tab->ApplyConfiguration();
|
||||
|
||||
if (Settings::IsDockedMode() && Settings::values.players.GetValue()[0].controller_type ==
|
||||
Settings::ControllerType::Handheld) {
|
||||
Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
|
||||
Settings::values.use_docked_mode.SetGlobal(true);
|
||||
}
|
||||
|
||||
system.ApplySettings();
|
||||
Settings::LogSettings();
|
||||
|
||||
|
@ -106,6 +106,11 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
|
||||
push(Settings::values.linkage.by_category[Settings::Category::System]);
|
||||
|
||||
for (auto setting : settings) {
|
||||
if (setting->Id() == Settings::values.use_docked_mode.Id() &&
|
||||
Settings::IsConfiguringGlobal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
|
||||
|
||||
if (widget == nullptr) {
|
||||
|
@ -1,18 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <QFileDialog>
|
||||
#include "yuzu/configuration/configure_ui.h"
|
||||
|
||||
#include <array>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QDirIterator>
|
||||
#include <QFileDialog>
|
||||
#include <QString>
|
||||
#include <QToolButton>
|
||||
#include <QVariant>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "ui_configure_ui.h"
|
||||
#include "yuzu/configuration/configure_ui.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
namespace {
|
||||
@ -54,8 +67,44 @@ QString GetTranslatedRowTextName(size_t index) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
static float GetUpFactor(Settings::ResolutionSetup res_setup) {
|
||||
Settings::ResolutionScalingInfo info{};
|
||||
Settings::TranslateResolutionInfo(res_setup, info);
|
||||
return info.up_factor;
|
||||
}
|
||||
|
||||
static void PopulateResolutionComboBox(QComboBox* screenshot_height, QWidget* parent) {
|
||||
screenshot_height->clear();
|
||||
|
||||
const auto& enumeration =
|
||||
Settings::EnumMetadata<Settings::ResolutionSetup>::Canonicalizations();
|
||||
std::set<u32> resolutions{};
|
||||
for (const auto& [name, value] : enumeration) {
|
||||
const float up_factor = GetUpFactor(value);
|
||||
u32 height_undocked = Layout::ScreenUndocked::Height * up_factor;
|
||||
u32 height_docked = Layout::ScreenDocked::Height * up_factor;
|
||||
resolutions.emplace(height_undocked);
|
||||
resolutions.emplace(height_docked);
|
||||
}
|
||||
|
||||
screenshot_height->addItem(parent->tr("Auto", "Screenshot height option"));
|
||||
for (const auto res : resolutions) {
|
||||
screenshot_height->addItem(QString::fromStdString(std::to_string(res)));
|
||||
}
|
||||
}
|
||||
|
||||
static u32 ScreenshotDimensionToInt(const QString& height) {
|
||||
try {
|
||||
return std::stoi(height.toStdString());
|
||||
} catch (std::invalid_argument&) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
|
||||
: QWidget(parent), ui{std::make_unique<Ui::ConfigureUi>()}, system{system_} {
|
||||
: QWidget(parent), ui{std::make_unique<Ui::ConfigureUi>()},
|
||||
ratio{Settings::values.aspect_ratio.GetValue()},
|
||||
resolution_setting{Settings::values.resolution_setup.GetValue()}, system{system_} {
|
||||
ui->setupUi(this);
|
||||
|
||||
InitializeLanguageComboBox();
|
||||
@ -68,6 +117,8 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
|
||||
InitializeIconSizeComboBox();
|
||||
InitializeRowComboBoxes();
|
||||
|
||||
PopulateResolutionComboBox(ui->screenshot_height, this);
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
// Force game list reload if any of the relevant settings are changed.
|
||||
@ -104,6 +155,10 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
|
||||
ui->screenshot_path_edit->setText(dir);
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->screenshot_height, &QComboBox::currentTextChanged, [this]() { UpdateWidthText(); });
|
||||
|
||||
UpdateWidthText();
|
||||
}
|
||||
|
||||
ConfigureUi::~ConfigureUi() = default;
|
||||
@ -123,6 +178,10 @@ void ConfigureUi::ApplyConfiguration() {
|
||||
UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
|
||||
Common::FS::SetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir,
|
||||
ui->screenshot_path_edit->text().toStdString());
|
||||
|
||||
const u32 height = ScreenshotDimensionToInt(ui->screenshot_height->currentText());
|
||||
UISettings::values.screenshot_height.SetValue(height);
|
||||
|
||||
system.ApplySettings();
|
||||
}
|
||||
|
||||
@ -147,6 +206,13 @@ void ConfigureUi::SetConfiguration() {
|
||||
UISettings::values.enable_screenshot_save_as.GetValue());
|
||||
ui->screenshot_path_edit->setText(QString::fromStdString(
|
||||
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)));
|
||||
|
||||
const auto height = UISettings::values.screenshot_height.GetValue();
|
||||
if (height == 0) {
|
||||
ui->screenshot_height->setCurrentIndex(0);
|
||||
} else {
|
||||
ui->screenshot_height->setCurrentText(QStringLiteral("%1").arg(height));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureUi::changeEvent(QEvent* event) {
|
||||
@ -317,3 +383,29 @@ void ConfigureUi::OnLanguageChanged(int index) {
|
||||
|
||||
emit LanguageChanged(ui->language_combobox->itemData(index).toString());
|
||||
}
|
||||
|
||||
void ConfigureUi::UpdateWidthText() {
|
||||
const u32 height = ScreenshotDimensionToInt(ui->screenshot_height->currentText());
|
||||
const u32 width = UISettings::CalculateWidth(height, ratio);
|
||||
if (height == 0) {
|
||||
const auto up_factor = GetUpFactor(resolution_setting);
|
||||
const u32 height_docked = Layout::ScreenDocked::Height * up_factor;
|
||||
const u32 width_docked = UISettings::CalculateWidth(height_docked, ratio);
|
||||
const u32 height_undocked = Layout::ScreenUndocked::Height * up_factor;
|
||||
const u32 width_undocked = UISettings::CalculateWidth(height_undocked, ratio);
|
||||
ui->screenshot_width->setText(tr("Auto (%1 x %2, %3 x %4)", "Screenshot width value")
|
||||
.arg(width_undocked)
|
||||
.arg(height_undocked)
|
||||
.arg(width_docked)
|
||||
.arg(height_docked));
|
||||
} else {
|
||||
ui->screenshot_width->setText(QStringLiteral("%1 x").arg(width));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureUi::UpdateScreenshotInfo(Settings::AspectRatio ratio_,
|
||||
Settings::ResolutionSetup resolution_setting_) {
|
||||
ratio = ratio_;
|
||||
resolution_setting = resolution_setting_;
|
||||
UpdateWidthText();
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
#include "common/settings_enums.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@ -23,6 +24,9 @@ public:
|
||||
|
||||
void ApplyConfiguration();
|
||||
|
||||
void UpdateScreenshotInfo(Settings::AspectRatio ratio,
|
||||
Settings::ResolutionSetup resolution_info);
|
||||
|
||||
private slots:
|
||||
void OnLanguageChanged(int index);
|
||||
|
||||
@ -44,7 +48,11 @@ private:
|
||||
void UpdateFirstRowComboBox(bool init = false);
|
||||
void UpdateSecondRowComboBox(bool init = false);
|
||||
|
||||
void UpdateWidthText();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureUi> ui;
|
||||
|
||||
Settings::AspectRatio ratio;
|
||||
Settings::ResolutionSetup resolution_setting;
|
||||
Core::System& system;
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>363</width>
|
||||
<height>562</height>
|
||||
<height>603</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -201,6 +201,41 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="screenshot_width">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="screenshot_height">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Resolution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -135,7 +135,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
INSERT(Settings, region_index, "Region:", "");
|
||||
INSERT(Settings, time_zone_index, "Time Zone:", "");
|
||||
INSERT(Settings, sound_index, "Sound Output Mode:", "");
|
||||
INSERT(Settings, use_docked_mode, "", "");
|
||||
INSERT(Settings, use_docked_mode, "Console Mode:", "");
|
||||
INSERT(Settings, current_user, "", "");
|
||||
|
||||
// Controls
|
||||
@ -379,6 +379,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
||||
PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"),
|
||||
PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"),
|
||||
}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
|
||||
{PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
|
||||
|
||||
#undef PAIR
|
||||
#undef CTX_PAIR
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QObject>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSizePolicy>
|
||||
#include <QSlider>
|
||||
@ -171,6 +172,65 @@ QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
|
||||
return combobox;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
const auto type = setting.EnumIndex();
|
||||
|
||||
QWidget* group = new QWidget(this);
|
||||
QHBoxLayout* layout = new QHBoxLayout(group);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
group->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
const ComboboxTranslations* enumeration{nullptr};
|
||||
if (combobox_enumerations.contains(type)) {
|
||||
enumeration = &combobox_enumerations.at(type);
|
||||
for (const auto& [id, name] : *enumeration) {
|
||||
QRadioButton* radio_button = new QRadioButton(name, group);
|
||||
layout->addWidget(radio_button);
|
||||
radio_buttons.push_back({id, radio_button});
|
||||
}
|
||||
} else {
|
||||
return group;
|
||||
}
|
||||
|
||||
const auto get_selected = [=]() -> u32 {
|
||||
for (const auto& [id, button] : radio_buttons) {
|
||||
if (button->isChecked()) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const auto set_index = [=](u32 value) {
|
||||
for (const auto& [id, button] : radio_buttons) {
|
||||
button->setChecked(id == value);
|
||||
}
|
||||
};
|
||||
|
||||
const u32 setting_value = std::stoi(setting.ToString());
|
||||
set_index(setting_value);
|
||||
|
||||
serializer = [get_selected]() {
|
||||
int current = get_selected();
|
||||
return std::to_string(current);
|
||||
};
|
||||
|
||||
restore_func = [this, set_index]() {
|
||||
const u32 global_value = std::stoi(RelevantDefault(setting));
|
||||
set_index(global_value);
|
||||
};
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
for (const auto& [id, button] : radio_buttons) {
|
||||
QObject::connect(button, &QAbstractButton::clicked, [touch]() { touch(); });
|
||||
}
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch, bool managed) {
|
||||
@ -235,6 +295,7 @@ QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& gi
|
||||
slider->setValue(std::stoi(setting.ToString()));
|
||||
|
||||
slider->setInvertedAppearance(reversed);
|
||||
slider->setInvertedControls(reversed);
|
||||
|
||||
serializer = [this]() { return std::to_string(slider->value()); };
|
||||
restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); };
|
||||
@ -410,6 +471,8 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
|
||||
return RequestType::Slider;
|
||||
case Settings::Specialization::Countable:
|
||||
return RequestType::SpinBox;
|
||||
case Settings::Specialization::Radio:
|
||||
return RequestType::RadioGroup;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -438,7 +501,11 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
|
||||
if (setting.TypeId() == typeid(bool)) {
|
||||
data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch);
|
||||
} else if (setting.IsEnum()) {
|
||||
if (request == RequestType::RadioGroup) {
|
||||
data_component = CreateRadioGroup(serializer, restore_func, touch);
|
||||
} else {
|
||||
data_component = CreateCombobox(serializer, restore_func, touch);
|
||||
}
|
||||
} else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
|
||||
type == typeid(s64) || type == typeid(u8)) {
|
||||
switch (request) {
|
||||
|
@ -22,6 +22,7 @@ class QObject;
|
||||
class QPushButton;
|
||||
class QSlider;
|
||||
class QSpinBox;
|
||||
class QRadioButton;
|
||||
|
||||
namespace Settings {
|
||||
class BasicSetting;
|
||||
@ -38,6 +39,7 @@ enum class RequestType {
|
||||
LineEdit,
|
||||
HexEdit,
|
||||
DateTimeEdit,
|
||||
RadioGroup,
|
||||
MaxEnum,
|
||||
};
|
||||
|
||||
@ -91,6 +93,7 @@ public:
|
||||
QSlider* slider{};
|
||||
QComboBox* combobox{};
|
||||
QDateTimeEdit* date_time_edit{};
|
||||
std::vector<std::pair<u32, QRadioButton*>> radio_buttons{};
|
||||
|
||||
private:
|
||||
void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
|
||||
@ -106,6 +109,9 @@ private:
|
||||
QWidget* CreateCombobox(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch);
|
||||
QWidget* CreateRadioGroup(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch);
|
||||
QWidget* CreateLineEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func, const std::function<void()>& touch,
|
||||
bool managed = true);
|
||||
|
@ -191,8 +191,9 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
|
||||
}
|
||||
|
||||
QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name,
|
||||
const std::vector<u8>& icon, Loader::AppLoader& loader,
|
||||
u64 program_id, const CompatibilityList& compatibility_list,
|
||||
const std::size_t size, const std::vector<u8>& icon,
|
||||
Loader::AppLoader& loader, u64 program_id,
|
||||
const CompatibilityList& compatibility_list,
|
||||
const FileSys::PatchManager& patch) {
|
||||
const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
@ -210,7 +211,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
|
||||
file_type_string, program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(file_type_string),
|
||||
new GameListItemSize(Common::FS::GetSize(path)),
|
||||
new GameListItemSize(size),
|
||||
};
|
||||
|
||||
const auto patch_versions = GetGameListCachedObject(
|
||||
@ -278,8 +279,8 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||
GetMetadataFromControlNCA(patch, *control, icon, name);
|
||||
}
|
||||
|
||||
emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
|
||||
compatibility_list, patch),
|
||||
emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
||||
program_id, compatibility_list, patch),
|
||||
parent_dir);
|
||||
}
|
||||
}
|
||||
@ -354,8 +355,9 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, id,
|
||||
compatibility_list, patch),
|
||||
emit EntryReady(MakeGameListEntry(physical_name, name,
|
||||
Common::FS::GetSize(physical_name), icon,
|
||||
*loader, id, compatibility_list, patch),
|
||||
parent_dir);
|
||||
}
|
||||
} else {
|
||||
@ -368,8 +370,9 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader,
|
||||
program_id, compatibility_list, patch),
|
||||
emit EntryReady(
|
||||
MakeGameListEntry(physical_name, name, Common::FS::GetSize(physical_name),
|
||||
icon, *loader, program_id, compatibility_list, patch),
|
||||
parent_dir);
|
||||
}
|
||||
}
|
||||
|
@ -1158,9 +1158,9 @@ void GMainWindow::InitializeWidgets() {
|
||||
[this](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
|
||||
for (auto const& docked_mode_pair : Config::use_docked_mode_texts_map) {
|
||||
context_menu.addAction(docked_mode_pair.second, [this, docked_mode_pair] {
|
||||
if (docked_mode_pair.first != Settings::values.use_docked_mode.GetValue()) {
|
||||
for (auto const& pair : Config::use_docked_mode_texts_map) {
|
||||
context_menu.addAction(pair.second, [this, &pair] {
|
||||
if (pair.first != Settings::values.use_docked_mode.GetValue()) {
|
||||
OnToggleDockedMode();
|
||||
}
|
||||
});
|
||||
@ -1811,6 +1811,43 @@ bool GMainWindow::SelectAndSetCurrentUser(
|
||||
return true;
|
||||
}
|
||||
|
||||
void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
|
||||
// Ensure all NCAs are registered before launching the game
|
||||
const auto file = vfs->OpenFile(filepath, FileSys::Mode::Read);
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(*system, file);
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file_type = loader->GetFileType();
|
||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
u64 program_id = 0;
|
||||
const auto res2 = loader->ReadProgramId(program_id);
|
||||
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
||||
provider->AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id,
|
||||
file);
|
||||
} else if (res2 == Loader::ResultStatus::Success &&
|
||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||
const auto nsp = file_type == Loader::FileType::NSP
|
||||
? std::make_shared<FileSys::NSP>(file)
|
||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||
entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
|
||||
StartGameType type) {
|
||||
LOG_INFO(Frontend, "yuzu starting...");
|
||||
@ -1825,6 +1862,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
|
||||
last_filename_booted = filename;
|
||||
|
||||
ConfigureFilesystemProvider(filename.toStdString());
|
||||
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
|
||||
const auto loader = Loader::GetLoader(*system, v_file, program_id, program_index);
|
||||
|
||||
@ -3636,7 +3674,7 @@ void GMainWindow::OnTasReset() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnToggleDockedMode() {
|
||||
const bool is_docked = Settings::values.use_docked_mode.GetValue();
|
||||
const bool is_docked = Settings::IsDockedMode();
|
||||
auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||
|
||||
@ -3650,7 +3688,8 @@ void GMainWindow::OnToggleDockedMode() {
|
||||
controller_dialog->refreshConfiguration();
|
||||
}
|
||||
|
||||
Settings::values.use_docked_mode.SetValue(!is_docked);
|
||||
Settings::values.use_docked_mode.SetValue(is_docked ? Settings::ConsoleMode::Handheld
|
||||
: Settings::ConsoleMode::Docked);
|
||||
UpdateDockedButton();
|
||||
OnDockedModeChanged(is_docked, !is_docked, *system);
|
||||
}
|
||||
@ -4080,10 +4119,10 @@ void GMainWindow::UpdateGPUAccuracyButton() {
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateDockedButton() {
|
||||
const bool is_docked = Settings::values.use_docked_mode.GetValue();
|
||||
dock_status_button->setChecked(is_docked);
|
||||
const auto console_mode = Settings::values.use_docked_mode.GetValue();
|
||||
dock_status_button->setChecked(Settings::IsDockedMode());
|
||||
dock_status_button->setText(
|
||||
Config::use_docked_mode_texts_map.find(is_docked)->second.toUpper());
|
||||
Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateAPIText() {
|
||||
|
@ -399,6 +399,7 @@ private:
|
||||
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
|
||||
bool CheckDarkMode();
|
||||
bool CheckSystemArchiveDecryption();
|
||||
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||
|
||||
QString GetTasStateDescription() const;
|
||||
bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
|
||||
|
@ -36,4 +36,20 @@ bool IsDarkTheme() {
|
||||
|
||||
Values values = {};
|
||||
|
||||
u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
|
||||
switch (ratio) {
|
||||
case Settings::AspectRatio::R4_3:
|
||||
return height * 4 / 3;
|
||||
case Settings::AspectRatio::R21_9:
|
||||
return height * 21 / 9;
|
||||
case Settings::AspectRatio::R16_10:
|
||||
return height * 16 / 10;
|
||||
case Settings::AspectRatio::R16_9:
|
||||
case Settings::AspectRatio::Stretch:
|
||||
// TODO: Move this function wherever appropriate to implement Stretched aspect
|
||||
break;
|
||||
}
|
||||
return height * 16 / 9;
|
||||
}
|
||||
|
||||
} // namespace UISettings
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <QVector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_enums.h"
|
||||
|
||||
using Settings::Category;
|
||||
using Settings::Setting;
|
||||
@ -127,8 +128,10 @@ struct Values {
|
||||
// logging
|
||||
Setting<bool> show_console{linkage, false, "showConsole", Category::Ui};
|
||||
|
||||
// Screenshots
|
||||
Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as",
|
||||
Category::Screenshots};
|
||||
Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
|
||||
|
||||
QString roms_path;
|
||||
QString symbols_path;
|
||||
@ -187,6 +190,8 @@ struct Values {
|
||||
|
||||
extern Values values;
|
||||
|
||||
u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
|
||||
|
||||
} // namespace UISettings
|
||||
|
||||
Q_DECLARE_METATYPE(UISettings::GameDir*);
|
||||
|
Reference in New Issue
Block a user