Compare commits
40 Commits
android-47
...
android-51
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9af082a1a | ||
|
|
bc4e58eb51 | ||
|
|
8674724ef0 | ||
|
|
a8edbb7019 | ||
|
|
d8c8fbe41f | ||
|
|
234cc45192 | ||
|
|
b923f5aa7e | ||
|
|
18ad55be0b | ||
|
|
4e71628097 | ||
|
|
92e6ff30a1 | ||
|
|
49df2b9715 | ||
|
|
2f2de400e1 | ||
|
|
9e134c3da2 | ||
|
|
59b3c30f94 | ||
|
|
a669e37ddb | ||
|
|
7d89f2c146 | ||
|
|
51ffc2c66c | ||
|
|
e41655960e | ||
|
|
ccd163ab2c | ||
|
|
182fb83556 | ||
|
|
2c4ebeb51d | ||
|
|
35b77b9599 | ||
|
|
bc4ad5e62d | ||
|
|
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()
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
||||
-----
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>();
|
||||
@@ -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;
|
||||
|
||||
@@ -207,9 +207,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 +267,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) {
|
||||
|
||||
@@ -525,6 +525,7 @@ 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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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{};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -31,9 +31,9 @@ CredHandle cred_handle;
|
||||
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_DEFAULT_CREDS; // don't automatically present a client certificate
|
||||
SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols
|
||||
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
|
||||
// certificate, and presenting one to some arbitrary server
|
||||
@@ -227,16 +227,15 @@ 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,
|
||||
0, // Reserved1
|
||||
0, // TargetDataRep not used with Schannel
|
||||
initial_call_done ? &input_desc : nullptr,
|
||||
0, // Reserved2
|
||||
initial_call_done ? nullptr : &ctxt, &output_desc, &attr,
|
||||
nullptr); // ptsExpiry
|
||||
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,
|
||||
0, // Reserved2
|
||||
initial_call_done ? nullptr : &ctxt, &output_desc, &attr,
|
||||
nullptr); // ptsExpiry
|
||||
|
||||
if (output_buffers[0].pvBuffer) {
|
||||
const std::span span(static_cast<u8*>(output_buffers[0].pvBuffer),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,7 +668,12 @@ 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) {
|
||||
return;
|
||||
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)};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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::values.use_docked_mode.GetValue() ? 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(),
|
||||
|
||||
@@ -592,8 +592,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
|
||||
|
||||
@@ -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_,
|
||||
const std::function<void()>& expose_compute_option_,
|
||||
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,
|
||||
const ConfigurationShared::Builder& builder, QWidget* parent)
|
||||
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_,
|
||||
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group,
|
||||
const ConfigurationShared::Builder& builder,
|
||||
QWidget* parent = nullptr);
|
||||
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);
|
||||
~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;
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <QTimer>
|
||||
|
||||
#include "common/fs/fs_util.h"
|
||||
#include "common/settings_enums.h"
|
||||
#include "configuration/shared_widget.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -57,7 +58,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);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -235,6 +235,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))); };
|
||||
|
||||
@@ -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,9 +370,10 @@ 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),
|
||||
parent_dir);
|
||||
emit EntryReady(
|
||||
MakeGameListEntry(physical_name, name, Common::FS::GetSize(physical_name),
|
||||
icon, *loader, program_id, compatibility_list, patch),
|
||||
parent_dir);
|
||||
}
|
||||
}
|
||||
} else if (is_dir) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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