Compare commits
93 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db6fbd5894 | ||
|
|
f1d8c83e1c | ||
|
|
07632ad825 | ||
|
|
b2305dcee0 | ||
|
|
b971b82275 | ||
|
|
84b5804834 | ||
|
|
539675b21a | ||
|
|
739d90ee66 | ||
|
|
ed89bcc767 | ||
|
|
f1aabc21ee | ||
|
|
80a0f2a118 | ||
|
|
6001538139 | ||
|
|
63cc4e417f | ||
|
|
e60733aad3 | ||
|
|
5fb27f83cf | ||
|
|
f16a94fb39 | ||
|
|
e5abf11186 | ||
|
|
1074c87f18 | ||
|
|
7a051c4973 | ||
|
|
a4306b9e56 | ||
|
|
6744e7ea4a | ||
|
|
fb0fefc75c | ||
|
|
a0ee597b19 | ||
|
|
a45a57641f | ||
|
|
fca26980a2 | ||
|
|
995067538d | ||
|
|
01a1adfb0c | ||
|
|
798c1b457d | ||
|
|
d7a2dc4cea | ||
|
|
d39b457566 | ||
|
|
755506d404 | ||
|
|
7351ca8c75 | ||
|
|
75a01475d1 | ||
|
|
6d8d7ebc66 | ||
|
|
7f4d96d873 | ||
|
|
4c269e5ced | ||
|
|
0974533c96 | ||
|
|
df27813e76 | ||
|
|
bd2feb6873 | ||
|
|
48aa076049 | ||
|
|
e73c53fad1 | ||
|
|
63d23835ef | ||
|
|
0f60ccdde2 | ||
|
|
f9e748b721 | ||
|
|
1c7d106aac | ||
|
|
83b1b259bd | ||
|
|
95cd2e17ab | ||
|
|
77bdd09aa2 | ||
|
|
5311b562aa | ||
|
|
9c2773409a | ||
|
|
713e204a8a | ||
|
|
3c7a115afe | ||
|
|
ec13746e4d | ||
|
|
cf76769026 | ||
|
|
4e2464a713 | ||
|
|
2a87ddcce2 | ||
|
|
dff8f1f9f8 | ||
|
|
e7c77b90c4 | ||
|
|
6ae0b83442 | ||
|
|
35f7740b6c | ||
|
|
2af7e40ce6 | ||
|
|
1db10b8f4c | ||
|
|
155c8ba04c | ||
|
|
40cf9288e9 | ||
|
|
7bd3558c64 | ||
|
|
b700d657d7 | ||
|
|
fa4d6df4c5 | ||
|
|
4f678284e2 | ||
|
|
be06b21f7b | ||
|
|
76bd617b2d | ||
|
|
c0dc8f9d25 | ||
|
|
52882a93a5 | ||
|
|
9f8e17cb18 | ||
|
|
3f910efb40 | ||
|
|
38b585a309 | ||
|
|
b1b1ed7597 | ||
|
|
f6cb128eac | ||
|
|
28aa697528 | ||
|
|
24c2930012 | ||
|
|
86946ea13c | ||
|
|
f3630a0713 | ||
|
|
a0c499aef7 | ||
|
|
f2eead3b5b | ||
|
|
6a0010d0c6 | ||
|
|
8562b516c0 | ||
|
|
9ff3401133 | ||
|
|
9d55e5586f | ||
|
|
8584a77eb2 | ||
|
|
1a84209418 | ||
|
|
c94b398f14 | ||
|
|
a6db8e5f4d | ||
|
|
c387a72c76 | ||
|
|
7527402a46 |
@@ -1,4 +1,13 @@
|
||||
<!--
|
||||
---
|
||||
name: Bug Report / Feature Request
|
||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
|
||||
|
||||
Please read the FAQ:
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: yuzu Discord
|
||||
url: https://discord.com/invite/u77vRWY
|
||||
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
|
||||
- name: Community forums
|
||||
url: https://community.citra-emu.org
|
||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
||||
@@ -118,8 +118,17 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
# Configure C++ standard
|
||||
# ===========================
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
# boost asio's concept usage doesn't play nicely with some compilers yet.
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
|
||||
if (MSVC)
|
||||
add_compile_options(/std:c++latest)
|
||||
|
||||
# cubeb and boost still make use of deprecated result_of.
|
||||
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
# Output binaries to bin/
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
@@ -151,7 +160,7 @@ macro(yuzu_find_packages)
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Boost 1.71 boost/1.72.0"
|
||||
"Catch2 2.11 catch2/2.11.0"
|
||||
"fmt 6.2 fmt/6.2.0"
|
||||
"fmt 7.0 fmt/7.0.1"
|
||||
# can't use until https://github.com/bincrafters/community/issues/1173
|
||||
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
@@ -211,7 +220,7 @@ if(ENABLE_QT)
|
||||
|
||||
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
|
||||
endif()
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT})
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
@@ -287,7 +296,7 @@ if (CONAN_REQUIRED_LIBS)
|
||||
if(ENABLE_QT)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets OpenGL)
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets)
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
@@ -330,10 +339,14 @@ elseif(SDL2_FOUND)
|
||||
endif()
|
||||
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
find_package(LibUSB)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
|
||||
if(NOT APPLE)
|
||||
include(FindPkgConfig)
|
||||
find_package(LibUSB)
|
||||
endif()
|
||||
if (NOT LIBUSB_FOUND)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
endif()
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
|
||||
@@ -15,7 +15,6 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
icuuc*.dll
|
||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ yuzu emulator
|
||||
=============
|
||||
[](https://travis-ci.com/yuzu-emu/yuzu)
|
||||
[](https://dev.azure.com/yuzu-emu/yuzu/)
|
||||
[](https://discord.gg/XQV6dn9)
|
||||
[](https://discord.com/invite/u77vRWY)
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
@@ -16,7 +16,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx
|
||||
|
||||
Check out our [website](https://yuzu-emu.org/)!
|
||||
|
||||
For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9).
|
||||
For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
|
||||
|
||||
### Development
|
||||
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 9250d5ae8f...8188e3fbbc
43
externals/find-modules/FindLibUSB.cmake
vendored
Normal file
43
externals/find-modules/FindLibUSB.cmake
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# - Find libusb-1.0 library
|
||||
# This module defines
|
||||
# LIBUSB_INCLUDE_DIR, where to find bluetooth.h
|
||||
# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0.
|
||||
# LIBUSB_FOUND, If false, do not try to use libusb-1.0.
|
||||
#
|
||||
# Copyright (c) 2009, Michal Cihar, <michal@cihar.com>
|
||||
#
|
||||
# vim: expandtab sw=4 ts=4 sts=4:
|
||||
|
||||
if(ANDROID)
|
||||
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "libusb-1.0 not found.")
|
||||
elseif (NOT LIBUSB_FOUND)
|
||||
pkg_check_modules (LIBUSB_PKG libusb-1.0)
|
||||
|
||||
find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h
|
||||
PATHS
|
||||
${LIBUSB_PKG_INCLUDE_DIRS}
|
||||
/usr/include/libusb-1.0
|
||||
/usr/include
|
||||
/usr/local/include/libusb-1.0
|
||||
/usr/local/include
|
||||
)
|
||||
|
||||
find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb
|
||||
PATHS
|
||||
${LIBUSB_PKG_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
)
|
||||
|
||||
if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
|
||||
else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "libusb-1.0 not found.")
|
||||
endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
|
||||
mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
|
||||
endif ()
|
||||
|
||||
@@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
|
||||
if (Settings::values.enable_audio_stretching) {
|
||||
if (Settings::values.enable_audio_stretching.GetValue()) {
|
||||
const std::vector<s16> in{impl->queue.Pop()};
|
||||
const std::size_t num_in{in.size() / num_channels};
|
||||
s16* const out{reinterpret_cast<s16*>(buffer)};
|
||||
|
||||
@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
|
||||
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
||||
|
||||
release_event = Core::Timing::CreateEvent(
|
||||
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
|
||||
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); });
|
||||
}
|
||||
|
||||
void Stream::Play() {
|
||||
@@ -66,15 +66,6 @@ s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const {
|
||||
return ns.count();
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
/// DSP signals before playing the last sample, in HLE we emulate this in this way
|
||||
s64 base_samples = std::max<s64>(static_cast<s64>(num_samples) - 1, 0);
|
||||
const auto ns =
|
||||
std::chrono::nanoseconds((static_cast<u64>(base_samples) * 1000000000ULL) / sample_rate);
|
||||
return ns.count();
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)};
|
||||
|
||||
@@ -89,7 +80,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::PlayNextBuffer() {
|
||||
void Stream::PlayNextBuffer(s64 cycles_late) {
|
||||
if (!IsPlaying()) {
|
||||
// Ensure we are in playing state before playing the next buffer
|
||||
sink_stream.Flush();
|
||||
@@ -114,18 +105,17 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
if (core_timing.IsHostTiming()) {
|
||||
core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {});
|
||||
} else {
|
||||
core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {});
|
||||
}
|
||||
core_timing.ScheduleEvent(
|
||||
GetBufferReleaseNS(*active_buffer) -
|
||||
(Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
|
||||
release_event, {});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
void Stream::ReleaseActiveBuffer(s64 cycles_late) {
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
PlayNextBuffer();
|
||||
PlayNextBuffer(cycles_late);
|
||||
}
|
||||
|
||||
bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
|
||||
@@ -90,10 +90,10 @@ public:
|
||||
|
||||
private:
|
||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||
void PlayNextBuffer();
|
||||
void PlayNextBuffer(s64 cycles_late = 0);
|
||||
|
||||
/// Releases the actively playing buffer, signalling that it has been completed
|
||||
void ReleaseActiveBuffer();
|
||||
void ReleaseActiveBuffer(s64 cycles_late = 0);
|
||||
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
|
||||
@@ -11,7 +11,9 @@ namespace Common {
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value + (size - value % size) % size);
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
|
||||
|
||||
@@ -147,8 +147,8 @@ struct System::Impl {
|
||||
|
||||
device_memory = std::make_unique<Core::DeviceMemory>(system);
|
||||
|
||||
is_multicore = Settings::values.use_multi_core;
|
||||
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation;
|
||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
|
||||
kernel.SetMulticore(is_multicore);
|
||||
cpu_manager.SetMulticore(is_multicore);
|
||||
@@ -162,7 +162,7 @@ struct System::Impl {
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
Settings::values.custom_rtc_differential =
|
||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||
Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr)
|
||||
|
||||
@@ -172,7 +172,7 @@ void CoreTiming::ClearPendingEvents() {
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
basic_lock.lock();
|
||||
std::scoped_lock lock{basic_lock};
|
||||
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type.lock().get() == event_type.get();
|
||||
@@ -183,12 +183,10 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
event_queue.erase(itr, event_queue.end());
|
||||
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
basic_lock.unlock();
|
||||
}
|
||||
|
||||
std::optional<s64> CoreTiming::Advance() {
|
||||
std::scoped_lock advance_scope{advance_lock};
|
||||
std::scoped_lock basic_scope{basic_lock};
|
||||
std::scoped_lock lock{advance_lock, basic_lock};
|
||||
global_timer = GetGlobalTimeNs().count();
|
||||
|
||||
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
|
||||
|
||||
@@ -695,8 +695,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
|
||||
if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) {
|
||||
return;
|
||||
}
|
||||
if (id == S128KeyType::Titlekey) {
|
||||
Key128 rights_id;
|
||||
std::memcpy(rights_id.data(), &field2, sizeof(u64));
|
||||
@@ -716,8 +717,9 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
if (iter2 != s128_file_id.end())
|
||||
if (iter2 != s128_file_id.end()) {
|
||||
WriteKeyToFile(category, iter2->first, key);
|
||||
}
|
||||
|
||||
// Variable cases
|
||||
if (id == S128KeyType::KeyArea) {
|
||||
@@ -745,16 +747,18 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
|
||||
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
|
||||
if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) {
|
||||
return;
|
||||
}
|
||||
const auto iter = std::find_if(
|
||||
s256_file_id.begin(), s256_file_id.end(),
|
||||
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
if (iter != s256_file_id.end())
|
||||
if (iter != s256_file_id.end()) {
|
||||
WriteKeyToFile(KeyCategory::Standard, iter->first, key);
|
||||
}
|
||||
s256_keys[{id, field1, field2}] = key;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,19 +112,26 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
const auto new_path =
|
||||
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
|
||||
return nullptr;
|
||||
|
||||
if (cache.find(old_path) != cache.end()) {
|
||||
auto cached = cache[old_path];
|
||||
if (!cached.expired()) {
|
||||
auto file = cached.lock();
|
||||
file->Open(new_path, "r+b");
|
||||
cache.erase(old_path);
|
||||
cache[new_path] = file;
|
||||
auto file = cache[old_path].lock();
|
||||
|
||||
if (!cache[old_path].expired()) {
|
||||
file->Close();
|
||||
}
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cache.erase(old_path);
|
||||
file->Open(new_path, "r+b");
|
||||
cache[new_path] = file;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return OpenFile(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
|
||||
const float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const float emulation_aspect_ratio = EmulationAspectRatio(
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio);
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
|
||||
|
||||
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
|
||||
@@ -123,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
|
||||
: kernel.CreateNewUserProcessID();
|
||||
process->capabilities.InitializeForMetadatalessProcess();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
|
||||
[&] { return distribution(rng); });
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// licensed under GPLv2 or later under exception provided by the author.
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
@@ -31,22 +32,20 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
|
||||
GlobalScheduler::~GlobalScheduler() = default;
|
||||
|
||||
void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
|
||||
global_list_guard.lock();
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.push_back(std::move(thread));
|
||||
global_list_guard.unlock();
|
||||
}
|
||||
|
||||
void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
global_list_guard.lock();
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
global_list_guard.unlock();
|
||||
}
|
||||
|
||||
u32 GlobalScheduler::SelectThreads() {
|
||||
ASSERT(is_locked);
|
||||
const auto update_thread = [](Thread* thread, Scheduler& sched) {
|
||||
sched.guard.lock();
|
||||
std::scoped_lock lock{sched.guard};
|
||||
if (thread != sched.selected_thread_set.get()) {
|
||||
if (thread == nullptr) {
|
||||
++sched.idle_selection_count;
|
||||
@@ -57,7 +56,6 @@ u32 GlobalScheduler::SelectThreads() {
|
||||
sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
|
||||
sched.is_context_switch_pending = reschedule_pending;
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
sched.guard.unlock();
|
||||
return reschedule_pending;
|
||||
};
|
||||
if (!is_reselection_pending.load()) {
|
||||
@@ -757,11 +755,12 @@ void Scheduler::OnSwitch(void* this_scheduler) {
|
||||
|
||||
void Scheduler::SwitchToCurrent() {
|
||||
while (true) {
|
||||
guard.lock();
|
||||
selected_thread = selected_thread_set;
|
||||
current_thread = selected_thread;
|
||||
is_context_switch_pending = false;
|
||||
guard.unlock();
|
||||
{
|
||||
std::scoped_lock lock{guard};
|
||||
selected_thread = selected_thread_set;
|
||||
current_thread = selected_thread;
|
||||
is_context_switch_pending = false;
|
||||
}
|
||||
while (!is_context_switch_pending) {
|
||||
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
|
||||
current_thread->context_guard.lock();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -271,7 +272,7 @@ ISelfController::ISelfController(Core::System& system,
|
||||
{41, nullptr, "IsSystemBufferSharingEnabled"},
|
||||
{42, nullptr, "GetSystemSharedLayerHandle"},
|
||||
{43, nullptr, "GetSystemSharedBufferHandle"},
|
||||
{44, nullptr, "CreateManagedDisplaySeparableLayer"},
|
||||
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
|
||||
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
|
||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||
{51, nullptr, "ApproveToDisplay"},
|
||||
@@ -461,6 +462,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
|
||||
rb.Push(*layer_id);
|
||||
}
|
||||
|
||||
void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
// This calls nn::vi::CreateRecordingLayer() which creates another layer.
|
||||
// Currently we do not support more than 1 layer per display, output 1 layer id for now.
|
||||
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
|
||||
// side effects.
|
||||
// TODO: Support multiple layers
|
||||
const auto display_id = nvflinger->OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger->CreateLayer(*display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*layer_id);
|
||||
}
|
||||
|
||||
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@@ -730,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1353,14 +1372,25 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::array<u8, 0x10> version_string{};
|
||||
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
const auto res = pm.GetControlMetadata();
|
||||
const auto res = [this] {
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
FileSys::PatchManager pm{title_id};
|
||||
auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
return res;
|
||||
}
|
||||
|
||||
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
if (res.first != nullptr) {
|
||||
const auto& version = res.first->GetVersionString();
|
||||
std::copy(version.begin(), version.end(), version_string.begin());
|
||||
} else {
|
||||
constexpr u128 default_version = {1, 0};
|
||||
std::memcpy(version_string.data(), default_version.data(), sizeof(u128));
|
||||
constexpr char default_version[]{"1.0.0"};
|
||||
std::memcpy(version_string.data(), default_version, sizeof(default_version));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
@@ -1377,7 +1407,19 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
u32 supported_languages = 0;
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
const auto res = [this] {
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
FileSys::PatchManager pm{title_id};
|
||||
auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
return res;
|
||||
}
|
||||
|
||||
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ private:
|
||||
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx);
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -121,11 +121,83 @@ public:
|
||||
{39, nullptr, "PrepareShutdown"},
|
||||
{40, nullptr, "ListApplyDeltaTask"},
|
||||
{41, nullptr, "ClearNotEnoughSpaceStateOfApplyDeltaTask"},
|
||||
{42, nullptr, "Unknown1"},
|
||||
{43, nullptr, "Unknown2"},
|
||||
{44, nullptr, "Unknown3"},
|
||||
{45, nullptr, "Unknown4"},
|
||||
{46, nullptr, "Unknown5"},
|
||||
{42, nullptr, "Unknown42"},
|
||||
{43, nullptr, "Unknown43"},
|
||||
{44, nullptr, "Unknown44"},
|
||||
{45, nullptr, "Unknown45"},
|
||||
{46, nullptr, "Unknown46"},
|
||||
{47, nullptr, "Unknown47"},
|
||||
{48, nullptr, "Unknown48"},
|
||||
{49, nullptr, "Unknown49"},
|
||||
{50, nullptr, "Unknown50"},
|
||||
{51, nullptr, "Unknown51"},
|
||||
{52, nullptr, "Unknown52"},
|
||||
{53, nullptr, "Unknown53"},
|
||||
{54, nullptr, "Unknown54"},
|
||||
{55, nullptr, "Unknown55"},
|
||||
{56, nullptr, "Unknown56"},
|
||||
{57, nullptr, "Unknown57"},
|
||||
{58, nullptr, "Unknown58"},
|
||||
{59, nullptr, "Unknown59"},
|
||||
{60, nullptr, "Unknown60"},
|
||||
{61, nullptr, "Unknown61"},
|
||||
{62, nullptr, "Unknown62"},
|
||||
{63, nullptr, "Unknown63"},
|
||||
{64, nullptr, "Unknown64"},
|
||||
{65, nullptr, "Unknown65"},
|
||||
{66, nullptr, "Unknown66"},
|
||||
{67, nullptr, "Unknown67"},
|
||||
{68, nullptr, "Unknown68"},
|
||||
{69, nullptr, "Unknown69"},
|
||||
{70, nullptr, "Unknown70"},
|
||||
{71, nullptr, "Unknown71"},
|
||||
{72, nullptr, "Unknown72"},
|
||||
{73, nullptr, "Unknown73"},
|
||||
{74, nullptr, "Unknown74"},
|
||||
{75, nullptr, "Unknown75"},
|
||||
{76, nullptr, "Unknown76"},
|
||||
{77, nullptr, "Unknown77"},
|
||||
{78, nullptr, "Unknown78"},
|
||||
{79, nullptr, "Unknown79"},
|
||||
{80, nullptr, "Unknown80"},
|
||||
{81, nullptr, "Unknown81"},
|
||||
{82, nullptr, "Unknown82"},
|
||||
{83, nullptr, "Unknown83"},
|
||||
{84, nullptr, "Unknown84"},
|
||||
{85, nullptr, "Unknown85"},
|
||||
{86, nullptr, "Unknown86"},
|
||||
{87, nullptr, "Unknown87"},
|
||||
{88, nullptr, "Unknown88"},
|
||||
{89, nullptr, "Unknown89"},
|
||||
{90, nullptr, "Unknown90"},
|
||||
{91, nullptr, "Unknown91"},
|
||||
{92, nullptr, "Unknown92"},
|
||||
{93, nullptr, "Unknown93"},
|
||||
{94, nullptr, "Unknown94"},
|
||||
{95, nullptr, "Unknown95"},
|
||||
{96, nullptr, "Unknown96"},
|
||||
{97, nullptr, "Unknown97"},
|
||||
{98, nullptr, "Unknown98"},
|
||||
{99, nullptr, "Unknown99"},
|
||||
{100, nullptr, "Unknown100"},
|
||||
{101, nullptr, "Unknown101"},
|
||||
{102, nullptr, "Unknown102"},
|
||||
{103, nullptr, "Unknown103"},
|
||||
{104, nullptr, "Unknown104"},
|
||||
{105, nullptr, "Unknown105"},
|
||||
{106, nullptr, "Unknown106"},
|
||||
{107, nullptr, "Unknown107"},
|
||||
{108, nullptr, "Unknown108"},
|
||||
{109, nullptr, "Unknown109"},
|
||||
{110, nullptr, "Unknown110"},
|
||||
{111, nullptr, "Unknown111"},
|
||||
{112, nullptr, "Unknown112"},
|
||||
{113, nullptr, "Unknown113"},
|
||||
{114, nullptr, "Unknown114"},
|
||||
{115, nullptr, "Unknown115"},
|
||||
{116, nullptr, "Unknown116"},
|
||||
{117, nullptr, "Unknown117"},
|
||||
{118, nullptr, "Unknown118"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -142,6 +214,7 @@ public:
|
||||
{1, nullptr, "RefreshDebugAvailability"},
|
||||
{2, nullptr, "ClearDebugResponse"},
|
||||
{3, nullptr, "RegisterDebugResponse"},
|
||||
{4, nullptr, "IsLargeResourceAvailable"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -164,6 +237,8 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestDeviceAuthenticationToken"},
|
||||
{1, nullptr, "RequestCachedDeviceAuthenticationToken"},
|
||||
{2, nullptr, "RequestEdgeToken"},
|
||||
{3, nullptr, "RequestCachedEdgeToken"},
|
||||
{100, nullptr, "RequestRegisterDeviceAccount"},
|
||||
{101, nullptr, "RequestUnregisterDeviceAccount"},
|
||||
{102, nullptr, "RequestDeviceAccountStatus"},
|
||||
@@ -181,7 +256,8 @@ public:
|
||||
{305, nullptr, "RequestCreateVirtualAccount"},
|
||||
{306, nullptr, "RequestDeviceLinkStatus"},
|
||||
{400, nullptr, "GetAccountByVirtualAccount"},
|
||||
{500, nullptr, "RequestSyncTicket"},
|
||||
{401, nullptr, "GetVirtualAccount"},
|
||||
{500, nullptr, "RequestSyncTicketLegacy"},
|
||||
{501, nullptr, "RequestDownloadTicket"},
|
||||
{502, nullptr, "RequestDownloadTicketForPrepurchasedContents"},
|
||||
{503, nullptr, "RequestSyncTicket"},
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{24, nullptr, "DestroyTokenWithApplicationId"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
{26, nullptr, "ListenToMyApplicationId"},
|
||||
{31, nullptr, "UploadTokenToBaaS"},
|
||||
{32, nullptr, "DestroyTokenForBaaS"},
|
||||
{33, nullptr, "CreateTokenForBaaS"},
|
||||
|
||||
@@ -104,7 +104,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
|
||||
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
|
||||
{99, nullptr, "LaunchDevMenu"},
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
@@ -254,7 +254,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
{2170, nullptr, "GetRightsEnvironmentStatus"},
|
||||
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
|
||||
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
@@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
|
||||
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
|
||||
|
||||
// Get language code from settings
|
||||
const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
|
||||
const auto language_code =
|
||||
Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
|
||||
|
||||
// Convert to application language, get priority list
|
||||
const auto application_language = ConvertToApplicationLanguage(language_code);
|
||||
@@ -445,8 +446,8 @@ IApplicationVersionInterface::IApplicationVersionInterface()
|
||||
|
||||
IApplicationVersionInterface::~IApplicationVersionInterface() = default;
|
||||
|
||||
IContentManagerInterface::IContentManagerInterface()
|
||||
: ServiceFramework{"IContentManagerInterface"} {
|
||||
IContentManagementInterface::IContentManagementInterface()
|
||||
: ServiceFramework{"IContentManagementInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
@@ -463,7 +464,7 @@ IContentManagerInterface::IContentManagerInterface()
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IContentManagerInterface::~IContentManagerInterface() = default;
|
||||
IContentManagementInterface::~IContentManagementInterface() = default;
|
||||
|
||||
IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
|
||||
// clang-format off
|
||||
@@ -545,7 +546,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
|
||||
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
|
||||
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
|
||||
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
|
||||
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -572,9 +573,9 @@ public:
|
||||
{6, nullptr, "TerminateApplication"},
|
||||
{7, nullptr, "PrepareLaunchProgramFromHost"},
|
||||
{8, nullptr, "LaunchApplication"},
|
||||
{9, nullptr, "LaunchApplicationWithStorageId"},
|
||||
{10, nullptr, "TerminateApplication2"},
|
||||
{11, nullptr, "GetRunningApplicationProcessId"},
|
||||
{9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
|
||||
{10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
|
||||
{11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
|
||||
{12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
|
||||
{13, nullptr, "CreateApplicationResourceForDevelop"},
|
||||
{14, nullptr, "IsPreomiaForDevelop"},
|
||||
@@ -636,6 +637,10 @@ public:
|
||||
{9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
|
||||
{10, nullptr, "NotifySystemUpdateForContentDelivery"},
|
||||
{11, nullptr, "PrepareShutdown"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
{13, nullptr, "Unknown13"},
|
||||
{14, nullptr, "Unknown14"},
|
||||
{15, nullptr, "Unknown15"},
|
||||
{16, nullptr, "DestroySystemUpdateTask"},
|
||||
{17, nullptr, "RequestSendSystemUpdate"},
|
||||
{18, nullptr, "GetSendSystemUpdateProgress"},
|
||||
|
||||
@@ -40,10 +40,10 @@ public:
|
||||
~IApplicationVersionInterface() override;
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface();
|
||||
~IContentManagerInterface() override;
|
||||
explicit IContentManagementInterface();
|
||||
~IContentManagementInterface() override;
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
|
||||
@@ -163,7 +163,7 @@ PL_U::PL_U(Core::System& system)
|
||||
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
|
||||
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
|
||||
{100, nullptr, "RequestApplicationFunctionAuthorization"},
|
||||
{101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"},
|
||||
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
|
||||
{102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
|
||||
{1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
|
||||
{1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
|
||||
|
||||
@@ -144,7 +144,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
pid = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
|
||||
@@ -154,7 +154,7 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -187,13 +187,14 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
|
||||
{4, &NVDRV::QueryEvent, "QueryEvent"},
|
||||
{5, nullptr, "MapSharedMem"},
|
||||
{6, &NVDRV::GetStatus, "GetStatus"},
|
||||
{7, nullptr, "ForceSetClientPID"},
|
||||
{8, &NVDRV::SetClientPID, "SetClientPID"},
|
||||
{7, nullptr, "SetAruidForTest"},
|
||||
{8, &NVDRV::SetAruid, "SetAruid"},
|
||||
{9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
|
||||
{10, nullptr, "InitializeDevtools"},
|
||||
{11, &NVDRV::Ioctl2, "Ioctl2"},
|
||||
{12, &NVDRV::Ioctl3, "Ioctl3"},
|
||||
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
|
||||
{13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled,
|
||||
"SetGraphicsFirmwareMemoryMarginEnabled"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ private:
|
||||
void Close(Kernel::HLERequestContext& ctx);
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void QueryEvent(Kernel::HLERequestContext& ctx);
|
||||
void SetClientPID(Kernel::HLERequestContext& ctx);
|
||||
void FinishInitialize(Kernel::HLERequestContext& ctx);
|
||||
void SetAruid(Kernel::HLERequestContext& ctx);
|
||||
void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
|
||||
void GetStatus(Kernel::HLERequestContext& ctx);
|
||||
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
|
||||
void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
|
||||
|
||||
@@ -10,19 +10,19 @@ namespace Service::Nvidia {
|
||||
|
||||
NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &NVMEMP::Cmd0, "Cmd0"},
|
||||
{1, &NVMEMP::Cmd1, "Cmd1"},
|
||||
{0, &NVMEMP::Open, "Open"},
|
||||
{1, &NVMEMP::GetAruid, "GetAruid"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
NVMEMP::~NVMEMP() = default;
|
||||
|
||||
void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) {
|
||||
void NVMEMP::Open(Kernel::HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void NVMEMP::Cmd1(Kernel::HLERequestContext& ctx) {
|
||||
void NVMEMP::GetAruid(Kernel::HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ public:
|
||||
~NVMEMP() override;
|
||||
|
||||
private:
|
||||
void Cmd0(Kernel::HLERequestContext& ctx);
|
||||
void Cmd1(Kernel::HLERequestContext& ctx);
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
void GetAruid(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -36,6 +36,9 @@ public:
|
||||
{18, nullptr, "ReleaseIrq"},
|
||||
{19, nullptr, "SetIrqEnable"},
|
||||
{20, nullptr, "SetAspmEnable"},
|
||||
{21, nullptr, "SetResetUponResumeEnable"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ public:
|
||||
{24, nullptr, "GetModuleStateTable"},
|
||||
{25, nullptr, "GetPowerDomainStateTable"},
|
||||
{26, nullptr, "GetFuseInfo"},
|
||||
{27, nullptr, "GetDramId"},
|
||||
{28, nullptr, "IsPoweredOn"},
|
||||
{29, nullptr, "GetVoltage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -78,13 +78,13 @@ public:
|
||||
: ServiceFramework{"pm:dmnt"}, kernel(kernel) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetDebugProcesses"},
|
||||
{1, nullptr, "StartDebugProcess"},
|
||||
{2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
|
||||
{3, nullptr, "EnableDebugForTitleId"},
|
||||
{4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
|
||||
{5, nullptr, "EnableDebugForApplication"},
|
||||
{6, nullptr, "DisableDebug"},
|
||||
{0, nullptr, "GetJitDebugProcessIdList"},
|
||||
{1, nullptr, "StartProcess"},
|
||||
{2, &DebugMonitor::GetProcessId, "GetProcessId"},
|
||||
{3, nullptr, "HookToCreateProcess"},
|
||||
{4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
|
||||
{5, nullptr, "HookToCreateApplicationProgress"},
|
||||
{6, nullptr, "ClearHook"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetTitlePid(Kernel::HLERequestContext& ctx) {
|
||||
void GetProcessId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
rb.Push((*process)->GetProcessID());
|
||||
}
|
||||
|
||||
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
|
||||
void GetApplicationProcessId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
|
||||
}
|
||||
@@ -163,15 +163,15 @@ public:
|
||||
: ServiceFramework{"pm:shell"}, kernel(kernel) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "LaunchProcess"},
|
||||
{1, nullptr, "TerminateProcessByPid"},
|
||||
{2, nullptr, "TerminateProcessByTitleId"},
|
||||
{3, nullptr, "GetProcessEventWaiter"},
|
||||
{4, nullptr, "GetProcessEventType"},
|
||||
{0, nullptr, "LaunchProgram"},
|
||||
{1, nullptr, "TerminateProcess"},
|
||||
{2, nullptr, "TerminateProgram"},
|
||||
{3, nullptr, "GetProcessEventHandle"},
|
||||
{4, nullptr, "GetProcessEventInfo"},
|
||||
{5, nullptr, "NotifyBootFinished"},
|
||||
{6, &Shell::GetApplicationPid, "GetApplicationPid"},
|
||||
{6, &Shell::GetApplicationProcessIdForShell, "GetApplicationProcessIdForShell"},
|
||||
{7, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{8, nullptr, "EnableAdditionalSystemThreads"},
|
||||
{8, nullptr, "BoostApplicationThreadResourceLimit"},
|
||||
{9, nullptr, "GetBootFinishedEventHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
|
||||
void GetApplicationProcessIdForShell(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ public:
|
||||
{40101, nullptr, "SetUserAgreementCheckEnabled"},
|
||||
{50100, nullptr, "ReadAllApplicationReportFiles"},
|
||||
{90100, nullptr, "ReadAllReportFiles"},
|
||||
{90101, nullptr, "Unknown90101"},
|
||||
{90102, nullptr, "Unknown90102"},
|
||||
{90200, nullptr, "GetStatistics"},
|
||||
{90201, nullptr, "GetThroughputHistory"},
|
||||
{90300, nullptr, "GetLastUploadError"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ public:
|
||||
{4, nullptr, "Cancel"},
|
||||
{5, nullptr, "PrintModuleInformation"},
|
||||
{6, nullptr, "GetModuleInformation"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
{15, nullptr, "GetBatteryAgePercentage"},
|
||||
{16, nullptr, "GetBatteryChargeInfoEvent"},
|
||||
{17, nullptr, "GetBatteryChargeInfoFields"},
|
||||
{18, nullptr, "GetBatteryChargeCalibratedEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
|
||||
}
|
||||
|
||||
void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
|
||||
const auto language_code = available_language_codes[Settings::values.language_index];
|
||||
const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
|
||||
const auto key_code =
|
||||
std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
|
||||
[=](const auto& element) { return element.first == language_code; });
|
||||
@@ -99,7 +99,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
|
||||
if (key_code == language_to_layout.cend()) {
|
||||
LOG_ERROR(Service_SET,
|
||||
"Could not find keyboard layout for language index {}, defaulting to English us",
|
||||
Settings::values.language_index);
|
||||
Settings::values.language_index.GetValue());
|
||||
} else {
|
||||
layout = key_code->second;
|
||||
}
|
||||
@@ -163,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
|
||||
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index]);
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
|
||||
}
|
||||
|
||||
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -175,7 +175,7 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(Settings::values.region_index);
|
||||
rb.Push(Settings::values.region_index.GetValue());
|
||||
}
|
||||
|
||||
void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace Service::SM {
|
||||
|
||||
void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
|
||||
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
|
||||
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
|
||||
ctx.Session()->ConvertToDomain();
|
||||
@@ -22,7 +22,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(1); // Converted sessions start with 1 request handler
|
||||
}
|
||||
|
||||
void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
|
||||
// and that we probably want to actually make an entirely new Session, but we still need to
|
||||
// verify this on hardware.
|
||||
@@ -33,10 +33,10 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
|
||||
}
|
||||
|
||||
void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession");
|
||||
void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called, using CloneCurrentObject");
|
||||
|
||||
DuplicateSession(ctx);
|
||||
CloneCurrentObject(ctx);
|
||||
}
|
||||
|
||||
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
@@ -47,13 +47,14 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u16>(0x1000);
|
||||
}
|
||||
|
||||
// https://switchbrew.org/wiki/IPC_Marshalling
|
||||
Controller::Controller() : ServiceFramework("IpcController") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &Controller::ConvertSessionToDomain, "ConvertSessionToDomain"},
|
||||
{0x00000001, nullptr, "ConvertDomainToSession"},
|
||||
{0x00000002, &Controller::DuplicateSession, "DuplicateSession"},
|
||||
{0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
|
||||
{0x00000004, &Controller::DuplicateSessionEx, "DuplicateSessionEx"},
|
||||
{0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"},
|
||||
{1, nullptr, "CopyFromCurrentDomain"},
|
||||
{2, &Controller::CloneCurrentObject, "CloneCurrentObject"},
|
||||
{3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
|
||||
{4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ public:
|
||||
~Controller() override;
|
||||
|
||||
private:
|
||||
void ConvertSessionToDomain(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSession(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSessionEx(Kernel::HLERequestContext& ctx);
|
||||
void ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObject(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObjectEx(Kernel::HLERequestContext& ctx);
|
||||
void QueryPointerBufferSize(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
|
||||
{12, nullptr, "GetDeviceId"},
|
||||
{13, nullptr, "DeleteSettings"},
|
||||
{14, nullptr, "ImportSettings"},
|
||||
{15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
|
||||
{20, nullptr, "Resolve"},
|
||||
{21, nullptr, "ResolveEx"},
|
||||
{30, nullptr, "GetNasServiceSetting"},
|
||||
@@ -28,6 +29,11 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
|
||||
{60, nullptr, "ReadSaveDataFromFsForTest"},
|
||||
{61, nullptr, "WriteSaveDataToFsForTest"},
|
||||
{62, nullptr, "DeleteSaveDataOfFsForTest"},
|
||||
{63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
|
||||
{64, nullptr, "SetWithoutDomainExchangeFqdns"},
|
||||
{100, nullptr, "GetApplicationServerEnvironmentType"},
|
||||
{101, nullptr, "SetApplicationServerEnvironmentType"},
|
||||
{102, nullptr, "DeleteApplicationServerEnvironmentType"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
|
||||
void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u8 use_nsd_resolve;
|
||||
u32 unknown;
|
||||
@@ -29,15 +29,20 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SetDnsAddressesPrivate"},
|
||||
{1, nullptr, "GetDnsAddressPrivate"},
|
||||
{2, nullptr, "GetHostByName"},
|
||||
{3, nullptr, "GetHostByAddr"},
|
||||
{4, nullptr, "GetHostStringError"},
|
||||
{5, nullptr, "GetGaiStringError"},
|
||||
{6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
|
||||
{7, nullptr, "GetNameInfo"},
|
||||
{8, nullptr, "RequestCancelHandle"},
|
||||
{9, nullptr, "CancelSocketCall"},
|
||||
{11, nullptr, "ClearDnsIpServerAddressArray"},
|
||||
{2, nullptr, "GetHostByNameRequest"},
|
||||
{3, nullptr, "GetHostByAddrRequest"},
|
||||
{4, nullptr, "GetHostStringErrorRequest"},
|
||||
{5, nullptr, "GetGaiStringErrorRequest"},
|
||||
{6, &SFDNSRES::GetAddrInfoRequest, "GetAddrInfoRequest"},
|
||||
{7, nullptr, "GetNameInfoRequest"},
|
||||
{8, nullptr, "RequestCancelHandleRequest"},
|
||||
{9, nullptr, "CancelRequest"},
|
||||
{10, nullptr, "GetHostByNameRequestWithOptions"},
|
||||
{11, nullptr, "GetHostByAddrRequestWithOptions"},
|
||||
{12, nullptr, "GetAddrInfoRequestWithOptions"},
|
||||
{13, nullptr, "GetNameInfoRequestWithOptions"},
|
||||
{14, nullptr, "ResolverSetOptionRequest"},
|
||||
{15, nullptr, "ResolverGetOptionRequest"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
~SFDNSRES() override;
|
||||
|
||||
private:
|
||||
void GetAddrInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Sockets
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Service::SPL {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
|
||||
rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
|
||||
@@ -9,35 +9,36 @@ namespace Service::SPL {
|
||||
SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetConfig"},
|
||||
{1, nullptr, "UserExpMod"},
|
||||
{1, nullptr, "ModularExponentiate"},
|
||||
{2, nullptr, "GenerateAesKek"},
|
||||
{3, nullptr, "LoadAesKey"},
|
||||
{4, nullptr, "GenerateAesKey"},
|
||||
{5, nullptr, "SetConfig"},
|
||||
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
|
||||
{9, nullptr, "LoadSecureExpModKey"},
|
||||
{10, nullptr, "SecureExpMod"},
|
||||
{9, nullptr, "ImportLotusKey"},
|
||||
{10, nullptr, "DecryptLotusMessage"},
|
||||
{11, nullptr, "IsDevelopment"},
|
||||
{12, nullptr, "GenerateSpecificAesKey"},
|
||||
{13, nullptr, "DecryptPrivk"},
|
||||
{13, nullptr, "DecryptDeviceUniqueData"},
|
||||
{14, nullptr, "DecryptAesKey"},
|
||||
{15, nullptr, "DecryptAesCtr"},
|
||||
{15, nullptr, "CryptAesCtr"},
|
||||
{16, nullptr, "ComputeCmac"},
|
||||
{17, nullptr, "LoadRsaOaepKey"},
|
||||
{18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
|
||||
{17, nullptr, "ImportEsKey"},
|
||||
{18, nullptr, "UnwrapTitleKey"},
|
||||
{19, nullptr, "LoadTitleKey"},
|
||||
{20, nullptr, "UnwrapAesWrappedTitleKey"},
|
||||
{21, nullptr, "LockAesEngine"},
|
||||
{22, nullptr, "UnlockAesEngine"},
|
||||
{23, nullptr, "GetSplWaitEvent"},
|
||||
{24, nullptr, "SetSharedData"},
|
||||
{25, nullptr, "GetSharedData"},
|
||||
{26, nullptr, "ImportSslRsaKey"},
|
||||
{27, nullptr, "SecureExpModWithSslKey"},
|
||||
{28, nullptr, "ImportEsRsaKey"},
|
||||
{29, nullptr, "SecureExpModWithEsKey"},
|
||||
{30, nullptr, "EncryptManuRsaKeyForImport"},
|
||||
{31, nullptr, "GetPackage2Hash"},
|
||||
{20, nullptr, "PrepareEsCommonKey"},
|
||||
{21, nullptr, "AllocateAesKeyslot"},
|
||||
{22, nullptr, "DeallocateAesKeySlot"},
|
||||
{23, nullptr, "GetAesKeyslotAvailableEvent"},
|
||||
{24, nullptr, "SetBootReason"},
|
||||
{25, nullptr, "GetBootReason"},
|
||||
{26, nullptr, "DecryptAndStoreSslClientCertKey"},
|
||||
{27, nullptr, "ModularExponentiateWithSslClientCertKey"},
|
||||
{28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
|
||||
{29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
|
||||
{30, nullptr, "ReencryptDeviceUniqueData "},
|
||||
{31, nullptr, "PrepareEsArchiveKey"}, // This is also GetPackage2Hash?
|
||||
{32, nullptr, "LoadPreparedAesKey"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,13 @@ public:
|
||||
: ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
|
||||
{2, nullptr, "GetTestOffset"},
|
||||
{3, nullptr, "SetTestOffset"},
|
||||
{100, nullptr, "GetRtcValue"},
|
||||
{101, nullptr, "IsRtcResetDetected"},
|
||||
{102, nullptr, "GetSetupResultValue"},
|
||||
{200, nullptr, "GetInternalOffset"},
|
||||
{201, nullptr, "SetInternalOffset"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetDsEndpoint"},
|
||||
{1, nullptr, "GetSetupEvent"},
|
||||
{2, nullptr, "Unknown"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "EnableInterface"},
|
||||
{4, nullptr, "DisableInterface"},
|
||||
{5, nullptr, "CtrlInPostBufferAsync"},
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
{9, nullptr, "SetBinaryObjectStore"},
|
||||
{10, nullptr, "Enable"},
|
||||
{11, nullptr, "Disable"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -69,13 +70,13 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Open"},
|
||||
{1, nullptr, "Close"},
|
||||
{2, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Populate"},
|
||||
{4, nullptr, "PostBufferAsync"},
|
||||
{5, nullptr, "GetXferReport"},
|
||||
{6, nullptr, "PostBufferMultiAsync"},
|
||||
{7, nullptr, "Unknown3"},
|
||||
{8, nullptr, "Unknown4"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -88,13 +89,13 @@ public:
|
||||
explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "SetInterface"},
|
||||
{2, nullptr, "GetInterface"},
|
||||
{3, nullptr, "GetAlternateInterface"},
|
||||
{4, nullptr, "GetCurrentFrame"},
|
||||
{5, nullptr, "CtrlXferAsync"},
|
||||
{6, nullptr, "Unknown2"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "GetCtrlXferReport"},
|
||||
{8, nullptr, "ResetDevice"},
|
||||
{9, nullptr, "OpenUsbEp"},
|
||||
@@ -118,7 +119,7 @@ public:
|
||||
{5, nullptr, "DestroyInterfaceAvailableEvent"},
|
||||
{6, nullptr, "GetInterfaceStateChangeEvent"},
|
||||
{7, nullptr, "AcquireUsbIf"},
|
||||
{8, nullptr, "Unknown1"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -179,8 +180,8 @@ public:
|
||||
{4, nullptr, "GetFwRevision"},
|
||||
{5, nullptr, "GetManufacturerId"},
|
||||
{6, nullptr, "GetDeviceId"},
|
||||
{7, nullptr, "Unknown1"},
|
||||
{8, nullptr, "Unknown2"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -215,12 +216,12 @@ public:
|
||||
explicit USB_PM() : ServiceFramework{"usb:pm"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -519,9 +519,9 @@ private:
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
IGBPConnectResponseParcel response{
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
|
||||
Settings::values.resolution_factor),
|
||||
Settings::values.resolution_factor.GetValue()),
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
||||
Settings::values.resolution_factor)};
|
||||
Settings::values.resolution_factor.GetValue())};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
break;
|
||||
}
|
||||
@@ -700,6 +700,7 @@ public:
|
||||
{3215, nullptr, "SetDisplayGamma"},
|
||||
{3216, nullptr, "GetDisplayCmuLuma"},
|
||||
{3217, nullptr, "SetDisplayCmuLuma"},
|
||||
{6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
|
||||
{8225, nullptr, "GetSharedBufferMemoryHandleId"},
|
||||
{8250, nullptr, "OpenSharedLayer"},
|
||||
{8251, nullptr, "CloseSharedLayer"},
|
||||
@@ -748,14 +749,14 @@ private:
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
|
||||
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
|
||||
@@ -785,6 +786,7 @@ public:
|
||||
{2300, nullptr, "AcquireLayerTexturePresentingEvent"},
|
||||
{2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
|
||||
{2302, nullptr, "GetDisplayHotplugEvent"},
|
||||
{2303, nullptr, "GetDisplayModeChangedEvent"},
|
||||
{2402, nullptr, "GetDisplayHotplugState"},
|
||||
{2501, nullptr, "GetCompositorErrorInfo"},
|
||||
{2601, nullptr, "GetDisplayErrorEvent"},
|
||||
@@ -1029,9 +1031,9 @@ private:
|
||||
// between docked and undocked dimensions. We take the liberty of applying
|
||||
// the resolution scaling factor here.
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1064,8 +1066,8 @@ private:
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
DisplayInfo display_info;
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -12,6 +12,7 @@ VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &VI_U::GetDisplayService, "GetDisplayService"},
|
||||
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -15,34 +15,37 @@ public:
|
||||
explicit WLANInfra() : ServiceFramework{"wlan:inf"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{0, nullptr, "OpenMode"},
|
||||
{1, nullptr, "CloseMode"},
|
||||
{2, nullptr, "GetMacAddress"},
|
||||
{3, nullptr, "StartScan"},
|
||||
{4, nullptr, "StopScan"},
|
||||
{5, nullptr, "Connect"},
|
||||
{6, nullptr, "CancelConnect"},
|
||||
{7, nullptr, "Disconnect"},
|
||||
{8, nullptr, "Unknown3"},
|
||||
{9, nullptr, "Unknown4"},
|
||||
{8, nullptr, "GetConnectionEvent"},
|
||||
{9, nullptr, "GetConnectionStatus"},
|
||||
{10, nullptr, "GetState"},
|
||||
{11, nullptr, "GetScanResult"},
|
||||
{12, nullptr, "GetRssi"},
|
||||
{13, nullptr, "ChangeRxAntenna"},
|
||||
{14, nullptr, "Unknown5"},
|
||||
{15, nullptr, "Unknown6"},
|
||||
{14, nullptr, "GetFwVersion"},
|
||||
{15, nullptr, "RequestSleep"},
|
||||
{16, nullptr, "RequestWakeUp"},
|
||||
{17, nullptr, "RequestIfUpDown"},
|
||||
{18, nullptr, "Unknown7"},
|
||||
{19, nullptr, "Unknown8"},
|
||||
{20, nullptr, "Unknown9"},
|
||||
{21, nullptr, "Unknown10"},
|
||||
{22, nullptr, "Unknown11"},
|
||||
{23, nullptr, "Unknown12"},
|
||||
{24, nullptr, "Unknown13"},
|
||||
{25, nullptr, "Unknown14"},
|
||||
{26, nullptr, "Unknown15"},
|
||||
{27, nullptr, "Unknown16"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{26, nullptr, "Unknown26"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "Unknown28"},
|
||||
{29, nullptr, "Unknown29"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -55,12 +58,12 @@ public:
|
||||
explicit WLANLocal() : ServiceFramework{"wlan:lcl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "GetMacAddress"},
|
||||
{7, nullptr, "CreateBss"},
|
||||
{8, nullptr, "DestroyBss"},
|
||||
@@ -72,38 +75,42 @@ public:
|
||||
{14, nullptr, "CancelJoin"},
|
||||
{15, nullptr, "Disconnect"},
|
||||
{16, nullptr, "SetBeaconLostCount"},
|
||||
{17, nullptr, "Unknown7"},
|
||||
{18, nullptr, "Unknown8"},
|
||||
{19, nullptr, "Unknown9"},
|
||||
{17, nullptr, "Unknown17"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "GetBssIndicationEvent"},
|
||||
{21, nullptr, "GetBssIndicationInfo"},
|
||||
{22, nullptr, "GetState"},
|
||||
{23, nullptr, "GetAllowedChannels"},
|
||||
{24, nullptr, "AddIe"},
|
||||
{25, nullptr, "DeleteIe"},
|
||||
{26, nullptr, "Unknown10"},
|
||||
{27, nullptr, "Unknown11"},
|
||||
{26, nullptr, "Unknown26"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "CreateRxEntry"},
|
||||
{29, nullptr, "DeleteRxEntry"},
|
||||
{30, nullptr, "Unknown12"},
|
||||
{31, nullptr, "Unknown13"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "AddMatchingDataToRxEntry"},
|
||||
{33, nullptr, "RemoveMatchingDataFromRxEntry"},
|
||||
{34, nullptr, "GetScanResult"},
|
||||
{35, nullptr, "Unknown14"},
|
||||
{35, nullptr, "Unknown35"},
|
||||
{36, nullptr, "SetActionFrameWithBeacon"},
|
||||
{37, nullptr, "CancelActionFrameWithBeacon"},
|
||||
{38, nullptr, "CreateRxEntryForActionFrame"},
|
||||
{39, nullptr, "DeleteRxEntryForActionFrame"},
|
||||
{40, nullptr, "Unknown15"},
|
||||
{41, nullptr, "Unknown16"},
|
||||
{40, nullptr, "Unknown40"},
|
||||
{41, nullptr, "Unknown41"},
|
||||
{42, nullptr, "CancelGetActionFrame"},
|
||||
{43, nullptr, "GetRssi"},
|
||||
{44, nullptr, "Unknown17"},
|
||||
{45, nullptr, "Unknown18"},
|
||||
{46, nullptr, "Unknown19"},
|
||||
{47, nullptr, "Unknown20"},
|
||||
{48, nullptr, "Unknown21"},
|
||||
{44, nullptr, "Unknown44"},
|
||||
{45, nullptr, "Unknown45"},
|
||||
{46, nullptr, "Unknown46"},
|
||||
{47, nullptr, "Unknown47"},
|
||||
{48, nullptr, "Unknown48"},
|
||||
{49, nullptr, "Unknown49"},
|
||||
{50, nullptr, "Unknown50"},
|
||||
{51, nullptr, "Unknown51"},
|
||||
{52, nullptr, "Unknown52"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -142,18 +149,19 @@ public:
|
||||
explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "GetMacAddress"},
|
||||
{7, nullptr, "SwitchTsfTimerFunction"},
|
||||
{8, nullptr, "Unknown7"},
|
||||
{9, nullptr, "Unknown8"},
|
||||
{10, nullptr, "Unknown9"},
|
||||
{11, nullptr, "Unknown10"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -548,9 +548,9 @@ struct Memory::Impl {
|
||||
// longer exist, and we should just leave the pagetable entry blank.
|
||||
page_type = Common::PageType::Unmapped;
|
||||
} else {
|
||||
page_type = Common::PageType::Memory;
|
||||
current_page_table->pointers[vaddr >> PAGE_BITS] =
|
||||
pointer - (vaddr & ~PAGE_MASK);
|
||||
page_type = Common::PageType::Memory;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -591,9 +591,12 @@ struct Memory::Impl {
|
||||
base + page_table.pointers.size());
|
||||
|
||||
if (!target) {
|
||||
ASSERT_MSG(type != Common::PageType::Memory,
|
||||
"Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
|
||||
|
||||
while (base != end) {
|
||||
page_table.pointers[base] = nullptr;
|
||||
page_table.attributes[base] = type;
|
||||
page_table.pointers[base] = nullptr;
|
||||
page_table.backing_addr[base] = 0;
|
||||
|
||||
base += 1;
|
||||
|
||||
@@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() {
|
||||
}
|
||||
|
||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) {
|
||||
if (!Settings::values.use_frame_limit.GetValue() ||
|
||||
Settings::values.use_multi_core.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = Clock::now();
|
||||
|
||||
const double sleep_scale = Settings::values.frame_limit / 100.0;
|
||||
const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
|
||||
|
||||
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
||||
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
||||
|
||||
@@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{
|
||||
}
|
||||
|
||||
Values values = {};
|
||||
bool configuring_global = true;
|
||||
|
||||
std::string GetTimeZoneString() {
|
||||
static constexpr std::array<const char*, 46> timezones{{
|
||||
@@ -73,9 +74,9 @@ std::string GetTimeZoneString() {
|
||||
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
|
||||
}};
|
||||
|
||||
ASSERT(Settings::values.time_zone_index < timezones.size());
|
||||
ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
|
||||
|
||||
return timezones[Settings::values.time_zone_index];
|
||||
return timezones[Settings::values.time_zone_index.GetValue()];
|
||||
}
|
||||
|
||||
void Apply() {
|
||||
@@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) {
|
||||
|
||||
void LogSettings() {
|
||||
LOG_INFO(Config, "yuzu Configuration:");
|
||||
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
|
||||
LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode);
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0));
|
||||
LogSetting("System_CurrentUser", Settings::values.current_user);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index);
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index);
|
||||
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index);
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue());
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index.GetValue());
|
||||
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue());
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue());
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue());
|
||||
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue());
|
||||
LogSetting("Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
|
||||
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
|
||||
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy);
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue());
|
||||
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue());
|
||||
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue());
|
||||
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
||||
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
|
||||
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
@@ -131,15 +132,56 @@ float Volume() {
|
||||
if (values.audio_muted) {
|
||||
return 0.0f;
|
||||
}
|
||||
return values.volume;
|
||||
return values.volume.GetValue();
|
||||
}
|
||||
|
||||
bool IsGPULevelExtreme() {
|
||||
return values.gpu_accuracy == GPUAccuracy::Extreme;
|
||||
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
|
||||
}
|
||||
|
||||
bool IsGPULevelHigh() {
|
||||
return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High;
|
||||
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
|
||||
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
|
||||
}
|
||||
|
||||
void RestoreGlobalState() {
|
||||
// If a game is running, DO NOT restore the global settings state
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Audio
|
||||
values.enable_audio_stretching.SetGlobal(true);
|
||||
values.volume.SetGlobal(true);
|
||||
|
||||
// Core
|
||||
values.use_multi_core.SetGlobal(true);
|
||||
|
||||
// Renderer
|
||||
values.renderer_backend.SetGlobal(true);
|
||||
values.vulkan_device.SetGlobal(true);
|
||||
values.aspect_ratio.SetGlobal(true);
|
||||
values.max_anisotropy.SetGlobal(true);
|
||||
values.use_frame_limit.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.use_disk_shader_cache.SetGlobal(true);
|
||||
values.gpu_accuracy.SetGlobal(true);
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.use_assembly_shaders.SetGlobal(true);
|
||||
values.use_fast_gpu_time.SetGlobal(true);
|
||||
values.force_30fps_mode.SetGlobal(true);
|
||||
values.bg_red.SetGlobal(true);
|
||||
values.bg_green.SetGlobal(true);
|
||||
values.bg_blue.SetGlobal(true);
|
||||
|
||||
// System
|
||||
values.language_index.SetGlobal(true);
|
||||
values.region_index.SetGlobal(true);
|
||||
values.time_zone_index.SetGlobal(true);
|
||||
values.rng_seed.SetGlobal(true);
|
||||
values.custom_rtc.SetGlobal(true);
|
||||
values.sound_index.SetGlobal(true);
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -382,20 +382,85 @@ enum class GPUAccuracy : u32 {
|
||||
Extreme = 2,
|
||||
};
|
||||
|
||||
extern bool configuring_global;
|
||||
|
||||
template <typename Type>
|
||||
class Setting final {
|
||||
public:
|
||||
Setting() = default;
|
||||
explicit Setting(Type val) : global{val} {}
|
||||
~Setting() = default;
|
||||
void SetGlobal(bool to_global) {
|
||||
use_global = to_global;
|
||||
}
|
||||
bool UsingGlobal() const {
|
||||
return use_global;
|
||||
}
|
||||
Type GetValue(bool need_global = false) const {
|
||||
if (use_global || need_global) {
|
||||
return global;
|
||||
}
|
||||
return local;
|
||||
}
|
||||
void SetValue(const Type& value) {
|
||||
if (use_global) {
|
||||
global = value;
|
||||
} else {
|
||||
local = value;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool use_global = true;
|
||||
Type global{};
|
||||
Type local{};
|
||||
};
|
||||
|
||||
struct Values {
|
||||
// Audio
|
||||
std::string audio_device_id;
|
||||
std::string sink_id;
|
||||
bool audio_muted;
|
||||
Setting<bool> enable_audio_stretching;
|
||||
Setting<float> volume;
|
||||
|
||||
// Core
|
||||
Setting<bool> use_multi_core;
|
||||
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend;
|
||||
bool renderer_debug;
|
||||
Setting<int> vulkan_device;
|
||||
|
||||
Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
|
||||
Setting<int> aspect_ratio;
|
||||
Setting<int> max_anisotropy;
|
||||
Setting<bool> use_frame_limit;
|
||||
Setting<u16> frame_limit;
|
||||
Setting<bool> use_disk_shader_cache;
|
||||
Setting<GPUAccuracy> gpu_accuracy;
|
||||
Setting<bool> use_asynchronous_gpu_emulation;
|
||||
Setting<bool> use_vsync;
|
||||
Setting<bool> use_assembly_shaders;
|
||||
Setting<bool> force_30fps_mode;
|
||||
Setting<bool> use_fast_gpu_time;
|
||||
|
||||
Setting<float> bg_red;
|
||||
Setting<float> bg_green;
|
||||
Setting<float> bg_blue;
|
||||
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
std::optional<u32> rng_seed;
|
||||
Setting<std::optional<u32>> rng_seed;
|
||||
// Measured in seconds since epoch
|
||||
std::optional<std::chrono::seconds> custom_rtc;
|
||||
Setting<std::optional<std::chrono::seconds>> custom_rtc;
|
||||
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
|
||||
std::chrono::seconds custom_rtc_differential;
|
||||
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
s32 region_index;
|
||||
s32 time_zone_index;
|
||||
s32 sound_index;
|
||||
Setting<s32> language_index;
|
||||
Setting<s32> region_index;
|
||||
Setting<s32> time_zone_index;
|
||||
Setting<s32> sound_index;
|
||||
|
||||
// Controls
|
||||
std::array<PlayerInput, 10> players;
|
||||
@@ -419,8 +484,7 @@ struct Values {
|
||||
u16 udp_input_port;
|
||||
u8 udp_pad_index;
|
||||
|
||||
// Core
|
||||
bool use_multi_core;
|
||||
bool use_docked_mode;
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
@@ -432,39 +496,6 @@ struct Values {
|
||||
NANDUserSize nand_user_size;
|
||||
SDMCSize sdmc_size;
|
||||
|
||||
// Renderer
|
||||
RendererBackend renderer_backend;
|
||||
bool renderer_debug;
|
||||
int vulkan_device;
|
||||
|
||||
u16 resolution_factor{1};
|
||||
int aspect_ratio;
|
||||
int max_anisotropy;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_disk_shader_cache;
|
||||
GPUAccuracy gpu_accuracy;
|
||||
bool use_asynchronous_gpu_emulation;
|
||||
bool use_vsync;
|
||||
bool use_assembly_shaders;
|
||||
bool force_30fps_mode;
|
||||
bool use_fast_gpu_time;
|
||||
|
||||
float bg_red;
|
||||
float bg_green;
|
||||
float bg_blue;
|
||||
|
||||
std::string log_filter;
|
||||
|
||||
bool use_dev_keys;
|
||||
|
||||
// Audio
|
||||
bool audio_muted;
|
||||
std::string sink_id;
|
||||
bool enable_audio_stretching;
|
||||
std::string audio_device_id;
|
||||
float volume;
|
||||
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
bool use_gdbstub;
|
||||
@@ -477,7 +508,11 @@ struct Values {
|
||||
bool disable_cpu_opt;
|
||||
bool disable_macro_jit;
|
||||
|
||||
// BCAT
|
||||
// Misceallaneous
|
||||
std::string log_filter;
|
||||
bool use_dev_keys;
|
||||
|
||||
// Services
|
||||
std::string bcat_backend;
|
||||
bool bcat_boxcat_local;
|
||||
|
||||
@@ -501,4 +536,7 @@ std::string GetTimeZoneString();
|
||||
void Apply();
|
||||
void LogSettings();
|
||||
|
||||
// Restore the global state of all applicable settings in the Values struct
|
||||
void RestoreGlobalState();
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
// Log user configuration information
|
||||
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
|
||||
AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
AddField(field_type, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching.GetValue());
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
AddField(field_type, "Renderer_Backend",
|
||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor.GetValue());
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache",
|
||||
Settings::values.use_disk_shader_cache.GetValue());
|
||||
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
||||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy));
|
||||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
|
||||
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders",
|
||||
Settings::values.use_assembly_shaders.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,13 @@ Adapter::Adapter() {
|
||||
LOG_INFO(Input, "GC Adapter Initialization started");
|
||||
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
libusb_init(&libusb_ctx);
|
||||
|
||||
StartScanThread();
|
||||
const int init_res = libusb_init(&libusb_ctx);
|
||||
if (init_res == LIBUSB_SUCCESS) {
|
||||
StartScanThread();
|
||||
} else {
|
||||
LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
|
||||
}
|
||||
}
|
||||
|
||||
GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
|
||||
@@ -211,17 +215,26 @@ void Adapter::Setup() {
|
||||
adapter_controllers_status.fill(ControllerTypes::None);
|
||||
|
||||
// pointer to list of connected usb devices
|
||||
libusb_device** devices;
|
||||
libusb_device** devices{};
|
||||
|
||||
// populate the list of devices, get the count
|
||||
const std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices);
|
||||
const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
|
||||
if (device_count < 0) {
|
||||
LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
|
||||
detect_thread_running = false; // Stop the loop constantly checking for gc adapter
|
||||
// TODO: For hotplug+gc adapter checkbox implementation, revert this.
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < device_count; ++index) {
|
||||
if (CheckDeviceAccess(devices[index])) {
|
||||
// GC Adapter found and accessible, registering it
|
||||
GetGCEndpoint(devices[index]);
|
||||
break;
|
||||
if (devices != nullptr) {
|
||||
for (std::size_t index = 0; index < device_count; ++index) {
|
||||
if (CheckDeviceAccess(devices[index])) {
|
||||
// GC Adapter found and accessible, registering it
|
||||
GetGCEndpoint(devices[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(devices, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +292,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
|
||||
|
||||
void Adapter::GetGCEndpoint(libusb_device* device) {
|
||||
libusb_config_descriptor* config = nullptr;
|
||||
libusb_get_config_descriptor(device, 0, &config);
|
||||
const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
|
||||
if (config_descriptor_return != LIBUSB_SUCCESS) {
|
||||
LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
|
||||
config_descriptor_return);
|
||||
return;
|
||||
}
|
||||
|
||||
for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
|
||||
const libusb_interface* interfaceContainer = &config->interface[ic];
|
||||
for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <libusb.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
@@ -96,7 +96,8 @@ public:
|
||||
}
|
||||
if (is_written) {
|
||||
map->MarkAsModified(true, GetModifiedTicks());
|
||||
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::IsGPULevelHigh() &&
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
MarkForAsyncFlush(map);
|
||||
}
|
||||
if (!map->is_written) {
|
||||
@@ -369,7 +370,8 @@ private:
|
||||
}
|
||||
if (modified_inheritance) {
|
||||
map->MarkAsModified(true, GetModifiedTicks());
|
||||
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::IsGPULevelHigh() &&
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
MarkForAsyncFlush(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ u64 GPU::GetTicks() const {
|
||||
constexpr u64 gpu_ticks_den = 625;
|
||||
|
||||
u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
|
||||
if (Settings::values.use_fast_gpu_time) {
|
||||
if (Settings::values.use_fast_gpu_time.GetValue()) {
|
||||
nanoseconds /= 256;
|
||||
}
|
||||
const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
}
|
||||
|
||||
query->BindCounter(Stream(type).Current(), timestamp);
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
AsyncFlushQuery(cpu_addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
|
||||
void RendererBase::RefreshBaseSettings() {
|
||||
UpdateCurrentFramebufferLayout();
|
||||
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit;
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
|
||||
renderer_settings.set_background_color = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -229,15 +229,15 @@ Device::Device()
|
||||
// uniform buffers as "push constants"
|
||||
has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
|
||||
|
||||
use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 &&
|
||||
GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback &&
|
||||
GLAD_GL_NV_transform_feedback2;
|
||||
use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() &&
|
||||
GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
|
||||
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
|
||||
|
||||
if (Settings::values.use_assembly_shaders && !use_assembly_shaders) {
|
||||
if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
|
||||
LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
|
||||
std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
// Skip games without title id
|
||||
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
|
||||
if (!Settings::values.use_disk_shader_cache || !has_title_id) {
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
frame_mailbox = std::make_unique<FrameMailbox>();
|
||||
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
|
||||
// Create shader programs
|
||||
OGLShader vertex_shader;
|
||||
@@ -561,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
}
|
||||
|
||||
// Set projection matrix
|
||||
|
||||
@@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept {
|
||||
raw = 0;
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
|
||||
if (regs.stencil_two_side_enable) {
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
|
||||
} else {
|
||||
back.action_stencil_fail.Assign(front.action_stencil_fail);
|
||||
back.action_depth_fail.Assign(front.action_depth_fail);
|
||||
back.action_depth_pass.Assign(front.action_depth_pass);
|
||||
back.test_func.Assign(front.test_func);
|
||||
}
|
||||
depth_test_enable.Assign(regs.depth_test_enable);
|
||||
depth_write_enable.Assign(regs.depth_write_enabled);
|
||||
depth_bounds_enable.Assign(regs.depth_bounds_enable);
|
||||
stencil_enable.Assign(regs.stencil_enable);
|
||||
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
|
||||
}
|
||||
|
||||
void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
|
||||
void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
|
||||
const auto& clip = regs.view_volume_clip_control;
|
||||
const std::array enabled_lut = {regs.polygon_offset_point_enable,
|
||||
regs.polygon_offset_line_enable,
|
||||
regs.polygon_offset_fill_enable};
|
||||
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
|
||||
|
||||
u32 packed_front_face = PackFrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
// Flip front face
|
||||
packed_front_face = 1 - packed_front_face;
|
||||
}
|
||||
|
||||
raw = 0;
|
||||
topology.Assign(topology_index);
|
||||
primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
|
||||
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
|
||||
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
|
||||
depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
|
||||
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
|
||||
cull_face.Assign(PackCullFace(regs.cull_face));
|
||||
front_face.Assign(packed_front_face);
|
||||
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
|
||||
patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
|
||||
tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
|
||||
@@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
|
||||
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
|
||||
logic_op.Assign(PackLogicOp(regs.logic_op.operation));
|
||||
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
|
||||
std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
|
||||
}
|
||||
|
||||
void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept {
|
||||
std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
binding_divisors[index] =
|
||||
regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||
const auto& input = regs.vertex_attrib_format[index];
|
||||
auto& attribute = attributes[index];
|
||||
attribute.raw = 0;
|
||||
attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
|
||||
attribute.buffer.Assign(input.buffer);
|
||||
attribute.offset.Assign(input.offset);
|
||||
attribute.type.Assign(static_cast<u32>(input.type.Value()));
|
||||
attribute.size.Assign(static_cast<u32>(input.size.Value()));
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < std::size(attachments); ++index) {
|
||||
attachments[index].Fill(regs, index);
|
||||
}
|
||||
}
|
||||
|
||||
void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
|
||||
const auto& transform = regs.viewport_transform;
|
||||
std::transform(transform.begin(), transform.end(), swizzles.begin(),
|
||||
std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
|
||||
[](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
|
||||
|
||||
if (!has_extended_dynamic_state) {
|
||||
no_extended_dynamic_state.Assign(1);
|
||||
dynamic_state.Fill(regs);
|
||||
}
|
||||
}
|
||||
|
||||
void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
|
||||
@@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
|
||||
enable.Assign(1);
|
||||
}
|
||||
|
||||
void FixedPipelineState::Fill(const Maxwell& regs) {
|
||||
rasterizer.Fill(regs);
|
||||
depth_stencil.Fill(regs);
|
||||
color_blending.Fill(regs);
|
||||
viewport_swizzles.Fill(regs);
|
||||
void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
|
||||
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
|
||||
u32 packed_front_face = PackFrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
// Flip front face
|
||||
packed_front_face = 1 - packed_front_face;
|
||||
}
|
||||
|
||||
raw1 = 0;
|
||||
raw2 = 0;
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
|
||||
if (regs.stencil_two_side_enable) {
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
|
||||
} else {
|
||||
back.action_stencil_fail.Assign(front.action_stencil_fail);
|
||||
back.action_depth_fail.Assign(front.action_depth_fail);
|
||||
back.action_depth_pass.Assign(front.action_depth_pass);
|
||||
back.test_func.Assign(front.test_func);
|
||||
}
|
||||
stencil_enable.Assign(regs.stencil_enable);
|
||||
depth_write_enable.Assign(regs.depth_write_enabled);
|
||||
depth_bounds_enable.Assign(regs.depth_bounds_enable);
|
||||
depth_test_enable.Assign(regs.depth_test_enable);
|
||||
front_face.Assign(packed_front_face);
|
||||
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
|
||||
topology.Assign(topology_index);
|
||||
cull_face.Assign(PackCullFace(regs.cull_face));
|
||||
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
const auto& input = regs.vertex_array[index];
|
||||
VertexBinding& binding = vertex_bindings[index];
|
||||
binding.raw = 0;
|
||||
binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
|
||||
binding.stride.Assign(static_cast<u16>(input.stride.Value()));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::Hash() const noexcept {
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
|
||||
return static_cast<std::size_t>(hash);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
|
||||
return std::memcmp(this, &rhs, sizeof *this) == 0;
|
||||
return std::memcmp(this, &rhs, Size()) == 0;
|
||||
}
|
||||
|
||||
u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
|
||||
|
||||
@@ -60,14 +60,6 @@ struct FixedPipelineState {
|
||||
|
||||
void Fill(const Maxwell& regs, std::size_t index);
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const BlendingAttachment& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const BlendingAttachment& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
constexpr std::array<bool, 4> Mask() const noexcept {
|
||||
return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
|
||||
}
|
||||
@@ -97,83 +89,77 @@ struct FixedPipelineState {
|
||||
}
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
union Binding {
|
||||
u16 raw;
|
||||
BitField<0, 1, u16> enabled;
|
||||
BitField<1, 12, u16> stride;
|
||||
};
|
||||
union VertexAttribute {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enabled;
|
||||
BitField<1, 5, u32> buffer;
|
||||
BitField<6, 14, u32> offset;
|
||||
BitField<20, 3, u32> type;
|
||||
BitField<23, 6, u32> size;
|
||||
|
||||
union Attribute {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enabled;
|
||||
BitField<1, 5, u32> buffer;
|
||||
BitField<6, 14, u32> offset;
|
||||
BitField<20, 3, u32> type;
|
||||
BitField<23, 6, u32> size;
|
||||
|
||||
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
|
||||
}
|
||||
|
||||
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
|
||||
}
|
||||
};
|
||||
|
||||
std::array<Binding, Maxwell::NumVertexArrays> bindings;
|
||||
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
|
||||
std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
|
||||
|
||||
void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
|
||||
auto& binding = bindings[index];
|
||||
binding.raw = 0;
|
||||
binding.enabled.Assign(enabled ? 1 : 0);
|
||||
binding.stride.Assign(static_cast<u16>(stride));
|
||||
binding_divisors[index] = divisor;
|
||||
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
|
||||
}
|
||||
|
||||
void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset,
|
||||
Maxwell::VertexAttribute::Type type,
|
||||
Maxwell::VertexAttribute::Size size) noexcept {
|
||||
auto& attribute = attributes[index];
|
||||
attribute.raw = 0;
|
||||
attribute.enabled.Assign(enabled ? 1 : 0);
|
||||
attribute.buffer.Assign(buffer);
|
||||
attribute.offset.Assign(offset);
|
||||
attribute.type.Assign(static_cast<u32>(type));
|
||||
attribute.size.Assign(static_cast<u32>(size));
|
||||
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct Rasterizer {
|
||||
template <std::size_t Position>
|
||||
union StencilFace {
|
||||
BitField<Position + 0, 3, u32> action_stencil_fail;
|
||||
BitField<Position + 3, 3, u32> action_depth_fail;
|
||||
BitField<Position + 6, 3, u32> action_depth_pass;
|
||||
BitField<Position + 9, 3, u32> test_func;
|
||||
|
||||
Maxwell::StencilOp ActionStencilFail() const noexcept {
|
||||
return UnpackStencilOp(action_stencil_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthFail() const noexcept {
|
||||
return UnpackStencilOp(action_depth_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthPass() const noexcept {
|
||||
return UnpackStencilOp(action_depth_pass);
|
||||
}
|
||||
|
||||
Maxwell::ComparisonOp TestFunc() const noexcept {
|
||||
return UnpackComparisonOp(test_func);
|
||||
}
|
||||
};
|
||||
|
||||
union VertexBinding {
|
||||
u16 raw;
|
||||
BitField<0, 12, u16> stride;
|
||||
BitField<12, 1, u16> enabled;
|
||||
};
|
||||
|
||||
struct DynamicState {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 4, u32> topology;
|
||||
BitField<4, 1, u32> primitive_restart_enable;
|
||||
BitField<5, 1, u32> cull_enable;
|
||||
BitField<6, 1, u32> depth_bias_enable;
|
||||
BitField<7, 1, u32> depth_clamp_disabled;
|
||||
BitField<8, 1, u32> ndc_minus_one_to_one;
|
||||
BitField<9, 2, u32> cull_face;
|
||||
BitField<11, 1, u32> front_face;
|
||||
BitField<12, 2, u32> polygon_mode;
|
||||
BitField<14, 5, u32> patch_control_points_minus_one;
|
||||
BitField<19, 2, u32> tessellation_primitive;
|
||||
BitField<21, 2, u32> tessellation_spacing;
|
||||
BitField<23, 1, u32> tessellation_clockwise;
|
||||
BitField<24, 1, u32> logic_op_enable;
|
||||
BitField<25, 4, u32> logic_op;
|
||||
BitField<29, 1, u32> rasterize_enable;
|
||||
u32 raw1;
|
||||
StencilFace<0> front;
|
||||
StencilFace<12> back;
|
||||
BitField<24, 1, u32> stencil_enable;
|
||||
BitField<25, 1, u32> depth_write_enable;
|
||||
BitField<26, 1, u32> depth_bounds_enable;
|
||||
BitField<27, 1, u32> depth_test_enable;
|
||||
BitField<28, 1, u32> front_face;
|
||||
BitField<29, 3, u32> depth_test_func;
|
||||
};
|
||||
union {
|
||||
u32 raw2;
|
||||
BitField<0, 4, u32> topology;
|
||||
BitField<4, 2, u32> cull_face;
|
||||
BitField<6, 1, u32> cull_enable;
|
||||
};
|
||||
std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
|
||||
|
||||
// TODO(Rodrigo): Move this to push constants
|
||||
u32 point_size;
|
||||
void Fill(const Maxwell& regs);
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
|
||||
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
|
||||
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
|
||||
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
|
||||
return UnpackComparisonOp(depth_test_func);
|
||||
}
|
||||
|
||||
Maxwell::CullFace CullFace() const noexcept {
|
||||
@@ -183,70 +169,36 @@ struct FixedPipelineState {
|
||||
Maxwell::FrontFace FrontFace() const noexcept {
|
||||
return UnpackFrontFace(front_face.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthStencil {
|
||||
template <std::size_t Position>
|
||||
union StencilFace {
|
||||
BitField<Position + 0, 3, u32> action_stencil_fail;
|
||||
BitField<Position + 3, 3, u32> action_depth_fail;
|
||||
BitField<Position + 6, 3, u32> action_depth_pass;
|
||||
BitField<Position + 9, 3, u32> test_func;
|
||||
|
||||
Maxwell::StencilOp ActionStencilFail() const noexcept {
|
||||
return UnpackStencilOp(action_stencil_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthFail() const noexcept {
|
||||
return UnpackStencilOp(action_depth_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthPass() const noexcept {
|
||||
return UnpackStencilOp(action_depth_pass);
|
||||
}
|
||||
|
||||
Maxwell::ComparisonOp TestFunc() const noexcept {
|
||||
return UnpackComparisonOp(test_func);
|
||||
}
|
||||
};
|
||||
|
||||
union {
|
||||
u32 raw;
|
||||
StencilFace<0> front;
|
||||
StencilFace<12> back;
|
||||
BitField<24, 1, u32> depth_test_enable;
|
||||
BitField<25, 1, u32> depth_write_enable;
|
||||
BitField<26, 1, u32> depth_bounds_enable;
|
||||
BitField<27, 1, u32> stencil_enable;
|
||||
BitField<28, 3, u32> depth_test_func;
|
||||
};
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
|
||||
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
|
||||
return UnpackComparisonOp(depth_test_func);
|
||||
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
|
||||
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct ColorBlending {
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> no_extended_dynamic_state;
|
||||
BitField<2, 1, u32> primitive_restart_enable;
|
||||
BitField<3, 1, u32> depth_bias_enable;
|
||||
BitField<4, 1, u32> depth_clamp_disabled;
|
||||
BitField<5, 1, u32> ndc_minus_one_to_one;
|
||||
BitField<6, 2, u32> polygon_mode;
|
||||
BitField<8, 5, u32> patch_control_points_minus_one;
|
||||
BitField<13, 2, u32> tessellation_primitive;
|
||||
BitField<15, 2, u32> tessellation_spacing;
|
||||
BitField<17, 1, u32> tessellation_clockwise;
|
||||
BitField<18, 1, u32> logic_op_enable;
|
||||
BitField<19, 4, u32> logic_op;
|
||||
BitField<23, 1, u32> rasterize_enable;
|
||||
};
|
||||
u32 point_size;
|
||||
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
|
||||
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
|
||||
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
|
||||
DynamicState dynamic_state;
|
||||
|
||||
struct ViewportSwizzles {
|
||||
std::array<u16, Maxwell::NumViewports> swizzles;
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
};
|
||||
|
||||
VertexInput vertex_input;
|
||||
Rasterizer rasterizer;
|
||||
DepthStencil depth_stencil;
|
||||
ColorBlending color_blending;
|
||||
ViewportSwizzles viewport_swizzles;
|
||||
|
||||
void Fill(const Maxwell& regs);
|
||||
void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
@@ -255,6 +207,11 @@ struct FixedPipelineState {
|
||||
bool operator!=(const FixedPipelineState& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::size_t Size() const noexcept {
|
||||
const std::size_t total_size = sizeof *this;
|
||||
return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
|
||||
|
||||
@@ -409,7 +409,7 @@ bool RendererVulkan::PickDevices() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const s32 device_index = Settings::values.vulkan_device;
|
||||
const s32 device_index = Settings::values.vulkan_device.GetValue();
|
||||
if (device_index < 0 || device_index >= static_cast<s32>(devices->size())) {
|
||||
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
|
||||
return false;
|
||||
|
||||
@@ -141,24 +141,28 @@ struct ScreenRectVertex {
|
||||
std::array<f32, 2> tex_coord;
|
||||
|
||||
static VkVertexInputBindingDescription GetDescription() {
|
||||
VkVertexInputBindingDescription description;
|
||||
description.binding = 0;
|
||||
description.stride = sizeof(ScreenRectVertex);
|
||||
description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
return description;
|
||||
return {
|
||||
.binding = 0,
|
||||
.stride = sizeof(ScreenRectVertex),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
};
|
||||
}
|
||||
|
||||
static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() {
|
||||
std::array<VkVertexInputAttributeDescription, 2> attributes;
|
||||
attributes[0].location = 0;
|
||||
attributes[0].binding = 0;
|
||||
attributes[0].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attributes[0].offset = offsetof(ScreenRectVertex, position);
|
||||
attributes[1].location = 1;
|
||||
attributes[1].binding = 0;
|
||||
attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attributes[1].offset = offsetof(ScreenRectVertex, tex_coord);
|
||||
return attributes;
|
||||
return {{
|
||||
{
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = offsetof(ScreenRectVertex, position),
|
||||
},
|
||||
{
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
.offset = offsetof(ScreenRectVertex, tex_coord),
|
||||
},
|
||||
}};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -267,20 +271,25 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
|
||||
blit_image->Transition(0, 1, 0, 1, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
VkBufferImageCopy copy;
|
||||
copy.bufferOffset = image_offset;
|
||||
copy.bufferRowLength = 0;
|
||||
copy.bufferImageHeight = 0;
|
||||
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
copy.imageSubresource.mipLevel = 0;
|
||||
copy.imageSubresource.baseArrayLayer = 0;
|
||||
copy.imageSubresource.layerCount = 1;
|
||||
copy.imageOffset.x = 0;
|
||||
copy.imageOffset.y = 0;
|
||||
copy.imageOffset.z = 0;
|
||||
copy.imageExtent.width = framebuffer.width;
|
||||
copy.imageExtent.height = framebuffer.height;
|
||||
copy.imageExtent.depth = 1;
|
||||
const VkBufferImageCopy copy{
|
||||
.bufferOffset = image_offset,
|
||||
.bufferRowLength = 0,
|
||||
.bufferImageHeight = 0,
|
||||
.imageSubresource =
|
||||
{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.imageOffset = {.x = 0, .y = 0, .z = 0},
|
||||
.imageExtent =
|
||||
{
|
||||
.width = framebuffer.width,
|
||||
.height = framebuffer.height,
|
||||
.depth = 1,
|
||||
},
|
||||
};
|
||||
scheduler.Record(
|
||||
[buffer = *buffer, image = *blit_image->GetHandle(), copy](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.CopyBufferToImage(buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
|
||||
@@ -295,11 +304,9 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
|
||||
descriptor_set = descriptor_sets[image_index], buffer = *buffer,
|
||||
size = swapchain.GetSize(), pipeline = *pipeline,
|
||||
layout = *pipeline_layout](vk::CommandBuffer cmdbuf) {
|
||||
VkClearValue clear_color;
|
||||
clear_color.color.float32[0] = 0.0f;
|
||||
clear_color.color.float32[1] = 0.0f;
|
||||
clear_color.color.float32[2] = 0.0f;
|
||||
clear_color.color.float32[3] = 0.0f;
|
||||
const VkClearValue clear_color{
|
||||
.color = {.float32 = {0.0f, 0.0f, 0.0f, 0.0f}},
|
||||
};
|
||||
|
||||
VkRenderPassBeginInfo renderpass_bi;
|
||||
renderpass_bi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
@@ -379,93 +386,109 @@ void VKBlitScreen::CreateSemaphores() {
|
||||
}
|
||||
|
||||
void VKBlitScreen::CreateDescriptorPool() {
|
||||
std::array<VkDescriptorPoolSize, 2> pool_sizes;
|
||||
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
pool_sizes[0].descriptorCount = static_cast<u32>(image_count);
|
||||
pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
pool_sizes[1].descriptorCount = static_cast<u32>(image_count);
|
||||
const std::array<VkDescriptorPoolSize, 2> pool_sizes{{
|
||||
{
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = static_cast<u32>(image_count),
|
||||
},
|
||||
{
|
||||
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = static_cast<u32>(image_count),
|
||||
},
|
||||
}};
|
||||
|
||||
VkDescriptorPoolCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
ci.maxSets = static_cast<u32>(image_count);
|
||||
ci.poolSizeCount = static_cast<u32>(pool_sizes.size());
|
||||
ci.pPoolSizes = pool_sizes.data();
|
||||
const VkDescriptorPoolCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = static_cast<u32>(image_count),
|
||||
.poolSizeCount = static_cast<u32>(pool_sizes.size()),
|
||||
.pPoolSizes = pool_sizes.data(),
|
||||
};
|
||||
descriptor_pool = device.GetLogical().CreateDescriptorPool(ci);
|
||||
}
|
||||
|
||||
void VKBlitScreen::CreateRenderPass() {
|
||||
VkAttachmentDescription color_attachment;
|
||||
color_attachment.flags = 0;
|
||||
color_attachment.format = swapchain.GetImageFormat();
|
||||
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
const VkAttachmentDescription color_attachment{
|
||||
.flags = 0,
|
||||
.format = swapchain.GetImageFormat(),
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
};
|
||||
|
||||
VkAttachmentReference color_attachment_ref;
|
||||
color_attachment_ref.attachment = 0;
|
||||
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
const VkAttachmentReference color_attachment_ref{
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
};
|
||||
|
||||
VkSubpassDescription subpass_description;
|
||||
subpass_description.flags = 0;
|
||||
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass_description.inputAttachmentCount = 0;
|
||||
subpass_description.pInputAttachments = nullptr;
|
||||
subpass_description.colorAttachmentCount = 1;
|
||||
subpass_description.pColorAttachments = &color_attachment_ref;
|
||||
subpass_description.pResolveAttachments = nullptr;
|
||||
subpass_description.pDepthStencilAttachment = nullptr;
|
||||
subpass_description.preserveAttachmentCount = 0;
|
||||
subpass_description.pPreserveAttachments = nullptr;
|
||||
const VkSubpassDescription subpass_description{
|
||||
.flags = 0,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.inputAttachmentCount = 0,
|
||||
.pInputAttachments = nullptr,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &color_attachment_ref,
|
||||
.pResolveAttachments = nullptr,
|
||||
.pDepthStencilAttachment = nullptr,
|
||||
.preserveAttachmentCount = 0,
|
||||
.pPreserveAttachments = nullptr,
|
||||
};
|
||||
|
||||
VkSubpassDependency dependency;
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.srcAccessMask = 0;
|
||||
dependency.dstAccessMask =
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dependency.dependencyFlags = 0;
|
||||
const VkSubpassDependency dependency{
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dependencyFlags = 0,
|
||||
};
|
||||
|
||||
VkRenderPassCreateInfo renderpass_ci;
|
||||
renderpass_ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderpass_ci.pNext = nullptr;
|
||||
renderpass_ci.flags = 0;
|
||||
renderpass_ci.attachmentCount = 1;
|
||||
renderpass_ci.pAttachments = &color_attachment;
|
||||
renderpass_ci.subpassCount = 1;
|
||||
renderpass_ci.pSubpasses = &subpass_description;
|
||||
renderpass_ci.dependencyCount = 1;
|
||||
renderpass_ci.pDependencies = &dependency;
|
||||
const VkRenderPassCreateInfo renderpass_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &color_attachment,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass_description,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &dependency,
|
||||
};
|
||||
|
||||
renderpass = device.GetLogical().CreateRenderPass(renderpass_ci);
|
||||
}
|
||||
|
||||
void VKBlitScreen::CreateDescriptorSetLayout() {
|
||||
std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings;
|
||||
layout_bindings[0].binding = 0;
|
||||
layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
layout_bindings[0].descriptorCount = 1;
|
||||
layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
layout_bindings[0].pImmutableSamplers = nullptr;
|
||||
layout_bindings[1].binding = 1;
|
||||
layout_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
layout_bindings[1].descriptorCount = 1;
|
||||
layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
layout_bindings[1].pImmutableSamplers = nullptr;
|
||||
const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.bindingCount = static_cast<u32>(layout_bindings.size());
|
||||
ci.pBindings = layout_bindings.data();
|
||||
const VkDescriptorSetLayoutCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.bindingCount = static_cast<u32>(layout_bindings.size()),
|
||||
.pBindings = layout_bindings.data(),
|
||||
};
|
||||
|
||||
descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci);
|
||||
}
|
||||
@@ -473,175 +496,192 @@ void VKBlitScreen::CreateDescriptorSetLayout() {
|
||||
void VKBlitScreen::CreateDescriptorSets() {
|
||||
const std::vector layouts(image_count, *descriptor_set_layout);
|
||||
|
||||
VkDescriptorSetAllocateInfo ai;
|
||||
ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
ai.pNext = nullptr;
|
||||
ai.descriptorPool = *descriptor_pool;
|
||||
ai.descriptorSetCount = static_cast<u32>(image_count);
|
||||
ai.pSetLayouts = layouts.data();
|
||||
const VkDescriptorSetAllocateInfo ai{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.descriptorPool = *descriptor_pool,
|
||||
.descriptorSetCount = static_cast<u32>(image_count),
|
||||
.pSetLayouts = layouts.data(),
|
||||
};
|
||||
|
||||
descriptor_sets = descriptor_pool.Allocate(ai);
|
||||
}
|
||||
|
||||
void VKBlitScreen::CreatePipelineLayout() {
|
||||
VkPipelineLayoutCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.setLayoutCount = 1;
|
||||
ci.pSetLayouts = descriptor_set_layout.address();
|
||||
ci.pushConstantRangeCount = 0;
|
||||
ci.pPushConstantRanges = nullptr;
|
||||
const VkPipelineLayoutCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = descriptor_set_layout.address(),
|
||||
.pushConstantRangeCount = 0,
|
||||
.pPushConstantRanges = nullptr,
|
||||
};
|
||||
pipeline_layout = device.GetLogical().CreatePipelineLayout(ci);
|
||||
}
|
||||
|
||||
void VKBlitScreen::CreateGraphicsPipeline() {
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;
|
||||
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader_stages[0].pNext = nullptr;
|
||||
shader_stages[0].flags = 0;
|
||||
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
shader_stages[0].module = *vertex_shader;
|
||||
shader_stages[0].pName = "main";
|
||||
shader_stages[0].pSpecializationInfo = nullptr;
|
||||
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
shader_stages[1].pNext = nullptr;
|
||||
shader_stages[1].flags = 0;
|
||||
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shader_stages[1].module = *fragment_shader;
|
||||
shader_stages[1].pName = "main";
|
||||
shader_stages[1].pSpecializationInfo = nullptr;
|
||||
const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = *vertex_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = *fragment_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
}};
|
||||
|
||||
const auto vertex_binding_description = ScreenRectVertex::GetDescription();
|
||||
const auto vertex_attrs_description = ScreenRectVertex::GetAttributes();
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_ci;
|
||||
vertex_input_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertex_input_ci.pNext = nullptr;
|
||||
vertex_input_ci.flags = 0;
|
||||
vertex_input_ci.vertexBindingDescriptionCount = 1;
|
||||
vertex_input_ci.pVertexBindingDescriptions = &vertex_binding_description;
|
||||
vertex_input_ci.vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()};
|
||||
vertex_input_ci.pVertexAttributeDescriptions = vertex_attrs_description.data();
|
||||
const VkPipelineVertexInputStateCreateInfo vertex_input_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.vertexBindingDescriptionCount = 1,
|
||||
.pVertexBindingDescriptions = &vertex_binding_description,
|
||||
.vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()},
|
||||
.pVertexAttributeDescriptions = vertex_attrs_description.data(),
|
||||
};
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo input_assembly_ci;
|
||||
input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
input_assembly_ci.pNext = nullptr;
|
||||
input_assembly_ci.flags = 0;
|
||||
input_assembly_ci.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
input_assembly_ci.primitiveRestartEnable = VK_FALSE;
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
|
||||
.primitiveRestartEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_state_ci;
|
||||
viewport_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewport_state_ci.pNext = nullptr;
|
||||
viewport_state_ci.flags = 0;
|
||||
viewport_state_ci.viewportCount = 1;
|
||||
viewport_state_ci.pViewports = nullptr;
|
||||
viewport_state_ci.scissorCount = 1;
|
||||
viewport_state_ci.pScissors = nullptr;
|
||||
const VkPipelineViewportStateCreateInfo viewport_state_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = 1,
|
||||
.pViewports = nullptr,
|
||||
.scissorCount = 1,
|
||||
.pScissors = nullptr,
|
||||
};
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_ci;
|
||||
rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterization_ci.pNext = nullptr;
|
||||
rasterization_ci.flags = 0;
|
||||
rasterization_ci.depthClampEnable = VK_FALSE;
|
||||
rasterization_ci.rasterizerDiscardEnable = VK_FALSE;
|
||||
rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rasterization_ci.cullMode = VK_CULL_MODE_NONE;
|
||||
rasterization_ci.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
rasterization_ci.depthBiasEnable = VK_FALSE;
|
||||
rasterization_ci.depthBiasConstantFactor = 0.0f;
|
||||
rasterization_ci.depthBiasClamp = 0.0f;
|
||||
rasterization_ci.depthBiasSlopeFactor = 0.0f;
|
||||
rasterization_ci.lineWidth = 1.0f;
|
||||
const VkPipelineRasterizationStateCreateInfo rasterization_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthClampEnable = VK_FALSE,
|
||||
.rasterizerDiscardEnable = VK_FALSE,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
||||
.depthBiasEnable = VK_FALSE,
|
||||
.depthBiasConstantFactor = 0.0f,
|
||||
.depthBiasClamp = 0.0f,
|
||||
.depthBiasSlopeFactor = 0.0f,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampling_ci;
|
||||
multisampling_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisampling_ci.pNext = nullptr;
|
||||
multisampling_ci.flags = 0;
|
||||
multisampling_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
multisampling_ci.sampleShadingEnable = VK_FALSE;
|
||||
multisampling_ci.minSampleShading = 0.0f;
|
||||
multisampling_ci.pSampleMask = nullptr;
|
||||
multisampling_ci.alphaToCoverageEnable = VK_FALSE;
|
||||
multisampling_ci.alphaToOneEnable = VK_FALSE;
|
||||
const VkPipelineMultisampleStateCreateInfo multisampling_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.sampleShadingEnable = VK_FALSE,
|
||||
.minSampleShading = 0.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment;
|
||||
color_blend_attachment.blendEnable = VK_FALSE;
|
||||
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
const VkPipelineColorBlendAttachmentState color_blend_attachment{
|
||||
.blendEnable = VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_ci;
|
||||
color_blend_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
color_blend_ci.flags = 0;
|
||||
color_blend_ci.pNext = nullptr;
|
||||
color_blend_ci.logicOpEnable = VK_FALSE;
|
||||
color_blend_ci.logicOp = VK_LOGIC_OP_COPY;
|
||||
color_blend_ci.attachmentCount = 1;
|
||||
color_blend_ci.pAttachments = &color_blend_attachment;
|
||||
color_blend_ci.blendConstants[0] = 0.0f;
|
||||
color_blend_ci.blendConstants[1] = 0.0f;
|
||||
color_blend_ci.blendConstants[2] = 0.0f;
|
||||
color_blend_ci.blendConstants[3] = 0.0f;
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_COPY,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &color_blend_attachment,
|
||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
};
|
||||
|
||||
static constexpr std::array dynamic_states = {VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR};
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state_ci;
|
||||
dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynamic_state_ci.pNext = nullptr;
|
||||
dynamic_state_ci.flags = 0;
|
||||
dynamic_state_ci.dynamicStateCount = static_cast<u32>(dynamic_states.size());
|
||||
dynamic_state_ci.pDynamicStates = dynamic_states.data();
|
||||
static constexpr std::array dynamic_states{
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
};
|
||||
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.dynamicStateCount = static_cast<u32>(dynamic_states.size()),
|
||||
.pDynamicStates = dynamic_states.data(),
|
||||
};
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_ci;
|
||||
pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_ci.pNext = nullptr;
|
||||
pipeline_ci.flags = 0;
|
||||
pipeline_ci.stageCount = static_cast<u32>(shader_stages.size());
|
||||
pipeline_ci.pStages = shader_stages.data();
|
||||
pipeline_ci.pVertexInputState = &vertex_input_ci;
|
||||
pipeline_ci.pInputAssemblyState = &input_assembly_ci;
|
||||
pipeline_ci.pTessellationState = nullptr;
|
||||
pipeline_ci.pViewportState = &viewport_state_ci;
|
||||
pipeline_ci.pRasterizationState = &rasterization_ci;
|
||||
pipeline_ci.pMultisampleState = &multisampling_ci;
|
||||
pipeline_ci.pDepthStencilState = nullptr;
|
||||
pipeline_ci.pColorBlendState = &color_blend_ci;
|
||||
pipeline_ci.pDynamicState = &dynamic_state_ci;
|
||||
pipeline_ci.layout = *pipeline_layout;
|
||||
pipeline_ci.renderPass = *renderpass;
|
||||
pipeline_ci.subpass = 0;
|
||||
pipeline_ci.basePipelineHandle = 0;
|
||||
pipeline_ci.basePipelineIndex = 0;
|
||||
const VkGraphicsPipelineCreateInfo pipeline_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(shader_stages.size()),
|
||||
.pStages = shader_stages.data(),
|
||||
.pVertexInputState = &vertex_input_ci,
|
||||
.pInputAssemblyState = &input_assembly_ci,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &viewport_state_ci,
|
||||
.pRasterizationState = &rasterization_ci,
|
||||
.pMultisampleState = &multisampling_ci,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &color_blend_ci,
|
||||
.pDynamicState = &dynamic_state_ci,
|
||||
.layout = *pipeline_layout,
|
||||
.renderPass = *renderpass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = 0,
|
||||
.basePipelineIndex = 0,
|
||||
};
|
||||
|
||||
pipeline = device.GetLogical().CreateGraphicsPipeline(pipeline_ci);
|
||||
}
|
||||
|
||||
void VKBlitScreen::CreateSampler() {
|
||||
VkSamplerCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.magFilter = VK_FILTER_LINEAR;
|
||||
ci.minFilter = VK_FILTER_NEAREST;
|
||||
ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
ci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
ci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
ci.mipLodBias = 0.0f;
|
||||
ci.anisotropyEnable = VK_FALSE;
|
||||
ci.maxAnisotropy = 0.0f;
|
||||
ci.compareEnable = VK_FALSE;
|
||||
ci.compareOp = VK_COMPARE_OP_NEVER;
|
||||
ci.minLod = 0.0f;
|
||||
ci.maxLod = 0.0f;
|
||||
ci.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
|
||||
ci.unnormalizedCoordinates = VK_FALSE;
|
||||
const VkSamplerCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.magFilter = VK_FILTER_LINEAR,
|
||||
.minFilter = VK_FILTER_NEAREST,
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
||||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
.mipLodBias = 0.0f,
|
||||
.anisotropyEnable = VK_FALSE,
|
||||
.maxAnisotropy = 0.0f,
|
||||
.compareEnable = VK_FALSE,
|
||||
.compareOp = VK_COMPARE_OP_NEVER,
|
||||
.minLod = 0.0f,
|
||||
.maxLod = 0.0f,
|
||||
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
|
||||
sampler = device.GetLogical().CreateSampler(ci);
|
||||
}
|
||||
@@ -650,15 +690,16 @@ void VKBlitScreen::CreateFramebuffers() {
|
||||
const VkExtent2D size{swapchain.GetSize()};
|
||||
framebuffers.resize(image_count);
|
||||
|
||||
VkFramebufferCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.renderPass = *renderpass;
|
||||
ci.attachmentCount = 1;
|
||||
ci.width = size.width;
|
||||
ci.height = size.height;
|
||||
ci.layers = 1;
|
||||
VkFramebufferCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.renderPass = *renderpass,
|
||||
.attachmentCount = 1,
|
||||
.width = size.width,
|
||||
.height = size.height,
|
||||
.layers = 1,
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < image_count; ++i) {
|
||||
const VkImageView image_view{swapchain.GetImageViewIndex(i)};
|
||||
@@ -678,16 +719,17 @@ void VKBlitScreen::ReleaseRawImages() {
|
||||
}
|
||||
|
||||
void VKBlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) {
|
||||
VkBufferCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.size = CalculateBufferSize(framebuffer);
|
||||
ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
||||
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
ci.queueFamilyIndexCount = 0;
|
||||
ci.pQueueFamilyIndices = nullptr;
|
||||
const VkBufferCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = CalculateBufferSize(framebuffer),
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
};
|
||||
|
||||
buffer = device.GetLogical().CreateBuffer(ci);
|
||||
buffer_commit = memory_manager.Commit(buffer, true);
|
||||
@@ -697,24 +739,28 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
|
||||
raw_images.resize(image_count);
|
||||
raw_buffer_commits.resize(image_count);
|
||||
|
||||
VkImageCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.imageType = VK_IMAGE_TYPE_2D;
|
||||
ci.format = GetFormat(framebuffer);
|
||||
ci.extent.width = framebuffer.width;
|
||||
ci.extent.height = framebuffer.height;
|
||||
ci.extent.depth = 1;
|
||||
ci.mipLevels = 1;
|
||||
ci.arrayLayers = 1;
|
||||
ci.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
ci.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
ci.queueFamilyIndexCount = 0;
|
||||
ci.pQueueFamilyIndices = nullptr;
|
||||
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
const VkImageCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = GetFormat(framebuffer),
|
||||
.extent =
|
||||
{
|
||||
.width = framebuffer.width,
|
||||
.height = framebuffer.height,
|
||||
.depth = 1,
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_LINEAR,
|
||||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < image_count; ++i) {
|
||||
raw_images[i] = std::make_unique<VKImage>(device, scheduler, ci, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
@@ -723,39 +769,43 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer)
|
||||
}
|
||||
|
||||
void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const {
|
||||
VkDescriptorBufferInfo buffer_info;
|
||||
buffer_info.buffer = *buffer;
|
||||
buffer_info.offset = offsetof(BufferData, uniform);
|
||||
buffer_info.range = sizeof(BufferData::uniform);
|
||||
const VkDescriptorBufferInfo buffer_info{
|
||||
.buffer = *buffer,
|
||||
.offset = offsetof(BufferData, uniform),
|
||||
.range = sizeof(BufferData::uniform),
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet ubo_write;
|
||||
ubo_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
ubo_write.pNext = nullptr;
|
||||
ubo_write.dstSet = descriptor_sets[image_index];
|
||||
ubo_write.dstBinding = 0;
|
||||
ubo_write.dstArrayElement = 0;
|
||||
ubo_write.descriptorCount = 1;
|
||||
ubo_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
ubo_write.pImageInfo = nullptr;
|
||||
ubo_write.pBufferInfo = &buffer_info;
|
||||
ubo_write.pTexelBufferView = nullptr;
|
||||
const VkWriteDescriptorSet ubo_write{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
.dstSet = descriptor_sets[image_index],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.pImageInfo = nullptr,
|
||||
.pBufferInfo = &buffer_info,
|
||||
.pTexelBufferView = nullptr,
|
||||
};
|
||||
|
||||
VkDescriptorImageInfo image_info;
|
||||
image_info.sampler = *sampler;
|
||||
image_info.imageView = image_view;
|
||||
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
const VkDescriptorImageInfo image_info{
|
||||
.sampler = *sampler,
|
||||
.imageView = image_view,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet sampler_write;
|
||||
sampler_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
sampler_write.pNext = nullptr;
|
||||
sampler_write.dstSet = descriptor_sets[image_index];
|
||||
sampler_write.dstBinding = 1;
|
||||
sampler_write.dstArrayElement = 0;
|
||||
sampler_write.descriptorCount = 1;
|
||||
sampler_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
sampler_write.pImageInfo = &image_info;
|
||||
sampler_write.pBufferInfo = nullptr;
|
||||
sampler_write.pTexelBufferView = nullptr;
|
||||
const VkWriteDescriptorSet sampler_write{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
.dstSet = descriptor_sets[image_index],
|
||||
.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = &image_info,
|
||||
.pBufferInfo = nullptr,
|
||||
.pTexelBufferView = nullptr,
|
||||
};
|
||||
|
||||
device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
|
||||
}
|
||||
|
||||
@@ -313,6 +313,16 @@ bool VKDevice::Create() {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
|
||||
}
|
||||
|
||||
VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
|
||||
if (ext_extended_dynamic_state) {
|
||||
dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
|
||||
dynamic_state.pNext = nullptr;
|
||||
dynamic_state.extendedDynamicState = VK_TRUE;
|
||||
SetNext(next, dynamic_state);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
|
||||
}
|
||||
|
||||
if (!ext_depth_range_unrestricted) {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
|
||||
}
|
||||
@@ -541,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() {
|
||||
bool has_ext_subgroup_size_control{};
|
||||
bool has_ext_transform_feedback{};
|
||||
bool has_ext_custom_border_color{};
|
||||
bool has_ext_extended_dynamic_state{};
|
||||
for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
|
||||
Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
|
||||
Test(extension, khr_uniform_buffer_standard_layout,
|
||||
@@ -558,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
|
||||
false);
|
||||
Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
|
||||
false);
|
||||
Test(extension, has_ext_extended_dynamic_state,
|
||||
VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
|
||||
if (Settings::values.renderer_debug) {
|
||||
Test(extension, nv_device_diagnostics_config,
|
||||
VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
|
||||
@@ -643,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() {
|
||||
}
|
||||
}
|
||||
|
||||
if (has_ext_extended_dynamic_state) {
|
||||
VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
|
||||
dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
|
||||
dynamic_state.pNext = nullptr;
|
||||
features.pNext = &dynamic_state;
|
||||
physical.GetFeatures2KHR(features);
|
||||
|
||||
if (dynamic_state.extendedDynamicState) {
|
||||
extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
ext_extended_dynamic_state = true;
|
||||
}
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,11 @@ public:
|
||||
return ext_custom_border_color;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_extended_dynamic_state.
|
||||
bool IsExtExtendedDynamicStateSupported() const {
|
||||
return ext_extended_dynamic_state;
|
||||
}
|
||||
|
||||
/// Returns the vendor name reported from Vulkan.
|
||||
std::string_view GetVendorName() const {
|
||||
return vendor_name;
|
||||
@@ -239,6 +244,7 @@ private:
|
||||
bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
|
||||
bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
|
||||
bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
|
||||
bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
|
||||
bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
|
||||
|
||||
// Telemetry parameters
|
||||
|
||||
@@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
|
||||
|
||||
vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
|
||||
const SPIRVProgram& program) const {
|
||||
const auto& vi = fixed_state.vertex_input;
|
||||
const auto& ds = fixed_state.depth_stencil;
|
||||
const auto& cd = fixed_state.color_blending;
|
||||
const auto& rs = fixed_state.rasterizer;
|
||||
const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles;
|
||||
const auto& state = fixed_state;
|
||||
const auto& viewport_swizzles = state.viewport_swizzles;
|
||||
|
||||
FixedPipelineState::DynamicState dynamic;
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
// Insert dummy values, as long as they are valid they don't matter as extended dynamic
|
||||
// state is ignored
|
||||
dynamic.raw1 = 0;
|
||||
dynamic.raw2 = 0;
|
||||
for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) {
|
||||
// Enable all vertex bindings
|
||||
binding.raw = 0;
|
||||
binding.enabled.Assign(1);
|
||||
}
|
||||
} else {
|
||||
dynamic = state.dynamic_state;
|
||||
}
|
||||
|
||||
std::vector<VkVertexInputBindingDescription> vertex_bindings;
|
||||
std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
|
||||
for (std::size_t index = 0; index < std::size(vi.bindings); ++index) {
|
||||
const auto& binding = vi.bindings[index];
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
const auto& binding = dynamic.vertex_bindings[index];
|
||||
if (!binding.enabled) {
|
||||
continue;
|
||||
}
|
||||
const bool instanced = vi.binding_divisors[index] != 0;
|
||||
const bool instanced = state.binding_divisors[index] != 0;
|
||||
const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
auto& vertex_binding = vertex_bindings.emplace_back();
|
||||
@@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
if (instanced) {
|
||||
auto& binding_divisor = vertex_binding_divisors.emplace_back();
|
||||
binding_divisor.binding = static_cast<u32>(index);
|
||||
binding_divisor.divisor = vi.binding_divisors[index];
|
||||
binding_divisor.divisor = state.binding_divisors[index];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VkVertexInputAttributeDescription> vertex_attributes;
|
||||
const auto& input_attributes = program[0]->entries.attributes;
|
||||
for (std::size_t index = 0; index < std::size(vi.attributes); ++index) {
|
||||
const auto& attribute = vi.attributes[index];
|
||||
for (std::size_t index = 0; index < state.attributes.size(); ++index) {
|
||||
const auto& attribute = state.attributes[index];
|
||||
if (!attribute.enabled) {
|
||||
continue;
|
||||
}
|
||||
@@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
input_assembly_ci.pNext = nullptr;
|
||||
input_assembly_ci.flags = 0;
|
||||
input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology());
|
||||
input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
|
||||
input_assembly_ci.primitiveRestartEnable =
|
||||
rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
|
||||
state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
|
||||
|
||||
VkPipelineTessellationStateCreateInfo tessellation_ci;
|
||||
tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
||||
tessellation_ci.pNext = nullptr;
|
||||
tessellation_ci.flags = 0;
|
||||
tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1;
|
||||
tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_ci;
|
||||
viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
@@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterization_ci.pNext = nullptr;
|
||||
rasterization_ci.flags = 0;
|
||||
rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
|
||||
rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
|
||||
rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
|
||||
rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
|
||||
rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rasterization_ci.cullMode =
|
||||
rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE;
|
||||
rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace());
|
||||
rasterization_ci.depthBiasEnable = rs.depth_bias_enable;
|
||||
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE;
|
||||
rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace());
|
||||
rasterization_ci.depthBiasEnable = state.depth_bias_enable;
|
||||
rasterization_ci.depthBiasConstantFactor = 0.0f;
|
||||
rasterization_ci.depthBiasClamp = 0.0f;
|
||||
rasterization_ci.depthBiasSlopeFactor = 0.0f;
|
||||
@@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
depth_stencil_ci.pNext = nullptr;
|
||||
depth_stencil_ci.flags = 0;
|
||||
depth_stencil_ci.depthTestEnable = ds.depth_test_enable;
|
||||
depth_stencil_ci.depthWriteEnable = ds.depth_write_enable;
|
||||
depth_stencil_ci.depthCompareOp =
|
||||
ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS;
|
||||
depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable;
|
||||
depth_stencil_ci.stencilTestEnable = ds.stencil_enable;
|
||||
depth_stencil_ci.front = GetStencilFaceState(ds.front);
|
||||
depth_stencil_ci.back = GetStencilFaceState(ds.back);
|
||||
depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable;
|
||||
depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable;
|
||||
depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable
|
||||
? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc())
|
||||
: VK_COMPARE_OP_ALWAYS;
|
||||
depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable;
|
||||
depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable;
|
||||
depth_stencil_ci.front = GetStencilFaceState(dynamic.front);
|
||||
depth_stencil_ci.back = GetStencilFaceState(dynamic.back);
|
||||
depth_stencil_ci.minDepthBounds = 0.0f;
|
||||
depth_stencil_ci.maxDepthBounds = 0.0f;
|
||||
|
||||
@@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
static constexpr std::array COMPONENT_TABLE = {
|
||||
VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
|
||||
VK_COLOR_COMPONENT_A_BIT};
|
||||
const auto& blend = cd.attachments[index];
|
||||
const auto& blend = state.attachments[index];
|
||||
|
||||
VkColorComponentFlags color_components = 0;
|
||||
for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
|
||||
@@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
color_blend_ci.pAttachments = cb_attachments.data();
|
||||
std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
|
||||
|
||||
static constexpr std::array dynamic_states = {
|
||||
std::vector dynamic_states = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
|
||||
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE};
|
||||
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
|
||||
};
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
static constexpr std::array extended = {
|
||||
VK_DYNAMIC_STATE_CULL_MODE_EXT,
|
||||
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
|
||||
VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
|
||||
VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
|
||||
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
|
||||
VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
|
||||
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
|
||||
};
|
||||
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
|
||||
}
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state_ci;
|
||||
dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
|
||||
@@ -116,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries,
|
||||
} // Anonymous namespace
|
||||
|
||||
std::size_t GraphicsPipelineCacheKey::Hash() const noexcept {
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
|
||||
return static_cast<std::size_t>(hash);
|
||||
}
|
||||
|
||||
bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
|
||||
return std::memcmp(&rhs, this, sizeof *this) == 0;
|
||||
return std::memcmp(&rhs, this, Size()) == 0;
|
||||
}
|
||||
|
||||
std::size_t ComputePipelineCacheKey::Hash() const noexcept {
|
||||
@@ -312,18 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
|
||||
Specialization specialization;
|
||||
if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) {
|
||||
if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
|
||||
device.IsExtExtendedDynamicStateSupported()) {
|
||||
float point_size;
|
||||
std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float));
|
||||
std::memcpy(&point_size, &fixed_state.point_size, sizeof(float));
|
||||
specialization.point_size = point_size;
|
||||
ASSERT(point_size != 0.0f);
|
||||
}
|
||||
for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
|
||||
const auto& attribute = fixed_state.vertex_input.attributes[i];
|
||||
const auto& attribute = fixed_state.attributes[i];
|
||||
specialization.enabled_attributes[i] = attribute.enabled.Value() != 0;
|
||||
specialization.attribute_types[i] = attribute.Type();
|
||||
}
|
||||
specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one;
|
||||
specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
|
||||
|
||||
SPIRVProgram program;
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
|
||||
@@ -44,10 +44,10 @@ class VKUpdateDescriptorQueue;
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
struct GraphicsPipelineCacheKey {
|
||||
FixedPipelineState fixed_state;
|
||||
RenderPassParams renderpass_params;
|
||||
u32 padding;
|
||||
std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
|
||||
u64 padding; // This is necessary for unique object representations
|
||||
FixedPipelineState fixed_state;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
@@ -56,6 +56,10 @@ struct GraphicsPipelineCacheKey {
|
||||
bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::size_t Size() const noexcept {
|
||||
return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
|
||||
static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
|
||||
|
||||
@@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) {
|
||||
scissor.max_y < regs.zeta_height;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) {
|
||||
std::array<VkDeviceSize, N> expanded;
|
||||
std::copy(strides.begin(), strides.end(), expanded.begin());
|
||||
return expanded;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
class BufferBindings final {
|
||||
public:
|
||||
void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) {
|
||||
void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) {
|
||||
vertex.buffers[vertex.num_buffers] = buffer;
|
||||
vertex.offsets[vertex.num_buffers] = offset;
|
||||
vertex.sizes[vertex.num_buffers] = size;
|
||||
vertex.strides[vertex.num_buffers] = static_cast<u16>(stride);
|
||||
++vertex.num_buffers;
|
||||
}
|
||||
|
||||
@@ -202,76 +211,76 @@ public:
|
||||
index.type = type;
|
||||
}
|
||||
|
||||
void Bind(VKScheduler& scheduler) const {
|
||||
void Bind(const VKDevice& device, VKScheduler& scheduler) const {
|
||||
// Use this large switch case to avoid dispatching more memory in the record lambda than
|
||||
// what we need. It looks horrible, but it's the best we can do on standard C++.
|
||||
switch (vertex.num_buffers) {
|
||||
case 0:
|
||||
return BindStatic<0>(scheduler);
|
||||
return BindStatic<0>(device, scheduler);
|
||||
case 1:
|
||||
return BindStatic<1>(scheduler);
|
||||
return BindStatic<1>(device, scheduler);
|
||||
case 2:
|
||||
return BindStatic<2>(scheduler);
|
||||
return BindStatic<2>(device, scheduler);
|
||||
case 3:
|
||||
return BindStatic<3>(scheduler);
|
||||
return BindStatic<3>(device, scheduler);
|
||||
case 4:
|
||||
return BindStatic<4>(scheduler);
|
||||
return BindStatic<4>(device, scheduler);
|
||||
case 5:
|
||||
return BindStatic<5>(scheduler);
|
||||
return BindStatic<5>(device, scheduler);
|
||||
case 6:
|
||||
return BindStatic<6>(scheduler);
|
||||
return BindStatic<6>(device, scheduler);
|
||||
case 7:
|
||||
return BindStatic<7>(scheduler);
|
||||
return BindStatic<7>(device, scheduler);
|
||||
case 8:
|
||||
return BindStatic<8>(scheduler);
|
||||
return BindStatic<8>(device, scheduler);
|
||||
case 9:
|
||||
return BindStatic<9>(scheduler);
|
||||
return BindStatic<9>(device, scheduler);
|
||||
case 10:
|
||||
return BindStatic<10>(scheduler);
|
||||
return BindStatic<10>(device, scheduler);
|
||||
case 11:
|
||||
return BindStatic<11>(scheduler);
|
||||
return BindStatic<11>(device, scheduler);
|
||||
case 12:
|
||||
return BindStatic<12>(scheduler);
|
||||
return BindStatic<12>(device, scheduler);
|
||||
case 13:
|
||||
return BindStatic<13>(scheduler);
|
||||
return BindStatic<13>(device, scheduler);
|
||||
case 14:
|
||||
return BindStatic<14>(scheduler);
|
||||
return BindStatic<14>(device, scheduler);
|
||||
case 15:
|
||||
return BindStatic<15>(scheduler);
|
||||
return BindStatic<15>(device, scheduler);
|
||||
case 16:
|
||||
return BindStatic<16>(scheduler);
|
||||
return BindStatic<16>(device, scheduler);
|
||||
case 17:
|
||||
return BindStatic<17>(scheduler);
|
||||
return BindStatic<17>(device, scheduler);
|
||||
case 18:
|
||||
return BindStatic<18>(scheduler);
|
||||
return BindStatic<18>(device, scheduler);
|
||||
case 19:
|
||||
return BindStatic<19>(scheduler);
|
||||
return BindStatic<19>(device, scheduler);
|
||||
case 20:
|
||||
return BindStatic<20>(scheduler);
|
||||
return BindStatic<20>(device, scheduler);
|
||||
case 21:
|
||||
return BindStatic<21>(scheduler);
|
||||
return BindStatic<21>(device, scheduler);
|
||||
case 22:
|
||||
return BindStatic<22>(scheduler);
|
||||
return BindStatic<22>(device, scheduler);
|
||||
case 23:
|
||||
return BindStatic<23>(scheduler);
|
||||
return BindStatic<23>(device, scheduler);
|
||||
case 24:
|
||||
return BindStatic<24>(scheduler);
|
||||
return BindStatic<24>(device, scheduler);
|
||||
case 25:
|
||||
return BindStatic<25>(scheduler);
|
||||
return BindStatic<25>(device, scheduler);
|
||||
case 26:
|
||||
return BindStatic<26>(scheduler);
|
||||
return BindStatic<26>(device, scheduler);
|
||||
case 27:
|
||||
return BindStatic<27>(scheduler);
|
||||
return BindStatic<27>(device, scheduler);
|
||||
case 28:
|
||||
return BindStatic<28>(scheduler);
|
||||
return BindStatic<28>(device, scheduler);
|
||||
case 29:
|
||||
return BindStatic<29>(scheduler);
|
||||
return BindStatic<29>(device, scheduler);
|
||||
case 30:
|
||||
return BindStatic<30>(scheduler);
|
||||
return BindStatic<30>(device, scheduler);
|
||||
case 31:
|
||||
return BindStatic<31>(scheduler);
|
||||
return BindStatic<31>(device, scheduler);
|
||||
case 32:
|
||||
return BindStatic<32>(scheduler);
|
||||
return BindStatic<32>(device, scheduler);
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -282,6 +291,8 @@ private:
|
||||
std::size_t num_buffers = 0;
|
||||
std::array<VkBuffer, Maxwell::NumVertexArrays> buffers;
|
||||
std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets;
|
||||
std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes;
|
||||
std::array<u16, Maxwell::NumVertexArrays> strides;
|
||||
} vertex;
|
||||
|
||||
struct {
|
||||
@@ -291,15 +302,23 @@ private:
|
||||
} index;
|
||||
|
||||
template <std::size_t N>
|
||||
void BindStatic(VKScheduler& scheduler) const {
|
||||
if (index.buffer) {
|
||||
BindStatic<N, true>(scheduler);
|
||||
void BindStatic(const VKDevice& device, VKScheduler& scheduler) const {
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
if (index.buffer) {
|
||||
BindStatic<N, true, true>(scheduler);
|
||||
} else {
|
||||
BindStatic<N, false, true>(scheduler);
|
||||
}
|
||||
} else {
|
||||
BindStatic<N, false>(scheduler);
|
||||
if (index.buffer) {
|
||||
BindStatic<N, true, false>(scheduler);
|
||||
} else {
|
||||
BindStatic<N, false, false>(scheduler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t N, bool is_indexed>
|
||||
template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state>
|
||||
void BindStatic(VKScheduler& scheduler) const {
|
||||
static_assert(N <= Maxwell::NumVertexArrays);
|
||||
if constexpr (N == 0) {
|
||||
@@ -311,6 +330,31 @@ private:
|
||||
std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin());
|
||||
std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin());
|
||||
|
||||
if constexpr (has_extended_dynamic_state) {
|
||||
// With extended dynamic states we can specify the length and stride of a vertex buffer
|
||||
std::array<VkDeviceSize, N> sizes;
|
||||
std::array<u16, N> strides;
|
||||
std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
|
||||
std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin());
|
||||
|
||||
if constexpr (is_indexed) {
|
||||
scheduler.Record(
|
||||
[buffers, offsets, sizes, strides, index = index](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type);
|
||||
cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
|
||||
offsets.data(), sizes.data(),
|
||||
ExpandStrides(strides).data());
|
||||
});
|
||||
} else {
|
||||
scheduler.Record([buffers, offsets, sizes, strides](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
|
||||
offsets.data(), sizes.data(),
|
||||
ExpandStrides(strides).data());
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (is_indexed) {
|
||||
// Indexed draw
|
||||
scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
|
||||
@@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
GraphicsPipelineCacheKey key;
|
||||
key.fixed_state.Fill(gpu.regs);
|
||||
key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
|
||||
|
||||
buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
|
||||
|
||||
@@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
|
||||
UpdateDynamicStates();
|
||||
|
||||
buffer_bindings.Bind(scheduler);
|
||||
buffer_bindings.Bind(device, scheduler);
|
||||
|
||||
BeginTransformFeedback();
|
||||
|
||||
@@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
SetupVertexArrays(fixed_state.vertex_input, buffer_bindings);
|
||||
SetupVertexArrays(buffer_bindings);
|
||||
|
||||
const u32 base_instance = regs.vb_base_instance;
|
||||
const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
|
||||
@@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() {
|
||||
UpdateBlendConstants(regs);
|
||||
UpdateDepthBounds(regs);
|
||||
UpdateStencilFaces(regs);
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
UpdateCullMode(regs);
|
||||
UpdateDepthBoundsTestEnable(regs);
|
||||
UpdateDepthTestEnable(regs);
|
||||
UpdateDepthWriteEnable(regs);
|
||||
UpdateDepthCompareOp(regs);
|
||||
UpdateFrontFace(regs);
|
||||
UpdatePrimitiveTopology(regs);
|
||||
UpdateStencilOp(regs);
|
||||
UpdateStencilTestEnable(regs);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerVulkan::BeginTransformFeedback() {
|
||||
@@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() {
|
||||
[](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
|
||||
BufferBindings& buffer_bindings) {
|
||||
void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||
const auto& attrib = regs.vertex_attrib_format[index];
|
||||
if (attrib.IsConstant()) {
|
||||
vertex_input.SetAttribute(index, false, 0, 0, {}, {});
|
||||
continue;
|
||||
}
|
||||
vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
|
||||
attrib.size.Value());
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
const auto& vertex_array = regs.vertex_array[index];
|
||||
if (!vertex_array.IsEnabled()) {
|
||||
vertex_input.SetBinding(index, false, 0, 0);
|
||||
continue;
|
||||
}
|
||||
vertex_input.SetBinding(
|
||||
index, true, vertex_array.stride,
|
||||
regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
|
||||
|
||||
const GPUVAddr start{vertex_array.StartAddress()};
|
||||
const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
|
||||
|
||||
ASSERT(end >= start);
|
||||
const std::size_t size{end - start};
|
||||
const std::size_t size = end - start;
|
||||
if (size == 0) {
|
||||
buffer_bindings.AddVertexBinding(DefaultBuffer(), 0);
|
||||
buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0);
|
||||
continue;
|
||||
}
|
||||
const auto info = buffer_cache.UploadMemory(start, size);
|
||||
buffer_bindings.AddVertexBinding(info.handle, info.offset);
|
||||
buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchCullMode()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record(
|
||||
[enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBoundsTestEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthBoundsTestEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthTestEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthTestEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthWriteEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthWriteEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthCompareOp()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func));
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchFrontFace()) {
|
||||
return;
|
||||
}
|
||||
|
||||
VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
|
||||
: VK_FRONT_FACE_CLOCKWISE;
|
||||
}
|
||||
scheduler.Record(
|
||||
[front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchPrimitiveTopology()) {
|
||||
return;
|
||||
}
|
||||
const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
|
||||
scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchStencilOp()) {
|
||||
return;
|
||||
}
|
||||
const Maxwell::StencilOp fail = regs.stencil_front_op_fail;
|
||||
const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail;
|
||||
const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
|
||||
const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
|
||||
if (regs.stencil_two_side_enable) {
|
||||
scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
|
||||
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
|
||||
MaxwellToVK::ComparisonOp(compare));
|
||||
});
|
||||
} else {
|
||||
const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
|
||||
const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
|
||||
const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
|
||||
const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func;
|
||||
scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
|
||||
back_compare](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
|
||||
MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
|
||||
MaxwellToVK::ComparisonOp(compare));
|
||||
cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
|
||||
MaxwellToVK::StencilOp(back_zpass),
|
||||
MaxwellToVK::StencilOp(back_zfail),
|
||||
MaxwellToVK::ComparisonOp(back_compare));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchStencilTestEnable()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilTestEnableEXT(enable);
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const {
|
||||
std::size_t size = CalculateVertexArraysSize();
|
||||
if (is_indexed) {
|
||||
|
||||
@@ -185,8 +185,7 @@ private:
|
||||
|
||||
bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
|
||||
|
||||
void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
|
||||
BufferBindings& buffer_bindings);
|
||||
void SetupVertexArrays(BufferBindings& buffer_bindings);
|
||||
|
||||
void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed);
|
||||
|
||||
@@ -246,6 +245,16 @@ private:
|
||||
void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
|
||||
|
||||
std::size_t CalculateComputeStreamBufferSize() const;
|
||||
|
||||
@@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() {
|
||||
flags[BlendConstants] = true;
|
||||
flags[DepthBounds] = true;
|
||||
flags[StencilProperties] = true;
|
||||
flags[CullMode] = true;
|
||||
flags[DepthBoundsEnable] = true;
|
||||
flags[DepthTestEnable] = true;
|
||||
flags[DepthWriteEnable] = true;
|
||||
flags[DepthCompareOp] = true;
|
||||
flags[FrontFace] = true;
|
||||
flags[PrimitiveTopology] = true;
|
||||
flags[StencilOp] = true;
|
||||
flags[StencilTestEnable] = true;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) {
|
||||
table[OFF(stencil_back_func_mask)] = StencilProperties;
|
||||
}
|
||||
|
||||
void SetupDirtyCullMode(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(cull_face)] = CullMode;
|
||||
table[OFF(cull_test_enabled)] = CullMode;
|
||||
}
|
||||
|
||||
void SetupDirtyDepthBoundsEnable(Tables& tables) {
|
||||
tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
|
||||
}
|
||||
|
||||
void SetupDirtyDepthTestEnable(Tables& tables) {
|
||||
tables[0][OFF(depth_test_enable)] = DepthTestEnable;
|
||||
}
|
||||
|
||||
void SetupDirtyDepthWriteEnable(Tables& tables) {
|
||||
tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
|
||||
}
|
||||
|
||||
void SetupDirtyDepthCompareOp(Tables& tables) {
|
||||
tables[0][OFF(depth_test_func)] = DepthCompareOp;
|
||||
}
|
||||
|
||||
void SetupDirtyFrontFace(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(front_face)] = FrontFace;
|
||||
table[OFF(screen_y_control)] = FrontFace;
|
||||
}
|
||||
|
||||
void SetupDirtyPrimitiveTopology(Tables& tables) {
|
||||
tables[0][OFF(draw.topology)] = PrimitiveTopology;
|
||||
}
|
||||
|
||||
void SetupDirtyStencilOp(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(stencil_front_op_fail)] = StencilOp;
|
||||
table[OFF(stencil_front_op_zfail)] = StencilOp;
|
||||
table[OFF(stencil_front_op_zpass)] = StencilOp;
|
||||
table[OFF(stencil_front_func_func)] = StencilOp;
|
||||
table[OFF(stencil_back_op_fail)] = StencilOp;
|
||||
table[OFF(stencil_back_op_zfail)] = StencilOp;
|
||||
table[OFF(stencil_back_op_zpass)] = StencilOp;
|
||||
table[OFF(stencil_back_func_func)] = StencilOp;
|
||||
|
||||
// Table 0 is used by StencilProperties
|
||||
tables[1][OFF(stencil_two_side_enable)] = StencilOp;
|
||||
}
|
||||
|
||||
void SetupDirtyStencilTestEnable(Tables& tables) {
|
||||
tables[0][OFF(stencil_enable)] = StencilTestEnable;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
StateTracker::StateTracker(Core::System& system)
|
||||
@@ -90,6 +150,14 @@ void StateTracker::Initialize() {
|
||||
SetupDirtyBlendConstants(tables);
|
||||
SetupDirtyDepthBounds(tables);
|
||||
SetupDirtyStencilProperties(tables);
|
||||
SetupDirtyCullMode(tables);
|
||||
SetupDirtyDepthBoundsEnable(tables);
|
||||
SetupDirtyDepthTestEnable(tables);
|
||||
SetupDirtyDepthWriteEnable(tables);
|
||||
SetupDirtyDepthCompareOp(tables);
|
||||
SetupDirtyFrontFace(tables);
|
||||
SetupDirtyPrimitiveTopology(tables);
|
||||
SetupDirtyStencilOp(tables);
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateCommandBufferState() {
|
||||
|
||||
@@ -26,6 +26,16 @@ enum : u8 {
|
||||
DepthBounds,
|
||||
StencilProperties,
|
||||
|
||||
CullMode,
|
||||
DepthBoundsEnable,
|
||||
DepthTestEnable,
|
||||
DepthWriteEnable,
|
||||
DepthCompareOp,
|
||||
FrontFace,
|
||||
PrimitiveTopology,
|
||||
StencilOp,
|
||||
StencilTestEnable,
|
||||
|
||||
Last
|
||||
};
|
||||
static_assert(Last <= std::numeric_limits<u8>::max());
|
||||
@@ -64,6 +74,46 @@ public:
|
||||
return Exchange(Dirty::StencilProperties, false);
|
||||
}
|
||||
|
||||
bool TouchCullMode() {
|
||||
return Exchange(Dirty::CullMode, false);
|
||||
}
|
||||
|
||||
bool TouchDepthBoundsTestEnable() {
|
||||
return Exchange(Dirty::DepthBoundsEnable, false);
|
||||
}
|
||||
|
||||
bool TouchDepthTestEnable() {
|
||||
return Exchange(Dirty::DepthTestEnable, false);
|
||||
}
|
||||
|
||||
bool TouchDepthBoundsEnable() {
|
||||
return Exchange(Dirty::DepthBoundsEnable, false);
|
||||
}
|
||||
|
||||
bool TouchDepthWriteEnable() {
|
||||
return Exchange(Dirty::DepthWriteEnable, false);
|
||||
}
|
||||
|
||||
bool TouchDepthCompareOp() {
|
||||
return Exchange(Dirty::DepthCompareOp, false);
|
||||
}
|
||||
|
||||
bool TouchFrontFace() {
|
||||
return Exchange(Dirty::FrontFace, false);
|
||||
}
|
||||
|
||||
bool TouchPrimitiveTopology() {
|
||||
return Exchange(Dirty::PrimitiveTopology, false);
|
||||
}
|
||||
|
||||
bool TouchStencilOp() {
|
||||
return Exchange(Dirty::StencilOp, false);
|
||||
}
|
||||
|
||||
bool TouchStencilTestEnable() {
|
||||
return Exchange(Dirty::StencilTestEnable, false);
|
||||
}
|
||||
|
||||
private:
|
||||
bool Exchange(std::size_t id, bool new_value) const noexcept {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
|
||||
@@ -121,7 +121,7 @@ void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
|
||||
|
||||
// Substract from the preferred heap size some bytes to avoid getting out of memory.
|
||||
const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
|
||||
const VkDeviceSize allocable_size = heap_size - 4 * 1024 * 1024;
|
||||
const VkDeviceSize allocable_size = heap_size - 9 * 1024 * 1024;
|
||||
|
||||
VkBufferCreateInfo buffer_ci;
|
||||
buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
|
||||
@@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkCmdSetStencilWriteMask);
|
||||
X(vkCmdSetViewport);
|
||||
X(vkCmdWaitEvents);
|
||||
X(vkCmdBindVertexBuffers2EXT);
|
||||
X(vkCmdSetCullModeEXT);
|
||||
X(vkCmdSetDepthBoundsTestEnableEXT);
|
||||
X(vkCmdSetDepthCompareOpEXT);
|
||||
X(vkCmdSetDepthTestEnableEXT);
|
||||
X(vkCmdSetDepthWriteEnableEXT);
|
||||
X(vkCmdSetFrontFaceEXT);
|
||||
X(vkCmdSetPrimitiveTopologyEXT);
|
||||
X(vkCmdSetStencilOpEXT);
|
||||
X(vkCmdSetStencilTestEnableEXT);
|
||||
X(vkCreateBuffer);
|
||||
X(vkCreateBufferView);
|
||||
X(vkCreateCommandPool);
|
||||
|
||||
@@ -207,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch {
|
||||
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
|
||||
PFN_vkCmdSetViewport vkCmdSetViewport;
|
||||
PFN_vkCmdWaitEvents vkCmdWaitEvents;
|
||||
PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT;
|
||||
PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT;
|
||||
PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT;
|
||||
PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT;
|
||||
PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT;
|
||||
PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT;
|
||||
PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT;
|
||||
PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT;
|
||||
PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT;
|
||||
PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
|
||||
PFN_vkCreateBuffer vkCreateBuffer;
|
||||
PFN_vkCreateBufferView vkCreateBufferView;
|
||||
PFN_vkCreateCommandPool vkCreateCommandPool;
|
||||
@@ -969,6 +979,50 @@ public:
|
||||
buffer_barriers.data(), image_barriers.size(), image_barriers.data());
|
||||
}
|
||||
|
||||
void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers,
|
||||
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
|
||||
const VkDeviceSize* strides) const noexcept {
|
||||
dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets,
|
||||
sizes, strides);
|
||||
}
|
||||
|
||||
void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept {
|
||||
dld->vkCmdSetCullModeEXT(handle, cull_mode);
|
||||
}
|
||||
|
||||
void SetDepthBoundsTestEnableEXT(bool enable) const noexcept {
|
||||
dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
|
||||
}
|
||||
|
||||
void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept {
|
||||
dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
|
||||
}
|
||||
|
||||
void SetDepthTestEnableEXT(bool enable) const noexcept {
|
||||
dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
|
||||
}
|
||||
|
||||
void SetDepthWriteEnableEXT(bool enable) const noexcept {
|
||||
dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
|
||||
}
|
||||
|
||||
void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
|
||||
dld->vkCmdSetFrontFaceEXT(handle, front_face);
|
||||
}
|
||||
|
||||
void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept {
|
||||
dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
|
||||
}
|
||||
|
||||
void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op,
|
||||
VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept {
|
||||
dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
|
||||
}
|
||||
|
||||
void SetStencilTestEnableEXT(bool enable) const noexcept {
|
||||
dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
|
||||
}
|
||||
|
||||
void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
|
||||
const VkDeviceSize* offsets,
|
||||
const VkDeviceSize* sizes) const noexcept {
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace VideoCommon {
|
||||
template <class T>
|
||||
class ShaderCache {
|
||||
static constexpr u64 PAGE_BITS = 14;
|
||||
static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS;
|
||||
|
||||
struct Entry {
|
||||
VAddr addr_start;
|
||||
@@ -87,8 +88,8 @@ protected:
|
||||
const VAddr addr_end = addr + size;
|
||||
Entry* const entry = NewEntry(addr, addr_end, data.get());
|
||||
|
||||
const u64 page_end = addr_end >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
|
||||
const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
|
||||
invalidation_cache[page].push_back(entry);
|
||||
}
|
||||
|
||||
@@ -108,20 +109,13 @@ private:
|
||||
/// @pre invalidation_mutex is locked
|
||||
void InvalidatePagesInRegion(VAddr addr, std::size_t size) {
|
||||
const VAddr addr_end = addr + size;
|
||||
const u64 page_end = addr_end >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page <= page_end; ++page) {
|
||||
const auto it = invalidation_cache.find(page);
|
||||
const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
|
||||
for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
|
||||
auto it = invalidation_cache.find(page);
|
||||
if (it == invalidation_cache.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<Entry*>& entries = it->second;
|
||||
InvalidatePageEntries(entries, addr, addr_end);
|
||||
|
||||
// If there's nothing else in this page, remove it to avoid overpopulating the hash map.
|
||||
if (entries.empty()) {
|
||||
invalidation_cache.erase(it);
|
||||
}
|
||||
InvalidatePageEntries(it->second, addr, addr_end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +125,22 @@ private:
|
||||
if (marked_for_removal.empty()) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{lookup_mutex};
|
||||
// Remove duplicates
|
||||
std::sort(marked_for_removal.begin(), marked_for_removal.end());
|
||||
marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
|
||||
marked_for_removal.end());
|
||||
|
||||
std::vector<T*> removed_shaders;
|
||||
removed_shaders.reserve(marked_for_removal.size());
|
||||
|
||||
std::scoped_lock lock{lookup_mutex};
|
||||
|
||||
for (Entry* const entry : marked_for_removal) {
|
||||
if (lookup_cache.erase(entry->addr_start) > 0) {
|
||||
removed_shaders.push_back(entry->data);
|
||||
}
|
||||
removed_shaders.push_back(entry->data);
|
||||
|
||||
const auto it = lookup_cache.find(entry->addr_start);
|
||||
ASSERT(it != lookup_cache.end());
|
||||
lookup_cache.erase(it);
|
||||
}
|
||||
marked_for_removal.clear();
|
||||
|
||||
@@ -154,17 +155,33 @@ private:
|
||||
/// @param addr_end Non-inclusive end address of the invalidation
|
||||
/// @pre invalidation_mutex is locked
|
||||
void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) {
|
||||
auto it = entries.begin();
|
||||
while (it != entries.end()) {
|
||||
Entry* const entry = *it;
|
||||
std::size_t index = 0;
|
||||
while (index < entries.size()) {
|
||||
Entry* const entry = entries[index];
|
||||
if (!entry->Overlaps(addr, addr_end)) {
|
||||
++it;
|
||||
++index;
|
||||
continue;
|
||||
}
|
||||
UnmarkMemory(entry);
|
||||
marked_for_removal.push_back(entry);
|
||||
|
||||
it = entries.erase(it);
|
||||
UnmarkMemory(entry);
|
||||
RemoveEntryFromInvalidationCache(entry);
|
||||
marked_for_removal.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Removes all references to an entry in the invalidation cache
|
||||
/// @param entry Entry to remove from the invalidation cache
|
||||
/// @pre invalidation_mutex is locked
|
||||
void RemoveEntryFromInvalidationCache(const Entry* entry) {
|
||||
const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
|
||||
for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) {
|
||||
const auto entries_it = invalidation_cache.find(page);
|
||||
ASSERT(entries_it != invalidation_cache.end());
|
||||
std::vector<Entry*>& entries = entries_it->second;
|
||||
|
||||
const auto entry_it = std::find(entries.begin(), entries.end(), entry);
|
||||
ASSERT(entry_it != entries.end());
|
||||
entries.erase(entry_it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,16 +199,11 @@ private:
|
||||
}
|
||||
|
||||
/// @brief Removes a vector of shaders from a list
|
||||
/// @param removed_shaders Shaders to be removed from the storage, it can contain duplicates
|
||||
/// @param removed_shaders Shaders to be removed from the storage
|
||||
/// @pre invalidation_mutex is locked
|
||||
/// @pre lookup_mutex is locked
|
||||
void RemoveShadersFromStorage(std::vector<T*> removed_shaders) {
|
||||
// Remove duplicates
|
||||
std::sort(removed_shaders.begin(), removed_shaders.end());
|
||||
removed_shaders.erase(std::unique(removed_shaders.begin(), removed_shaders.end()),
|
||||
removed_shaders.end());
|
||||
|
||||
// Now that there are no duplicates, we can notify removals
|
||||
// Notify removals
|
||||
for (T* const shader : removed_shaders) {
|
||||
OnShaderRemoval(shader);
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ public:
|
||||
auto& surface = render_targets[index].target;
|
||||
surface->MarkAsRenderTarget(false, NO_RT);
|
||||
const auto& cr_params = surface->GetSurfaceParams();
|
||||
if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
AsyncFlushSurface(surface);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
|
||||
};
|
||||
|
||||
unsigned SettingsMinimumAnisotropy() noexcept {
|
||||
switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) {
|
||||
switch (static_cast<Anisotropy>(Settings::values.max_anisotropy.GetValue())) {
|
||||
default:
|
||||
case Anisotropy::Default:
|
||||
return 1U;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace {
|
||||
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||
Core::System& system,
|
||||
Core::Frontend::GraphicsContext& context) {
|
||||
switch (Settings::values.renderer_backend) {
|
||||
switch (Settings::values.renderer_backend.GetValue()) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
|
||||
#ifdef HAS_VULKAN
|
||||
@@ -42,7 +42,7 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
|
||||
std::move(context));
|
||||
}
|
||||
@@ -51,8 +51,8 @@ std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Cor
|
||||
|
||||
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||
return static_cast<u16>(
|
||||
Settings::values.resolution_factor != 0
|
||||
? Settings::values.resolution_factor
|
||||
Settings::values.resolution_factor.GetValue() != 0
|
||||
? Settings::values.resolution_factor.GetValue()
|
||||
: renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio());
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ add_executable(yuzu
|
||||
compatibility_list.h
|
||||
configuration/config.cpp
|
||||
configuration/config.h
|
||||
configuration/configuration_shared.cpp
|
||||
configuration/configuration_shared.h
|
||||
configuration/configure.ui
|
||||
configuration/configure_audio.cpp
|
||||
configuration/configure_audio.h
|
||||
@@ -60,9 +62,12 @@ add_executable(yuzu
|
||||
configuration/configure_mouse_advanced.cpp
|
||||
configuration/configure_mouse_advanced.h
|
||||
configuration/configure_mouse_advanced.ui
|
||||
configuration/configure_per_general.cpp
|
||||
configuration/configure_per_general.h
|
||||
configuration/configure_per_general.ui
|
||||
configuration/configure_per_game.cpp
|
||||
configuration/configure_per_game.h
|
||||
configuration/configure_per_game.ui
|
||||
configuration/configure_per_game_addons.cpp
|
||||
configuration/configure_per_game_addons.h
|
||||
configuration/configure_per_game_addons.ui
|
||||
configuration/configure_profile_manager.cpp
|
||||
configuration/configure_profile_manager.h
|
||||
configuration/configure_profile_manager.ui
|
||||
@@ -93,11 +98,13 @@ add_executable(yuzu
|
||||
game_list_p.h
|
||||
game_list_worker.cpp
|
||||
game_list_worker.h
|
||||
hotkeys.cpp
|
||||
hotkeys.h
|
||||
install_dialog.cpp
|
||||
install_dialog.h
|
||||
loading_screen.cpp
|
||||
loading_screen.h
|
||||
loading_screen.ui
|
||||
hotkeys.cpp
|
||||
hotkeys.h
|
||||
main.cpp
|
||||
main.h
|
||||
main.ui
|
||||
@@ -147,7 +154,7 @@ endif()
|
||||
create_target_directory_groups(yuzu)
|
||||
|
||||
target_link_libraries(yuzu PRIVATE common core input_common video_core)
|
||||
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
|
||||
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets)
|
||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||
|
||||
if (ENABLE_VULKAN AND NOT WIN32)
|
||||
|
||||
@@ -145,7 +145,7 @@ public:
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = share_context->format();
|
||||
format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
|
||||
format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0);
|
||||
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setShareContext(share_context);
|
||||
@@ -495,7 +495,7 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
#ifdef HAS_OPENGL
|
||||
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
|
||||
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
|
||||
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
|
||||
// Bind the shared contexts to the main surface in case the backend wants to take over
|
||||
// presentation
|
||||
@@ -511,7 +511,7 @@ bool GRenderWindow::InitRenderTarget() {
|
||||
|
||||
first_frame = false;
|
||||
|
||||
switch (Settings::values.renderer_backend) {
|
||||
switch (Settings::values.renderer_backend.GetValue()) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
if (!InitializeOpenGL()) {
|
||||
return false;
|
||||
@@ -538,7 +538,7 @@ bool GRenderWindow::InitRenderTarget() {
|
||||
OnFramebufferSizeChanged();
|
||||
BackupGeometry();
|
||||
|
||||
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
|
||||
if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
|
||||
if (!LoadOpenGL()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -13,17 +13,20 @@
|
||||
#include "input_common/udp/client.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
|
||||
Config::Config() {
|
||||
Config::Config(const std::string& config_file, bool is_global) {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
|
||||
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;
|
||||
FileUtil::CreateFullPath(qt_config_loc);
|
||||
qt_config =
|
||||
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
global = is_global;
|
||||
Reload();
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
Save();
|
||||
if (global) {
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
|
||||
@@ -212,7 +215,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
|
||||
@@ -220,8 +223,8 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
|
||||
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||
@@ -402,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() {
|
||||
void Config::ReadAudioValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||
|
||||
Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.enable_audio_stretching =
|
||||
ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool();
|
||||
Settings::values.audio_device_id =
|
||||
ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat();
|
||||
if (global) {
|
||||
Settings::values.sink_id =
|
||||
ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.audio_device_id =
|
||||
ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
|
||||
.toString()
|
||||
.toStdString();
|
||||
}
|
||||
ReadSettingGlobal(Settings::values.enable_audio_stretching,
|
||||
QStringLiteral("enable_audio_stretching"), true);
|
||||
ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -440,6 +446,8 @@ void Config::ReadControlValues() {
|
||||
.toInt());
|
||||
Settings::values.udp_pad_index =
|
||||
static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
|
||||
Settings::values.use_docked_mode =
|
||||
ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -447,7 +455,7 @@ void Config::ReadControlValues() {
|
||||
void Config::ReadCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool();
|
||||
ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -628,32 +636,28 @@ void Config::ReadPathValues() {
|
||||
void Config::ReadRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
Settings::values.renderer_backend =
|
||||
static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt());
|
||||
Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool();
|
||||
Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
|
||||
Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt();
|
||||
Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt();
|
||||
Settings::values.use_frame_limit =
|
||||
ReadSetting(QStringLiteral("use_frame_limit"), true).toBool();
|
||||
Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toUInt();
|
||||
Settings::values.use_disk_shader_cache =
|
||||
ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
|
||||
const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt();
|
||||
Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
|
||||
Settings::values.use_asynchronous_gpu_emulation =
|
||||
ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
|
||||
Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
|
||||
Settings::values.use_assembly_shaders =
|
||||
ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool();
|
||||
Settings::values.use_fast_gpu_time =
|
||||
ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool();
|
||||
Settings::values.force_30fps_mode =
|
||||
ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
|
||||
ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
|
||||
ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
|
||||
ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
|
||||
ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
|
||||
ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
|
||||
ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
|
||||
ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100);
|
||||
ReadSettingGlobal(Settings::values.use_disk_shader_cache,
|
||||
QStringLiteral("use_disk_shader_cache"), true);
|
||||
ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
|
||||
ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
|
||||
QStringLiteral("use_asynchronous_gpu_emulation"), false);
|
||||
ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
|
||||
ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
|
||||
false);
|
||||
ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
|
||||
true);
|
||||
ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
|
||||
|
||||
Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
|
||||
Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
|
||||
Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
|
||||
ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
|
||||
ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
|
||||
ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -665,11 +669,13 @@ void Config::ReadShortcutValues() {
|
||||
const auto& [keyseq, context] = shortcut;
|
||||
qt_config->beginGroup(group);
|
||||
qt_config->beginGroup(name);
|
||||
// No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
|
||||
// for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
|
||||
// a file dialog in windowed mode
|
||||
UISettings::values.shortcuts.push_back(
|
||||
{name,
|
||||
group,
|
||||
{ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(),
|
||||
ReadSetting(QStringLiteral("Context"), context).toInt()}});
|
||||
{ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
|
||||
qt_config->endGroup();
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -680,35 +686,45 @@ void Config::ReadShortcutValues() {
|
||||
void Config::ReadSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
Settings::values.use_docked_mode =
|
||||
ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
|
||||
ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0);
|
||||
Settings::values.current_user =
|
||||
std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1);
|
||||
|
||||
Settings::values.current_user = std::clamp<int>(
|
||||
ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1);
|
||||
ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1);
|
||||
|
||||
Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt();
|
||||
ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1);
|
||||
|
||||
Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt();
|
||||
ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0);
|
||||
|
||||
Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt();
|
||||
|
||||
const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool();
|
||||
if (rng_seed_enabled) {
|
||||
Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong();
|
||||
} else {
|
||||
Settings::values.rng_seed = std::nullopt;
|
||||
bool rng_seed_enabled;
|
||||
ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
|
||||
bool rng_seed_global =
|
||||
global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool();
|
||||
Settings::values.rng_seed.SetGlobal(rng_seed_global);
|
||||
if (global || !rng_seed_global) {
|
||||
if (rng_seed_enabled) {
|
||||
Settings::values.rng_seed.SetValue(
|
||||
ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong());
|
||||
} else {
|
||||
Settings::values.rng_seed.SetValue(std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
const auto custom_rtc_enabled =
|
||||
ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
|
||||
if (custom_rtc_enabled) {
|
||||
Settings::values.custom_rtc =
|
||||
std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong());
|
||||
} else {
|
||||
Settings::values.custom_rtc = std::nullopt;
|
||||
bool custom_rtc_enabled;
|
||||
ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false);
|
||||
bool custom_rtc_global =
|
||||
global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool();
|
||||
Settings::values.custom_rtc.SetGlobal(custom_rtc_global);
|
||||
if (global || !custom_rtc_global) {
|
||||
if (custom_rtc_enabled) {
|
||||
Settings::values.custom_rtc.SetValue(
|
||||
std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()));
|
||||
} else {
|
||||
Settings::values.custom_rtc.SetValue(std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt();
|
||||
ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -802,18 +818,20 @@ void Config::ReadWebServiceValues() {
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
ReadControlValues();
|
||||
if (global) {
|
||||
ReadControlValues();
|
||||
ReadDataStorageValues();
|
||||
ReadDebuggingValues();
|
||||
ReadDisabledAddOnValues();
|
||||
ReadServiceValues();
|
||||
ReadUIValues();
|
||||
ReadWebServiceValues();
|
||||
ReadMiscellaneousValues();
|
||||
}
|
||||
ReadCoreValues();
|
||||
ReadRendererValues();
|
||||
ReadAudioValues();
|
||||
ReadDataStorageValues();
|
||||
ReadSystemValues();
|
||||
ReadMiscellaneousValues();
|
||||
ReadDebuggingValues();
|
||||
ReadWebServiceValues();
|
||||
ReadServiceValues();
|
||||
ReadDisabledAddOnValues();
|
||||
ReadUIValues();
|
||||
}
|
||||
|
||||
void Config::SavePlayerValues() {
|
||||
@@ -900,30 +918,35 @@ void Config::SaveTouchscreenValues() {
|
||||
}
|
||||
|
||||
void Config::SaveValues() {
|
||||
SaveControlValues();
|
||||
if (global) {
|
||||
SaveControlValues();
|
||||
SaveDataStorageValues();
|
||||
SaveDebuggingValues();
|
||||
SaveDisabledAddOnValues();
|
||||
SaveServiceValues();
|
||||
SaveUIValues();
|
||||
SaveWebServiceValues();
|
||||
SaveMiscellaneousValues();
|
||||
}
|
||||
SaveCoreValues();
|
||||
SaveRendererValues();
|
||||
SaveAudioValues();
|
||||
SaveDataStorageValues();
|
||||
SaveSystemValues();
|
||||
SaveMiscellaneousValues();
|
||||
SaveDebuggingValues();
|
||||
SaveWebServiceValues();
|
||||
SaveServiceValues();
|
||||
SaveDisabledAddOnValues();
|
||||
SaveUIValues();
|
||||
}
|
||||
|
||||
void Config::SaveAudioValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||
|
||||
WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id),
|
||||
QStringLiteral("auto"));
|
||||
WriteSetting(QStringLiteral("enable_audio_stretching"),
|
||||
Settings::values.enable_audio_stretching, true);
|
||||
WriteSetting(QStringLiteral("output_device"),
|
||||
QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto"));
|
||||
WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f);
|
||||
if (global) {
|
||||
WriteSetting(QStringLiteral("output_engine"),
|
||||
QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto"));
|
||||
WriteSetting(QStringLiteral("output_device"),
|
||||
QString::fromStdString(Settings::values.audio_device_id),
|
||||
QStringLiteral("auto"));
|
||||
}
|
||||
WriteSettingGlobal(QStringLiteral("enable_audio_stretching"),
|
||||
Settings::values.enable_audio_stretching, true);
|
||||
WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -946,6 +969,7 @@ void Config::SaveControlValues() {
|
||||
WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
|
||||
InputCommon::CemuhookUDP::DEFAULT_PORT);
|
||||
WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
|
||||
WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -953,7 +977,7 @@ void Config::SaveControlValues() {
|
||||
void Config::SaveCoreValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Core"));
|
||||
|
||||
WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
|
||||
WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1076,29 +1100,34 @@ void Config::SavePathValues() {
|
||||
void Config::SaveRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
|
||||
WriteSettingGlobal(QStringLiteral("backend"),
|
||||
static_cast<int>(Settings::values.renderer_backend.GetValue(global)),
|
||||
Settings::values.renderer_backend.UsingGlobal(), 0);
|
||||
WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
|
||||
WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
|
||||
WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
|
||||
WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
|
||||
WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
|
||||
WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
|
||||
WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
|
||||
true);
|
||||
WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy),
|
||||
0);
|
||||
WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
|
||||
Settings::values.use_asynchronous_gpu_emulation, false);
|
||||
WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
|
||||
WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders,
|
||||
false);
|
||||
WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true);
|
||||
WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
|
||||
WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
|
||||
WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
|
||||
WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
|
||||
WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
|
||||
WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
|
||||
WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"),
|
||||
Settings::values.use_disk_shader_cache, true);
|
||||
WriteSettingGlobal(QStringLiteral("gpu_accuracy"),
|
||||
static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
|
||||
Settings::values.gpu_accuracy.UsingGlobal(), 0);
|
||||
WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
|
||||
Settings::values.use_asynchronous_gpu_emulation, false);
|
||||
WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
|
||||
WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
|
||||
Settings::values.use_assembly_shaders, false);
|
||||
WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
|
||||
true);
|
||||
WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
|
||||
false);
|
||||
|
||||
// Cast to double because Qt's written float values are not human-readable
|
||||
WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0);
|
||||
WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0);
|
||||
WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0);
|
||||
WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
|
||||
WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
|
||||
WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1126,23 +1155,28 @@ void Config::SaveShortcutValues() {
|
||||
void Config::SaveSystemValues() {
|
||||
qt_config->beginGroup(QStringLiteral("System"));
|
||||
|
||||
WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
|
||||
WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
|
||||
WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1);
|
||||
WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1);
|
||||
WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
|
||||
WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1);
|
||||
WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1);
|
||||
WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
|
||||
|
||||
WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false);
|
||||
WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0);
|
||||
WriteSettingGlobal(QStringLiteral("rng_seed_enabled"),
|
||||
Settings::values.rng_seed.GetValue(global).has_value(),
|
||||
Settings::values.rng_seed.UsingGlobal(), false);
|
||||
WriteSettingGlobal(QStringLiteral("rng_seed"),
|
||||
Settings::values.rng_seed.GetValue(global).value_or(0),
|
||||
Settings::values.rng_seed.UsingGlobal(), 0);
|
||||
|
||||
WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
|
||||
false);
|
||||
WriteSetting(QStringLiteral("custom_rtc"),
|
||||
QVariant::fromValue<long long>(
|
||||
Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
|
||||
0);
|
||||
WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"),
|
||||
Settings::values.custom_rtc.GetValue(global).has_value(),
|
||||
Settings::values.custom_rtc.UsingGlobal(), false);
|
||||
WriteSettingGlobal(
|
||||
QStringLiteral("custom_rtc"),
|
||||
QVariant::fromValue<long long>(
|
||||
Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()),
|
||||
Settings::values.custom_rtc.UsingGlobal(), 0);
|
||||
|
||||
WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
|
||||
WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@@ -1234,6 +1268,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) {
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
setting.SetGlobal(use_global);
|
||||
if (global || !use_global) {
|
||||
setting.SetValue(ReadSetting(name).value<Type>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
|
||||
const QVariant& default_value) {
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
setting.SetGlobal(use_global);
|
||||
if (global || !use_global) {
|
||||
setting.SetValue(ReadSetting(name, default_value).value<Type>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::ReadSettingGlobal(Type& setting, const QString& name,
|
||||
const QVariant& default_value) const {
|
||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||
if (global || !use_global) {
|
||||
setting = ReadSetting(name, default_value).value<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::WriteSetting(const QString& name, const QVariant& value) {
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
@@ -1244,6 +1306,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) {
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
|
||||
}
|
||||
if (global || !setting.UsingGlobal()) {
|
||||
qt_config->setValue(name, setting.GetValue(global));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
|
||||
const QVariant& default_value) {
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
|
||||
}
|
||||
if (global || !setting.UsingGlobal()) {
|
||||
qt_config->setValue(name + QStringLiteral("/default"),
|
||||
setting.GetValue(global) == default_value.value<Type>());
|
||||
qt_config->setValue(name, setting.GetValue(global));
|
||||
}
|
||||
}
|
||||
|
||||
void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
|
||||
const QVariant& default_value) {
|
||||
if (!global) {
|
||||
qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
|
||||
}
|
||||
if (global || !use_global) {
|
||||
qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
|
||||
qt_config->setValue(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
void Config::Reload() {
|
||||
ReadValues();
|
||||
// To apply default value changes
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include "core/settings.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
@@ -15,7 +16,7 @@ class QSettings;
|
||||
|
||||
class Config {
|
||||
public:
|
||||
Config();
|
||||
explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
|
||||
~Config();
|
||||
|
||||
void Reload();
|
||||
@@ -82,9 +83,33 @@ private:
|
||||
|
||||
QVariant ReadSetting(const QString& name) const;
|
||||
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
|
||||
// Templated ReadSettingGlobal functions will also look for the use_global setting and set
|
||||
// both the value and the global state properly
|
||||
template <typename Type>
|
||||
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name);
|
||||
template <typename Type>
|
||||
void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
|
||||
const QVariant& default_value);
|
||||
template <typename Type>
|
||||
void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
|
||||
// Templated WriteSettingGlobal functions will also write the global state if needed and will
|
||||
// skip writing the actual setting if it defers to the global value
|
||||
void WriteSetting(const QString& name, const QVariant& value);
|
||||
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
|
||||
template <typename Type>
|
||||
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting);
|
||||
template <typename Type>
|
||||
void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
|
||||
const QVariant& default_value);
|
||||
void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
|
||||
const QVariant& default_value);
|
||||
|
||||
std::unique_ptr<QSettings> qt_config;
|
||||
std::string qt_config_loc;
|
||||
|
||||
bool global;
|
||||
};
|
||||
|
||||
// These metatype declarations cannot be in core/settings.h because core is devoid of QT
|
||||
Q_DECLARE_METATYPE(Settings::RendererBackend);
|
||||
Q_DECLARE_METATYPE(Settings::GPUAccuracy);
|
||||
|
||||
76
src/yuzu/configuration/configuration_shared.cpp
Normal file
76
src/yuzu/configuration/configuration_shared.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include "core/settings.h"
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
#include "yuzu/configuration/configure_per_game.h"
|
||||
|
||||
void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
|
||||
const QCheckBox* checkbox) {
|
||||
if (checkbox->checkState() == Qt::PartiallyChecked) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(checkbox->checkState() == Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
|
||||
const QComboBox* combobox) {
|
||||
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
|
||||
const QComboBox* combobox) {
|
||||
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(static_cast<Settings::RendererBackend>(
|
||||
combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
|
||||
const Settings::Setting<bool>* setting) {
|
||||
if (setting->UsingGlobal()) {
|
||||
checkbox->setCheckState(Qt::PartiallyChecked);
|
||||
} else {
|
||||
checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetPerGameSetting(QComboBox* combobox,
|
||||
const Settings::Setting<int>* setting) {
|
||||
combobox->setCurrentIndex(setting->UsingGlobal()
|
||||
? ConfigurationShared::USE_GLOBAL_INDEX
|
||||
: setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetPerGameSetting(
|
||||
QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) {
|
||||
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
|
||||
: static_cast<int>(setting->GetValue()) +
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetPerGameSetting(
|
||||
QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) {
|
||||
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
|
||||
: static_cast<int>(setting->GetValue()) +
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) {
|
||||
const QString use_global_text = ConfigurePerGame::tr("Use global configuration");
|
||||
combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
|
||||
combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
|
||||
}
|
||||
36
src/yuzu/configuration/configuration_shared.h
Normal file
36
src/yuzu/configuration/configuration_shared.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QString>
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace ConfigurationShared {
|
||||
|
||||
constexpr int USE_GLOBAL_INDEX = 0;
|
||||
constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
|
||||
constexpr int USE_GLOBAL_OFFSET = 2;
|
||||
|
||||
// Global-aware apply and set functions
|
||||
|
||||
void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox);
|
||||
void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
|
||||
void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
|
||||
const QComboBox* combobox);
|
||||
void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting,
|
||||
const QComboBox* combobox);
|
||||
|
||||
void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
|
||||
void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting);
|
||||
void SetPerGameSetting(QComboBox* combobox,
|
||||
const Settings::Setting<Settings::RendererBackend>* setting);
|
||||
void SetPerGameSetting(QComboBox* combobox,
|
||||
const Settings::Setting<Settings::GPUAccuracy>* setting);
|
||||
|
||||
void InsertGlobalItem(QComboBox* combobox);
|
||||
|
||||
} // namespace ConfigurationShared
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_audio.h"
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
#include "yuzu/configuration/configure_audio.h"
|
||||
|
||||
ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
@@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureAudio::UpdateAudioDevices);
|
||||
|
||||
ui->volume_label->setVisible(Settings::configuring_global);
|
||||
ui->volume_combo_box->setVisible(!Settings::configuring_global);
|
||||
|
||||
SetupPerGameUI();
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
@@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() {
|
||||
|
||||
SetAudioDeviceFromDeviceID();
|
||||
|
||||
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
|
||||
ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
|
||||
ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
|
||||
|
||||
if (Settings::configuring_global) {
|
||||
ui->toggle_audio_stretching->setChecked(
|
||||
Settings::values.enable_audio_stretching.GetValue());
|
||||
} else {
|
||||
ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching,
|
||||
&Settings::values.enable_audio_stretching);
|
||||
if (Settings::values.volume.UsingGlobal()) {
|
||||
ui->volume_combo_box->setCurrentIndex(0);
|
||||
ui->volume_slider->setEnabled(false);
|
||||
} else {
|
||||
ui->volume_combo_box->setCurrentIndex(1);
|
||||
ui->volume_slider->setEnabled(true);
|
||||
}
|
||||
}
|
||||
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||
}
|
||||
|
||||
@@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
|
||||
}
|
||||
|
||||
void ConfigureAudio::ApplyConfiguration() {
|
||||
Settings::values.sink_id =
|
||||
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
|
||||
Settings::values.audio_device_id =
|
||||
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.volume =
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
|
||||
if (Settings::configuring_global) {
|
||||
Settings::values.sink_id =
|
||||
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
Settings::values.audio_device_id =
|
||||
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
|
||||
.toStdString();
|
||||
|
||||
// Guard if during game and set to game-specific value
|
||||
if (Settings::values.enable_audio_stretching.UsingGlobal()) {
|
||||
Settings::values.enable_audio_stretching.SetValue(
|
||||
ui->toggle_audio_stretching->isChecked());
|
||||
}
|
||||
if (Settings::values.volume.UsingGlobal()) {
|
||||
Settings::values.volume.SetValue(
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) /
|
||||
ui->volume_slider->maximum());
|
||||
}
|
||||
} else {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
|
||||
ui->toggle_audio_stretching);
|
||||
if (ui->volume_combo_box->currentIndex() == 0) {
|
||||
Settings::values.volume.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.volume.SetGlobal(false);
|
||||
Settings::values.volume.SetValue(
|
||||
static_cast<float>(ui->volume_slider->sliderPosition()) /
|
||||
ui->volume_slider->maximum());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureAudio::changeEvent(QEvent* event) {
|
||||
@@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||
}
|
||||
|
||||
void ConfigureAudio::SetupPerGameUI() {
|
||||
if (Settings::configuring_global) {
|
||||
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
|
||||
ui->toggle_audio_stretching->setEnabled(
|
||||
Settings::values.enable_audio_stretching.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ui->toggle_audio_stretching->setTristate(true);
|
||||
connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
|
||||
this, [this](int index) { ui->volume_slider->setEnabled(index == 1); });
|
||||
|
||||
ui->output_sink_combo_box->setVisible(false);
|
||||
ui->output_sink_label->setVisible(false);
|
||||
ui->audio_device_combo_box->setVisible(false);
|
||||
ui->audio_device_label->setVisible(false);
|
||||
}
|
||||
|
||||
@@ -34,5 +34,7 @@ private:
|
||||
void SetAudioDeviceFromDeviceID();
|
||||
void SetVolumeIndicatorText(int percentage);
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureAudio> ui;
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>188</width>
|
||||
<height>246</height>
|
||||
<width>367</width>
|
||||
<height>368</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
@@ -18,9 +18,9 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<layout class="QHBoxLayout" name="_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_1">
|
||||
<widget class="QLabel" name="output_sink_label">
|
||||
<property name="text">
|
||||
<string>Output Engine:</string>
|
||||
</property>
|
||||
@@ -31,20 +31,20 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_audio_stretching">
|
||||
<property name="toolTip">
|
||||
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable audio stretching</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<widget class="QCheckBox" name="toggle_audio_stretching">
|
||||
<property name="toolTip">
|
||||
<string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable audio stretching</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="QLabel" name="audio_device_label">
|
||||
<property name="text">
|
||||
<string>Audio Device:</string>
|
||||
</property>
|
||||
@@ -61,7 +61,21 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<widget class="QComboBox" name="volume_combo_box">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use global volume</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set volume:</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="volume_label">
|
||||
<property name="text">
|
||||
<string>Volume:</string>
|
||||
</property>
|
||||
@@ -74,7 +88,7 @@
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<width>30</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
|
||||
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
|
||||
Settings::configuring_global = true;
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->hotkeysTab->Populate(registry);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
@@ -7,17 +7,21 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_general.h"
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
#include "yuzu/configuration/configure_general.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
SetupPerGameUI();
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled);
|
||||
connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() {
|
||||
ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked);
|
||||
});
|
||||
}
|
||||
|
||||
ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
@@ -26,27 +30,56 @@ void ConfigureGeneral::SetConfiguration() {
|
||||
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
|
||||
|
||||
ui->use_multi_core->setEnabled(runtime_lock);
|
||||
ui->use_multi_core->setChecked(Settings::values.use_multi_core);
|
||||
ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
|
||||
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
|
||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
|
||||
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
|
||||
|
||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
|
||||
ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked());
|
||||
ui->frame_limit->setValue(Settings::values.frame_limit);
|
||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
|
||||
ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
|
||||
|
||||
if (!Settings::configuring_global) {
|
||||
if (Settings::values.use_multi_core.UsingGlobal()) {
|
||||
ui->use_multi_core->setCheckState(Qt::PartiallyChecked);
|
||||
}
|
||||
if (Settings::values.use_frame_limit.UsingGlobal()) {
|
||||
ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked);
|
||||
}
|
||||
}
|
||||
|
||||
ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked &&
|
||||
ui->toggle_frame_limit->isEnabled());
|
||||
}
|
||||
|
||||
void ConfigureGeneral::ApplyConfiguration() {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
|
||||
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
|
||||
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
|
||||
if (Settings::configuring_global) {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
|
||||
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
|
||||
UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
|
||||
|
||||
Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
|
||||
Settings::values.frame_limit = ui->frame_limit->value();
|
||||
Settings::values.use_multi_core = ui->use_multi_core->isChecked();
|
||||
// Guard if during game and set to game-specific value
|
||||
if (Settings::values.use_frame_limit.UsingGlobal()) {
|
||||
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
|
||||
Qt::Checked);
|
||||
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
|
||||
Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
|
||||
}
|
||||
} else {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core,
|
||||
ui->use_multi_core);
|
||||
|
||||
bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked;
|
||||
Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
|
||||
Settings::values.frame_limit.SetGlobal(global_frame_limit);
|
||||
if (!global_frame_limit) {
|
||||
Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
|
||||
Qt::Checked);
|
||||
Settings::values.frame_limit.SetValue(ui->frame_limit->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGeneral::changeEvent(QEvent* event) {
|
||||
@@ -60,3 +93,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
|
||||
void ConfigureGeneral::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::SetupPerGameUI() {
|
||||
if (Settings::configuring_global) {
|
||||
ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
|
||||
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ui->toggle_check_exit->setVisible(false);
|
||||
ui->toggle_user_on_boot->setVisible(false);
|
||||
ui->toggle_background_pause->setVisible(false);
|
||||
ui->toggle_hide_mouse->setVisible(false);
|
||||
|
||||
ui->toggle_frame_limit->setTristate(true);
|
||||
ui->use_multi_core->setTristate(true);
|
||||
}
|
||||
|
||||
@@ -28,5 +28,7 @@ private:
|
||||
|
||||
void SetConfiguration();
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_graphics.h"
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
#include "yuzu/configuration/configure_graphics.h"
|
||||
|
||||
#ifdef HAS_VULKAN
|
||||
@@ -21,16 +22,18 @@
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
|
||||
vulkan_device = Settings::values.vulkan_device;
|
||||
vulkan_device = Settings::values.vulkan_device.GetValue();
|
||||
RetrieveVulkanDevices();
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
SetupPerGameUI();
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { UpdateDeviceComboBox(); });
|
||||
connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
|
||||
connect(ui->device, qOverload<int>(&QComboBox::activated), this,
|
||||
[this](int device) { UpdateDeviceSelection(device); });
|
||||
|
||||
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
|
||||
@@ -40,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
}
|
||||
UpdateBackgroundColorButton(new_bg_color);
|
||||
});
|
||||
|
||||
ui->bg_label->setVisible(Settings::configuring_global);
|
||||
ui->bg_combobox->setVisible(!Settings::configuring_global);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::UpdateDeviceSelection(int device) {
|
||||
@@ -57,27 +63,95 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
|
||||
|
||||
ui->api->setEnabled(runtime_lock);
|
||||
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
|
||||
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
|
||||
ui->use_disk_shader_cache->setEnabled(runtime_lock);
|
||||
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
|
||||
ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
|
||||
Settings::values.bg_blue));
|
||||
ui->use_disk_shader_cache->setEnabled(runtime_lock);
|
||||
|
||||
if (Settings::configuring_global) {
|
||||
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
|
||||
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
|
||||
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
|
||||
ui->use_asynchronous_gpu_emulation->setChecked(
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
} else {
|
||||
ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache,
|
||||
&Settings::values.use_disk_shader_cache);
|
||||
ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation,
|
||||
&Settings::values.use_asynchronous_gpu_emulation);
|
||||
|
||||
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
|
||||
ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
|
||||
&Settings::values.aspect_ratio);
|
||||
|
||||
ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
|
||||
ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
|
||||
}
|
||||
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
|
||||
Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue()));
|
||||
UpdateDeviceComboBox();
|
||||
}
|
||||
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.renderer_backend = GetCurrentGraphicsBackend();
|
||||
Settings::values.vulkan_device = vulkan_device;
|
||||
Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex();
|
||||
Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
|
||||
Settings::values.use_asynchronous_gpu_emulation =
|
||||
ui->use_asynchronous_gpu_emulation->isChecked();
|
||||
Settings::values.bg_red = static_cast<float>(bg_color.redF());
|
||||
Settings::values.bg_green = static_cast<float>(bg_color.greenF());
|
||||
Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
|
||||
if (Settings::configuring_global) {
|
||||
// Guard if during game and set to game-specific value
|
||||
if (Settings::values.renderer_backend.UsingGlobal()) {
|
||||
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
|
||||
}
|
||||
if (Settings::values.vulkan_device.UsingGlobal()) {
|
||||
Settings::values.vulkan_device.SetValue(vulkan_device);
|
||||
}
|
||||
if (Settings::values.aspect_ratio.UsingGlobal()) {
|
||||
Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex());
|
||||
}
|
||||
if (Settings::values.use_disk_shader_cache.UsingGlobal()) {
|
||||
Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked());
|
||||
}
|
||||
if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) {
|
||||
Settings::values.use_asynchronous_gpu_emulation.SetValue(
|
||||
ui->use_asynchronous_gpu_emulation->isChecked());
|
||||
}
|
||||
if (Settings::values.bg_red.UsingGlobal()) {
|
||||
Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
|
||||
Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
|
||||
Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
|
||||
}
|
||||
} else {
|
||||
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.renderer_backend.SetGlobal(true);
|
||||
Settings::values.vulkan_device.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.renderer_backend.SetGlobal(false);
|
||||
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
|
||||
if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
|
||||
Settings::values.vulkan_device.SetGlobal(false);
|
||||
Settings::values.vulkan_device.SetValue(vulkan_device);
|
||||
} else {
|
||||
Settings::values.vulkan_device.SetGlobal(true);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
|
||||
ui->aspect_ratio_combobox);
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
|
||||
ui->use_disk_shader_cache);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
|
||||
ui->use_asynchronous_gpu_emulation);
|
||||
|
||||
if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.bg_red.SetGlobal(true);
|
||||
Settings::values.bg_green.SetGlobal(true);
|
||||
Settings::values.bg_blue.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.bg_red.SetGlobal(false);
|
||||
Settings::values.bg_green.SetGlobal(false);
|
||||
Settings::values.bg_blue.SetGlobal(false);
|
||||
Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
|
||||
Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
|
||||
Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphics::changeEvent(QEvent* event) {
|
||||
@@ -106,19 +180,27 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
|
||||
ui->device->clear();
|
||||
|
||||
bool enabled = false;
|
||||
|
||||
if (!Settings::configuring_global &&
|
||||
ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
vulkan_device = Settings::values.vulkan_device.GetValue();
|
||||
}
|
||||
switch (GetCurrentGraphicsBackend()) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
ui->device->addItem(tr("OpenGL Graphics Device"));
|
||||
enabled = false;
|
||||
break;
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
for (const auto device : vulkan_devices) {
|
||||
for (const auto& device : vulkan_devices) {
|
||||
ui->device->addItem(device);
|
||||
}
|
||||
ui->device->setCurrentIndex(vulkan_device);
|
||||
enabled = !vulkan_devices.empty();
|
||||
break;
|
||||
}
|
||||
// If in per-game config and use global is selected, don't enable.
|
||||
enabled &= !(!Settings::configuring_global &&
|
||||
ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
|
||||
ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
|
||||
}
|
||||
|
||||
@@ -132,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
|
||||
}
|
||||
|
||||
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
|
||||
return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
|
||||
if (Settings::configuring_global) {
|
||||
return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
|
||||
}
|
||||
|
||||
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.renderer_backend.SetGlobal(true);
|
||||
return Settings::values.renderer_backend.GetValue();
|
||||
}
|
||||
Settings::values.renderer_backend.SetGlobal(false);
|
||||
return static_cast<Settings::RendererBackend>(ui->api->currentIndex() -
|
||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::SetupPerGameUI() {
|
||||
if (Settings::configuring_global) {
|
||||
ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
|
||||
ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
|
||||
ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(
|
||||
Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
|
||||
ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
|
||||
ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
|
||||
[this](int index) { ui->bg_button->setEnabled(index == 1); });
|
||||
|
||||
ui->use_disk_shader_cache->setTristate(true);
|
||||
ui->use_asynchronous_gpu_emulation->setTristate(true);
|
||||
ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox);
|
||||
ConfigurationShared::InsertGlobalItem(ui->api);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ private:
|
||||
|
||||
void RetrieveVulkanDevices();
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
Settings::RendererBackend GetCurrentGraphicsBackend() const;
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||
|
||||
@@ -121,6 +121,29 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QComboBox" name="bg_combobox">
|
||||
<property name="currentText">
|
||||
<string>Use global background color</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maxVisibleItems">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Use global background color</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Set background color:</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="bg_label">
|
||||
<property name="text">
|
||||
@@ -128,6 +151,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bg_button">
|
||||
<property name="maximumSize">
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_graphics_advanced.h"
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
#include "yuzu/configuration/configure_graphics_advanced.h"
|
||||
|
||||
ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
|
||||
@@ -12,6 +13,8 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
SetupPerGameUI();
|
||||
|
||||
SetConfiguration();
|
||||
}
|
||||
|
||||
@@ -19,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
|
||||
|
||||
void ConfigureGraphicsAdvanced::SetConfiguration() {
|
||||
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
|
||||
ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
|
||||
ui->use_vsync->setEnabled(runtime_lock);
|
||||
ui->use_vsync->setChecked(Settings::values.use_vsync);
|
||||
ui->use_assembly_shaders->setEnabled(runtime_lock);
|
||||
ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders);
|
||||
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);
|
||||
ui->force_30fps_mode->setEnabled(runtime_lock);
|
||||
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
|
||||
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
|
||||
ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy);
|
||||
|
||||
if (Settings::configuring_global) {
|
||||
ui->gpu_accuracy->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
|
||||
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
|
||||
ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
|
||||
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
|
||||
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
|
||||
ui->anisotropic_filtering_combobox->setCurrentIndex(
|
||||
Settings::values.max_anisotropy.GetValue());
|
||||
} else {
|
||||
ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
|
||||
ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
|
||||
ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
|
||||
&Settings::values.use_assembly_shaders);
|
||||
ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
|
||||
&Settings::values.use_fast_gpu_time);
|
||||
ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
|
||||
&Settings::values.force_30fps_mode);
|
||||
ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
|
||||
&Settings::values.max_anisotropy);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
||||
auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex());
|
||||
Settings::values.gpu_accuracy = gpu_accuracy;
|
||||
Settings::values.use_vsync = ui->use_vsync->isChecked();
|
||||
Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked();
|
||||
Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked();
|
||||
Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
|
||||
Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex();
|
||||
// Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
|
||||
const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
|
||||
ui->gpu_accuracy->currentIndex() -
|
||||
((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
|
||||
|
||||
if (Settings::configuring_global) {
|
||||
// Must guard in case of a during-game configuration when set to be game-specific.
|
||||
if (Settings::values.gpu_accuracy.UsingGlobal()) {
|
||||
Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
|
||||
}
|
||||
if (Settings::values.use_vsync.UsingGlobal()) {
|
||||
Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked());
|
||||
}
|
||||
if (Settings::values.use_assembly_shaders.UsingGlobal()) {
|
||||
Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
|
||||
}
|
||||
if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
|
||||
Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
|
||||
}
|
||||
if (Settings::values.force_30fps_mode.UsingGlobal()) {
|
||||
Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
|
||||
}
|
||||
if (Settings::values.max_anisotropy.UsingGlobal()) {
|
||||
Settings::values.max_anisotropy.SetValue(
|
||||
ui->anisotropic_filtering_combobox->currentIndex());
|
||||
}
|
||||
} else {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
|
||||
ui->anisotropic_filtering_combobox);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
|
||||
ui->use_assembly_shaders);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
|
||||
ui->use_fast_gpu_time);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
|
||||
ui->force_30fps_mode);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
|
||||
ui->anisotropic_filtering_combobox);
|
||||
|
||||
if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.gpu_accuracy.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.gpu_accuracy.SetGlobal(false);
|
||||
Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
|
||||
@@ -52,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
|
||||
void ConfigureGraphicsAdvanced::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
||||
// Disable if not global (only happens during game)
|
||||
if (Settings::configuring_global) {
|
||||
ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
|
||||
ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
|
||||
ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
|
||||
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
|
||||
ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
|
||||
ui->anisotropic_filtering_combobox->setEnabled(
|
||||
Settings::values.max_anisotropy.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
|
||||
ui->use_vsync->setTristate(true);
|
||||
ui->use_assembly_shaders->setTristate(true);
|
||||
ui->use_fast_gpu_time->setTristate(true);
|
||||
ui->force_30fps_mode->setTristate(true);
|
||||
ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);
|
||||
}
|
||||
|
||||
@@ -26,5 +26,7 @@ private:
|
||||
|
||||
void SetConfiguration();
|
||||
|
||||
void SetupPerGameUI();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user