Compare commits
159 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8248d76964 | ||
|
|
d7d2c27b48 | ||
|
|
ad0b295125 | ||
|
|
8a8924902b | ||
|
|
db43b37e15 | ||
|
|
617eeb09e0 | ||
|
|
9943a478fe | ||
|
|
4450a2688a | ||
|
|
0072003a14 | ||
|
|
347b50ad43 | ||
|
|
db4502b7b7 | ||
|
|
4a8cb9a706 | ||
|
|
bbeea7502c | ||
|
|
c700079e08 | ||
|
|
90cbcaa44a | ||
|
|
967307d3be | ||
|
|
821d295f24 | ||
|
|
af1151b1b0 | ||
|
|
0b733903f0 | ||
|
|
47b305387a | ||
|
|
81c8f92f2e | ||
|
|
5d95e62443 | ||
|
|
0f0b756775 | ||
|
|
19c6bf72db | ||
|
|
723edb4c06 | ||
|
|
47956a3bbc | ||
|
|
9d3cbf6a90 | ||
|
|
4f473cda64 | ||
|
|
85b591f6f0 | ||
|
|
f48187449e | ||
|
|
2ba195aa0d | ||
|
|
85d7a8f466 | ||
|
|
468bd9c1b0 | ||
|
|
c783cf443e | ||
|
|
92f37a229e | ||
|
|
adbf5ca50b | ||
|
|
69f8b6a53e | ||
|
|
c0650cd82c | ||
|
|
0d10c863a5 | ||
|
|
9cca0c2f83 | ||
|
|
3ce4edba64 | ||
|
|
311f500753 | ||
|
|
be6b7591d9 | ||
|
|
cc935d997b | ||
|
|
502dbfb9eb | ||
|
|
e07eb5b223 | ||
|
|
e54c940abf | ||
|
|
39ae2deb28 | ||
|
|
7f989378c0 | ||
|
|
3bbf4462db | ||
|
|
7785123b1c | ||
|
|
01da386617 | ||
|
|
169759e069 | ||
|
|
08d36afd40 | ||
|
|
3c060503bc | ||
|
|
70147e913f | ||
|
|
2025f847bb | ||
|
|
97e7663004 | ||
|
|
fd7af52ec3 | ||
|
|
772b6e4d28 | ||
|
|
8ebd6a21c5 | ||
|
|
267d483ed4 | ||
|
|
01f297f2e0 | ||
|
|
c07b0ffe47 | ||
|
|
d43e923990 | ||
|
|
7d5f93832c | ||
|
|
75c00c3cb0 | ||
|
|
6d165481ad | ||
|
|
fb563e75e9 | ||
|
|
5330ca396d | ||
|
|
757ddd8158 | ||
|
|
a66a0a6a53 | ||
|
|
104c523d3d | ||
|
|
be68ee88c2 | ||
|
|
b6d73ec9c2 | ||
|
|
98b36625fa | ||
|
|
8bef49cde5 | ||
|
|
2bab07c367 | ||
|
|
d84d9a64b3 | ||
|
|
07d080ecc8 | ||
|
|
f0125b2be8 | ||
|
|
51546ce57e | ||
|
|
201514cb50 | ||
|
|
a89dfc9183 | ||
|
|
2781201bfb | ||
|
|
9121d35e70 | ||
|
|
0648e023ea | ||
|
|
815f30dc10 | ||
|
|
f66e3181dc | ||
|
|
5892fc1555 | ||
|
|
2079bb4090 | ||
|
|
f26f53f35b | ||
|
|
0435b7d361 | ||
|
|
bef1844a51 | ||
|
|
8b50c660df | ||
|
|
af5a56ddc4 | ||
|
|
73bb87c06b | ||
|
|
52e83f0d5c | ||
|
|
5dbf91d739 | ||
|
|
4ad69ca96e | ||
|
|
0ca7b8269a | ||
|
|
1bbc61f5f1 | ||
|
|
8794e623d9 | ||
|
|
a82fdea1ac | ||
|
|
e90802e762 | ||
|
|
b608acd688 | ||
|
|
263200f982 | ||
|
|
9f027b1af2 | ||
|
|
666b37ad56 | ||
|
|
a683e42516 | ||
|
|
6d1477f214 | ||
|
|
93fe982a0c | ||
|
|
e2730372b8 | ||
|
|
450cbcfee6 | ||
|
|
bf9c010be5 | ||
|
|
ab65de2f96 | ||
|
|
393cdb15f5 | ||
|
|
edb291b3be | ||
|
|
6989fd65f3 | ||
|
|
8160e142e1 | ||
|
|
e02687ff47 | ||
|
|
0f8b977663 | ||
|
|
edb2caaae5 | ||
|
|
b284c43385 | ||
|
|
0475a167f8 | ||
|
|
883fab2fff | ||
|
|
f2f876e3ff | ||
|
|
ed0fe04b4f | ||
|
|
c3eb42de65 | ||
|
|
db6fbd5894 | ||
|
|
0fe09df386 | ||
|
|
f1d8c83e1c | ||
|
|
07632ad825 | ||
|
|
8df93132cd | ||
|
|
a1dddca4ab | ||
|
|
839c91cd14 | ||
|
|
a8ba6dc3c9 | ||
|
|
32b6fc4062 | ||
|
|
9ce6ea648f | ||
|
|
a67d00ef31 | ||
|
|
fd1c3aa14e | ||
|
|
e706501c8d | ||
|
|
da11a27f42 | ||
|
|
505aa3a4c1 | ||
|
|
a59ad9246b | ||
|
|
0193202964 | ||
|
|
042c6602a0 | ||
|
|
c574ab5aa1 | ||
|
|
b24b463c87 | ||
|
|
17242a8865 | ||
|
|
0373ead96e | ||
|
|
47e26d7bc7 | ||
|
|
48ff15602e | ||
|
|
4489ea6f53 | ||
|
|
2a9d17b7e7 | ||
|
|
b57475887b | ||
|
|
e3253b5f18 | ||
|
|
86abff48e1 | ||
|
|
7ad423923d |
@@ -5,7 +5,7 @@ cd /yuzu
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON
|
||||
|
||||
ninja
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ cd /yuzu
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
@@ -13,6 +13,7 @@ project(yuzu)
|
||||
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
|
||||
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
|
||||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
@@ -118,15 +119,15 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
# Configure C++ standard
|
||||
# ===========================
|
||||
|
||||
# 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)
|
||||
# boost asio's concept usage doesn't play nicely with MSVC yet.
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
@@ -224,6 +225,10 @@ if(ENABLE_QT)
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
||||
endif()
|
||||
if (NOT Qt5_FOUND)
|
||||
list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable")
|
||||
endif()
|
||||
@@ -345,6 +350,7 @@ if(NOT APPLE)
|
||||
endif()
|
||||
if (NOT LIBUSB_FOUND)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_INCLUDE_DIR "")
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ Most of the development happens on GitHub. It's also where [our central reposito
|
||||
|
||||
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||
|
||||
If you want to contribute to the user interface translation, please check out the [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu). We centralize translation work there, and periodically upstream translations.
|
||||
|
||||
### Building
|
||||
|
||||
* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)
|
||||
|
||||
2
dist/languages/.gitignore
vendored
Normal file
2
dist/languages/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Ignore the source language file
|
||||
en.ts
|
||||
8
dist/languages/.tx/config
vendored
Normal file
8
dist/languages/.tx/config
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[yuzu.emulator]
|
||||
file_filter = <lang>.ts
|
||||
source_file = en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
1
dist/languages/README.md
vendored
Normal file
1
dist/languages/README.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. Do not directly open PRs on github to modify the translation.
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 4f967387c0...82417da780
@@ -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(cycles_late); });
|
||||
name, [this](u64, std::chrono::nanoseconds ns_late) { ReleaseActiveBuffer(ns_late); });
|
||||
}
|
||||
|
||||
void Stream::Play() {
|
||||
@@ -59,11 +59,9 @@ Stream::State Stream::GetState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const {
|
||||
std::chrono::nanoseconds Stream::GetBufferReleaseNS(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
const auto ns =
|
||||
std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate);
|
||||
return ns.count();
|
||||
return std::chrono::nanoseconds((static_cast<u64>(num_samples) * 1000000000ULL) / sample_rate);
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
@@ -80,7 +78,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::PlayNextBuffer(s64 cycles_late) {
|
||||
void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
|
||||
if (!IsPlaying()) {
|
||||
// Ensure we are in playing state before playing the next buffer
|
||||
sink_stream.Flush();
|
||||
@@ -105,17 +103,18 @@ void Stream::PlayNextBuffer(s64 cycles_late) {
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
core_timing.ScheduleEvent(
|
||||
GetBufferReleaseNS(*active_buffer) -
|
||||
(Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
|
||||
release_event, {});
|
||||
const auto time_stretch_delta = Settings::values.enable_audio_stretching.GetValue()
|
||||
? std::chrono::nanoseconds::zero()
|
||||
: ns_late;
|
||||
const auto future_time = GetBufferReleaseNS(*active_buffer) - time_stretch_delta;
|
||||
core_timing.ScheduleEvent(future_time, release_event, {});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer(s64 cycles_late) {
|
||||
void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) {
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
PlayNextBuffer(cycles_late);
|
||||
PlayNextBuffer(ns_late);
|
||||
}
|
||||
|
||||
bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -90,16 +91,13 @@ public:
|
||||
|
||||
private:
|
||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||
void PlayNextBuffer(s64 cycles_late = 0);
|
||||
void PlayNextBuffer(std::chrono::nanoseconds ns_late = {});
|
||||
|
||||
/// Releases the actively playing buffer, signalling that it has been completed
|
||||
void ReleaseActiveBuffer(s64 cycles_late = 0);
|
||||
void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
|
||||
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseNSHostTiming(const Buffer& buffer) const;
|
||||
std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
@@ -54,66 +54,28 @@ public:
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
public:
|
||||
constexpr AlignmentAllocator() noexcept = default;
|
||||
|
||||
template <typename T2>
|
||||
constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
|
||||
|
||||
pointer address(reference r) noexcept {
|
||||
return std::addressof(r);
|
||||
T* allocate(size_type n) {
|
||||
return static_cast<T*>(::operator new (n * sizeof(T), std::align_val_t{Align}));
|
||||
}
|
||||
|
||||
const_pointer address(const_reference r) const noexcept {
|
||||
return std::addressof(r);
|
||||
}
|
||||
|
||||
pointer allocate(size_type n) {
|
||||
return static_cast<pointer>(::operator new (n, std::align_val_t{Align}));
|
||||
}
|
||||
|
||||
void deallocate(pointer p, size_type) {
|
||||
::operator delete (p, std::align_val_t{Align});
|
||||
}
|
||||
|
||||
void construct(pointer p, const value_type& wert) {
|
||||
new (p) value_type(wert);
|
||||
}
|
||||
|
||||
void destroy(pointer p) {
|
||||
p->~value_type();
|
||||
}
|
||||
|
||||
size_type max_size() const noexcept {
|
||||
return size_type(-1) / sizeof(value_type);
|
||||
void deallocate(T* p, size_type n) {
|
||||
::operator delete (p, n * sizeof(T), std::align_val_t{Align});
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
struct rebind {
|
||||
using other = AlignmentAllocator<T2, Align>;
|
||||
};
|
||||
|
||||
bool operator!=(const AlignmentAllocator<T, Align>& other) const noexcept {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// Returns true if and only if storage allocated from *this
|
||||
// can be deallocated from other, and vice versa.
|
||||
// Always returns true for stateless allocators.
|
||||
bool operator==(const AlignmentAllocator<T, Align>& other) const noexcept {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -17,43 +17,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include "common/common_types.h"
|
||||
|
||||
// GCC
|
||||
#ifdef __GNUC__
|
||||
|
||||
#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
|
||||
#define COMMON_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
// LLVM/clang
|
||||
#elif defined(__clang__)
|
||||
|
||||
#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
|
||||
#define COMMON_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
// MSVC
|
||||
#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
|
||||
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#endif
|
||||
|
||||
// Worst case, default to little endian.
|
||||
#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -675,17 +646,8 @@ struct AddEndian<T, SwapTag> {
|
||||
};
|
||||
|
||||
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
|
||||
#if COMMON_LITTLE_ENDIAN
|
||||
|
||||
using LETag = KeepTag;
|
||||
using BETag = SwapTag;
|
||||
|
||||
#else
|
||||
|
||||
using BETag = KeepTag;
|
||||
using LETag = SwapTag;
|
||||
|
||||
#endif
|
||||
using LETag = std::conditional_t<std::endian::native == std::endian::little, KeepTag, SwapTag>;
|
||||
using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>;
|
||||
|
||||
// Aliases for LE types
|
||||
using u16_le = AddEndian<u16, LETag>::type;
|
||||
|
||||
@@ -185,6 +185,7 @@ add_library(core STATIC
|
||||
hle/kernel/object.h
|
||||
hle/kernel/physical_core.cpp
|
||||
hle/kernel/physical_core.h
|
||||
hle/kernel/physical_memory.h
|
||||
hle/kernel/process.cpp
|
||||
hle/kernel/process.h
|
||||
hle/kernel/process_capability.cpp
|
||||
@@ -398,10 +399,13 @@ add_library(core STATIC
|
||||
hle/service/lm/manager.h
|
||||
hle/service/mig/mig.cpp
|
||||
hle/service/mig/mig.h
|
||||
hle/service/mii/manager.cpp
|
||||
hle/service/mii/manager.h
|
||||
hle/service/mii/mii.cpp
|
||||
hle/service/mii/mii.h
|
||||
hle/service/mii/mii_manager.cpp
|
||||
hle/service/mii/mii_manager.h
|
||||
hle/service/mii/raw_data.cpp
|
||||
hle/service/mii/raw_data.h
|
||||
hle/service/mii/types.h
|
||||
hle/service/mm/mm_u.cpp
|
||||
hle/service/mm/mm_u.h
|
||||
hle/service/ncm/ncm.cpp
|
||||
|
||||
@@ -142,10 +142,32 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Optimizations
|
||||
if (Settings::values.disable_cpu_opt) {
|
||||
config.enable_optimizations = false;
|
||||
config.enable_fast_dispatch = false;
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_block_linking) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
}
|
||||
if (!Settings::values.cpuopt_return_stack_buffer) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fast_dispatcher) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
|
||||
}
|
||||
if (!Settings::values.cpuopt_context_elimination) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
|
||||
}
|
||||
if (!Settings::values.cpuopt_const_prop) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
|
||||
}
|
||||
if (!Settings::values.cpuopt_misc_ir) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
|
||||
}
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
|
||||
@@ -191,15 +191,37 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
// Unpredictable instructions
|
||||
config.define_unpredictable_behaviour = true;
|
||||
|
||||
// Optimizations
|
||||
if (Settings::values.disable_cpu_opt) {
|
||||
config.enable_optimizations = false;
|
||||
config.enable_fast_dispatch = false;
|
||||
}
|
||||
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_block_linking) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
}
|
||||
if (!Settings::values.cpuopt_return_stack_buffer) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fast_dispatcher) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
|
||||
}
|
||||
if (!Settings::values.cpuopt_context_elimination) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
|
||||
}
|
||||
if (!Settings::values.cpuopt_const_prop) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
|
||||
}
|
||||
if (!Settings::values.cpuopt_misc_ir) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
|
||||
}
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
|
||||
// This is to consolidate system-wide constants that are used by multiple components of yuzu.
|
||||
|
||||
@@ -53,12 +53,12 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||
instance.ThreadLoop();
|
||||
}
|
||||
|
||||
void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) {
|
||||
void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
on_thread_init = std::move(on_thread_init_);
|
||||
event_fifo_id = 0;
|
||||
shutting_down = false;
|
||||
ticks = 0;
|
||||
const auto empty_timed_callback = [](u64, s64) {};
|
||||
const auto empty_timed_callback = [](u64, std::chrono::nanoseconds) {};
|
||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
||||
if (is_multicore) {
|
||||
timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
|
||||
@@ -106,11 +106,11 @@ bool CoreTiming::HasPendingEvents() const {
|
||||
return !(wait_set && event_queue.empty());
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type,
|
||||
u64 userdata) {
|
||||
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
const std::shared_ptr<EventType>& event_type, u64 userdata) {
|
||||
{
|
||||
std::scoped_lock scope{basic_lock};
|
||||
const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future);
|
||||
const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
|
||||
|
||||
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
|
||||
|
||||
@@ -195,8 +195,9 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
event_queue.pop_back();
|
||||
basic_lock.unlock();
|
||||
|
||||
if (auto event_type{evt.type.lock()}) {
|
||||
event_type->callback(evt.userdata, global_timer - evt.time);
|
||||
if (const auto event_type{evt.type.lock()}) {
|
||||
event_type->callback(
|
||||
evt.userdata, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)});
|
||||
}
|
||||
|
||||
basic_lock.lock();
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/wall_clock.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
/// A callback that may be scheduled for a particular core timing event.
|
||||
using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
|
||||
using TimedCallback = std::function<void(u64 userdata, std::chrono::nanoseconds ns_late)>;
|
||||
|
||||
/// Contains the characteristics of a particular event.
|
||||
struct EventType {
|
||||
@@ -42,12 +40,12 @@ struct EventType {
|
||||
* in main CPU clock cycles.
|
||||
*
|
||||
* To schedule an event, you first have to register its type. This is where you pass in the
|
||||
* callback. You then schedule events using the type id you get back.
|
||||
* callback. You then schedule events using the type ID you get back.
|
||||
*
|
||||
* The int cyclesLate that the callbacks get is how many cycles late it was.
|
||||
* The s64 ns_late that the callbacks get is how many ns late it was.
|
||||
* So to schedule a new event on a regular basis:
|
||||
* inside callback:
|
||||
* ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
|
||||
* ScheduleEvent(period_in_ns - ns_late, callback, "whatever")
|
||||
*/
|
||||
class CoreTiming {
|
||||
public:
|
||||
@@ -62,7 +60,7 @@ public:
|
||||
|
||||
/// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
|
||||
/// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
|
||||
void Initialize(std::function<void(void)>&& on_thread_init_);
|
||||
void Initialize(std::function<void()>&& on_thread_init_);
|
||||
|
||||
/// Tears down all timing related functionality.
|
||||
void Shutdown();
|
||||
@@ -95,8 +93,8 @@ public:
|
||||
bool HasPendingEvents() const;
|
||||
|
||||
/// Schedules an event in core timing
|
||||
void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type,
|
||||
u64 userdata = 0);
|
||||
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
const std::shared_ptr<EventType>& event_type, u64 userdata = 0);
|
||||
|
||||
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata);
|
||||
|
||||
@@ -141,8 +139,6 @@ private:
|
||||
|
||||
u64 global_timer = 0;
|
||||
|
||||
std::chrono::nanoseconds start_point;
|
||||
|
||||
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
||||
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
||||
// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
|
||||
@@ -161,7 +157,7 @@ private:
|
||||
std::atomic<bool> wait_set{};
|
||||
std::atomic<bool> shutting_down{};
|
||||
std::atomic<bool> has_started{};
|
||||
std::function<void(void)> on_thread_init{};
|
||||
std::function<void()> on_thread_init{};
|
||||
|
||||
bool is_multicore{};
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "common/fiber.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -46,9 +49,9 @@ public:
|
||||
|
||||
void Pause(bool paused);
|
||||
|
||||
std::function<void(void*)> GetGuestThreadStartFunc();
|
||||
std::function<void(void*)> GetIdleThreadStartFunc();
|
||||
std::function<void(void*)> GetSuspendThreadStartFunc();
|
||||
static std::function<void(void*)> GetGuestThreadStartFunc();
|
||||
static std::function<void(void*)> GetIdleThreadStartFunc();
|
||||
static std::function<void(void*)> GetSuspendThreadStartFunc();
|
||||
void* GetStartFuncParamater();
|
||||
|
||||
void PreemptSingleCore(bool from_running_enviroment = true);
|
||||
@@ -97,7 +100,6 @@ private:
|
||||
bool is_async_gpu{};
|
||||
bool is_multicore{};
|
||||
std::atomic<std::size_t> current_core{};
|
||||
std::size_t preemption_count{};
|
||||
std::size_t idle_count{};
|
||||
static constexpr std::size_t max_cycle_runs = 5;
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u64 NAND_USER_SIZE = 0x680000000; // 26624 MiB
|
||||
constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB
|
||||
constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB
|
||||
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
|
||||
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
|
||||
dump_root(std::move(dump_root_)),
|
||||
@@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const {
|
||||
|
||||
u64 BISFactory::GetSystemNANDFreeSpace() const {
|
||||
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
|
||||
if (sys_dir == nullptr)
|
||||
return 0;
|
||||
if (sys_dir == nullptr) {
|
||||
return GetSystemNANDTotalSpace();
|
||||
}
|
||||
|
||||
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
|
||||
}
|
||||
|
||||
u64 BISFactory::GetSystemNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_system_size);
|
||||
return NAND_SYSTEM_SIZE;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDFreeSpace() const {
|
||||
const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
|
||||
if (usr_dir == nullptr)
|
||||
return 0;
|
||||
|
||||
return GetUserNANDTotalSpace() - usr_dir->GetSize();
|
||||
// For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes.
|
||||
// Set the free space to be 1 MiB less than the total as a workaround to this issue.
|
||||
return GetUserNANDTotalSpace() - 0x100000;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_user_size);
|
||||
return NAND_USER_SIZE;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetFullNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_total_size);
|
||||
return NAND_TOTAL_SIZE;
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
|
||||
|
||||
@@ -240,7 +240,7 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
|
||||
|
||||
RomFSBuildContext::~RomFSBuildContext() = default;
|
||||
|
||||
std::map<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
|
||||
const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
|
||||
dir_hash_table_size = 4 * dir_hash_table_entry_count;
|
||||
@@ -294,7 +294,7 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
cur_dir->parent->child = cur_dir;
|
||||
}
|
||||
|
||||
std::map<u64, VirtualFile> out;
|
||||
std::multimap<u64, VirtualFile> out;
|
||||
|
||||
// Populate file tables.
|
||||
for (const auto& it : files) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
~RomFSBuildContext();
|
||||
|
||||
// This finalizes the context.
|
||||
std::map<u64, VirtualFile> Build();
|
||||
std::multimap<u64, VirtualFile> Build();
|
||||
|
||||
private:
|
||||
VirtualDir base;
|
||||
|
||||
@@ -547,6 +547,56 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
|
||||
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
|
||||
}
|
||||
|
||||
bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
|
||||
const auto delete_nca = [this](const NcaID& id) {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
if (dir->GetFileRelative(path) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
|
||||
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
// If an entry exists in the registered cache, remove it
|
||||
if (HasEntry(title_id, ContentRecordType::Meta)) {
|
||||
LOG_INFO(Loader,
|
||||
"Previously installed entry (v{}) for title_id={:016X} detected! "
|
||||
"Attempting to remove...",
|
||||
GetEntryVersion(title_id).value_or(0), title_id);
|
||||
// Get all the ncas associated with the current CNMT and delete them
|
||||
const auto meta_old_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
|
||||
const auto program_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
|
||||
const auto data_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
|
||||
const auto control_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
|
||||
const auto html_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
|
||||
const auto legal_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
|
||||
|
||||
delete_nca(meta_old_id);
|
||||
delete_nca(program_id);
|
||||
delete_nca(data_id);
|
||||
delete_nca(control_id);
|
||||
delete_nca(html_id);
|
||||
delete_nca(legal_id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
|
||||
const VfsCopyFunction& copy) {
|
||||
const auto ncas = nsp.GetNCAsCollapsed();
|
||||
@@ -560,31 +610,57 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
|
||||
// Install Metadata File
|
||||
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
|
||||
const auto meta_id = Common::HexStringToArray<16>(meta_id_raw);
|
||||
|
||||
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id);
|
||||
if (res != InstallResult::Success)
|
||||
return res;
|
||||
if ((*meta_iter)->GetSubdirectories().empty()) {
|
||||
LOG_ERROR(Loader,
|
||||
"The file you are attempting to install does not contain a section0 within the "
|
||||
"metadata NCA and is therefore malformed. Verify that the file is valid.");
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
|
||||
// Install all the other NCAs
|
||||
const auto section0 = (*meta_iter)->GetSubdirectories()[0];
|
||||
|
||||
if (section0->GetFiles().empty()) {
|
||||
LOG_ERROR(Loader,
|
||||
"The file you are attempting to install does not contain a CNMT within the "
|
||||
"metadata NCA and is therefore malformed. Verify that the file is valid.");
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
|
||||
const auto cnmt_file = section0->GetFiles()[0];
|
||||
const CNMT cnmt(cnmt_file);
|
||||
|
||||
const auto title_id = cnmt.GetTitleID();
|
||||
const auto result = RemoveExistingEntry(title_id);
|
||||
|
||||
// Install Metadata File
|
||||
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id);
|
||||
if (res != InstallResult::Success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Install all the other NCAs
|
||||
for (const auto& record : cnmt.GetContentRecords()) {
|
||||
// Ignore DeltaFragments, they are not useful to us
|
||||
if (record.type == ContentRecordType::DeltaFragment)
|
||||
if (record.type == ContentRecordType::DeltaFragment) {
|
||||
continue;
|
||||
}
|
||||
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
|
||||
if (nca == nullptr)
|
||||
if (nca == nullptr) {
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
||||
if (res2 != InstallResult::Success)
|
||||
if (res2 != InstallResult::Success) {
|
||||
return res2;
|
||||
}
|
||||
}
|
||||
|
||||
Refresh();
|
||||
if (result) {
|
||||
return InstallResult::OverwriteExisting;
|
||||
}
|
||||
return InstallResult::Success;
|
||||
}
|
||||
|
||||
@@ -610,8 +686,9 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
|
||||
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
|
||||
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
||||
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
||||
if (!RawInstallYuzuMeta(new_cnmt))
|
||||
if (!RawInstallYuzuMeta(new_cnmt)) {
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
|
||||
}
|
||||
|
||||
@@ -649,8 +726,9 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
|
||||
}
|
||||
|
||||
auto out = dir->CreateFileRelative(path);
|
||||
if (out == nullptr)
|
||||
if (out == nullptr) {
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
|
||||
: InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile
|
||||
|
||||
enum class InstallResult {
|
||||
Success,
|
||||
OverwriteExisting,
|
||||
ErrorAlreadyExists,
|
||||
ErrorCopyFailed,
|
||||
ErrorMetaFailed,
|
||||
@@ -154,6 +155,9 @@ public:
|
||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||
std::optional<u64> title_id = {}) const override;
|
||||
|
||||
// Removes an existing entry based on title id
|
||||
bool RemoveExistingEntry(u64 title_id);
|
||||
|
||||
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
|
||||
// there is a meta NCA and all of them are accessible.
|
||||
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
|
||||
|
||||
SDMCFactory::SDMCFactory(VirtualDir dir_)
|
||||
: dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
|
||||
@@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const {
|
||||
}
|
||||
|
||||
u64 SDMCFactory::GetSDMCTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.sdmc_size);
|
||||
return SDMC_TOTAL_SIZE;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) {
|
||||
static bool VerifyConcatenationMapContinuity(const std::multimap<u64, VirtualFile>& map) {
|
||||
const auto last_valid = --map.end();
|
||||
for (auto iter = map.begin(); iter != last_valid;) {
|
||||
const auto old = iter++;
|
||||
@@ -27,12 +27,12 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::s
|
||||
: name(std::move(name)) {
|
||||
std::size_t next_offset = 0;
|
||||
for (const auto& file : files_) {
|
||||
files[next_offset] = file;
|
||||
files.emplace(next_offset, file);
|
||||
next_offset += file->GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
|
||||
ConcatenatedVfsFile::ConcatenatedVfsFile(std::multimap<u64, VirtualFile> files_, std::string name)
|
||||
: files(std::move(files_)), name(std::move(name)) {
|
||||
ASSERT(VerifyConcatenationMapContinuity(files));
|
||||
}
|
||||
@@ -50,7 +50,7 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> f
|
||||
}
|
||||
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||
std::map<u64, VirtualFile> files,
|
||||
std::multimap<u64, VirtualFile> files,
|
||||
std::string name) {
|
||||
if (files.empty())
|
||||
return nullptr;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FileSys {
|
||||
// read-only.
|
||||
class ConcatenatedVfsFile : public VfsFile {
|
||||
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
|
||||
ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
|
||||
ConcatenatedVfsFile(std::multimap<u64, VirtualFile> files, std::string name);
|
||||
|
||||
public:
|
||||
~ConcatenatedVfsFile() override;
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
|
||||
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
||||
/// gaps with a given filler byte.
|
||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files,
|
||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::multimap<u64, VirtualFile> files,
|
||||
std::string name);
|
||||
|
||||
std::string GetName() const override;
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
private:
|
||||
// Maps starting offset to file -- more efficient.
|
||||
std::map<u64, VirtualFile> files;
|
||||
std::multimap<u64, VirtualFile> files;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,19 +11,20 @@
|
||||
namespace Core::Hardware {
|
||||
|
||||
InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
|
||||
gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) {
|
||||
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
|
||||
const u32 syncpt = static_cast<u32>(message >> 32);
|
||||
const u32 value = static_cast<u32>(message);
|
||||
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
|
||||
});
|
||||
gpu_interrupt_event =
|
||||
Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, std::chrono::nanoseconds) {
|
||||
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
|
||||
const u32 syncpt = static_cast<u32>(message >> 32);
|
||||
const u32 value = static_cast<u32>(message);
|
||||
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
|
||||
});
|
||||
}
|
||||
|
||||
InterruptManager::~InterruptManager() = default;
|
||||
|
||||
void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
|
||||
const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
|
||||
system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg);
|
||||
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg);
|
||||
}
|
||||
|
||||
} // namespace Core::Hardware
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Kernel {
|
||||
// Wake up num_to_wake (or all) threads in a vector.
|
||||
void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
|
||||
s32 num_to_wake) {
|
||||
auto& time_manager = system.Kernel().TimeManager();
|
||||
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
|
||||
// them all.
|
||||
std::size_t last = waiting_threads.size();
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -22,7 +24,7 @@ constexpr u16 GetGeneration(Handle handle) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
HandleTable::HandleTable() {
|
||||
HandleTable::HandleTable(KernelCore& kernel) : kernel{kernel} {
|
||||
Clear();
|
||||
}
|
||||
|
||||
@@ -103,9 +105,9 @@ bool HandleTable::IsValid(Handle handle) const {
|
||||
|
||||
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
|
||||
if (handle == CurrentThread) {
|
||||
return SharedFrom(GetCurrentThread());
|
||||
return SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
|
||||
} else if (handle == CurrentProcess) {
|
||||
return SharedFrom(Core::System::GetInstance().CurrentProcess());
|
||||
return SharedFrom(kernel.CurrentProcess());
|
||||
}
|
||||
|
||||
if (!IsValid(handle)) {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
enum KernelHandle : Handle {
|
||||
InvalidHandle = 0,
|
||||
CurrentThread = 0xFFFF8000,
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
|
||||
HandleTable();
|
||||
explicit HandleTable(KernelCore& kernel);
|
||||
~HandleTable();
|
||||
|
||||
/**
|
||||
@@ -134,6 +136,9 @@ private:
|
||||
|
||||
/// Head of the free slots linked list.
|
||||
u16 next_free_slot = 0;
|
||||
|
||||
/// Underlying kernel instance that this handle table operates under.
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -50,7 +50,8 @@ namespace Kernel {
|
||||
|
||||
struct KernelCore::Impl {
|
||||
explicit Impl(Core::System& system, KernelCore& kernel)
|
||||
: global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {}
|
||||
: global_scheduler{kernel}, synchronization{system}, time_manager{system},
|
||||
global_handle_table{kernel}, system{system} {}
|
||||
|
||||
void SetMulticore(bool is_multicore) {
|
||||
this->is_multicore = is_multicore;
|
||||
@@ -144,29 +145,32 @@ struct KernelCore::Impl {
|
||||
|
||||
void InitializePreemption(KernelCore& kernel) {
|
||||
preemption_event = Core::Timing::CreateEvent(
|
||||
"PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) {
|
||||
"PreemptionCallback", [this, &kernel](u64, std::chrono::nanoseconds) {
|
||||
{
|
||||
SchedulerLock lock(kernel);
|
||||
global_scheduler.PreemptThreads();
|
||||
}
|
||||
s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
|
||||
const auto time_interval = std::chrono::nanoseconds{
|
||||
Core::Timing::msToCycles(std::chrono::milliseconds(10))};
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
});
|
||||
|
||||
s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
|
||||
const auto time_interval =
|
||||
std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))};
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
}
|
||||
|
||||
void InitializeSuspendThreads() {
|
||||
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
std::string name = "Suspend Thread Id:" + std::to_string(i);
|
||||
std::function<void(void*)> init_func =
|
||||
system.GetCpuManager().GetSuspendThreadStartFunc();
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
ThreadType type =
|
||||
const auto type =
|
||||
static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
|
||||
auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
auto thread_res =
|
||||
Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
|
||||
suspend_threads[i] = std::move(thread_res).Unwrap();
|
||||
}
|
||||
}
|
||||
@@ -307,7 +311,7 @@ struct KernelCore::Impl {
|
||||
|
||||
// This is the kernel's handle table or supervisor handle table which
|
||||
// stores all the objects in place.
|
||||
Kernel::HandleTable global_handle_table;
|
||||
HandleTable global_handle_table;
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using
|
||||
/// the ConnectToPort SVC.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/memory/memory_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
@@ -29,40 +29,39 @@ enum : u64 {
|
||||
|
||||
// clang-format off
|
||||
constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{
|
||||
{ 32 /*bit_width*/, Size_2_MB /*addr*/, Size_1_GB - Size_2_MB /*size*/, AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ 32 /*bit_width*/, Size_1_GB /*addr*/, Size_4_GB - Size_1_GB /*size*/, AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ 32 /*bit_width*/, Invalid /*addr*/, Size_1_GB /*size*/, AddressSpaceInfo::Type::Heap, },
|
||||
{ 32 /*bit_width*/, Invalid /*addr*/, Size_1_GB /*size*/, AddressSpaceInfo::Type::Alias, },
|
||||
{ 36 /*bit_width*/, Size_128_MB /*addr*/, Size_2_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ 36 /*bit_width*/, Size_2_GB /*addr*/, Size_64_GB - Size_2_GB /*size*/, AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ 36 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Heap, },
|
||||
{ 36 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Alias, },
|
||||
{ 39 /*bit_width*/, Size_128_MB /*addr*/, Size_512_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Large64Bit, },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_64_GB /*size*/, AddressSpaceInfo::Type::Is32Bit },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Heap, },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_64_GB /*size*/, AddressSpaceInfo::Type::Alias, },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_2_GB /*size*/, AddressSpaceInfo::Type::Stack, },
|
||||
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Large64Bit, },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Is32Bit },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = AddressSpaceInfo::Type::Stack, },
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
|
||||
return index < std::size(AddressSpaceInfos) && AddressSpaceInfos[index].GetAddress() != Invalid;
|
||||
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid;
|
||||
}
|
||||
|
||||
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
|
||||
AddressSpaceIndices32Bit{
|
||||
0, 1, 0, 2, 0, 3,
|
||||
};
|
||||
using IndexArray = std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>;
|
||||
|
||||
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
|
||||
AddressSpaceIndices36Bit{
|
||||
4, 5, 4, 6, 4, 7,
|
||||
};
|
||||
constexpr IndexArray AddressSpaceIndices32Bit{
|
||||
0, 1, 0, 2, 0, 3,
|
||||
};
|
||||
|
||||
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
|
||||
AddressSpaceIndices39Bit{
|
||||
9, 8, 8, 10, 12, 11,
|
||||
};
|
||||
constexpr IndexArray AddressSpaceIndices36Bit{
|
||||
4, 5, 4, 6, 4, 7,
|
||||
};
|
||||
|
||||
constexpr IndexArray AddressSpaceIndices39Bit{
|
||||
9, 8, 8, 10, 12, 11,
|
||||
};
|
||||
|
||||
constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) {
|
||||
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
|
||||
@@ -80,37 +79,37 @@ constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) {
|
||||
|
||||
} // namespace
|
||||
|
||||
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, AddressSpaceInfo::Type type) {
|
||||
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
|
||||
const std::size_t index{static_cast<std::size_t>(type)};
|
||||
switch (width) {
|
||||
case 32:
|
||||
ASSERT(IsAllowed32BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetAddress();
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].address;
|
||||
case 36:
|
||||
ASSERT(IsAllowed36BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetAddress();
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].address;
|
||||
case 39:
|
||||
ASSERT(IsAllowed39BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetAddress();
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, AddressSpaceInfo::Type type) {
|
||||
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
|
||||
const std::size_t index{static_cast<std::size_t>(type)};
|
||||
switch (width) {
|
||||
case 32:
|
||||
ASSERT(IsAllowed32BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetSize();
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].size;
|
||||
case 36:
|
||||
ASSERT(IsAllowed36BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetSize();
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].size;
|
||||
case 39:
|
||||
ASSERT(IsAllowed39BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetSize();
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
namespace Kernel::Memory {
|
||||
|
||||
class AddressSpaceInfo final : NonCopyable {
|
||||
public:
|
||||
struct AddressSpaceInfo final {
|
||||
enum class Type : u32 {
|
||||
Is32Bit = 0,
|
||||
Small64Bit = 1,
|
||||
@@ -23,31 +22,13 @@ public:
|
||||
Count,
|
||||
};
|
||||
|
||||
private:
|
||||
std::size_t bit_width{};
|
||||
std::size_t addr{};
|
||||
std::size_t size{};
|
||||
Type type{};
|
||||
|
||||
public:
|
||||
static u64 GetAddressSpaceStart(std::size_t width, Type type);
|
||||
static std::size_t GetAddressSpaceSize(std::size_t width, Type type);
|
||||
|
||||
constexpr AddressSpaceInfo(std::size_t bit_width, std::size_t addr, std::size_t size, Type type)
|
||||
: bit_width{bit_width}, addr{addr}, size{size}, type{type} {}
|
||||
|
||||
constexpr std::size_t GetWidth() const {
|
||||
return bit_width;
|
||||
}
|
||||
constexpr std::size_t GetAddress() const {
|
||||
return addr;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return size;
|
||||
}
|
||||
constexpr Type GetType() const {
|
||||
return type;
|
||||
}
|
||||
const std::size_t bit_width{};
|
||||
const std::size_t address{};
|
||||
const std::size_t size{};
|
||||
const Type type{};
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
|
||||
@@ -66,8 +66,6 @@ private:
|
||||
const MemoryRegion application;
|
||||
const MemoryRegion applet;
|
||||
const MemoryRegion system;
|
||||
|
||||
const PAddr start_address{};
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
|
||||
@@ -408,7 +408,7 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
|
||||
Process::Process(Core::System& system)
|
||||
: SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>(
|
||||
system)},
|
||||
address_arbiter{system}, mutex{system}, system{system} {}
|
||||
handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
|
||||
|
||||
Process::~Process() = default;
|
||||
|
||||
|
||||
@@ -382,12 +382,6 @@ private:
|
||||
/// List of threads waiting for a condition variable
|
||||
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
|
||||
/// Name of this process
|
||||
std::string name;
|
||||
|
||||
/// Address of the top of the main thread's stack
|
||||
VAddr main_thread_stack_top{};
|
||||
|
||||
@@ -399,6 +393,12 @@ private:
|
||||
|
||||
/// Process total image size
|
||||
std::size_t image_size{};
|
||||
|
||||
/// Name of this process
|
||||
std::string name;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -802,7 +802,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
|
||||
void Scheduler::Initialize() {
|
||||
std::string name = "Idle Thread Id:" + std::to_string(core_id);
|
||||
std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc();
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
|
||||
auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
|
||||
|
||||
@@ -34,7 +34,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
|
||||
std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
|
||||
|
||||
session->request_event = Core::Timing::CreateEvent(
|
||||
name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); });
|
||||
name, [session](u64, std::chrono::nanoseconds) { session->CompleteSyncRequest(); });
|
||||
session->name = std::move(name);
|
||||
session->parent = std::move(parent);
|
||||
|
||||
@@ -184,8 +184,8 @@ ResultCode ServerSession::CompleteSyncRequest() {
|
||||
|
||||
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
|
||||
Core::Memory::Memory& memory) {
|
||||
ResultCode result = QueueSyncRequest(std::move(thread), memory);
|
||||
const u64 delay = kernel.IsMulticore() ? 0U : 20000U;
|
||||
const ResultCode result = QueueSyncRequest(std::move(thread), memory);
|
||||
const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -458,9 +458,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
auto* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||
auto& kernel = system.Kernel();
|
||||
using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type;
|
||||
Thread::ThreadSynchronizationObjects objects(handle_count);
|
||||
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
|
||||
@@ -1750,9 +1748,9 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
|
||||
// Only process up to 'target' threads, unless 'target' is less equal 0, in which case process
|
||||
// them all.
|
||||
std::size_t last = waiting_threads.size();
|
||||
if (target > 0)
|
||||
if (target > 0) {
|
||||
last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
}
|
||||
for (std::size_t index = 0; index < last; ++index) {
|
||||
auto& thread = waiting_threads[index];
|
||||
|
||||
@@ -1763,7 +1761,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
|
||||
|
||||
const std::size_t current_core = system.CurrentCoreIndex();
|
||||
auto& monitor = system.Monitor();
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Atomically read the value of the mutex.
|
||||
u32 mutex_val = 0;
|
||||
|
||||
@@ -19,7 +19,6 @@ Synchronization::Synchronization(Core::System& system) : system{system} {}
|
||||
void Synchronization::SignalObject(SynchronizationObject& obj) const {
|
||||
auto& kernel = system.Kernel();
|
||||
SchedulerLock lock(kernel);
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
if (obj.IsSignaled()) {
|
||||
for (auto thread : obj.GetWaitingThreads()) {
|
||||
if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) {
|
||||
|
||||
@@ -13,16 +13,8 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
@@ -36,6 +28,11 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
bool Thread::ShouldWait(const Thread* thread) const {
|
||||
@@ -158,7 +155,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
|
||||
std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top,
|
||||
Process* owner_process) {
|
||||
std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc();
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
|
||||
owner_process, std::move(init_func), init_func_parameter);
|
||||
@@ -540,13 +537,4 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
*/
|
||||
Thread* GetCurrentThread() {
|
||||
return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -583,8 +583,6 @@ private:
|
||||
|
||||
void SetCurrentPriority(u32 new_priority);
|
||||
|
||||
void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
|
||||
|
||||
Common::SpinLock context_guard{};
|
||||
ThreadContext32 context_32{};
|
||||
ThreadContext64 context_64{};
|
||||
@@ -680,9 +678,4 @@ private:
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
*/
|
||||
Thread* GetCurrentThread();
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Kernel {
|
||||
|
||||
TimeManager::TimeManager(Core::System& system_) : system{system_} {
|
||||
time_manager_event_type = Core::Timing::CreateEvent(
|
||||
"Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) {
|
||||
"Kernel::TimeManagerCallback", [this](u64 thread_handle, std::chrono::nanoseconds) {
|
||||
SchedulerLock lock(system.Kernel());
|
||||
Handle proper_handle = static_cast<Handle>(thread_handle);
|
||||
if (cancelled_events[proper_handle]) {
|
||||
@@ -34,7 +34,8 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64
|
||||
ASSERT(timetask);
|
||||
ASSERT(timetask->GetStatus() != ThreadStatus::Ready);
|
||||
ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex);
|
||||
system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle);
|
||||
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
|
||||
time_manager_event_type, event_handle);
|
||||
} else {
|
||||
event_handle = InvalidHandle;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,23 @@
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
namespace {
|
||||
enum class Request : u32 {
|
||||
Finalize = 0x4,
|
||||
SetUserWordInfo = 0x6,
|
||||
SetCustomizeDic = 0x7,
|
||||
Calc = 0xa,
|
||||
SetCustomizedDictionaries = 0xb,
|
||||
UnsetCustomizedDictionaries = 0xc,
|
||||
UnknownD = 0xd,
|
||||
UnknownE = 0xe,
|
||||
};
|
||||
constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8;
|
||||
constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
|
||||
constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
|
||||
constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
|
||||
constexpr bool INTERACTIVE_STATUS_OK = false;
|
||||
|
||||
} // Anonymous namespace
|
||||
static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
|
||||
KeyboardConfig config, std::u16string initial_text) {
|
||||
Core::Frontend::SoftwareKeyboardParameters params{};
|
||||
@@ -47,6 +59,7 @@ SoftwareKeyboard::~SoftwareKeyboard() = default;
|
||||
|
||||
void SoftwareKeyboard::Initialize() {
|
||||
complete = false;
|
||||
is_inline = false;
|
||||
initial_text.clear();
|
||||
final_data.clear();
|
||||
|
||||
@@ -56,6 +69,11 @@ void SoftwareKeyboard::Initialize() {
|
||||
ASSERT(keyboard_config_storage != nullptr);
|
||||
const auto& keyboard_config = keyboard_config_storage->GetData();
|
||||
|
||||
if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) {
|
||||
is_inline = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
|
||||
std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
|
||||
|
||||
@@ -87,16 +105,32 @@ void SoftwareKeyboard::ExecuteInteractive() {
|
||||
const auto storage = broker.PopInteractiveDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
const auto data = storage->GetData();
|
||||
const auto status = static_cast<bool>(data[0]);
|
||||
|
||||
if (status == INTERACTIVE_STATUS_OK) {
|
||||
complete = true;
|
||||
if (!is_inline) {
|
||||
const auto status = static_cast<bool>(data[0]);
|
||||
if (status == INTERACTIVE_STATUS_OK) {
|
||||
complete = true;
|
||||
} else {
|
||||
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
|
||||
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||
frontend.SendTextCheckDialog(
|
||||
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
|
||||
[this] { broker.SignalStateChanged(); });
|
||||
}
|
||||
} else {
|
||||
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
|
||||
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||
frontend.SendTextCheckDialog(
|
||||
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
|
||||
[this] { broker.SignalStateChanged(); });
|
||||
Request request{};
|
||||
std::memcpy(&request, data.data(), sizeof(Request));
|
||||
|
||||
switch (request) {
|
||||
case Request::Calc: {
|
||||
broker.PushNormalDataFromApplet(
|
||||
std::make_shared<IStorage>(std::move(std::vector<u8>{1})));
|
||||
broker.SignalStateChanged();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Request {:X} is not implemented", request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +142,10 @@ void SoftwareKeyboard::Execute() {
|
||||
}
|
||||
|
||||
const auto parameters = ConvertToFrontendParameters(config, initial_text);
|
||||
|
||||
frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); },
|
||||
parameters);
|
||||
if (!is_inline) {
|
||||
frontend.RequestText(
|
||||
[this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
|
||||
@@ -78,6 +78,7 @@ private:
|
||||
KeyboardConfig config;
|
||||
std::u16string initial_text;
|
||||
bool complete = false;
|
||||
bool is_inline = false;
|
||||
std::vector<u8> final_data;
|
||||
};
|
||||
|
||||
|
||||
@@ -55,6 +55,10 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
|
||||
ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
|
||||
// dir can be nullptr if path contains subdirectories, create those prior to creating the file.
|
||||
if (dir == nullptr) {
|
||||
dir = backing->CreateSubdirectory(FileUtil::GetParentPath(path));
|
||||
}
|
||||
auto file = dir->CreateFile(FileUtil::GetFilename(path));
|
||||
if (file == nullptr) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
|
||||
@@ -39,9 +39,10 @@ namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// TODO(ogniK): Find actual polling rate of hid
|
||||
constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66);
|
||||
[[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100);
|
||||
[[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100);
|
||||
constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66};
|
||||
[[maybe_unused]] constexpr auto accelerometer_update_ns =
|
||||
std::chrono::nanoseconds{1000000000 / 100};
|
||||
[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
|
||||
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
|
||||
|
||||
IAppletResource::IAppletResource(Core::System& system)
|
||||
@@ -75,14 +76,14 @@ IAppletResource::IAppletResource(Core::System& system)
|
||||
GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
|
||||
|
||||
// Register update callbacks
|
||||
pad_update_event =
|
||||
Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) {
|
||||
pad_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdatePadCallback", [this](u64 userdata, std::chrono::nanoseconds ns_late) {
|
||||
UpdateControllers(userdata, ns_late);
|
||||
});
|
||||
|
||||
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
|
||||
|
||||
system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event);
|
||||
system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
|
||||
|
||||
ReloadInputDevices();
|
||||
}
|
||||
@@ -107,7 +108,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushCopyObjects(shared_mem);
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) {
|
||||
void IAppletResource::UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
|
||||
@@ -118,7 +119,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) {
|
||||
controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event);
|
||||
core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
|
||||
}
|
||||
|
||||
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include <chrono>
|
||||
|
||||
#include "controllers/controller_base.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
@@ -65,7 +64,7 @@ private:
|
||||
}
|
||||
|
||||
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
|
||||
void UpdateControllers(u64 userdata, s64 cycles_late);
|
||||
void UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late);
|
||||
|
||||
std::shared_ptr<Kernel::SharedMemory> shared_mem;
|
||||
|
||||
|
||||
484
src/core/hle/service/mii/manager.cpp
Normal file
484
src/core/hle/service/mii/manager.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/mii/manager.h"
|
||||
#include "core/hle/service/mii/raw_data.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
|
||||
constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
|
||||
|
||||
constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
|
||||
constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
|
||||
constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13};
|
||||
constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23};
|
||||
constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0};
|
||||
constexpr std::array<u8, 62> EyeRotateLookup{
|
||||
{0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
|
||||
0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
|
||||
0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}};
|
||||
constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07,
|
||||
0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06,
|
||||
0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}};
|
||||
|
||||
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
|
||||
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
|
||||
std::array<T, DestArraySize> out{};
|
||||
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
|
||||
return out;
|
||||
}
|
||||
|
||||
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
|
||||
MiiStoreBitFields bf;
|
||||
std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
|
||||
|
||||
return {
|
||||
.uuid = data.data.uuid,
|
||||
.name = ResizeArray<char16_t, 10, 11>(data.data.name),
|
||||
.font_region = static_cast<u8>(bf.font_region.Value()),
|
||||
.favorite_color = static_cast<u8>(bf.favorite_color.Value()),
|
||||
.gender = static_cast<u8>(bf.gender.Value()),
|
||||
.height = static_cast<u8>(bf.height.Value()),
|
||||
.build = static_cast<u8>(bf.build.Value()),
|
||||
.type = static_cast<u8>(bf.type.Value()),
|
||||
.region_move = static_cast<u8>(bf.region_move.Value()),
|
||||
.faceline_type = static_cast<u8>(bf.faceline_type.Value()),
|
||||
.faceline_color = static_cast<u8>(bf.faceline_color.Value()),
|
||||
.faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()),
|
||||
.faceline_make = static_cast<u8>(bf.faceline_makeup.Value()),
|
||||
.hair_type = static_cast<u8>(bf.hair_type.Value()),
|
||||
.hair_color = static_cast<u8>(bf.hair_color.Value()),
|
||||
.hair_flip = static_cast<u8>(bf.hair_flip.Value()),
|
||||
.eye_type = static_cast<u8>(bf.eye_type.Value()),
|
||||
.eye_color = static_cast<u8>(bf.eye_color.Value()),
|
||||
.eye_scale = static_cast<u8>(bf.eye_scale.Value()),
|
||||
.eye_aspect = static_cast<u8>(bf.eye_aspect.Value()),
|
||||
.eye_rotate = static_cast<u8>(bf.eye_rotate.Value()),
|
||||
.eye_x = static_cast<u8>(bf.eye_x.Value()),
|
||||
.eye_y = static_cast<u8>(bf.eye_y.Value()),
|
||||
.eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()),
|
||||
.eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()),
|
||||
.eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()),
|
||||
.eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()),
|
||||
.eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()),
|
||||
.eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()),
|
||||
.eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3),
|
||||
.nose_type = static_cast<u8>(bf.nose_type.Value()),
|
||||
.nose_scale = static_cast<u8>(bf.nose_scale.Value()),
|
||||
.nose_y = static_cast<u8>(bf.nose_y.Value()),
|
||||
.mouth_type = static_cast<u8>(bf.mouth_type.Value()),
|
||||
.mouth_color = static_cast<u8>(bf.mouth_color.Value()),
|
||||
.mouth_scale = static_cast<u8>(bf.mouth_scale.Value()),
|
||||
.mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()),
|
||||
.mouth_y = static_cast<u8>(bf.mouth_y.Value()),
|
||||
.beard_color = static_cast<u8>(bf.beard_color.Value()),
|
||||
.beard_type = static_cast<u8>(bf.beard_type.Value()),
|
||||
.mustache_type = static_cast<u8>(bf.mustache_type.Value()),
|
||||
.mustache_scale = static_cast<u8>(bf.mustache_scale.Value()),
|
||||
.mustache_y = static_cast<u8>(bf.mustache_y.Value()),
|
||||
.glasses_type = static_cast<u8>(bf.glasses_type.Value()),
|
||||
.glasses_color = static_cast<u8>(bf.glasses_color.Value()),
|
||||
.glasses_scale = static_cast<u8>(bf.glasses_scale.Value()),
|
||||
.glasses_y = static_cast<u8>(bf.glasses_y.Value()),
|
||||
.mole_type = static_cast<u8>(bf.mole_type.Value()),
|
||||
.mole_scale = static_cast<u8>(bf.mole_scale.Value()),
|
||||
.mole_x = static_cast<u8>(bf.mole_x.Value()),
|
||||
.mole_y = static_cast<u8>(bf.mole_y.Value()),
|
||||
};
|
||||
}
|
||||
|
||||
u16 GenerateCrc16(const void* data, std::size_t size) {
|
||||
s32 crc{};
|
||||
for (std::size_t i = 0; i < size; i++) {
|
||||
crc ^= static_cast<const u8*>(data)[i] << 8;
|
||||
for (std::size_t j = 0; j < 8; j++) {
|
||||
crc <<= 1;
|
||||
if ((crc & 0x10000) != 0) {
|
||||
crc = (crc ^ 0x1021) & 0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Common::swap16(static_cast<u16>(crc));
|
||||
}
|
||||
|
||||
Common::UUID GenerateValidUUID() {
|
||||
auto uuid{Common::UUID::Generate()};
|
||||
|
||||
// Bit 7 must be set, and bit 6 unset for the UUID to be valid
|
||||
uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL;
|
||||
uuid.uuid[1] |= 0x0000000000000080ULL;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetRandomValue(T min, T max) {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(0, static_cast<u64>(max));
|
||||
return static_cast<T>(distribution(gen));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetRandomValue(T max) {
|
||||
return GetRandomValue<T>({}, max);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetArrayValue(const u8* data, std::size_t index) {
|
||||
T result{};
|
||||
std::memcpy(&result, &data[index * sizeof(T)], sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
if (gender == Gender::All) {
|
||||
gender = GetRandomValue<Gender>(Gender::Maximum);
|
||||
}
|
||||
|
||||
bf.gender.Assign(gender);
|
||||
bf.favorite_color.Assign(GetRandomValue<u8>(11));
|
||||
bf.region_move.Assign(0);
|
||||
bf.font_region.Assign(FontRegion::Standard);
|
||||
bf.type.Assign(0);
|
||||
bf.height.Assign(64);
|
||||
bf.build.Assign(64);
|
||||
|
||||
if (age == Age::All) {
|
||||
const auto temp{GetRandomValue<int>(10)};
|
||||
if (temp >= 8) {
|
||||
age = Age::Old;
|
||||
} else if (temp >= 4) {
|
||||
age = Age::Normal;
|
||||
} else {
|
||||
age = Age::Young;
|
||||
}
|
||||
}
|
||||
|
||||
if (race == Race::All) {
|
||||
const auto temp{GetRandomValue<int>(10)};
|
||||
if (temp >= 8) {
|
||||
race = Race::Black;
|
||||
} else if (temp >= 4) {
|
||||
race = Race::White;
|
||||
} else {
|
||||
race = Race::Asian;
|
||||
}
|
||||
}
|
||||
|
||||
u32 axis_y{};
|
||||
if (gender == Gender::Female && age == Age::Young) {
|
||||
axis_y = GetRandomValue<u32>(3);
|
||||
}
|
||||
|
||||
const std::size_t index{3 * static_cast<std::size_t>(age) +
|
||||
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
|
||||
|
||||
const auto faceline_type_info{
|
||||
GetArrayValue<RandomMiiData4>(&RawData::RandomMiiFaceline[0], index)};
|
||||
const auto faceline_color_info{GetArrayValue<RandomMiiData3>(
|
||||
RawData::RandomMiiFacelineColor.data(),
|
||||
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
|
||||
const auto faceline_wrinkle_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineWrinkle.data(), index)};
|
||||
const auto faceline_makeup_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineMakeup.data(), index)};
|
||||
const auto hair_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiHairType.data(), index)};
|
||||
const auto hair_color_info{GetArrayValue<RandomMiiData3>(RawData::RandomMiiHairColor.data(),
|
||||
3 * static_cast<std::size_t>(race) +
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto eye_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyeType.data(), index)};
|
||||
const auto eye_color_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiEyeColor.data(),
|
||||
static_cast<std::size_t>(race))};
|
||||
const auto eyebrow_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyebrowType.data(), index)};
|
||||
const auto nose_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiNoseType.data(), index)};
|
||||
const auto mouth_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiMouthType.data(), index)};
|
||||
const auto glasses_type_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiGlassType.data(),
|
||||
static_cast<std::size_t>(age))};
|
||||
|
||||
bf.faceline_type.Assign(
|
||||
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
|
||||
bf.faceline_color.Assign(
|
||||
faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
|
||||
bf.faceline_wrinkle.Assign(
|
||||
faceline_wrinkle_info
|
||||
.values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
|
||||
bf.faceline_makeup.Assign(
|
||||
faceline_makeup_info
|
||||
.values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
|
||||
|
||||
bf.hair_type.Assign(
|
||||
hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]);
|
||||
bf.hair_color.Assign(
|
||||
HairColorLookup[hair_color_info
|
||||
.values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]);
|
||||
bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum));
|
||||
|
||||
bf.eye_type.Assign(
|
||||
eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]);
|
||||
|
||||
const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
|
||||
const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
|
||||
const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
|
||||
const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]};
|
||||
|
||||
bf.eye_color.Assign(
|
||||
EyeColorLookup[eye_color_info
|
||||
.values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]);
|
||||
bf.eye_scale.Assign(4);
|
||||
bf.eye_aspect.Assign(3);
|
||||
bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate);
|
||||
bf.eye_x.Assign(2);
|
||||
bf.eye_y.Assign(axis_y + 12);
|
||||
|
||||
bf.eyebrow_type.Assign(
|
||||
eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
|
||||
|
||||
const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
|
||||
const auto eyebrow_y{race == Race::Asian ? 9 : 10};
|
||||
const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6};
|
||||
const auto eyebrow_rotate{
|
||||
32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]};
|
||||
|
||||
bf.eyebrow_color.Assign(bf.hair_color);
|
||||
bf.eyebrow_scale.Assign(4);
|
||||
bf.eyebrow_aspect.Assign(3);
|
||||
bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate);
|
||||
bf.eyebrow_x.Assign(2);
|
||||
bf.eyebrow_y.Assign(axis_y + eyebrow_y);
|
||||
|
||||
const auto nose_scale{gender == Gender::Female ? 3 : 4};
|
||||
|
||||
bf.nose_type.Assign(
|
||||
nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]);
|
||||
bf.nose_scale.Assign(nose_scale);
|
||||
bf.nose_y.Assign(axis_y + 9);
|
||||
|
||||
const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0};
|
||||
|
||||
bf.mouth_type.Assign(
|
||||
mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
|
||||
bf.mouth_color.Assign(MouthColorLookup[mouth_color]);
|
||||
bf.mouth_scale.Assign(4);
|
||||
bf.mouth_aspect.Assign(3);
|
||||
bf.mouth_y.Assign(axis_y + 13);
|
||||
|
||||
bf.beard_color.Assign(bf.hair_color);
|
||||
bf.mustache_scale.Assign(4);
|
||||
|
||||
if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) {
|
||||
const auto mustache_and_beard_flag{
|
||||
GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)};
|
||||
|
||||
auto beard_type{BeardType::None};
|
||||
auto mustache_type{MustacheType::None};
|
||||
|
||||
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
|
||||
BeardAndMustacheFlag::Beard) {
|
||||
beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5);
|
||||
}
|
||||
|
||||
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
|
||||
BeardAndMustacheFlag::Mustache) {
|
||||
mustache_type =
|
||||
GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5);
|
||||
}
|
||||
|
||||
bf.mustache_type.Assign(mustache_type);
|
||||
bf.beard_type.Assign(beard_type);
|
||||
bf.mustache_y.Assign(10);
|
||||
} else {
|
||||
bf.mustache_type.Assign(MustacheType::None);
|
||||
bf.beard_type.Assign(BeardType::None);
|
||||
bf.mustache_y.Assign(axis_y + 10);
|
||||
}
|
||||
|
||||
const auto glasses_type_start{GetRandomValue<std::size_t>(100)};
|
||||
u8 glasses_type{};
|
||||
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
|
||||
if (++glasses_type >= glasses_type_info.values_count) {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bf.glasses_type.Assign(glasses_type);
|
||||
bf.glasses_color.Assign(GlassesColorLookup[0]);
|
||||
bf.glasses_scale.Assign(4);
|
||||
bf.glasses_y.Assign(axis_y + 10);
|
||||
|
||||
bf.mole_type.Assign(0);
|
||||
bf.mole_scale.Assign(4);
|
||||
bf.mole_x.Assign(2);
|
||||
bf.mole_y.Assign(20);
|
||||
|
||||
return {DefaultMiiName, bf, user_id};
|
||||
}
|
||||
|
||||
MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
bf.font_region.Assign(info.font_region);
|
||||
bf.favorite_color.Assign(info.favorite_color);
|
||||
bf.gender.Assign(info.gender);
|
||||
bf.height.Assign(info.height);
|
||||
bf.build.Assign(info.weight);
|
||||
bf.type.Assign(info.type);
|
||||
bf.region_move.Assign(info.region);
|
||||
bf.faceline_type.Assign(info.face_type);
|
||||
bf.faceline_color.Assign(info.face_color);
|
||||
bf.faceline_wrinkle.Assign(info.face_wrinkle);
|
||||
bf.faceline_makeup.Assign(info.face_makeup);
|
||||
bf.hair_type.Assign(info.hair_type);
|
||||
bf.hair_color.Assign(HairColorLookup[info.hair_color]);
|
||||
bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip));
|
||||
bf.eye_type.Assign(info.eye_type);
|
||||
bf.eye_color.Assign(EyeColorLookup[info.eye_color]);
|
||||
bf.eye_scale.Assign(info.eye_scale);
|
||||
bf.eye_aspect.Assign(info.eye_aspect);
|
||||
bf.eye_rotate.Assign(info.eye_rotate);
|
||||
bf.eye_x.Assign(info.eye_x);
|
||||
bf.eye_y.Assign(info.eye_y);
|
||||
bf.eyebrow_type.Assign(info.eyebrow_type);
|
||||
bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]);
|
||||
bf.eyebrow_scale.Assign(info.eyebrow_scale);
|
||||
bf.eyebrow_aspect.Assign(info.eyebrow_aspect);
|
||||
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
|
||||
bf.eyebrow_x.Assign(info.eyebrow_x);
|
||||
bf.eyebrow_y.Assign(info.eyebrow_y - 3);
|
||||
bf.nose_type.Assign(info.nose_type);
|
||||
bf.nose_scale.Assign(info.nose_scale);
|
||||
bf.nose_y.Assign(info.nose_y);
|
||||
bf.mouth_type.Assign(info.mouth_type);
|
||||
bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]);
|
||||
bf.mouth_scale.Assign(info.mouth_scale);
|
||||
bf.mouth_aspect.Assign(info.mouth_aspect);
|
||||
bf.mouth_y.Assign(info.mouth_y);
|
||||
bf.beard_color.Assign(HairColorLookup[info.beard_color]);
|
||||
bf.beard_type.Assign(static_cast<BeardType>(info.beard_type));
|
||||
bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type));
|
||||
bf.mustache_scale.Assign(info.mustache_scale);
|
||||
bf.mustache_y.Assign(info.mustache_y);
|
||||
bf.glasses_type.Assign(info.glasses_type);
|
||||
bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]);
|
||||
bf.glasses_scale.Assign(info.glasses_scale);
|
||||
bf.glasses_y.Assign(info.glasses_y);
|
||||
bf.mole_type.Assign(info.mole_type);
|
||||
bf.mole_scale.Assign(info.mole_scale);
|
||||
bf.mole_x.Assign(info.mole_x);
|
||||
bf.mole_y.Assign(info.mole_y);
|
||||
|
||||
return {DefaultMiiName, bf, user_id};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MiiStoreData::MiiStoreData() = default;
|
||||
|
||||
MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
|
||||
const Common::UUID& user_id) {
|
||||
data.name = name;
|
||||
data.uuid = GenerateValidUUID();
|
||||
|
||||
std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
|
||||
data_crc = GenerateCrc16(data.data.data(), sizeof(data));
|
||||
device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID));
|
||||
}
|
||||
|
||||
MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {}
|
||||
|
||||
bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
|
||||
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool result{current_update_counter != update_counter};
|
||||
|
||||
current_update_counter = update_counter;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MiiManager::IsFullDatabase() const {
|
||||
// TODO(bunnei): We don't implement the Mii database, so it cannot be full
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 MiiManager::GetCount(SourceFlag source_flag) const {
|
||||
u32 count{};
|
||||
if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
|
||||
// TODO(bunnei): We don't implement the Mii database, but when we do, update this
|
||||
count += 0;
|
||||
}
|
||||
if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
|
||||
count += DefaultMiiCount;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info,
|
||||
SourceFlag source_flag) {
|
||||
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
|
||||
return ERROR_CANNOT_FIND_ENTRY;
|
||||
}
|
||||
|
||||
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
|
||||
return ERROR_CANNOT_FIND_ENTRY;
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
|
||||
return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::BuildDefault(std::size_t index) {
|
||||
return ConvertStoreDataToInfo(BuildDefaultStoreData(
|
||||
GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
|
||||
}
|
||||
|
||||
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
|
||||
std::vector<MiiInfoElement> result;
|
||||
|
||||
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
|
||||
return MakeResult(std::move(result));
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < DefaultMiiCount; index++) {
|
||||
result.emplace_back(BuildDefault(index), Source::Default);
|
||||
}
|
||||
|
||||
return MakeResult(std::move(result));
|
||||
}
|
||||
|
||||
ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
|
||||
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
|
||||
|
||||
index = INVALID_INDEX;
|
||||
|
||||
// TODO(bunnei): We don't implement the Mii database, so we can't have an index
|
||||
return ERROR_CANNOT_FIND_ENTRY;
|
||||
}
|
||||
|
||||
} // namespace Service::Mii
|
||||
331
src/core/hle/service/mii/manager.h
Normal file
331
src/core/hle/service/mii/manager.h
Normal file
@@ -0,0 +1,331 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
enum class Source : u32 {
|
||||
Database = 0,
|
||||
Default = 1,
|
||||
Account = 2,
|
||||
Friend = 3,
|
||||
};
|
||||
|
||||
enum class SourceFlag : u32 {
|
||||
None = 0,
|
||||
Database = 1 << 0,
|
||||
Default = 1 << 1,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
|
||||
|
||||
struct MiiInfo {
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
std::array<char16_t, 11> name{};
|
||||
u8 font_region{};
|
||||
u8 favorite_color{};
|
||||
u8 gender{};
|
||||
u8 height{};
|
||||
u8 build{};
|
||||
u8 type{};
|
||||
u8 region_move{};
|
||||
u8 faceline_type{};
|
||||
u8 faceline_color{};
|
||||
u8 faceline_wrinkle{};
|
||||
u8 faceline_make{};
|
||||
u8 hair_type{};
|
||||
u8 hair_color{};
|
||||
u8 hair_flip{};
|
||||
u8 eye_type{};
|
||||
u8 eye_color{};
|
||||
u8 eye_scale{};
|
||||
u8 eye_aspect{};
|
||||
u8 eye_rotate{};
|
||||
u8 eye_x{};
|
||||
u8 eye_y{};
|
||||
u8 eyebrow_type{};
|
||||
u8 eyebrow_color{};
|
||||
u8 eyebrow_scale{};
|
||||
u8 eyebrow_aspect{};
|
||||
u8 eyebrow_rotate{};
|
||||
u8 eyebrow_x{};
|
||||
u8 eyebrow_y{};
|
||||
u8 nose_type{};
|
||||
u8 nose_scale{};
|
||||
u8 nose_y{};
|
||||
u8 mouth_type{};
|
||||
u8 mouth_color{};
|
||||
u8 mouth_scale{};
|
||||
u8 mouth_aspect{};
|
||||
u8 mouth_y{};
|
||||
u8 beard_color{};
|
||||
u8 beard_type{};
|
||||
u8 mustache_type{};
|
||||
u8 mustache_scale{};
|
||||
u8 mustache_y{};
|
||||
u8 glasses_type{};
|
||||
u8 glasses_color{};
|
||||
u8 glasses_scale{};
|
||||
u8 glasses_y{};
|
||||
u8 mole_type{};
|
||||
u8 mole_scale{};
|
||||
u8 mole_x{};
|
||||
u8 mole_y{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
|
||||
static_assert(std::has_unique_object_representations_v<MiiInfo>,
|
||||
"All bits of MiiInfo must contribute to its value.");
|
||||
|
||||
#pragma pack(push, 4)
|
||||
|
||||
struct MiiInfoElement {
|
||||
MiiInfoElement(const MiiInfo& info, Source source) : info{info}, source{source} {}
|
||||
|
||||
MiiInfo info{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
|
||||
|
||||
struct MiiStoreBitFields {
|
||||
union {
|
||||
u32 word_0{};
|
||||
|
||||
BitField<0, 8, u32> hair_type;
|
||||
BitField<8, 7, u32> height;
|
||||
BitField<15, 1, u32> mole_type;
|
||||
BitField<16, 7, u32> build;
|
||||
BitField<23, 1, HairFlip> hair_flip;
|
||||
BitField<24, 7, u32> hair_color;
|
||||
BitField<31, 1, u32> type;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_1{};
|
||||
|
||||
BitField<0, 7, u32> eye_color;
|
||||
BitField<7, 1, Gender> gender;
|
||||
BitField<8, 7, u32> eyebrow_color;
|
||||
BitField<16, 7, u32> mouth_color;
|
||||
BitField<24, 7, u32> beard_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_2{};
|
||||
|
||||
BitField<0, 7, u32> glasses_color;
|
||||
BitField<8, 6, u32> eye_type;
|
||||
BitField<14, 2, u32> region_move;
|
||||
BitField<16, 6, u32> mouth_type;
|
||||
BitField<22, 2, FontRegion> font_region;
|
||||
BitField<24, 5, u32> eye_y;
|
||||
BitField<29, 3, u32> glasses_scale;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_3{};
|
||||
|
||||
BitField<0, 5, u32> eyebrow_type;
|
||||
BitField<5, 3, MustacheType> mustache_type;
|
||||
BitField<8, 5, u32> nose_type;
|
||||
BitField<13, 3, BeardType> beard_type;
|
||||
BitField<16, 5, u32> nose_y;
|
||||
BitField<21, 3, u32> mouth_aspect;
|
||||
BitField<24, 5, u32> mouth_y;
|
||||
BitField<29, 3, u32> eyebrow_aspect;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_4{};
|
||||
|
||||
BitField<0, 5, u32> mustache_y;
|
||||
BitField<5, 3, u32> eye_rotate;
|
||||
BitField<8, 5, u32> glasses_y;
|
||||
BitField<13, 3, u32> eye_aspect;
|
||||
BitField<16, 5, u32> mole_x;
|
||||
BitField<21, 3, u32> eye_scale;
|
||||
BitField<24, 5, u32> mole_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_5{};
|
||||
|
||||
BitField<0, 5, u32> glasses_type;
|
||||
BitField<8, 4, u32> favorite_color;
|
||||
BitField<12, 4, u32> faceline_type;
|
||||
BitField<16, 4, u32> faceline_color;
|
||||
BitField<20, 4, u32> faceline_wrinkle;
|
||||
BitField<24, 4, u32> faceline_makeup;
|
||||
BitField<28, 4, u32> eye_x;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_6{};
|
||||
|
||||
BitField<0, 4, u32> eyebrow_scale;
|
||||
BitField<4, 4, u32> eyebrow_rotate;
|
||||
BitField<8, 4, u32> eyebrow_x;
|
||||
BitField<12, 4, u32> eyebrow_y;
|
||||
BitField<16, 4, u32> nose_scale;
|
||||
BitField<20, 4, u32> mouth_scale;
|
||||
BitField<24, 4, u32> mustache_scale;
|
||||
BitField<28, 4, u32> mole_scale;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
|
||||
"MiiStoreBitFields is not trivially copyable.");
|
||||
|
||||
struct MiiStoreData {
|
||||
using Name = std::array<char16_t, 10>;
|
||||
|
||||
MiiStoreData();
|
||||
MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
|
||||
const Common::UUID& user_id);
|
||||
|
||||
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
||||
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
|
||||
// not suitable for our uses.
|
||||
struct {
|
||||
std::array<u8, 0x1C> data{};
|
||||
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
|
||||
|
||||
Name name{};
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
} data;
|
||||
|
||||
u16 data_crc{};
|
||||
u16 device_crc{};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
|
||||
|
||||
struct MiiStoreDataElement {
|
||||
MiiStoreData data{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
|
||||
|
||||
struct MiiDatabase {
|
||||
u32 magic{}; // 'NFDB'
|
||||
std::array<MiiStoreData, 0x64> miis{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 count{};
|
||||
u16 crc{};
|
||||
};
|
||||
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
|
||||
|
||||
struct RandomMiiValues {
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
|
||||
|
||||
struct RandomMiiData4 {
|
||||
Gender gender{};
|
||||
Age age{};
|
||||
Race race{};
|
||||
u32 values_count{};
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
|
||||
|
||||
struct RandomMiiData3 {
|
||||
u32 arg_1;
|
||||
u32 arg_2;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
|
||||
|
||||
struct RandomMiiData2 {
|
||||
u32 arg_1;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
|
||||
|
||||
struct DefaultMii {
|
||||
u32 face_type{};
|
||||
u32 face_color{};
|
||||
u32 face_wrinkle{};
|
||||
u32 face_makeup{};
|
||||
u32 hair_type{};
|
||||
u32 hair_color{};
|
||||
u32 hair_flip{};
|
||||
u32 eye_type{};
|
||||
u32 eye_color{};
|
||||
u32 eye_scale{};
|
||||
u32 eye_aspect{};
|
||||
u32 eye_rotate{};
|
||||
u32 eye_x{};
|
||||
u32 eye_y{};
|
||||
u32 eyebrow_type{};
|
||||
u32 eyebrow_color{};
|
||||
u32 eyebrow_scale{};
|
||||
u32 eyebrow_aspect{};
|
||||
u32 eyebrow_rotate{};
|
||||
u32 eyebrow_x{};
|
||||
u32 eyebrow_y{};
|
||||
u32 nose_type{};
|
||||
u32 nose_scale{};
|
||||
u32 nose_y{};
|
||||
u32 mouth_type{};
|
||||
u32 mouth_color{};
|
||||
u32 mouth_scale{};
|
||||
u32 mouth_aspect{};
|
||||
u32 mouth_y{};
|
||||
u32 mustache_type{};
|
||||
u32 beard_type{};
|
||||
u32 beard_color{};
|
||||
u32 mustache_scale{};
|
||||
u32 mustache_y{};
|
||||
u32 glasses_type{};
|
||||
u32 glasses_color{};
|
||||
u32 glasses_scale{};
|
||||
u32 glasses_y{};
|
||||
u32 mole_type{};
|
||||
u32 mole_scale{};
|
||||
u32 mole_x{};
|
||||
u32 mole_y{};
|
||||
u32 height{};
|
||||
u32 weight{};
|
||||
Gender gender{};
|
||||
u32 favorite_color{};
|
||||
u32 region{};
|
||||
FontRegion font_region{};
|
||||
u32 type{};
|
||||
INSERT_PADDING_WORDS(5);
|
||||
};
|
||||
static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
|
||||
// with providing an easy interface for HLE emulation of the mii service.
|
||||
class MiiManager {
|
||||
public:
|
||||
MiiManager();
|
||||
|
||||
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
|
||||
bool IsFullDatabase() const;
|
||||
u32 GetCount(SourceFlag source_flag) const;
|
||||
ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag);
|
||||
MiiInfo BuildRandom(Age age, Gender gender, Race race);
|
||||
MiiInfo BuildDefault(std::size_t index);
|
||||
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
|
||||
ResultCode GetIndex(const MiiInfo& info, u32& index);
|
||||
|
||||
private:
|
||||
const Common::UUID user_id;
|
||||
u64 update_counter{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
@@ -4,22 +4,17 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/mii/manager.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
|
||||
|
||||
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
|
||||
public:
|
||||
@@ -31,19 +26,19 @@ public:
|
||||
{2, &IDatabaseService::GetCount, "GetCount"},
|
||||
{3, &IDatabaseService::Get, "Get"},
|
||||
{4, &IDatabaseService::Get1, "Get1"},
|
||||
{5, nullptr, "UpdateLatest"},
|
||||
{5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
|
||||
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
|
||||
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
|
||||
{8, &IDatabaseService::Get2, "Get2"},
|
||||
{9, &IDatabaseService::Get3, "Get3"},
|
||||
{8, nullptr, "Get2"},
|
||||
{9, nullptr, "Get3"},
|
||||
{10, nullptr, "UpdateLatest1"},
|
||||
{11, &IDatabaseService::FindIndex, "FindIndex"},
|
||||
{12, &IDatabaseService::Move, "Move"},
|
||||
{13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
|
||||
{14, &IDatabaseService::Delete, "Delete"},
|
||||
{15, &IDatabaseService::DestroyFile, "DestroyFile"},
|
||||
{16, &IDatabaseService::DeleteFile, "DeleteFile"},
|
||||
{17, &IDatabaseService::Format, "Format"},
|
||||
{11, nullptr, "FindIndex"},
|
||||
{12, nullptr, "Move"},
|
||||
{13, nullptr, "AddOrReplace"},
|
||||
{14, nullptr, "Delete"},
|
||||
{15, nullptr, "DestroyFile"},
|
||||
{16, nullptr, "DeleteFile"},
|
||||
{17, nullptr, "Format"},
|
||||
{18, nullptr, "Import"},
|
||||
{19, nullptr, "Export"},
|
||||
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
|
||||
@@ -59,31 +54,26 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename OutType>
|
||||
std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
|
||||
u32 requested_size, u32& read_size) {
|
||||
read_size = std::min(requested_size, db.Size() - offset);
|
||||
|
||||
std::vector<u8> out(read_size * sizeof(OutType));
|
||||
|
||||
for (u32 i = 0; i < read_size; ++i) {
|
||||
const auto obj = (db.*getter)(offset + i);
|
||||
std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
|
||||
template <typename T>
|
||||
std::vector<u8> SerializeArray(const std::vector<T>& values) {
|
||||
std::vector<u8> out(values.size() * sizeof(T));
|
||||
std::size_t offset{};
|
||||
for (const auto& value : values) {
|
||||
std::memcpy(out.data() + offset, &value, sizeof(T));
|
||||
offset += sizeof(T);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void IsUpdated(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source={}", source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.CheckUpdatedFlag());
|
||||
db.ResetUpdatedFlag();
|
||||
rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
|
||||
}
|
||||
|
||||
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
|
||||
@@ -91,93 +81,126 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.Full());
|
||||
rb.Push(manager.IsFullDatabase());
|
||||
}
|
||||
|
||||
void GetCount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source={}", source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(db.Size());
|
||||
rb.Push<u32>(manager.GetCount(source_flag));
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiInfoElement
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[0], source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
|
||||
offsets[0] += read_size;
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->size() > 0) {
|
||||
ctx.WriteBuffer(SerializeArray(*result));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiInfo
|
||||
void Get1(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[1], source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
|
||||
offsets[1] += read_size;
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<MiiInfo> values;
|
||||
for (const auto& element : *result) {
|
||||
values.emplace_back(element.info);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(SerializeArray(values));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
void UpdateLatest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<MiiInfo>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.UpdateLatest(info, source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(*result);
|
||||
}
|
||||
|
||||
void BuildRandom(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
|
||||
|
||||
if (unknown1 > 3) {
|
||||
const auto age{rp.PopRaw<Age>()};
|
||||
const auto gender{rp.PopRaw<Gender>()};
|
||||
const auto race{rp.PopRaw<Race>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race);
|
||||
|
||||
if (age > Age::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
|
||||
LOG_ERROR(Service_Mii, "invalid age={}", age);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unknown2 > 2) {
|
||||
if (gender > Gender::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
|
||||
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unknown3 > 3) {
|
||||
if (race > Race::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
|
||||
LOG_ERROR(Service_Mii, "invalid race={}", race);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
|
||||
unknown1, unknown2, unknown3);
|
||||
|
||||
const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(info);
|
||||
rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race));
|
||||
}
|
||||
|
||||
void BuildDefault(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto index{rp.PopRaw<u32>()};
|
||||
const auto index{rp.Pop<u32>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with index={}", index);
|
||||
|
||||
if (index > 5) {
|
||||
LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
|
||||
@@ -187,168 +210,20 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
|
||||
|
||||
const auto info = db.CreateDefault(index);
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(info);
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiStoreDataElement
|
||||
void Get2(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[2], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(
|
||||
SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
|
||||
offsets[2] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiStoreData
|
||||
void Get3(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[3], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
|
||||
offsets[3] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
void FindIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
const auto unknown{rp.PopRaw<bool>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
const auto index = db.IndexOf(uuid);
|
||||
if (index > MAX_MIIS) {
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(RESULT_UNKNOWN);
|
||||
rb.Push(index);
|
||||
} else {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(index);
|
||||
}
|
||||
}
|
||||
|
||||
void Move(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
const auto index{rp.PopRaw<s32>()};
|
||||
|
||||
if (index < 0) {
|
||||
LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
|
||||
|
||||
const auto success = db.Move(uuid, index);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
|
||||
}
|
||||
|
||||
void AddOrReplace(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto data{rp.PopRaw<MiiStoreData>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
|
||||
Common::UTF16ToUTF8(data.Name()));
|
||||
|
||||
const auto success = db.AddOrReplace(data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
|
||||
}
|
||||
|
||||
void Delete(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
|
||||
|
||||
const auto success = db.Remove(uuid);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
|
||||
}
|
||||
|
||||
void DestroyFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
if (!db.IsTestModeEnabled()) {
|
||||
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.DestroyFile());
|
||||
}
|
||||
|
||||
void DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
if (!db.IsTestModeEnabled()) {
|
||||
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.DeleteFile());
|
||||
}
|
||||
|
||||
void Format(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
db.Clear();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(manager.BuildDefault(index));
|
||||
}
|
||||
|
||||
void GetIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<MiiInfo>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
|
||||
Common::UTF16ToUTF8(info.Name()));
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
const auto index = db.IndexOf(info);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
u32 index{};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(manager.GetIndex(info, index));
|
||||
rb.Push(index);
|
||||
}
|
||||
|
||||
@@ -364,12 +239,14 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
MiiManager db;
|
||||
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
|
||||
return current_interface_version >= interface_version;
|
||||
}
|
||||
|
||||
u32 current_interface_version = 0;
|
||||
MiiManager manager;
|
||||
|
||||
// Last read offsets of Get functions
|
||||
std::array<u32, 4> offsets{};
|
||||
u32 current_interface_version{};
|
||||
u64 current_update_counter{};
|
||||
};
|
||||
|
||||
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
|
||||
constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
|
||||
|
||||
// This value was retrieved from HW test
|
||||
constexpr MiiStoreData DEFAULT_MII = {
|
||||
{
|
||||
0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
|
||||
0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
|
||||
},
|
||||
{'y', 'u', 'z', 'u', '\0'},
|
||||
Common::UUID{1, 0},
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
// Default values taken from multiple real databases
|
||||
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
|
||||
|
||||
constexpr std::array<const char*, 4> SOURCE_NAMES{
|
||||
"Database",
|
||||
"Default",
|
||||
"Account",
|
||||
"Friend",
|
||||
};
|
||||
|
||||
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
|
||||
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
|
||||
std::array<T, DestArraySize> out{};
|
||||
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
|
||||
return out;
|
||||
}
|
||||
|
||||
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
|
||||
MiiStoreBitFields bf{};
|
||||
std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
|
||||
return {
|
||||
data.uuid,
|
||||
ResizeArray<char16_t, 10, 11>(data.name),
|
||||
static_cast<u8>(bf.font_region.Value()),
|
||||
static_cast<u8>(bf.favorite_color.Value()),
|
||||
static_cast<u8>(bf.gender.Value()),
|
||||
static_cast<u8>(bf.height.Value()),
|
||||
static_cast<u8>(bf.weight.Value()),
|
||||
static_cast<u8>(bf.mii_type.Value()),
|
||||
static_cast<u8>(bf.mii_region.Value()),
|
||||
static_cast<u8>(bf.face_type.Value()),
|
||||
static_cast<u8>(bf.face_color.Value()),
|
||||
static_cast<u8>(bf.face_wrinkle.Value()),
|
||||
static_cast<u8>(bf.face_makeup.Value()),
|
||||
static_cast<u8>(bf.hair_type.Value()),
|
||||
static_cast<u8>(bf.hair_color.Value()),
|
||||
static_cast<bool>(bf.hair_flip.Value()),
|
||||
static_cast<u8>(bf.eye_type.Value()),
|
||||
static_cast<u8>(bf.eye_color.Value()),
|
||||
static_cast<u8>(bf.eye_scale.Value()),
|
||||
static_cast<u8>(bf.eye_aspect.Value()),
|
||||
static_cast<u8>(bf.eye_rotate.Value()),
|
||||
static_cast<u8>(bf.eye_x.Value()),
|
||||
static_cast<u8>(bf.eye_y.Value()),
|
||||
static_cast<u8>(bf.eyebrow_type.Value()),
|
||||
static_cast<u8>(bf.eyebrow_color.Value()),
|
||||
static_cast<u8>(bf.eyebrow_scale.Value()),
|
||||
static_cast<u8>(bf.eyebrow_aspect.Value()),
|
||||
static_cast<u8>(bf.eyebrow_rotate.Value()),
|
||||
static_cast<u8>(bf.eyebrow_x.Value()),
|
||||
static_cast<u8>(bf.eyebrow_y.Value()),
|
||||
static_cast<u8>(bf.nose_type.Value()),
|
||||
static_cast<u8>(bf.nose_scale.Value()),
|
||||
static_cast<u8>(bf.nose_y.Value()),
|
||||
static_cast<u8>(bf.mouth_type.Value()),
|
||||
static_cast<u8>(bf.mouth_color.Value()),
|
||||
static_cast<u8>(bf.mouth_scale.Value()),
|
||||
static_cast<u8>(bf.mouth_aspect.Value()),
|
||||
static_cast<u8>(bf.mouth_y.Value()),
|
||||
static_cast<u8>(bf.facial_hair_color.Value()),
|
||||
static_cast<u8>(bf.beard_type.Value()),
|
||||
static_cast<u8>(bf.mustache_type.Value()),
|
||||
static_cast<u8>(bf.mustache_scale.Value()),
|
||||
static_cast<u8>(bf.mustache_y.Value()),
|
||||
static_cast<u8>(bf.glasses_type.Value()),
|
||||
static_cast<u8>(bf.glasses_color.Value()),
|
||||
static_cast<u8>(bf.glasses_scale.Value()),
|
||||
static_cast<u8>(bf.glasses_y.Value()),
|
||||
static_cast<u8>(bf.mole_type.Value()),
|
||||
static_cast<u8>(bf.mole_scale.Value()),
|
||||
static_cast<u8>(bf.mole_x.Value()),
|
||||
static_cast<u8>(bf.mole_y.Value()),
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
|
||||
MiiStoreData out{};
|
||||
out.name = ResizeArray<char16_t, 11, 10>(info.name);
|
||||
out.uuid = info.uuid;
|
||||
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
bf.hair_type.Assign(info.hair_type);
|
||||
bf.mole_type.Assign(info.mole_type);
|
||||
bf.height.Assign(info.height);
|
||||
bf.hair_flip.Assign(info.hair_flip);
|
||||
bf.weight.Assign(info.weight);
|
||||
bf.hair_color.Assign(info.hair_color);
|
||||
|
||||
bf.gender.Assign(info.gender);
|
||||
bf.eye_color.Assign(info.eye_color);
|
||||
bf.eyebrow_color.Assign(info.eyebrow_color);
|
||||
bf.mouth_color.Assign(info.mouth_color);
|
||||
bf.facial_hair_color.Assign(info.facial_hair_color);
|
||||
|
||||
bf.mii_type.Assign(info.mii_type);
|
||||
bf.glasses_color.Assign(info.glasses_color);
|
||||
bf.font_region.Assign(info.font_region);
|
||||
bf.eye_type.Assign(info.eye_type);
|
||||
bf.mii_region.Assign(info.mii_region);
|
||||
bf.mouth_type.Assign(info.mouth_type);
|
||||
bf.glasses_scale.Assign(info.glasses_scale);
|
||||
bf.eye_y.Assign(info.eye_y);
|
||||
|
||||
bf.mustache_type.Assign(info.mustache_type);
|
||||
bf.eyebrow_type.Assign(info.eyebrow_type);
|
||||
bf.beard_type.Assign(info.beard_type);
|
||||
bf.nose_type.Assign(info.nose_type);
|
||||
bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
|
||||
bf.nose_y.Assign(info.nose_y);
|
||||
bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
|
||||
bf.mouth_y.Assign(info.mouth_y);
|
||||
|
||||
bf.eye_rotate.Assign(info.eye_rotate);
|
||||
bf.mustache_y.Assign(info.mustache_y);
|
||||
bf.eye_aspect.Assign(info.eye_aspect_ratio);
|
||||
bf.glasses_y.Assign(info.glasses_y);
|
||||
bf.eye_scale.Assign(info.eye_scale);
|
||||
bf.mole_x.Assign(info.mole_x);
|
||||
bf.mole_y.Assign(info.mole_y);
|
||||
|
||||
bf.glasses_type.Assign(info.glasses_type);
|
||||
bf.face_type.Assign(info.face_type);
|
||||
bf.favorite_color.Assign(info.favorite_color);
|
||||
bf.face_wrinkle.Assign(info.face_wrinkle);
|
||||
bf.face_color.Assign(info.face_color);
|
||||
bf.eye_x.Assign(info.eye_x);
|
||||
bf.face_makeup.Assign(info.face_makeup);
|
||||
|
||||
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
|
||||
bf.eyebrow_scale.Assign(info.eyebrow_scale);
|
||||
bf.eyebrow_y.Assign(info.eyebrow_y);
|
||||
bf.eyebrow_x.Assign(info.eyebrow_x);
|
||||
bf.mouth_scale.Assign(info.mouth_scale);
|
||||
bf.nose_scale.Assign(info.nose_scale);
|
||||
bf.mole_scale.Assign(info.mole_scale);
|
||||
bf.mustache_scale.Assign(info.mustache_scale);
|
||||
|
||||
std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source) {
|
||||
if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) {
|
||||
return os << "[UNKNOWN SOURCE]";
|
||||
}
|
||||
|
||||
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
|
||||
return os;
|
||||
}
|
||||
|
||||
std::u16string MiiInfo::Name() const {
|
||||
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
||||
}
|
||||
|
||||
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
|
||||
return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
std::u16string MiiStoreData::Name() const {
|
||||
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
||||
}
|
||||
|
||||
MiiManager::MiiManager() = default;
|
||||
|
||||
MiiManager::~MiiManager() = default;
|
||||
|
||||
MiiInfo MiiManager::CreateRandom(RandomParameters params) {
|
||||
LOG_WARNING(Service_Mii,
|
||||
"(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
|
||||
params.unknown_1, params.unknown_2, params.unknown_3);
|
||||
|
||||
return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::CreateDefault(u32 index) {
|
||||
const auto new_mii = CreateMiiWithUniqueUUID();
|
||||
|
||||
database.miis.at(index) = new_mii;
|
||||
|
||||
EnsureDatabasePartition();
|
||||
return ConvertStoreDataToInfo(new_mii);
|
||||
}
|
||||
|
||||
bool MiiManager::CheckUpdatedFlag() const {
|
||||
return updated_flag;
|
||||
}
|
||||
|
||||
void MiiManager::ResetUpdatedFlag() {
|
||||
updated_flag = false;
|
||||
}
|
||||
|
||||
bool MiiManager::IsTestModeEnabled() const {
|
||||
return is_test_mode_enabled;
|
||||
}
|
||||
|
||||
bool MiiManager::Empty() const {
|
||||
return Size() == 0;
|
||||
}
|
||||
|
||||
bool MiiManager::Full() const {
|
||||
return Size() == MAX_MIIS;
|
||||
}
|
||||
|
||||
void MiiManager::Clear() {
|
||||
updated_flag = true;
|
||||
std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
|
||||
}
|
||||
|
||||
u32 MiiManager::Size() const {
|
||||
return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
|
||||
[](const MiiStoreData& elem) { return elem.uuid; }));
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::GetInfo(u32 index) const {
|
||||
return ConvertStoreDataToInfo(GetStoreData(index));
|
||||
}
|
||||
|
||||
MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
|
||||
return {GetInfo(index), Source::Database};
|
||||
}
|
||||
|
||||
MiiStoreData MiiManager::GetStoreData(u32 index) const {
|
||||
return database.miis.at(index);
|
||||
}
|
||||
|
||||
MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
|
||||
return {GetStoreData(index), Source::Database};
|
||||
}
|
||||
|
||||
bool MiiManager::Remove(Common::UUID uuid) {
|
||||
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
|
||||
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return false;
|
||||
|
||||
updated_flag = true;
|
||||
*iter = MiiStoreData{};
|
||||
EnsureDatabasePartition();
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 MiiManager::IndexOf(Common::UUID uuid) const {
|
||||
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
|
||||
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return INVALID_INDEX;
|
||||
|
||||
return static_cast<u32>(std::distance(database.miis.begin(), iter));
|
||||
}
|
||||
|
||||
u32 MiiManager::IndexOf(const MiiInfo& info) const {
|
||||
const auto iter =
|
||||
std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
|
||||
return ConvertStoreDataToInfo(elem) == info;
|
||||
});
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return INVALID_INDEX;
|
||||
|
||||
return static_cast<u32>(std::distance(database.miis.begin(), iter));
|
||||
}
|
||||
|
||||
bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
|
||||
const auto index = IndexOf(uuid);
|
||||
|
||||
if (index == INVALID_INDEX || new_index >= MAX_MIIS)
|
||||
return false;
|
||||
|
||||
updated_flag = true;
|
||||
const auto moving = database.miis[index];
|
||||
const auto replacing = database.miis[new_index];
|
||||
if (replacing.uuid) {
|
||||
database.miis[index] = replacing;
|
||||
database.miis[new_index] = moving;
|
||||
} else {
|
||||
database.miis[index] = MiiStoreData{};
|
||||
database.miis[new_index] = moving;
|
||||
}
|
||||
|
||||
EnsureDatabasePartition();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MiiManager::AddOrReplace(const MiiStoreData& data) {
|
||||
const auto index = IndexOf(data.uuid);
|
||||
|
||||
updated_flag = true;
|
||||
if (index == INVALID_INDEX) {
|
||||
const auto size = Size();
|
||||
if (size == MAX_MIIS)
|
||||
return false;
|
||||
database.miis[size] = data;
|
||||
} else {
|
||||
database.miis[index] = data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MiiManager::DestroyFile() {
|
||||
database = DEFAULT_MII_DATABASE;
|
||||
updated_flag = false;
|
||||
return DeleteFile();
|
||||
}
|
||||
|
||||
bool MiiManager::DeleteFile() {
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
|
||||
return FileUtil::Exists(path) && FileUtil::Delete(path);
|
||||
}
|
||||
|
||||
void MiiManager::WriteToFile() {
|
||||
const auto raw_path =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
|
||||
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
|
||||
FileUtil::Delete(raw_path);
|
||||
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
|
||||
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
LOG_WARNING(Service_Mii,
|
||||
"Failed to create full path of MiiDatabase.dat. Create the directory "
|
||||
"nand/system/save/8000000000000030 to mitigate this "
|
||||
"issue.");
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::IOFile save(path, "wb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
|
||||
"made in current session will be saved.");
|
||||
return;
|
||||
}
|
||||
|
||||
save.Resize(sizeof(MiiDatabase));
|
||||
if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
|
||||
LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
|
||||
"and/or regenerated on next run.");
|
||||
save.Resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MiiManager::ReadFromFile() {
|
||||
FileUtil::IOFile save(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
|
||||
"blank Mii database with no Miis.");
|
||||
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
|
||||
return;
|
||||
}
|
||||
|
||||
if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
|
||||
LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
|
||||
"Mii database with no Miis.");
|
||||
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureDatabasePartition();
|
||||
}
|
||||
|
||||
MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
|
||||
auto new_mii = DEFAULT_MII;
|
||||
|
||||
do {
|
||||
new_mii.uuid = Common::UUID::Generate();
|
||||
} while (IndexOf(new_mii.uuid) != INVALID_INDEX);
|
||||
|
||||
return new_mii;
|
||||
}
|
||||
|
||||
void MiiManager::EnsureDatabasePartition() {
|
||||
std::stable_partition(database.miis.begin(), database.miis.end(),
|
||||
[](const MiiStoreData& elem) { return elem.uuid; });
|
||||
}
|
||||
|
||||
} // namespace Service::Mii
|
||||
@@ -1,273 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
constexpr std::size_t MAX_MIIS{100};
|
||||
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
|
||||
|
||||
struct RandomParameters {
|
||||
u32 unknown_1{};
|
||||
u32 unknown_2{};
|
||||
u32 unknown_3{};
|
||||
};
|
||||
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
|
||||
|
||||
enum class Source : u32 {
|
||||
Database = 0,
|
||||
Default = 1,
|
||||
Account = 2,
|
||||
Friend = 3,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source);
|
||||
|
||||
struct MiiInfo {
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
std::array<char16_t, 11> name{};
|
||||
u8 font_region{};
|
||||
u8 favorite_color{};
|
||||
u8 gender{};
|
||||
u8 height{};
|
||||
u8 weight{};
|
||||
u8 mii_type{};
|
||||
u8 mii_region{};
|
||||
u8 face_type{};
|
||||
u8 face_color{};
|
||||
u8 face_wrinkle{};
|
||||
u8 face_makeup{};
|
||||
u8 hair_type{};
|
||||
u8 hair_color{};
|
||||
bool hair_flip{};
|
||||
u8 eye_type{};
|
||||
u8 eye_color{};
|
||||
u8 eye_scale{};
|
||||
u8 eye_aspect_ratio{};
|
||||
u8 eye_rotate{};
|
||||
u8 eye_x{};
|
||||
u8 eye_y{};
|
||||
u8 eyebrow_type{};
|
||||
u8 eyebrow_color{};
|
||||
u8 eyebrow_scale{};
|
||||
u8 eyebrow_aspect_ratio{};
|
||||
u8 eyebrow_rotate{};
|
||||
u8 eyebrow_x{};
|
||||
u8 eyebrow_y{};
|
||||
u8 nose_type{};
|
||||
u8 nose_scale{};
|
||||
u8 nose_y{};
|
||||
u8 mouth_type{};
|
||||
u8 mouth_color{};
|
||||
u8 mouth_scale{};
|
||||
u8 mouth_aspect_ratio{};
|
||||
u8 mouth_y{};
|
||||
u8 facial_hair_color{};
|
||||
u8 beard_type{};
|
||||
u8 mustache_type{};
|
||||
u8 mustache_scale{};
|
||||
u8 mustache_y{};
|
||||
u8 glasses_type{};
|
||||
u8 glasses_color{};
|
||||
u8 glasses_scale{};
|
||||
u8 glasses_y{};
|
||||
u8 mole_type{};
|
||||
u8 mole_scale{};
|
||||
u8 mole_x{};
|
||||
u8 mole_y{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
|
||||
static_assert(std::has_unique_object_representations_v<MiiInfo>,
|
||||
"All bits of MiiInfo must contribute to its value.");
|
||||
|
||||
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
|
||||
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct MiiInfoElement {
|
||||
MiiInfo info{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
|
||||
|
||||
struct MiiStoreBitFields {
|
||||
union {
|
||||
u32 word_0{};
|
||||
|
||||
BitField<24, 8, u32> hair_type;
|
||||
BitField<23, 1, u32> mole_type;
|
||||
BitField<16, 7, u32> height;
|
||||
BitField<15, 1, u32> hair_flip;
|
||||
BitField<8, 7, u32> weight;
|
||||
BitField<0, 7, u32> hair_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_1{};
|
||||
|
||||
BitField<31, 1, u32> gender;
|
||||
BitField<24, 7, u32> eye_color;
|
||||
BitField<16, 7, u32> eyebrow_color;
|
||||
BitField<8, 7, u32> mouth_color;
|
||||
BitField<0, 7, u32> facial_hair_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_2{};
|
||||
|
||||
BitField<31, 1, u32> mii_type;
|
||||
BitField<24, 7, u32> glasses_color;
|
||||
BitField<22, 2, u32> font_region;
|
||||
BitField<16, 6, u32> eye_type;
|
||||
BitField<14, 2, u32> mii_region;
|
||||
BitField<8, 6, u32> mouth_type;
|
||||
BitField<5, 3, u32> glasses_scale;
|
||||
BitField<0, 5, u32> eye_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_3{};
|
||||
|
||||
BitField<29, 3, u32> mustache_type;
|
||||
BitField<24, 5, u32> eyebrow_type;
|
||||
BitField<21, 3, u32> beard_type;
|
||||
BitField<16, 5, u32> nose_type;
|
||||
BitField<13, 3, u32> mouth_aspect;
|
||||
BitField<8, 5, u32> nose_y;
|
||||
BitField<5, 3, u32> eyebrow_aspect;
|
||||
BitField<0, 5, u32> mouth_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_4{};
|
||||
|
||||
BitField<29, 3, u32> eye_rotate;
|
||||
BitField<24, 5, u32> mustache_y;
|
||||
BitField<21, 3, u32> eye_aspect;
|
||||
BitField<16, 5, u32> glasses_y;
|
||||
BitField<13, 3, u32> eye_scale;
|
||||
BitField<8, 5, u32> mole_x;
|
||||
BitField<0, 5, u32> mole_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_5{};
|
||||
|
||||
BitField<24, 5, u32> glasses_type;
|
||||
BitField<20, 4, u32> face_type;
|
||||
BitField<16, 4, u32> favorite_color;
|
||||
BitField<12, 4, u32> face_wrinkle;
|
||||
BitField<8, 4, u32> face_color;
|
||||
BitField<4, 4, u32> eye_x;
|
||||
BitField<0, 4, u32> face_makeup;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_6{};
|
||||
|
||||
BitField<28, 4, u32> eyebrow_rotate;
|
||||
BitField<24, 4, u32> eyebrow_scale;
|
||||
BitField<20, 4, u32> eyebrow_y;
|
||||
BitField<16, 4, u32> eyebrow_x;
|
||||
BitField<12, 4, u32> mouth_scale;
|
||||
BitField<8, 4, u32> nose_scale;
|
||||
BitField<4, 4, u32> mole_scale;
|
||||
BitField<0, 4, u32> mustache_scale;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
|
||||
"MiiStoreBitFields is not trivially copyable.");
|
||||
|
||||
struct MiiStoreData {
|
||||
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
||||
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
|
||||
// not suitable for our uses.
|
||||
std::array<u8, 0x1C> data{};
|
||||
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
|
||||
|
||||
std::array<char16_t, 10> name{};
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
u16 crc_1{};
|
||||
u16 crc_2{};
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
|
||||
|
||||
struct MiiStoreDataElement {
|
||||
MiiStoreData data{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
|
||||
|
||||
struct MiiDatabase {
|
||||
u32 magic{}; // 'NFDB'
|
||||
std::array<MiiStoreData, MAX_MIIS> miis{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 count{};
|
||||
u16 crc{};
|
||||
};
|
||||
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
|
||||
#pragma pack(pop)
|
||||
|
||||
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
|
||||
// with providing an easy interface for HLE emulation of the mii service.
|
||||
class MiiManager {
|
||||
public:
|
||||
MiiManager();
|
||||
~MiiManager();
|
||||
|
||||
MiiInfo CreateRandom(RandomParameters params);
|
||||
MiiInfo CreateDefault(u32 index);
|
||||
|
||||
bool CheckUpdatedFlag() const;
|
||||
void ResetUpdatedFlag();
|
||||
|
||||
bool IsTestModeEnabled() const;
|
||||
|
||||
bool Empty() const;
|
||||
bool Full() const;
|
||||
|
||||
void Clear();
|
||||
|
||||
u32 Size() const;
|
||||
|
||||
MiiInfo GetInfo(u32 index) const;
|
||||
MiiInfoElement GetInfoElement(u32 index) const;
|
||||
MiiStoreData GetStoreData(u32 index) const;
|
||||
MiiStoreDataElement GetStoreDataElement(u32 index) const;
|
||||
|
||||
bool Remove(Common::UUID uuid);
|
||||
u32 IndexOf(Common::UUID uuid) const;
|
||||
u32 IndexOf(const MiiInfo& info) const;
|
||||
|
||||
bool Move(Common::UUID uuid, u32 new_index);
|
||||
bool AddOrReplace(const MiiStoreData& data);
|
||||
|
||||
bool DestroyFile();
|
||||
bool DeleteFile();
|
||||
|
||||
private:
|
||||
void WriteToFile();
|
||||
void ReadFromFile();
|
||||
|
||||
MiiStoreData CreateMiiWithUniqueUUID() const;
|
||||
|
||||
void EnsureDatabasePartition();
|
||||
|
||||
MiiDatabase database;
|
||||
bool updated_flag{};
|
||||
bool is_test_mode_enabled{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
2261
src/core/hle/service/mii/raw_data.cpp
Normal file
2261
src/core/hle/service/mii/raw_data.cpp
Normal file
File diff suppressed because it is too large
Load Diff
27
src/core/hle/service/mii/raw_data.h
Normal file
27
src/core/hle/service/mii/raw_data.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Mii::RawData {
|
||||
|
||||
extern const std::array<u8, 1728> DefaultMii;
|
||||
extern const std::array<u8, 3672> RandomMiiFaceline;
|
||||
extern const std::array<u8, 1200> RandomMiiFacelineColor;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineWrinkle;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineMakeup;
|
||||
extern const std::array<u8, 3672> RandomMiiHairType;
|
||||
extern const std::array<u8, 1800> RandomMiiHairColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyeType;
|
||||
extern const std::array<u8, 588> RandomMiiEyeColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyebrowType;
|
||||
extern const std::array<u8, 3672> RandomMiiNoseType;
|
||||
extern const std::array<u8, 3672> RandomMiiMouthType;
|
||||
extern const std::array<u8, 588> RandomMiiGlassType;
|
||||
|
||||
} // namespace Service::Mii::RawData
|
||||
67
src/core/hle/service/mii/types.h
Normal file
67
src/core/hle/service/mii/types.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
enum class Age : u32 {
|
||||
Young,
|
||||
Normal,
|
||||
Old,
|
||||
All,
|
||||
};
|
||||
|
||||
enum class BeardType : u32 {
|
||||
None,
|
||||
Beard1,
|
||||
Beard2,
|
||||
Beard3,
|
||||
Beard4,
|
||||
Beard5,
|
||||
};
|
||||
|
||||
enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache };
|
||||
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
|
||||
|
||||
enum class FontRegion : u32 {
|
||||
Standard,
|
||||
China,
|
||||
Korea,
|
||||
Taiwan,
|
||||
};
|
||||
|
||||
enum class Gender : u32 {
|
||||
Male,
|
||||
Female,
|
||||
All,
|
||||
Maximum = Female,
|
||||
};
|
||||
|
||||
enum class HairFlip : u32 {
|
||||
Left,
|
||||
Right,
|
||||
Maximum = Right,
|
||||
};
|
||||
|
||||
enum class MustacheType : u32 {
|
||||
None,
|
||||
Mustache1,
|
||||
Mustache2,
|
||||
Mustache3,
|
||||
Mustache4,
|
||||
Mustache5,
|
||||
};
|
||||
|
||||
enum class Race : u32 {
|
||||
Black,
|
||||
White,
|
||||
Asian,
|
||||
All,
|
||||
};
|
||||
|
||||
} // namespace Service::Mii
|
||||
@@ -28,8 +28,7 @@
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60);
|
||||
constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30);
|
||||
constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
|
||||
|
||||
void NVFlinger::VSyncThread(NVFlinger& nv_flinger) {
|
||||
nv_flinger.SplitVSync();
|
||||
@@ -67,20 +66,24 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
|
||||
guard = std::make_shared<std::mutex>();
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event =
|
||||
Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) {
|
||||
composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition", [this](u64, std::chrono::nanoseconds ns_late) {
|
||||
Lock();
|
||||
Compose();
|
||||
const auto ticks = GetNextTicks();
|
||||
this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late),
|
||||
composition_event);
|
||||
|
||||
const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
|
||||
const auto ticks_delta = ticks - ns_late;
|
||||
const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
|
||||
|
||||
this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
|
||||
});
|
||||
|
||||
if (system.IsMulticore()) {
|
||||
is_running = true;
|
||||
wait_event = std::make_unique<Common::Event>();
|
||||
vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this));
|
||||
} else {
|
||||
system.CoreTiming().ScheduleEvent(frame_ticks, composition_event);
|
||||
system.CoreTiming().ScheduleEvent(frame_ns, composition_event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace Core::Memory {
|
||||
|
||||
constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12);
|
||||
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
|
||||
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
|
||||
|
||||
StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
|
||||
@@ -188,10 +188,12 @@ CheatEngine::~CheatEngine() {
|
||||
}
|
||||
|
||||
void CheatEngine::Initialize() {
|
||||
event = Core::Timing::CreateEvent(
|
||||
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
|
||||
[this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); });
|
||||
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
|
||||
event = Core::Timing::CreateEvent("CheatEngine::FrameCallback::" +
|
||||
Common::HexToString(metadata.main_nso_build_id),
|
||||
[this](u64 userdata, std::chrono::nanoseconds ns_late) {
|
||||
FrameCallback(userdata, ns_late);
|
||||
});
|
||||
core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event);
|
||||
|
||||
metadata.process_id = system.CurrentProcess()->GetProcessID();
|
||||
metadata.title_id = system.CurrentProcess()->GetTitleID();
|
||||
@@ -217,7 +219,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
|
||||
|
||||
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
|
||||
|
||||
void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) {
|
||||
void CheatEngine::FrameCallback(u64, std::chrono::nanoseconds ns_late) {
|
||||
if (is_pending_reload.exchange(false)) {
|
||||
vm.LoadProgram(cheats);
|
||||
}
|
||||
@@ -230,7 +232,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) {
|
||||
|
||||
vm.Execute(metadata);
|
||||
|
||||
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event);
|
||||
core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
@@ -71,7 +72,7 @@ public:
|
||||
void Reload(std::vector<CheatEntry> cheats);
|
||||
|
||||
private:
|
||||
void FrameCallback(u64 userdata, s64 cycles_late);
|
||||
void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late);
|
||||
|
||||
DmntCheatVm vm;
|
||||
CheatProcessMetadata metadata;
|
||||
|
||||
@@ -190,6 +190,15 @@ void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i]));
|
||||
}
|
||||
} else if (auto rw_static_reg = std::get_if<ReadWriteStaticRegisterOpcode>(&opcode.opcode)) {
|
||||
callbacks->CommandLog("Opcode: Read/Write Static Register");
|
||||
if (rw_static_reg->static_idx < NumReadableStaticRegisters) {
|
||||
callbacks->CommandLog("Op Type: ReadStaticRegister");
|
||||
} else {
|
||||
callbacks->CommandLog("Op Type: WriteStaticRegister");
|
||||
}
|
||||
callbacks->CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
|
||||
callbacks->CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
|
||||
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
|
||||
callbacks->CommandLog("Opcode: Debug Log");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
|
||||
@@ -544,6 +553,16 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
|
||||
}
|
||||
opcode.opcode = save_restore_regmask;
|
||||
} break;
|
||||
case CheatVmOpcodeType::ReadWriteStaticRegister: {
|
||||
ReadWriteStaticRegisterOpcode rw_static_reg{};
|
||||
// C3000XXx
|
||||
// C3 = opcode 0xC3.
|
||||
// XX = static register index.
|
||||
// x = register index.
|
||||
rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
|
||||
rw_static_reg.idx = (first_dword & 0xF);
|
||||
opcode.opcode = rw_static_reg;
|
||||
} break;
|
||||
case CheatVmOpcodeType::DebugLog: {
|
||||
DebugLogOpcode debug_log{};
|
||||
// FFFTIX##
|
||||
@@ -667,6 +686,7 @@ void DmntCheatVm::ResetState() {
|
||||
registers.fill(0);
|
||||
saved_values.fill(0);
|
||||
loop_tops.fill(0);
|
||||
static_registers.fill(0);
|
||||
instruction_ptr = 0;
|
||||
condition_depth = 0;
|
||||
decode_success = true;
|
||||
@@ -1153,6 +1173,15 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (auto rw_static_reg =
|
||||
std::get_if<ReadWriteStaticRegisterOpcode>(&cur_opcode.opcode)) {
|
||||
if (rw_static_reg->static_idx < NumReadableStaticRegisters) {
|
||||
// Load a register with a static register.
|
||||
registers[rw_static_reg->idx] = static_registers[rw_static_reg->static_idx];
|
||||
} else {
|
||||
// Store a register to a static register.
|
||||
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
|
||||
}
|
||||
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
|
||||
// Read value from memory.
|
||||
u64 log_value = 0;
|
||||
|
||||
@@ -56,6 +56,7 @@ enum class CheatVmOpcodeType : u32 {
|
||||
BeginRegisterConditionalBlock = 0xC0,
|
||||
SaveRestoreRegister = 0xC1,
|
||||
SaveRestoreRegisterMask = 0xC2,
|
||||
ReadWriteStaticRegister = 0xC3,
|
||||
|
||||
// This is a meta entry, and not a real opcode.
|
||||
// This is to facilitate multi-nybble instruction decoding.
|
||||
@@ -237,6 +238,11 @@ struct SaveRestoreRegisterMaskOpcode {
|
||||
std::array<bool, 0x10> should_operate{};
|
||||
};
|
||||
|
||||
struct ReadWriteStaticRegisterOpcode {
|
||||
u32 static_idx{};
|
||||
u32 idx{};
|
||||
};
|
||||
|
||||
struct DebugLogOpcode {
|
||||
u32 bit_width{};
|
||||
u32 log_id{};
|
||||
@@ -259,7 +265,8 @@ struct CheatVmOpcode {
|
||||
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
|
||||
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
|
||||
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
|
||||
SaveRestoreRegisterMaskOpcode, DebugLogOpcode, UnrecognizedInstruction>
|
||||
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode,
|
||||
UnrecognizedInstruction>
|
||||
opcode{};
|
||||
};
|
||||
|
||||
@@ -281,6 +288,10 @@ public:
|
||||
|
||||
static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
|
||||
static constexpr std::size_t NumRegisters = 0x10;
|
||||
static constexpr std::size_t NumReadableStaticRegisters = 0x80;
|
||||
static constexpr std::size_t NumWritableStaticRegisters = 0x80;
|
||||
static constexpr std::size_t NumStaticRegisters =
|
||||
NumReadableStaticRegisters + NumWritableStaticRegisters;
|
||||
|
||||
explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks);
|
||||
~DmntCheatVm();
|
||||
@@ -302,6 +313,7 @@ private:
|
||||
std::array<u32, MaximumProgramOpcodeCount> program{};
|
||||
std::array<u64, NumRegisters> registers{};
|
||||
std::array<u64, NumRegisters> saved_values{};
|
||||
std::array<u64, NumStaticRegisters> static_registers{};
|
||||
std::array<std::size_t, NumRegisters> loop_tops{};
|
||||
|
||||
bool DecodeNextOpcode(CheatVmOpcode& out);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
@@ -65,18 +67,18 @@ Values values = {};
|
||||
bool configuring_global = true;
|
||||
|
||||
std::string GetTimeZoneString() {
|
||||
static constexpr std::array<const char*, 46> timezones{{
|
||||
static constexpr std::array timezones{
|
||||
"auto", "default", "CET", "CST6CDT", "Cuba", "EET", "Egypt", "Eire",
|
||||
"EST", "EST5EDT", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0",
|
||||
"Greenwich", "Hongkong", "HST", "Iceland", "Iran", "Israel", "Jamaica", "Japan",
|
||||
"Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Navajo", "NZ", "NZ-CHAT",
|
||||
"Poland", "Portugal", "PRC", "PST8PDT", "ROC", "ROK", "Singapore", "Turkey",
|
||||
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
|
||||
}};
|
||||
};
|
||||
|
||||
ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
|
||||
|
||||
return timezones[Settings::values.time_zone_index.GetValue()];
|
||||
const auto time_zone_index = static_cast<std::size_t>(values.time_zone_index.GetValue());
|
||||
ASSERT(time_zone_index < timezones.size());
|
||||
return timezones[time_zone_index];
|
||||
}
|
||||
|
||||
void Apply() {
|
||||
@@ -91,41 +93,41 @@ void Apply() {
|
||||
Service::HID::ReloadInputDevices();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LogSetting(const std::string& name, const T& value) {
|
||||
LOG_INFO(Config, "{}: {}", name, value);
|
||||
}
|
||||
|
||||
void LogSettings() {
|
||||
const auto log_setting = [](std::string_view name, const auto& value) {
|
||||
LOG_INFO(Config, "{}: {}", name, value);
|
||||
};
|
||||
|
||||
LOG_INFO(Config, "yuzu Configuration:");
|
||||
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.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.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.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));
|
||||
LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
|
||||
LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
|
||||
LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
|
||||
LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
|
||||
LogSetting("Services_BCATBackend", Settings::values.bcat_backend);
|
||||
LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
|
||||
log_setting("Controls_UseDockedMode", values.use_docked_mode);
|
||||
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
|
||||
log_setting("System_CurrentUser", values.current_user);
|
||||
log_setting("System_LanguageIndex", values.language_index.GetValue());
|
||||
log_setting("System_RegionIndex", values.region_index.GetValue());
|
||||
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
|
||||
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
|
||||
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
|
||||
log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
|
||||
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
|
||||
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
|
||||
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousGpuEmulation",
|
||||
values.use_asynchronous_gpu_emulation.GetValue());
|
||||
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
|
||||
log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
|
||||
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
|
||||
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
|
||||
log_setting("Audio_OutputEngine", values.sink_id);
|
||||
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
|
||||
log_setting("Audio_OutputDevice", values.audio_device_id);
|
||||
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
|
||||
log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
|
||||
log_setting("Debugging_UseGdbstub", values.use_gdbstub);
|
||||
log_setting("Debugging_GdbstubPort", values.gdbstub_port);
|
||||
log_setting("Debugging_ProgramArgs", values.program_args);
|
||||
log_setting("Services_BCATBackend", values.bcat_backend);
|
||||
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
|
||||
}
|
||||
|
||||
float Volume() {
|
||||
@@ -169,6 +171,7 @@ void RestoreGlobalState() {
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.use_assembly_shaders.SetGlobal(true);
|
||||
values.use_asynchronous_shaders.SetGlobal(true);
|
||||
values.use_fast_gpu_time.SetGlobal(true);
|
||||
values.force_30fps_mode.SetGlobal(true);
|
||||
values.bg_red.SetGlobal(true);
|
||||
@@ -184,4 +187,9 @@ void RestoreGlobalState() {
|
||||
values.sound_index.SetGlobal(true);
|
||||
}
|
||||
|
||||
void Sanitize() {
|
||||
values.use_asynchronous_gpu_emulation.SetValue(
|
||||
values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue());
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -346,31 +346,6 @@ struct TouchscreenInput {
|
||||
u32 rotation_angle;
|
||||
};
|
||||
|
||||
enum class NANDTotalSize : u64 {
|
||||
S29_1GB = 0x747C00000ULL,
|
||||
};
|
||||
|
||||
enum class NANDUserSize : u64 {
|
||||
S26GB = 0x680000000ULL,
|
||||
};
|
||||
|
||||
enum class NANDSystemSize : u64 {
|
||||
S2_5GB = 0xA0000000,
|
||||
};
|
||||
|
||||
enum class SDMCSize : u64 {
|
||||
S1GB = 0x40000000,
|
||||
S2GB = 0x80000000,
|
||||
S4GB = 0x100000000ULL,
|
||||
S8GB = 0x200000000ULL,
|
||||
S16GB = 0x400000000ULL,
|
||||
S32GB = 0x800000000ULL,
|
||||
S64GB = 0x1000000000ULL,
|
||||
S128GB = 0x2000000000ULL,
|
||||
S256GB = 0x4000000000ULL,
|
||||
S1TB = 0x10000000000ULL,
|
||||
};
|
||||
|
||||
enum class RendererBackend {
|
||||
OpenGL = 0,
|
||||
Vulkan = 1,
|
||||
@@ -382,6 +357,11 @@ enum class GPUAccuracy : u32 {
|
||||
Extreme = 2,
|
||||
};
|
||||
|
||||
enum class CPUAccuracy {
|
||||
Accurate = 0,
|
||||
DebugMode = 1,
|
||||
};
|
||||
|
||||
extern bool configuring_global;
|
||||
|
||||
template <typename Type>
|
||||
@@ -427,6 +407,18 @@ struct Values {
|
||||
// Core
|
||||
Setting<bool> use_multi_core;
|
||||
|
||||
// Cpu
|
||||
CPUAccuracy cpu_accuracy;
|
||||
|
||||
bool cpuopt_page_tables;
|
||||
bool cpuopt_block_linking;
|
||||
bool cpuopt_return_stack_buffer;
|
||||
bool cpuopt_fast_dispatcher;
|
||||
bool cpuopt_context_elimination;
|
||||
bool cpuopt_const_prop;
|
||||
bool cpuopt_misc_ir;
|
||||
bool cpuopt_reduce_misalign_checks;
|
||||
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend;
|
||||
bool renderer_debug;
|
||||
@@ -442,6 +434,7 @@ struct Values {
|
||||
Setting<bool> use_asynchronous_gpu_emulation;
|
||||
Setting<bool> use_vsync;
|
||||
Setting<bool> use_assembly_shaders;
|
||||
Setting<bool> use_asynchronous_shaders;
|
||||
Setting<bool> force_30fps_mode;
|
||||
Setting<bool> use_fast_gpu_time;
|
||||
|
||||
@@ -491,10 +484,6 @@ struct Values {
|
||||
bool gamecard_inserted;
|
||||
bool gamecard_current_game;
|
||||
std::string gamecard_path;
|
||||
NANDTotalSize nand_total_size;
|
||||
NANDSystemSize nand_system_size;
|
||||
NANDUserSize nand_user_size;
|
||||
SDMCSize sdmc_size;
|
||||
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
@@ -505,7 +494,6 @@ struct Values {
|
||||
bool dump_nso;
|
||||
bool reporting_services;
|
||||
bool quest_flag;
|
||||
bool disable_cpu_opt;
|
||||
bool disable_macro_jit;
|
||||
|
||||
// Misceallaneous
|
||||
@@ -539,4 +527,7 @@ void LogSettings();
|
||||
// Restore the global state of all applicable settings in the Values struct
|
||||
void RestoreGlobalState();
|
||||
|
||||
// Fixes settings that are known to cause issues with the emulator
|
||||
void Sanitize();
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -207,6 +207,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders",
|
||||
Settings::values.use_assembly_shaders.GetValue());
|
||||
AddField(field_type, "Renderer_UseAsynchronousShaders",
|
||||
Settings::values.use_asynchronous_shaders.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace Tools {
|
||||
namespace {
|
||||
|
||||
constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60);
|
||||
constexpr auto memory_freezer_ns = std::chrono::nanoseconds{1000000000 / 60};
|
||||
|
||||
u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
|
||||
switch (width) {
|
||||
@@ -55,10 +55,11 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
|
||||
|
||||
Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
|
||||
: core_timing{core_timing_}, memory{memory_} {
|
||||
event = Core::Timing::CreateEvent(
|
||||
"MemoryFreezer::FrameCallback",
|
||||
[this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); });
|
||||
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
|
||||
event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
|
||||
[this](u64 userdata, std::chrono::nanoseconds ns_late) {
|
||||
FrameCallback(userdata, ns_late);
|
||||
});
|
||||
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
||||
}
|
||||
|
||||
Freezer::~Freezer() {
|
||||
@@ -68,7 +69,7 @@ Freezer::~Freezer() {
|
||||
void Freezer::SetActive(bool active) {
|
||||
if (!this->active.exchange(active)) {
|
||||
FillEntryReads();
|
||||
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
|
||||
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
||||
LOG_DEBUG(Common_Memory, "Memory freezer activated!");
|
||||
} else {
|
||||
LOG_DEBUG(Common_Memory, "Memory freezer deactivated!");
|
||||
@@ -158,7 +159,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
|
||||
return entries;
|
||||
}
|
||||
|
||||
void Freezer::FrameCallback(u64 userdata, s64 ns_late) {
|
||||
void Freezer::FrameCallback(u64, std::chrono::nanoseconds ns_late) {
|
||||
if (!IsActive()) {
|
||||
LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
|
||||
return;
|
||||
@@ -173,7 +174,7 @@ void Freezer::FrameCallback(u64 userdata, s64 ns_late) {
|
||||
MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event);
|
||||
core_timing.ScheduleEvent(memory_freezer_ns - ns_late, event);
|
||||
}
|
||||
|
||||
void Freezer::FillEntryReads() {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
@@ -72,7 +73,7 @@ public:
|
||||
std::vector<Entry> GetEntries() const;
|
||||
|
||||
private:
|
||||
void FrameCallback(u64 userdata, s64 cycles_late);
|
||||
void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late);
|
||||
void FillEntryReads();
|
||||
|
||||
std::atomic_bool active{false};
|
||||
|
||||
@@ -30,7 +30,8 @@ if(SDL2_FOUND)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
|
||||
target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <libusb.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
|
||||
@@ -24,6 +25,7 @@ Adapter::Adapter() {
|
||||
LOG_INFO(Input, "GC Adapter Initialization started");
|
||||
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
get_origin.fill(true);
|
||||
|
||||
const int init_res = libusb_init(&libusb_ctx);
|
||||
if (init_res == LIBUSB_SUCCESS) {
|
||||
@@ -33,15 +35,10 @@ Adapter::Adapter() {
|
||||
}
|
||||
}
|
||||
|
||||
GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
|
||||
GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
|
||||
GCPadStatus pad = {};
|
||||
bool get_origin = false;
|
||||
|
||||
ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
|
||||
if (type != ControllerTypes::None) {
|
||||
get_origin = true;
|
||||
}
|
||||
|
||||
adapter_controllers_status[port] = type;
|
||||
|
||||
static constexpr std::array<PadButton, 8> b1_buttons{
|
||||
@@ -57,6 +54,11 @@ GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_pa
|
||||
PadButton::PAD_TRIGGER_L,
|
||||
};
|
||||
|
||||
if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) {
|
||||
// Controller may have been disconnected, recalibrate if reconnected.
|
||||
get_origin[port] = true;
|
||||
}
|
||||
|
||||
if (adapter_controllers_status[port] != ControllerTypes::None) {
|
||||
const u8 b1 = adapter_payload[1 + (9 * port) + 1];
|
||||
const u8 b2 = adapter_payload[1 + (9 * port) + 2];
|
||||
@@ -73,16 +75,22 @@ GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_pa
|
||||
}
|
||||
}
|
||||
|
||||
if (get_origin) {
|
||||
pad.button |= PAD_GET_ORIGIN;
|
||||
}
|
||||
|
||||
pad.stick_x = adapter_payload[1 + (9 * port) + 3];
|
||||
pad.stick_y = adapter_payload[1 + (9 * port) + 4];
|
||||
pad.substick_x = adapter_payload[1 + (9 * port) + 5];
|
||||
pad.substick_y = adapter_payload[1 + (9 * port) + 6];
|
||||
pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
|
||||
pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
|
||||
|
||||
if (get_origin[port]) {
|
||||
origin_status[port].stick_x = pad.stick_x;
|
||||
origin_status[port].stick_y = pad.stick_y;
|
||||
origin_status[port].substick_x = pad.substick_x;
|
||||
origin_status[port].substick_y = pad.substick_y;
|
||||
origin_status[port].trigger_left = pad.trigger_left;
|
||||
origin_status[port].trigger_right = pad.trigger_right;
|
||||
get_origin[port] = false;
|
||||
}
|
||||
}
|
||||
return pad;
|
||||
}
|
||||
@@ -131,31 +139,31 @@ void Adapter::Read() {
|
||||
for (std::size_t port = 0; port < pads.size(); ++port) {
|
||||
pads[port] = GetPadStatus(port, adapter_payload_copy);
|
||||
if (DeviceConnected(port) && configuring) {
|
||||
if (pads[port].button != PAD_GET_ORIGIN) {
|
||||
if (pads[port].button != 0) {
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
|
||||
// Accounting for a threshold here because of some controller variance
|
||||
if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD ||
|
||||
pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) {
|
||||
if (pads[port].stick_x > origin_status[port].stick_x + pads[port].THRESHOLD ||
|
||||
pads[port].stick_x < origin_status[port].stick_x - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::StickX;
|
||||
pads[port].axis_value = pads[port].stick_x;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD ||
|
||||
pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) {
|
||||
if (pads[port].stick_y > origin_status[port].stick_y + pads[port].THRESHOLD ||
|
||||
pads[port].stick_y < origin_status[port].stick_y - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::StickY;
|
||||
pads[port].axis_value = pads[port].stick_y;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD ||
|
||||
pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) {
|
||||
if (pads[port].substick_x > origin_status[port].substick_x + pads[port].THRESHOLD ||
|
||||
pads[port].substick_x < origin_status[port].substick_x - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::SubstickX;
|
||||
pads[port].axis_value = pads[port].substick_x;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD ||
|
||||
pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) {
|
||||
if (pads[port].substick_y > origin_status[port].substick_y + pads[port].THRESHOLD ||
|
||||
pads[port].substick_y < origin_status[port].substick_y - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::SubstickY;
|
||||
pads[port].axis_value = pads[port].substick_y;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
@@ -198,7 +206,7 @@ void Adapter::StartScanThread() {
|
||||
}
|
||||
|
||||
detect_thread_running = true;
|
||||
detect_thread = std::thread([=] { ScanThreadFunc(); });
|
||||
detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
|
||||
}
|
||||
|
||||
void Adapter::StopScanThread() {
|
||||
@@ -227,7 +235,7 @@ void Adapter::Setup() {
|
||||
}
|
||||
|
||||
if (devices != nullptr) {
|
||||
for (std::size_t index = 0; index < device_count; ++index) {
|
||||
for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
|
||||
if (CheckDeviceAccess(devices[index])) {
|
||||
// GC Adapter found and accessible, registering it
|
||||
GetGCEndpoint(devices[index]);
|
||||
@@ -236,6 +244,9 @@ void Adapter::Setup() {
|
||||
}
|
||||
libusb_free_device_list(devices, 1);
|
||||
}
|
||||
// Break out of the ScanThreadFunc() loop that is constantly looking for the device
|
||||
// Assumes user has GC adapter plugged in before launch to use the adapter
|
||||
detect_thread_running = false;
|
||||
}
|
||||
|
||||
bool Adapter::CheckDeviceAccess(libusb_device* device) {
|
||||
@@ -344,6 +355,7 @@ void Adapter::Reset() {
|
||||
adapter_input_thread.join();
|
||||
|
||||
adapter_controllers_status.fill(ControllerTypes::None);
|
||||
get_origin.fill(true);
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
|
||||
if (usb_adapter_handle) {
|
||||
@@ -357,15 +369,16 @@ void Adapter::Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Adapter::DeviceConnected(int port) {
|
||||
bool Adapter::DeviceConnected(std::size_t port) {
|
||||
return adapter_controllers_status[port] != ControllerTypes::None;
|
||||
}
|
||||
|
||||
void Adapter::ResetDeviceType(int port) {
|
||||
void Adapter::ResetDeviceType(std::size_t port) {
|
||||
adapter_controllers_status[port] = ControllerTypes::None;
|
||||
}
|
||||
|
||||
void Adapter::BeginConfiguration() {
|
||||
get_origin.fill(true);
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
@@ -395,4 +408,25 @@ const std::array<GCState, 4>& Adapter::GetPadState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
int Adapter::GetOriginValue(int port, int axis) const {
|
||||
const auto& status = origin_status[port];
|
||||
|
||||
switch (static_cast<PadAxes>(axis)) {
|
||||
case PadAxes::StickX:
|
||||
return status.stick_x;
|
||||
case PadAxes::StickY:
|
||||
return status.stick_y;
|
||||
case PadAxes::SubstickX:
|
||||
return status.substick_x;
|
||||
case PadAxes::SubstickY:
|
||||
return status.substick_y;
|
||||
case PadAxes::TriggerLeft:
|
||||
return status.trigger_left;
|
||||
case PadAxes::TriggerRight:
|
||||
return status.trigger_right;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace GCAdapter
|
||||
|
||||
@@ -8,17 +8,14 @@
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <libusb.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
namespace GCAdapter {
|
||||
struct libusb_context;
|
||||
struct libusb_device;
|
||||
struct libusb_device_handle;
|
||||
|
||||
enum {
|
||||
PAD_USE_ORIGIN = 0x0080,
|
||||
PAD_GET_ORIGIN = 0x2000,
|
||||
PAD_ERR_STATUS = 0x8000,
|
||||
};
|
||||
namespace GCAdapter {
|
||||
|
||||
enum class PadButton {
|
||||
PAD_BUTTON_LEFT = 0x0001,
|
||||
@@ -97,14 +94,19 @@ public:
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(std::size_t port);
|
||||
|
||||
std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
|
||||
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
|
||||
|
||||
std::array<GCState, 4>& GetPadState();
|
||||
const std::array<GCState, 4>& GetPadState() const;
|
||||
|
||||
int GetOriginValue(int port, int axis) const;
|
||||
|
||||
private:
|
||||
GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload);
|
||||
GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
|
||||
|
||||
void PadToState(const GCPadStatus& pad, GCState& state);
|
||||
|
||||
@@ -116,11 +118,8 @@ private:
|
||||
/// Stop scanning for the adapter
|
||||
void StopScanThread();
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(int port);
|
||||
|
||||
/// Resets status of device connected to port
|
||||
void ResetDeviceType(int port);
|
||||
void ResetDeviceType(std::size_t port);
|
||||
|
||||
/// Returns true if we successfully gain access to GC Adapter
|
||||
bool CheckDeviceAccess(libusb_device* device);
|
||||
@@ -156,6 +155,8 @@ private:
|
||||
|
||||
std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
|
||||
std::array<GCState, 4> state;
|
||||
std::array<bool, 4> get_origin;
|
||||
std::array<GCPadStatus, 4> origin_status;
|
||||
};
|
||||
|
||||
} // namespace GCAdapter
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
@@ -20,7 +21,10 @@ public:
|
||||
~GCButton() override;
|
||||
|
||||
bool GetStatus() const override {
|
||||
return gcadapter->GetPadState()[port].buttons.at(button);
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
return gcadapter->GetPadState()[port].buttons.at(button);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -34,22 +38,20 @@ public:
|
||||
explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
|
||||
GCAdapter::Adapter* adapter)
|
||||
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
|
||||
gcadapter(adapter) {
|
||||
// L/R triggers range is only in positive direction beginning near 0
|
||||
// 0.0 threshold equates to near half trigger press, but threshold accounts for variability.
|
||||
if (axis > 3) {
|
||||
threshold *= -0.5;
|
||||
}
|
||||
}
|
||||
gcadapter(adapter), origin_value(adapter->GetOriginValue(port_, axis_)) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
|
||||
if (trigger_if_greater) {
|
||||
// TODO: Might be worthwile to set a slider for the trigger threshold. It is currently
|
||||
// always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
|
||||
return axis_value > threshold;
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis);
|
||||
const float axis_value = (current_axis_value - origin_value) / 128.0f;
|
||||
if (trigger_if_greater) {
|
||||
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
|
||||
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
|
||||
return axis_value > threshold;
|
||||
}
|
||||
return axis_value < -threshold;
|
||||
}
|
||||
return axis_value < -threshold;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -58,6 +60,7 @@ private:
|
||||
float threshold;
|
||||
bool trigger_if_greater;
|
||||
GCAdapter::Adapter* gcadapter;
|
||||
const float origin_value;
|
||||
};
|
||||
|
||||
GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
|
||||
@@ -94,9 +97,12 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
|
||||
return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
|
||||
adapter.get());
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::ParamPackage GCButtonFactory::GetNextInput() {
|
||||
Common::ParamPackage GCButtonFactory::GetNextInput() const {
|
||||
Common::ParamPackage params;
|
||||
GCAdapter::GCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
@@ -144,14 +150,20 @@ void GCButtonFactory::EndConfiguration() {
|
||||
class GCAnalog final : public Input::AnalogDevice {
|
||||
public:
|
||||
GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
|
||||
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {}
|
||||
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
|
||||
origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
|
||||
origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {}
|
||||
|
||||
float GetAxis(int axis) const {
|
||||
std::lock_guard lock{mutex};
|
||||
// division is not by a perfect 128 to account for some variance in center location
|
||||
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
|
||||
// [20-230]
|
||||
return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
std::lock_guard lock{mutex};
|
||||
const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
|
||||
// division is not by a perfect 128 to account for some variance in center location
|
||||
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
|
||||
// [20-230]
|
||||
return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
|
||||
@@ -201,8 +213,10 @@ private:
|
||||
const int axis_x;
|
||||
const int axis_y;
|
||||
const float deadzone;
|
||||
mutable std::mutex mutex;
|
||||
GCAdapter::Adapter* gcadapter;
|
||||
const float origin_value_x;
|
||||
const float origin_value_y;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from GC Adapter
|
||||
@@ -249,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
|
||||
const u8 axis = static_cast<u8>(pad.axis);
|
||||
if (analog_x_axis == -1) {
|
||||
analog_x_axis = axis;
|
||||
controller_number = port;
|
||||
controller_number = static_cast<int>(port);
|
||||
} else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
|
||||
analog_y_axis = axis;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
*/
|
||||
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
Common::ParamPackage GetNextInput();
|
||||
Common::ParamPackage GetNextInput() const;
|
||||
|
||||
/// For device input configuration/polling
|
||||
void BeginConfiguration();
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <libusb.h>
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/analog_from_button.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
|
||||
@@ -234,7 +234,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=] {
|
||||
std::thread([=, this] {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
u16 min_x{UINT16_MAX};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -17,7 +18,6 @@
|
||||
namespace {
|
||||
// Numbers are chosen randomly to make sure the correct one is given.
|
||||
constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
|
||||
constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
|
||||
constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
|
||||
std::array<s64, 5> delays{};
|
||||
|
||||
@@ -25,12 +25,12 @@ std::bitset<CB_IDS.size()> callbacks_ran_flags;
|
||||
u64 expected_callback = 0;
|
||||
|
||||
template <unsigned int IDX>
|
||||
void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) {
|
||||
void HostCallbackTemplate(u64 userdata, std::chrono::nanoseconds ns_late) {
|
||||
static_assert(IDX < CB_IDS.size(), "IDX out of range");
|
||||
callbacks_ran_flags.set(IDX);
|
||||
REQUIRE(CB_IDS[IDX] == userdata);
|
||||
REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
|
||||
delays[IDX] = nanoseconds_late;
|
||||
delays[IDX] = ns_late.count();
|
||||
++expected_callback;
|
||||
}
|
||||
|
||||
@@ -77,10 +77,12 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
|
||||
u64 one_micro = 1000U;
|
||||
const u64 one_micro = 1000U;
|
||||
for (std::size_t i = 0; i < events.size(); i++) {
|
||||
u64 order = calls_order[i];
|
||||
core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
|
||||
const u64 order = calls_order[i];
|
||||
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
|
||||
|
||||
core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]);
|
||||
}
|
||||
/// test pause
|
||||
REQUIRE(callbacks_ran_flags.none());
|
||||
@@ -116,13 +118,16 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
|
||||
|
||||
expected_callback = 0;
|
||||
|
||||
u64 start = core_timing.GetGlobalTimeNs().count();
|
||||
u64 one_micro = 1000U;
|
||||
const u64 start = core_timing.GetGlobalTimeNs().count();
|
||||
const u64 one_micro = 1000U;
|
||||
|
||||
for (std::size_t i = 0; i < events.size(); i++) {
|
||||
u64 order = calls_order[i];
|
||||
core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
|
||||
const u64 order = calls_order[i];
|
||||
const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
|
||||
core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]);
|
||||
}
|
||||
u64 end = core_timing.GetGlobalTimeNs().count();
|
||||
|
||||
const u64 end = core_timing.GetGlobalTimeNs().count();
|
||||
const double scheduling_time = static_cast<double>(end - start);
|
||||
const double timer_time = static_cast<double>(TestTimerSpeed(core_timing));
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ add_library(video_core STATIC
|
||||
sampler_cache.cpp
|
||||
sampler_cache.h
|
||||
shader_cache.h
|
||||
shader_notify.cpp
|
||||
shader_notify.h
|
||||
shader/decode/arithmetic.cpp
|
||||
shader/decode/arithmetic_immediate.cpp
|
||||
shader/decode/bfe.cpp
|
||||
@@ -128,6 +130,8 @@ add_library(video_core STATIC
|
||||
shader/decode/other.cpp
|
||||
shader/ast.cpp
|
||||
shader/ast.h
|
||||
shader/async_shaders.cpp
|
||||
shader/async_shaders.h
|
||||
shader/compiler_settings.cpp
|
||||
shader/compiler_settings.h
|
||||
shader/control_flow.cpp
|
||||
|
||||
@@ -14,50 +14,45 @@
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
using namespace Texture;
|
||||
|
||||
MaxwellDMA::MaxwellDMA(Core::System& system, MemoryManager& memory_manager)
|
||||
: system{system}, memory_manager{memory_manager} {}
|
||||
|
||||
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid MaxwellDMA register, increase the size of the Regs structure");
|
||||
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
|
||||
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
||||
#define MAXWELLDMA_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
|
||||
|
||||
switch (method) {
|
||||
case MAXWELLDMA_REG_INDEX(exec): {
|
||||
HandleCopy();
|
||||
break;
|
||||
if (method == offsetof(Regs, launch_dma) / sizeof(u32)) {
|
||||
Launch();
|
||||
}
|
||||
}
|
||||
|
||||
#undef MAXWELLDMA_REG_INDEX
|
||||
}
|
||||
|
||||
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
for (std::size_t i = 0; i < amount; i++) {
|
||||
for (size_t i = 0; i < amount; ++i) {
|
||||
CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::HandleCopy() {
|
||||
LOG_TRACE(HW_GPU, "Requested a DMA copy");
|
||||
|
||||
const GPUVAddr source = regs.src_address.Address();
|
||||
const GPUVAddr dest = regs.dst_address.Address();
|
||||
void MaxwellDMA::Launch() {
|
||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
|
||||
static_cast<GPUVAddr>(regs.offset_out));
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
ASSERT(regs.exec.enable_swizzle == 0);
|
||||
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
|
||||
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
|
||||
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
|
||||
ASSERT(regs.dst_params.pos_x == 0);
|
||||
ASSERT(regs.dst_params.pos_y == 0);
|
||||
const LaunchDMA& launch = regs.launch_dma;
|
||||
ASSERT(launch.remap_enable == 0);
|
||||
ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
|
||||
ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
|
||||
ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
|
||||
ASSERT(regs.dst_params.origin.x == 0);
|
||||
ASSERT(regs.dst_params.origin.y == 0);
|
||||
|
||||
if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
|
||||
const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
|
||||
|
||||
if (!is_src_pitch && !is_dst_pitch) {
|
||||
// If both the source and the destination are in block layout, assert.
|
||||
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
|
||||
return;
|
||||
@@ -66,144 +61,161 @@ void MaxwellDMA::HandleCopy() {
|
||||
// All copies here update the main memory, so mark all rasterizer states as invalid.
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
|
||||
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
|
||||
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
|
||||
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
|
||||
// y_count).
|
||||
if (!regs.exec.enable_2d) {
|
||||
memory_manager.CopyBlock(dest, source, regs.x_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// If both the source and the destination are in linear layout, perform a line-by-line
|
||||
// copy. We're going to take a subrect of size (x_count, y_count) from the source
|
||||
// rectangle. There is no need to manually flush/invalidate the regions because
|
||||
// CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.y_count; ++line) {
|
||||
const GPUVAddr source_line = source + line * regs.src_pitch;
|
||||
const GPUVAddr dest_line = dest + line * regs.dst_pitch;
|
||||
memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(regs.exec.enable_2d == 1);
|
||||
|
||||
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
|
||||
ASSERT(regs.src_params.BlockDepth() == 0);
|
||||
// Optimized path for micro copies.
|
||||
if (regs.dst_pitch * regs.y_count < Texture::GetGOBSize() && regs.dst_pitch <= 64) {
|
||||
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
|
||||
const std::size_t src_size = Texture::GetGOBSize();
|
||||
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
|
||||
u32 pos_x = regs.src_params.pos_x;
|
||||
u32 pos_y = regs.src_params.pos_y;
|
||||
const u64 offset =
|
||||
Texture::GetGOBOffset(regs.src_params.size_x, regs.src_params.size_y, pos_x, pos_y,
|
||||
regs.src_params.BlockDepth(), bytes_per_pixel);
|
||||
const u32 x_in_gob = 64 / bytes_per_pixel;
|
||||
pos_x = pos_x % x_in_gob;
|
||||
pos_y = pos_y % 8;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
|
||||
regs.src_params.size_x, bytes_per_pixel, read_buffer.data(),
|
||||
write_buffer.data(), regs.src_params.BlockHeight(), pos_x,
|
||||
pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
|
||||
return;
|
||||
}
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
|
||||
const std::size_t src_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y,
|
||||
regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
|
||||
|
||||
const std::size_t src_layer_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, 1,
|
||||
regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
|
||||
|
||||
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
Texture::UnswizzleSubrect(
|
||||
regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
|
||||
read_buffer.data() + src_layer_size * regs.src_params.pos_z, write_buffer.data(),
|
||||
regs.src_params.BlockHeight(), regs.src_params.pos_x, regs.src_params.pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
if (is_src_pitch && is_dst_pitch) {
|
||||
CopyPitchToPitch();
|
||||
} else {
|
||||
ASSERT(regs.dst_params.BlockDepth() == 0);
|
||||
ASSERT(launch.multi_line_enable == 1);
|
||||
|
||||
const u32 bytes_per_pixel = regs.src_pitch / regs.x_count;
|
||||
|
||||
const std::size_t dst_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y,
|
||||
regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
|
||||
|
||||
const std::size_t dst_layer_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1,
|
||||
regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
|
||||
|
||||
const std::size_t src_size = regs.src_pitch * regs.y_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
if (!is_src_pitch && is_dst_pitch) {
|
||||
CopyBlockLinearToPitch();
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
CopyPitchToBlockLinear();
|
||||
}
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::SwizzleSubrect(
|
||||
regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, bytes_per_pixel,
|
||||
write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, read_buffer.data(),
|
||||
regs.dst_params.BlockHeight(), regs.dst_params.pos_x, regs.dst_params.pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyPitchToPitch() {
|
||||
// When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D
|
||||
// buffer of length `line_length_in`.
|
||||
// Otherwise we copy a 2D image of dimensions (line_length_in, line_count).
|
||||
if (!regs.launch_dma.multi_line_enable) {
|
||||
memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform a line-by-line copy.
|
||||
// We're going to take a subrect of size (line_length_in, line_count) from the source rectangle.
|
||||
// There is no need to manually flush/invalidate the regions because CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.line_count; ++line) {
|
||||
const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
|
||||
const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
|
||||
memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyBlockLinearToPitch() {
|
||||
ASSERT(regs.src_params.block_size.depth == 0);
|
||||
|
||||
// Optimized path for micro copies.
|
||||
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
|
||||
if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X) {
|
||||
FastCopyBlockLinearToPitch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Deswizzle the input and copy it over.
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const Parameters& src_params = regs.src_params;
|
||||
const u32 width = src_params.width;
|
||||
const u32 height = src_params.height;
|
||||
const u32 depth = src_params.depth;
|
||||
const u32 block_height = src_params.block_size.height;
|
||||
const u32 block_depth = src_params.block_size.depth;
|
||||
const size_t src_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
const size_t src_layer_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
|
||||
read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(),
|
||||
block_height, src_params.origin.x, src_params.origin.y);
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyPitchToBlockLinear() {
|
||||
const auto& dst_params = regs.dst_params;
|
||||
const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
|
||||
const u32 width = dst_params.width;
|
||||
const u32 height = dst_params.height;
|
||||
const u32 depth = dst_params.depth;
|
||||
const u32 block_height = dst_params.block_size.height;
|
||||
const u32 block_depth = dst_params.block_size.depth;
|
||||
const size_t dst_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
const size_t dst_layer_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
|
||||
|
||||
const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
if (regs.dst_params.block_size.depth > 0) {
|
||||
ASSERT(dst_params.layer == 0);
|
||||
SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height,
|
||||
bytes_per_pixel, block_height, block_depth, dst_params.origin.x,
|
||||
dst_params.origin.y, write_buffer.data(), read_buffer.data());
|
||||
} else {
|
||||
SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel,
|
||||
write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(),
|
||||
block_height, dst_params.origin.x, dst_params.origin.y);
|
||||
}
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::FastCopyBlockLinearToPitch() {
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const size_t src_size = GOB_SIZE;
|
||||
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
|
||||
u32 pos_x = regs.src_params.origin.x;
|
||||
u32 pos_y = regs.src_params.origin.y;
|
||||
const u64 offset = GetGOBOffset(regs.src_params.width, regs.src_params.height, pos_x, pos_y,
|
||||
regs.src_params.block_size.height, bytes_per_pixel);
|
||||
const u32 x_in_gob = 64 / bytes_per_pixel;
|
||||
pos_x = pos_x % x_in_gob;
|
||||
pos_y = pos_y % 8;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
|
||||
bytes_per_pixel, read_buffer.data(), write_buffer.data(),
|
||||
regs.src_params.block_size.height, pos_x, pos_y);
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -24,12 +24,167 @@ class MemoryManager;
|
||||
namespace Tegra::Engines {
|
||||
|
||||
/**
|
||||
* This Engine is known as GK104_Copy. Documentation can be found in:
|
||||
* This engine is known as gk104_copy. Documentation can be found in:
|
||||
* https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h
|
||||
* https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml
|
||||
*/
|
||||
|
||||
class MaxwellDMA final : public EngineInterface {
|
||||
public:
|
||||
struct PackedGPUVAddr {
|
||||
u32 upper;
|
||||
u32 lower;
|
||||
|
||||
constexpr operator GPUVAddr() const noexcept {
|
||||
return (static_cast<GPUVAddr>(upper & 0xff) << 32) | lower;
|
||||
}
|
||||
};
|
||||
|
||||
union BlockSize {
|
||||
BitField<0, 4, u32> width;
|
||||
BitField<4, 4, u32> height;
|
||||
BitField<8, 4, u32> depth;
|
||||
BitField<12, 4, u32> gob_height;
|
||||
};
|
||||
static_assert(sizeof(BlockSize) == 4);
|
||||
|
||||
union Origin {
|
||||
BitField<0, 16, u32> x;
|
||||
BitField<16, 16, u32> y;
|
||||
};
|
||||
static_assert(sizeof(Origin) == 4);
|
||||
|
||||
struct Parameters {
|
||||
BlockSize block_size;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
u32 layer;
|
||||
Origin origin;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 24);
|
||||
|
||||
struct Semaphore {
|
||||
PackedGPUVAddr address;
|
||||
u32 payload;
|
||||
};
|
||||
static_assert(sizeof(Semaphore) == 12);
|
||||
|
||||
struct RenderEnable {
|
||||
enum class Mode : u32 {
|
||||
FALSE = 0,
|
||||
TRUE = 1,
|
||||
CONDITIONAL = 2,
|
||||
RENDER_IF_EQUAL = 3,
|
||||
RENDER_IF_NOT_EQUAL = 4,
|
||||
};
|
||||
|
||||
PackedGPUVAddr address;
|
||||
BitField<0, 3, Mode> mode;
|
||||
};
|
||||
static_assert(sizeof(RenderEnable) == 12);
|
||||
|
||||
enum class PhysModeTarget : u32 {
|
||||
LOCAL_FB = 0,
|
||||
COHERENT_SYSMEM = 1,
|
||||
NONCOHERENT_SYSMEM = 2,
|
||||
};
|
||||
using PhysMode = BitField<0, 2, PhysModeTarget>;
|
||||
|
||||
union LaunchDMA {
|
||||
enum class DataTransferType : u32 {
|
||||
NONE = 0,
|
||||
PIPELINED = 1,
|
||||
NON_PIPELINED = 2,
|
||||
};
|
||||
|
||||
enum class SemaphoreType : u32 {
|
||||
NONE = 0,
|
||||
RELEASE_ONE_WORD_SEMAPHORE = 1,
|
||||
RELEASE_FOUR_WORD_SEMAPHORE = 2,
|
||||
};
|
||||
|
||||
enum class InterruptType : u32 {
|
||||
NONE = 0,
|
||||
BLOCKING = 1,
|
||||
NON_BLOCKING = 2,
|
||||
};
|
||||
|
||||
enum class MemoryLayout : u32 {
|
||||
BLOCKLINEAR = 0,
|
||||
PITCH = 1,
|
||||
};
|
||||
|
||||
enum class Type : u32 {
|
||||
VIRTUAL = 0,
|
||||
PHYSICAL = 1,
|
||||
};
|
||||
|
||||
enum class SemaphoreReduction : u32 {
|
||||
IMIN = 0,
|
||||
IMAX = 1,
|
||||
IXOR = 2,
|
||||
IAND = 3,
|
||||
IOR = 4,
|
||||
IADD = 5,
|
||||
INC = 6,
|
||||
DEC = 7,
|
||||
FADD = 0xA,
|
||||
};
|
||||
|
||||
enum class SemaphoreReductionSign : u32 {
|
||||
SIGNED = 0,
|
||||
UNSIGNED = 1,
|
||||
};
|
||||
|
||||
enum class BypassL2 : u32 {
|
||||
USE_PTE_SETTING = 0,
|
||||
FORCE_VOLATILE = 1,
|
||||
};
|
||||
|
||||
BitField<0, 2, DataTransferType> data_transfer_type;
|
||||
BitField<2, 1, u32> flush_enable;
|
||||
BitField<3, 2, SemaphoreType> semaphore_type;
|
||||
BitField<5, 2, InterruptType> interrupt_type;
|
||||
BitField<7, 1, MemoryLayout> src_memory_layout;
|
||||
BitField<8, 1, MemoryLayout> dst_memory_layout;
|
||||
BitField<9, 1, u32> multi_line_enable;
|
||||
BitField<10, 1, u32> remap_enable;
|
||||
BitField<11, 1, u32> rmwdisable;
|
||||
BitField<12, 1, Type> src_type;
|
||||
BitField<13, 1, Type> dst_type;
|
||||
BitField<14, 4, SemaphoreReduction> semaphore_reduction;
|
||||
BitField<18, 1, SemaphoreReductionSign> semaphore_reduction_sign;
|
||||
BitField<19, 1, u32> reduction_enable;
|
||||
BitField<20, 1, BypassL2> bypass_l2;
|
||||
};
|
||||
static_assert(sizeof(LaunchDMA) == 4);
|
||||
|
||||
struct RemapConst {
|
||||
enum Swizzle : u32 {
|
||||
SRC_X = 0,
|
||||
SRC_Y = 1,
|
||||
SRC_Z = 2,
|
||||
SRC_W = 3,
|
||||
CONST_A = 4,
|
||||
CONST_B = 5,
|
||||
NO_WRITE = 6,
|
||||
};
|
||||
|
||||
PackedGPUVAddr address;
|
||||
|
||||
union {
|
||||
BitField<0, 3, Swizzle> dst_x;
|
||||
BitField<4, 3, Swizzle> dst_y;
|
||||
BitField<8, 3, Swizzle> dst_z;
|
||||
BitField<12, 3, Swizzle> dst_w;
|
||||
BitField<16, 2, u32> component_size_minus_one;
|
||||
BitField<20, 2, u32> num_src_components_minus_one;
|
||||
BitField<24, 2, u32> num_dst_components_minus_one;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(RemapConst) == 12);
|
||||
|
||||
explicit MaxwellDMA(Core::System& system, MemoryManager& memory_manager);
|
||||
~MaxwellDMA() = default;
|
||||
|
||||
@@ -40,144 +195,19 @@ public:
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
|
||||
struct Regs {
|
||||
static constexpr std::size_t NUM_REGS = 0x1D6;
|
||||
|
||||
struct Parameters {
|
||||
union {
|
||||
BitField<0, 4, u32> block_depth;
|
||||
BitField<4, 4, u32> block_height;
|
||||
BitField<8, 4, u32> block_width;
|
||||
};
|
||||
u32 size_x;
|
||||
u32 size_y;
|
||||
u32 size_z;
|
||||
u32 pos_z;
|
||||
union {
|
||||
BitField<0, 16, u32> pos_x;
|
||||
BitField<16, 16, u32> pos_y;
|
||||
};
|
||||
|
||||
u32 BlockHeight() const {
|
||||
return block_height.Value();
|
||||
}
|
||||
|
||||
u32 BlockDepth() const {
|
||||
return block_depth.Value();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
|
||||
|
||||
enum class ComponentMode : u32 {
|
||||
Src0 = 0,
|
||||
Src1 = 1,
|
||||
Src2 = 2,
|
||||
Src3 = 3,
|
||||
Const0 = 4,
|
||||
Const1 = 5,
|
||||
Zero = 6,
|
||||
};
|
||||
|
||||
enum class CopyMode : u32 {
|
||||
None = 0,
|
||||
Unk1 = 1,
|
||||
Unk2 = 2,
|
||||
};
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
None = 0,
|
||||
Short = 1,
|
||||
Long = 2,
|
||||
};
|
||||
|
||||
enum class QueryIntr : u32 {
|
||||
None = 0,
|
||||
Block = 1,
|
||||
NonBlock = 2,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_UNION_PADDING_WORDS(0xC0);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 2, CopyMode> copy_mode;
|
||||
BitField<2, 1, u32> flush;
|
||||
|
||||
BitField<3, 2, QueryMode> query_mode;
|
||||
BitField<5, 2, QueryIntr> query_intr;
|
||||
|
||||
BitField<7, 1, u32> is_src_linear;
|
||||
BitField<8, 1, u32> is_dst_linear;
|
||||
|
||||
BitField<9, 1, u32> enable_2d;
|
||||
BitField<10, 1, u32> enable_swizzle;
|
||||
};
|
||||
} exec;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x3F);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} src_address;
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} dst_address;
|
||||
|
||||
u32 src_pitch;
|
||||
u32 dst_pitch;
|
||||
u32 x_count;
|
||||
u32 y_count;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xB8);
|
||||
|
||||
u32 const0;
|
||||
u32 const1;
|
||||
union {
|
||||
BitField<0, 4, ComponentMode> component0;
|
||||
BitField<4, 4, ComponentMode> component1;
|
||||
BitField<8, 4, ComponentMode> component2;
|
||||
BitField<12, 4, ComponentMode> component3;
|
||||
BitField<16, 2, u32> component_size;
|
||||
BitField<20, 3, u32> src_num_components;
|
||||
BitField<24, 3, u32> dst_num_components;
|
||||
|
||||
u32 SrcBytePerPixel() const {
|
||||
return src_num_components.Value() * component_size.Value();
|
||||
}
|
||||
u32 DstBytePerPixel() const {
|
||||
return dst_num_components.Value() * component_size.Value();
|
||||
}
|
||||
} swizzle_config;
|
||||
|
||||
Parameters dst_params;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
|
||||
Parameters src_params;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x13);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
private:
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void Launch();
|
||||
|
||||
void CopyPitchToPitch();
|
||||
|
||||
void CopyBlockLinearToPitch();
|
||||
|
||||
void CopyPitchToBlockLinear();
|
||||
|
||||
void FastCopyBlockLinearToPitch();
|
||||
|
||||
Core::System& system;
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
@@ -185,28 +215,58 @@ private:
|
||||
std::vector<u8> read_buffer;
|
||||
std::vector<u8> write_buffer;
|
||||
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void HandleCopy();
|
||||
};
|
||||
static constexpr std::size_t NUM_REGS = 0x800;
|
||||
struct Regs {
|
||||
union {
|
||||
struct {
|
||||
u32 reserved[0x40];
|
||||
u32 nop;
|
||||
u32 reserved01[0xf];
|
||||
u32 pm_trigger;
|
||||
u32 reserved02[0x3f];
|
||||
Semaphore semaphore;
|
||||
u32 reserved03[0x2];
|
||||
RenderEnable render_enable;
|
||||
PhysMode src_phys_mode;
|
||||
PhysMode dst_phys_mode;
|
||||
u32 reserved04[0x26];
|
||||
LaunchDMA launch_dma;
|
||||
u32 reserved05[0x3f];
|
||||
PackedGPUVAddr offset_in;
|
||||
PackedGPUVAddr offset_out;
|
||||
u32 pitch_in;
|
||||
u32 pitch_out;
|
||||
u32 line_length_in;
|
||||
u32 line_count;
|
||||
u32 reserved06[0xb8];
|
||||
RemapConst remap_const;
|
||||
Parameters dst_params;
|
||||
u32 reserved07[0x1];
|
||||
Parameters src_params;
|
||||
u32 reserved08[0x275];
|
||||
u32 pm_trigger_end;
|
||||
u32 reserved09[0x3ba];
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(exec, 0xC0);
|
||||
ASSERT_REG_POSITION(src_address, 0x100);
|
||||
ASSERT_REG_POSITION(dst_address, 0x102);
|
||||
ASSERT_REG_POSITION(src_pitch, 0x104);
|
||||
ASSERT_REG_POSITION(dst_pitch, 0x105);
|
||||
ASSERT_REG_POSITION(x_count, 0x106);
|
||||
ASSERT_REG_POSITION(y_count, 0x107);
|
||||
ASSERT_REG_POSITION(const0, 0x1C0);
|
||||
ASSERT_REG_POSITION(const1, 0x1C1);
|
||||
ASSERT_REG_POSITION(swizzle_config, 0x1C2);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
ASSERT_REG_POSITION(launch_dma, 0xC0);
|
||||
ASSERT_REG_POSITION(offset_in, 0x100);
|
||||
ASSERT_REG_POSITION(offset_out, 0x102);
|
||||
ASSERT_REG_POSITION(pitch_in, 0x104);
|
||||
ASSERT_REG_POSITION(pitch_out, 0x105);
|
||||
ASSERT_REG_POSITION(line_length_in, 0x106);
|
||||
ASSERT_REG_POSITION(line_count, 0x107);
|
||||
ASSERT_REG_POSITION(remap_const, 0x1C0);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -36,6 +37,7 @@ GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& render
|
||||
kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager);
|
||||
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, *memory_manager);
|
||||
kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager);
|
||||
shader_notify = std::make_unique<VideoCore::ShaderNotify>();
|
||||
}
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
@@ -33,6 +33,7 @@ class System;
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
class ShaderNotify;
|
||||
} // namespace VideoCore
|
||||
|
||||
namespace Tegra {
|
||||
@@ -207,6 +208,14 @@ public:
|
||||
return *renderer;
|
||||
}
|
||||
|
||||
VideoCore::ShaderNotify& ShaderNotify() {
|
||||
return *shader_notify;
|
||||
}
|
||||
|
||||
const VideoCore::ShaderNotify& ShaderNotify() const {
|
||||
return *shader_notify;
|
||||
}
|
||||
|
||||
// Waits for the GPU to finish working
|
||||
virtual void WaitIdle() const = 0;
|
||||
|
||||
@@ -347,6 +356,8 @@ private:
|
||||
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
|
||||
/// Inline memory engine
|
||||
std::unique_ptr<Engines::KeplerMemory> kepler_memory;
|
||||
/// Shader build notifier
|
||||
std::unique_ptr<VideoCore::ShaderNotify> shader_notify;
|
||||
|
||||
std::array<std::atomic<u32>, Service::Nvidia::MaxSyncPoints> syncpoints{};
|
||||
|
||||
|
||||
@@ -103,8 +103,9 @@ public:
|
||||
virtual ~CachedMacro() = default;
|
||||
/**
|
||||
* Executes the macro code with the specified input parameters.
|
||||
* @param code The macro byte code to execute
|
||||
*
|
||||
* @param parameters The parameters of the macro
|
||||
* @param method The method to execute
|
||||
*/
|
||||
virtual void Execute(const std::vector<u32>& parameters, u32 method) = 0;
|
||||
};
|
||||
|
||||
@@ -12,13 +12,11 @@ namespace Tegra {
|
||||
|
||||
namespace {
|
||||
// HLE'd functions
|
||||
static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d,
|
||||
const std::vector<u32>& parameters) {
|
||||
void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
|
||||
const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B);
|
||||
|
||||
maxwell3d.regs.draw.topology.Assign(
|
||||
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] &
|
||||
~(0x3ffffff << 26)));
|
||||
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0x3ffffff));
|
||||
maxwell3d.regs.vb_base_instance = parameters[5];
|
||||
maxwell3d.mme_draw.instance_count = instance_count;
|
||||
maxwell3d.regs.vb_element_base = parameters[3];
|
||||
@@ -33,8 +31,7 @@ static void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d,
|
||||
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
|
||||
}
|
||||
|
||||
static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d,
|
||||
const std::vector<u32>& parameters) {
|
||||
void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
|
||||
const u32 count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||
|
||||
maxwell3d.regs.vertex_buffer.first = parameters[3];
|
||||
@@ -52,8 +49,7 @@ static void HLE_0D61FC9FAAC9FCAD(Engines::Maxwell3D& maxwell3d,
|
||||
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
|
||||
}
|
||||
|
||||
static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d,
|
||||
const std::vector<u32>& parameters) {
|
||||
void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
|
||||
const u32 instance_count = (maxwell3d.GetRegisterValue(0xD1B) & parameters[2]);
|
||||
const u32 element_base = parameters[4];
|
||||
const u32 base_instance = parameters[5];
|
||||
@@ -81,12 +77,12 @@ static void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d,
|
||||
maxwell3d.CallMethodFromMME(0x8e5, 0x0);
|
||||
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
|
||||
}
|
||||
} // namespace
|
||||
} // Anonymous namespace
|
||||
|
||||
constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{
|
||||
std::make_pair<u64, HLEFunction>(0x771BB18C62444DA0, &HLE_771BB18C62444DA0),
|
||||
std::make_pair<u64, HLEFunction>(0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD),
|
||||
std::make_pair<u64, HLEFunction>(0x0217920100488FF7, &HLE_0217920100488FF7),
|
||||
{0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
|
||||
{0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
|
||||
{0x0217920100488FF7, &HLE_0217920100488FF7},
|
||||
}};
|
||||
|
||||
HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
|
||||
|
||||
@@ -233,6 +233,8 @@ Device::Device()
|
||||
GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
|
||||
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
|
||||
|
||||
use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue();
|
||||
|
||||
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);
|
||||
|
||||
@@ -104,6 +104,10 @@ public:
|
||||
return use_assembly_shaders;
|
||||
}
|
||||
|
||||
bool UseAsynchronousShaders() const {
|
||||
return use_asynchronous_shaders;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool TestVariableAoffi();
|
||||
static bool TestPreciseBug();
|
||||
@@ -127,6 +131,7 @@ private:
|
||||
bool has_fast_buffer_sub_data{};
|
||||
bool has_nv_viewport_array2{};
|
||||
bool use_assembly_shaders{};
|
||||
bool use_asynchronous_shaders{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -149,7 +149,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
|
||||
shader_cache{*this, system, emu_window, device}, query_cache{system, *this},
|
||||
buffer_cache{*this, system, device, STREAM_BUFFER_SIZE},
|
||||
fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system},
|
||||
screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} {
|
||||
screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
|
||||
async_shaders{emu_window} {
|
||||
CheckExtensions();
|
||||
|
||||
unified_uniform_buffer.Create();
|
||||
@@ -162,6 +163,23 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
|
||||
nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (device.UseAsynchronousShaders()) {
|
||||
// Max worker threads we should allow
|
||||
constexpr auto MAX_THREADS = 2u;
|
||||
// Amount of threads we should reserve for other parts of yuzu
|
||||
constexpr auto RESERVED_THREADS = 6u;
|
||||
// Get the amount of threads we can use(this can return zero)
|
||||
const auto cpu_thread_count =
|
||||
std::max(RESERVED_THREADS, std::thread::hardware_concurrency());
|
||||
// Deduce how many "extra" threads we have to use.
|
||||
const auto max_threads_unused = cpu_thread_count - RESERVED_THREADS;
|
||||
// Always allow at least 1 thread regardless of our settings
|
||||
const auto max_worker_count = std::max(1u, max_threads_unused);
|
||||
// Don't use more than MAX_THREADS
|
||||
const auto worker_count = std::min(max_worker_count, MAX_THREADS);
|
||||
async_shaders.AllocateWorkers(worker_count);
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerOpenGL::~RasterizerOpenGL() {
|
||||
@@ -336,7 +354,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Shader* const shader = shader_cache.GetStageProgram(program);
|
||||
Shader* shader = shader_cache.GetStageProgram(program, async_shaders);
|
||||
|
||||
if (device.UseAssemblyShaders()) {
|
||||
// Check for ARB limitation. We only have 16 SSBOs per context state. To workaround this
|
||||
@@ -353,7 +371,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
SetupDrawTextures(stage, shader);
|
||||
SetupDrawImages(stage, shader);
|
||||
|
||||
const GLuint program_handle = shader->GetHandle();
|
||||
const GLuint program_handle = shader->IsBuilt() ? shader->GetHandle() : 0;
|
||||
switch (program) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
case Maxwell::ShaderProgram::VertexB:
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/shader/async_shaders.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -91,6 +92,14 @@ public:
|
||||
return num_queued_commands > 0;
|
||||
}
|
||||
|
||||
VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
|
||||
return async_shaders;
|
||||
}
|
||||
|
||||
const VideoCommon::Shader::AsyncShaders& GetAsyncShaders() const {
|
||||
return async_shaders;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Configures the color and depth framebuffer states.
|
||||
void ConfigureFramebuffers();
|
||||
@@ -242,6 +251,7 @@ private:
|
||||
ScreenInfo& screen_info;
|
||||
ProgramManager& program_manager;
|
||||
StateTracker& state_tracker;
|
||||
VideoCommon::Shader::AsyncShaders async_shaders;
|
||||
|
||||
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
|
||||
|
||||
@@ -177,6 +177,12 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
OGLAssemblyProgram& operator=(OGLAssemblyProgram&& o) noexcept {
|
||||
Release();
|
||||
handle = std::exchange(o.handle, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
void Release();
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "video_core/shader/registry.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
#include "video_core/shader_cache.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -140,9 +141,24 @@ std::shared_ptr<Registry> MakeRegistry(const ShaderDiskCacheEntry& entry) {
|
||||
return registry;
|
||||
}
|
||||
|
||||
std::unordered_set<GLenum> GetSupportedFormats() {
|
||||
GLint num_formats;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
|
||||
|
||||
std::vector<GLint> formats(num_formats);
|
||||
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
|
||||
|
||||
std::unordered_set<GLenum> supported_formats;
|
||||
for (const GLint format : formats) {
|
||||
supported_formats.insert(static_cast<GLenum>(format));
|
||||
}
|
||||
return supported_formats;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 unique_identifier,
|
||||
const ShaderIR& ir, const Registry& registry,
|
||||
bool hint_retrievable = false) {
|
||||
const ShaderIR& ir, const Registry& registry, bool hint_retrievable) {
|
||||
const std::string shader_id = MakeShaderID(unique_identifier, shader_type);
|
||||
LOG_INFO(Render_OpenGL, "{}", shader_id);
|
||||
|
||||
@@ -181,30 +197,17 @@ ProgramSharedPtr BuildShader(const Device& device, ShaderType shader_type, u64 u
|
||||
return program;
|
||||
}
|
||||
|
||||
std::unordered_set<GLenum> GetSupportedFormats() {
|
||||
GLint num_formats;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
|
||||
|
||||
std::vector<GLint> formats(num_formats);
|
||||
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, formats.data());
|
||||
|
||||
std::unordered_set<GLenum> supported_formats;
|
||||
for (const GLint format : formats) {
|
||||
supported_formats.insert(static_cast<GLenum>(format));
|
||||
}
|
||||
return supported_formats;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Shader::Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry_, ShaderEntries entries_,
|
||||
ProgramSharedPtr program_)
|
||||
: registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)} {
|
||||
ProgramSharedPtr program_, bool is_built)
|
||||
: registry{std::move(registry_)}, entries{std::move(entries_)}, program{std::move(program_)},
|
||||
is_built(is_built) {
|
||||
handle = program->assembly_program.handle;
|
||||
if (handle == 0) {
|
||||
handle = program->source_program.handle;
|
||||
}
|
||||
ASSERT(handle != 0);
|
||||
if (is_built) {
|
||||
ASSERT(handle != 0);
|
||||
}
|
||||
}
|
||||
|
||||
Shader::~Shader() = default;
|
||||
@@ -214,42 +217,82 @@ GLuint Shader::GetHandle() const {
|
||||
return handle;
|
||||
}
|
||||
|
||||
std::unique_ptr<Shader> Shader::CreateStageFromMemory(const ShaderParameters& params,
|
||||
Maxwell::ShaderProgram program_type,
|
||||
ProgramCode code, ProgramCode code_b) {
|
||||
bool Shader::IsBuilt() const {
|
||||
return is_built;
|
||||
}
|
||||
|
||||
void Shader::AsyncOpenGLBuilt(OGLProgram new_program) {
|
||||
program->source_program = std::move(new_program);
|
||||
handle = program->source_program.handle;
|
||||
is_built = true;
|
||||
}
|
||||
|
||||
void Shader::AsyncGLASMBuilt(OGLAssemblyProgram new_program) {
|
||||
program->assembly_program = std::move(new_program);
|
||||
handle = program->assembly_program.handle;
|
||||
is_built = true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Shader> Shader::CreateStageFromMemory(
|
||||
const ShaderParameters& params, Maxwell::ShaderProgram program_type, ProgramCode code,
|
||||
ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) {
|
||||
const auto shader_type = GetShaderType(program_type);
|
||||
const std::size_t size_in_bytes = code.size() * sizeof(u64);
|
||||
|
||||
auto registry = std::make_shared<Registry>(shader_type, params.system.GPU().Maxwell3D());
|
||||
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
|
||||
// TODO(Rodrigo): Handle VertexA shaders
|
||||
// std::optional<ShaderIR> ir_b;
|
||||
// if (!code_b.empty()) {
|
||||
// ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
|
||||
// }
|
||||
auto program = BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
|
||||
auto& gpu = params.system.GPU();
|
||||
gpu.ShaderNotify().MarkSharderBuilding();
|
||||
|
||||
ShaderDiskCacheEntry entry;
|
||||
entry.type = shader_type;
|
||||
entry.code = std::move(code);
|
||||
entry.code_b = std::move(code_b);
|
||||
entry.unique_identifier = params.unique_identifier;
|
||||
entry.bound_buffer = registry->GetBoundBuffer();
|
||||
entry.graphics_info = registry->GetGraphicsInfo();
|
||||
entry.keys = registry->GetKeys();
|
||||
entry.bound_samplers = registry->GetBoundSamplers();
|
||||
entry.bindless_samplers = registry->GetBindlessSamplers();
|
||||
params.disk_cache.SaveEntry(std::move(entry));
|
||||
auto registry = std::make_shared<Registry>(shader_type, gpu.Maxwell3D());
|
||||
if (!async_shaders.IsShaderAsync(params.system.GPU()) ||
|
||||
!params.device.UseAsynchronousShaders()) {
|
||||
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
|
||||
// TODO(Rodrigo): Handle VertexA shaders
|
||||
// std::optional<ShaderIR> ir_b;
|
||||
// if (!code_b.empty()) {
|
||||
// ir_b.emplace(code_b, STAGE_MAIN_OFFSET);
|
||||
// }
|
||||
auto program =
|
||||
BuildShader(params.device, shader_type, params.unique_identifier, ir, *registry);
|
||||
ShaderDiskCacheEntry entry;
|
||||
entry.type = shader_type;
|
||||
entry.code = std::move(code);
|
||||
entry.code_b = std::move(code_b);
|
||||
entry.unique_identifier = params.unique_identifier;
|
||||
entry.bound_buffer = registry->GetBoundBuffer();
|
||||
entry.graphics_info = registry->GetGraphicsInfo();
|
||||
entry.keys = registry->GetKeys();
|
||||
entry.bound_samplers = registry->GetBoundSamplers();
|
||||
entry.bindless_samplers = registry->GetBindlessSamplers();
|
||||
params.disk_cache.SaveEntry(std::move(entry));
|
||||
|
||||
return std::unique_ptr<Shader>(new Shader(
|
||||
std::move(registry), MakeEntries(params.device, ir, shader_type), std::move(program)));
|
||||
gpu.ShaderNotify().MarkShaderComplete();
|
||||
|
||||
return std::unique_ptr<Shader>(new Shader(std::move(registry),
|
||||
MakeEntries(params.device, ir, shader_type),
|
||||
std::move(program), true));
|
||||
} else {
|
||||
// Required for entries
|
||||
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
|
||||
auto entries = MakeEntries(params.device, ir, shader_type);
|
||||
|
||||
async_shaders.QueueOpenGLShader(params.device, shader_type, params.unique_identifier,
|
||||
std::move(code), std::move(code_b), STAGE_MAIN_OFFSET,
|
||||
COMPILER_SETTINGS, *registry, cpu_addr);
|
||||
|
||||
auto program = std::make_shared<ProgramHandle>();
|
||||
return std::unique_ptr<Shader>(
|
||||
new Shader(std::move(registry), std::move(entries), std::move(program), false));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params,
|
||||
ProgramCode code) {
|
||||
const std::size_t size_in_bytes = code.size() * sizeof(u64);
|
||||
|
||||
auto& engine = params.system.GPU().KeplerCompute();
|
||||
auto& gpu = params.system.GPU();
|
||||
gpu.ShaderNotify().MarkSharderBuilding();
|
||||
|
||||
auto& engine = gpu.KeplerCompute();
|
||||
auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
|
||||
const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
|
||||
const u64 uid = params.unique_identifier;
|
||||
@@ -266,6 +309,8 @@ std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& p
|
||||
entry.bindless_samplers = registry->GetBindlessSamplers();
|
||||
params.disk_cache.SaveEntry(std::move(entry));
|
||||
|
||||
gpu.ShaderNotify().MarkShaderComplete();
|
||||
|
||||
return std::unique_ptr<Shader>(new Shader(std::move(registry),
|
||||
MakeEntries(params.device, ir, ShaderType::Compute),
|
||||
std::move(program)));
|
||||
@@ -436,14 +481,51 @@ ProgramSharedPtr ShaderCacheOpenGL::GeneratePrecompiledProgram(
|
||||
return program;
|
||||
}
|
||||
|
||||
Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
|
||||
Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
|
||||
VideoCommon::Shader::AsyncShaders& async_shaders) {
|
||||
if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
|
||||
return last_shaders[static_cast<std::size_t>(program)];
|
||||
auto* last_shader = last_shaders[static_cast<std::size_t>(program)];
|
||||
if (last_shader->IsBuilt()) {
|
||||
return last_shader;
|
||||
}
|
||||
}
|
||||
|
||||
auto& memory_manager{system.GPU().MemoryManager()};
|
||||
const GPUVAddr address{GetShaderAddress(system, program)};
|
||||
|
||||
if (device.UseAsynchronousShaders() && async_shaders.HasCompletedWork()) {
|
||||
auto completed_work = async_shaders.GetCompletedWork();
|
||||
for (auto& work : completed_work) {
|
||||
Shader* shader = TryGet(work.cpu_address);
|
||||
auto& gpu = system.GPU();
|
||||
gpu.ShaderNotify().MarkShaderComplete();
|
||||
if (shader == nullptr) {
|
||||
continue;
|
||||
}
|
||||
using namespace VideoCommon::Shader;
|
||||
if (work.backend == AsyncShaders::Backend::OpenGL) {
|
||||
shader->AsyncOpenGLBuilt(std::move(work.program.opengl));
|
||||
} else if (work.backend == AsyncShaders::Backend::GLASM) {
|
||||
shader->AsyncGLASMBuilt(std::move(work.program.glasm));
|
||||
}
|
||||
|
||||
ShaderDiskCacheEntry entry;
|
||||
entry.type = work.shader_type;
|
||||
entry.code = std::move(work.code);
|
||||
entry.code_b = std::move(work.code_b);
|
||||
entry.unique_identifier = work.uid;
|
||||
|
||||
auto& registry = shader->GetRegistry();
|
||||
|
||||
entry.bound_buffer = registry.GetBoundBuffer();
|
||||
entry.graphics_info = registry.GetGraphicsInfo();
|
||||
entry.keys = registry.GetKeys();
|
||||
entry.bound_samplers = registry.GetBoundSamplers();
|
||||
entry.bindless_samplers = registry.GetBindlessSamplers();
|
||||
disk_cache.SaveEntry(std::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
// Look up shader in the cache based on address
|
||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(address)};
|
||||
if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) {
|
||||
@@ -471,7 +553,8 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
|
||||
std::unique_ptr<Shader> shader;
|
||||
const auto found = runtime_cache.find(unique_identifier);
|
||||
if (found == runtime_cache.end()) {
|
||||
shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b));
|
||||
shader = Shader::CreateStageFromMemory(params, program, std::move(code), std::move(code_b),
|
||||
async_shaders, cpu_addr.value_or(0));
|
||||
} else {
|
||||
shader = Shader::CreateFromCache(params, found->second);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
class AsyncShaders;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class Device;
|
||||
@@ -61,6 +65,11 @@ struct ShaderParameters {
|
||||
u64 unique_identifier;
|
||||
};
|
||||
|
||||
ProgramSharedPtr BuildShader(const Device& device, Tegra::Engines::ShaderType shader_type,
|
||||
u64 unique_identifier, const VideoCommon::Shader::ShaderIR& ir,
|
||||
const VideoCommon::Shader::Registry& registry,
|
||||
bool hint_retrievable = false);
|
||||
|
||||
class Shader final {
|
||||
public:
|
||||
~Shader();
|
||||
@@ -68,15 +77,28 @@ public:
|
||||
/// Gets the GL program handle for the shader
|
||||
GLuint GetHandle() const;
|
||||
|
||||
bool IsBuilt() const;
|
||||
|
||||
/// Gets the shader entries for the shader
|
||||
const ShaderEntries& GetEntries() const {
|
||||
return entries;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Shader> CreateStageFromMemory(const ShaderParameters& params,
|
||||
Maxwell::ShaderProgram program_type,
|
||||
ProgramCode program_code,
|
||||
ProgramCode program_code_b);
|
||||
const VideoCommon::Shader::Registry& GetRegistry() const {
|
||||
return *registry;
|
||||
}
|
||||
|
||||
/// Mark a OpenGL shader as built
|
||||
void AsyncOpenGLBuilt(OGLProgram new_program);
|
||||
|
||||
/// Mark a GLASM shader as built
|
||||
void AsyncGLASMBuilt(OGLAssemblyProgram new_program);
|
||||
|
||||
static std::unique_ptr<Shader> CreateStageFromMemory(
|
||||
const ShaderParameters& params, Maxwell::ShaderProgram program_type,
|
||||
ProgramCode program_code, ProgramCode program_code_b,
|
||||
VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr);
|
||||
|
||||
static std::unique_ptr<Shader> CreateKernelFromMemory(const ShaderParameters& params,
|
||||
ProgramCode code);
|
||||
|
||||
@@ -85,12 +107,13 @@ public:
|
||||
|
||||
private:
|
||||
explicit Shader(std::shared_ptr<VideoCommon::Shader::Registry> registry, ShaderEntries entries,
|
||||
ProgramSharedPtr program);
|
||||
ProgramSharedPtr program, bool is_built = true);
|
||||
|
||||
std::shared_ptr<VideoCommon::Shader::Registry> registry;
|
||||
ShaderEntries entries;
|
||||
ProgramSharedPtr program;
|
||||
GLuint handle = 0;
|
||||
bool is_built{};
|
||||
};
|
||||
|
||||
class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> {
|
||||
@@ -104,7 +127,8 @@ public:
|
||||
const VideoCore::DiskResourceLoadCallback& callback);
|
||||
|
||||
/// Gets the current specified shader stage program
|
||||
Shader* GetStageProgram(Maxwell::ShaderProgram program);
|
||||
Shader* GetStageProgram(Maxwell::ShaderProgram program,
|
||||
VideoCommon::Shader::AsyncShaders& async_shaders);
|
||||
|
||||
/// Gets a compute kernel in the passed address
|
||||
Shader* GetComputeKernel(GPUVAddr code_addr);
|
||||
|
||||
@@ -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}, {});
|
||||
}
|
||||
|
||||
@@ -39,16 +39,17 @@ std::unique_ptr<VKStreamBuffer> CreateStreamBuffer(const VKDevice& device, VKSch
|
||||
|
||||
Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKScheduler& scheduler_,
|
||||
VKStagingBufferPool& staging_pool_, VAddr cpu_addr, std::size_t size)
|
||||
: VideoCommon::BufferBlock{cpu_addr, size}, scheduler{scheduler_}, staging_pool{staging_pool_} {
|
||||
VkBufferCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.size = static_cast<VkDeviceSize>(size);
|
||||
ci.usage = BUFFER_USAGE | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
ci.queueFamilyIndexCount = 0;
|
||||
ci.pQueueFamilyIndices = nullptr;
|
||||
: BufferBlock{cpu_addr, size}, scheduler{scheduler_}, staging_pool{staging_pool_} {
|
||||
const VkBufferCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = static_cast<VkDeviceSize>(size),
|
||||
.usage = BUFFER_USAGE | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
};
|
||||
|
||||
buffer.handle = device.GetLogical().CreateBuffer(ci);
|
||||
buffer.commit = memory_manager.Commit(buffer.handle, false);
|
||||
@@ -66,16 +67,17 @@ void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {
|
||||
scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.CopyBuffer(staging, handle, VkBufferCopy{0, offset, size});
|
||||
|
||||
VkBufferMemoryBarrier barrier;
|
||||
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
||||
barrier.pNext = nullptr;
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = UPLOAD_ACCESS_BARRIERS;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.buffer = handle;
|
||||
barrier.offset = offset;
|
||||
barrier.size = size;
|
||||
const VkBufferMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = UPLOAD_ACCESS_BARRIERS,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = handle,
|
||||
.offset = offset,
|
||||
.size = size,
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, UPLOAD_PIPELINE_STAGE, 0, {},
|
||||
barrier, {});
|
||||
});
|
||||
@@ -87,16 +89,17 @@ void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {
|
||||
|
||||
const VkBuffer handle = Handle();
|
||||
scheduler.Record([staging = *staging.handle, handle, offset, size](vk::CommandBuffer cmdbuf) {
|
||||
VkBufferMemoryBarrier barrier;
|
||||
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
||||
barrier.pNext = nullptr;
|
||||
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.buffer = handle;
|
||||
barrier.offset = offset;
|
||||
barrier.size = size;
|
||||
const VkBufferMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = handle,
|
||||
.offset = offset,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
|
||||
@@ -115,32 +115,32 @@ constexpr u8 quad_array[] = {
|
||||
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
|
||||
|
||||
VkDescriptorSetLayoutBinding BuildQuadArrayPassDescriptorSetLayoutBinding() {
|
||||
VkDescriptorSetLayoutBinding binding;
|
||||
binding.binding = 0;
|
||||
binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
binding.descriptorCount = 1;
|
||||
binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
binding.pImmutableSamplers = nullptr;
|
||||
return binding;
|
||||
return {
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
};
|
||||
}
|
||||
|
||||
VkDescriptorUpdateTemplateEntryKHR BuildQuadArrayPassDescriptorUpdateTemplateEntry() {
|
||||
VkDescriptorUpdateTemplateEntryKHR entry;
|
||||
entry.dstBinding = 0;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = 1;
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
entry.offset = 0;
|
||||
entry.stride = sizeof(DescriptorUpdateEntry);
|
||||
return entry;
|
||||
return {
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = 0,
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
};
|
||||
}
|
||||
|
||||
VkPushConstantRange BuildComputePushConstantRange(std::size_t size) {
|
||||
VkPushConstantRange range;
|
||||
range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
range.offset = 0;
|
||||
range.size = static_cast<u32>(size);
|
||||
return range;
|
||||
return {
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.offset = 0,
|
||||
.size = static_cast<u32>(size),
|
||||
};
|
||||
}
|
||||
|
||||
// Uint8 SPIR-V module. Generated from the "shaders/" directory.
|
||||
@@ -344,29 +344,33 @@ constexpr u8 QUAD_INDEXED_SPV[] = {
|
||||
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
|
||||
|
||||
std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() {
|
||||
std::array<VkDescriptorSetLayoutBinding, 2> bindings;
|
||||
bindings[0].binding = 0;
|
||||
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
bindings[0].descriptorCount = 1;
|
||||
bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
bindings[0].pImmutableSamplers = nullptr;
|
||||
bindings[1].binding = 1;
|
||||
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
bindings[1].descriptorCount = 1;
|
||||
bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
bindings[1].pImmutableSamplers = nullptr;
|
||||
return bindings;
|
||||
return {{
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}};
|
||||
}
|
||||
|
||||
VkDescriptorUpdateTemplateEntryKHR BuildInputOutputDescriptorUpdateTemplate() {
|
||||
VkDescriptorUpdateTemplateEntryKHR entry;
|
||||
entry.dstBinding = 0;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = 2;
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
entry.offset = 0;
|
||||
entry.stride = sizeof(DescriptorUpdateEntry);
|
||||
return entry;
|
||||
return {
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 2,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = 0,
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
};
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
@@ -376,37 +380,37 @@ VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descripto
|
||||
vk::Span<VkDescriptorUpdateTemplateEntryKHR> templates,
|
||||
vk::Span<VkPushConstantRange> push_constants, std::size_t code_size,
|
||||
const u8* code) {
|
||||
VkDescriptorSetLayoutCreateInfo descriptor_layout_ci;
|
||||
descriptor_layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
descriptor_layout_ci.pNext = nullptr;
|
||||
descriptor_layout_ci.flags = 0;
|
||||
descriptor_layout_ci.bindingCount = bindings.size();
|
||||
descriptor_layout_ci.pBindings = bindings.data();
|
||||
descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(descriptor_layout_ci);
|
||||
descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.bindingCount = bindings.size(),
|
||||
.pBindings = bindings.data(),
|
||||
});
|
||||
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_ci;
|
||||
pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipeline_layout_ci.pNext = nullptr;
|
||||
pipeline_layout_ci.flags = 0;
|
||||
pipeline_layout_ci.setLayoutCount = 1;
|
||||
pipeline_layout_ci.pSetLayouts = descriptor_set_layout.address();
|
||||
pipeline_layout_ci.pushConstantRangeCount = push_constants.size();
|
||||
pipeline_layout_ci.pPushConstantRanges = push_constants.data();
|
||||
layout = device.GetLogical().CreatePipelineLayout(pipeline_layout_ci);
|
||||
layout = device.GetLogical().CreatePipelineLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = descriptor_set_layout.address(),
|
||||
.pushConstantRangeCount = push_constants.size(),
|
||||
.pPushConstantRanges = push_constants.data(),
|
||||
});
|
||||
|
||||
if (!templates.empty()) {
|
||||
VkDescriptorUpdateTemplateCreateInfoKHR template_ci;
|
||||
template_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR;
|
||||
template_ci.pNext = nullptr;
|
||||
template_ci.flags = 0;
|
||||
template_ci.descriptorUpdateEntryCount = templates.size();
|
||||
template_ci.pDescriptorUpdateEntries = templates.data();
|
||||
template_ci.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR;
|
||||
template_ci.descriptorSetLayout = *descriptor_set_layout;
|
||||
template_ci.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
template_ci.pipelineLayout = *layout;
|
||||
template_ci.set = 0;
|
||||
descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplateKHR(template_ci);
|
||||
descriptor_template = device.GetLogical().CreateDescriptorUpdateTemplateKHR({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.descriptorUpdateEntryCount = templates.size(),
|
||||
.pDescriptorUpdateEntries = templates.data(),
|
||||
.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
|
||||
.descriptorSetLayout = *descriptor_set_layout,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.pipelineLayout = *layout,
|
||||
.set = 0,
|
||||
});
|
||||
|
||||
descriptor_allocator.emplace(descriptor_pool, *descriptor_set_layout);
|
||||
}
|
||||
@@ -414,32 +418,32 @@ VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descripto
|
||||
auto code_copy = std::make_unique<u32[]>(code_size / sizeof(u32) + 1);
|
||||
std::memcpy(code_copy.get(), code, code_size);
|
||||
|
||||
VkShaderModuleCreateInfo module_ci;
|
||||
module_ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
module_ci.pNext = nullptr;
|
||||
module_ci.flags = 0;
|
||||
module_ci.codeSize = code_size;
|
||||
module_ci.pCode = code_copy.get();
|
||||
module = device.GetLogical().CreateShaderModule(module_ci);
|
||||
module = device.GetLogical().CreateShaderModule({
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.codeSize = code_size,
|
||||
.pCode = code_copy.get(),
|
||||
});
|
||||
|
||||
VkComputePipelineCreateInfo pipeline_ci;
|
||||
pipeline_ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
pipeline_ci.pNext = nullptr;
|
||||
pipeline_ci.flags = 0;
|
||||
pipeline_ci.layout = *layout;
|
||||
pipeline_ci.basePipelineHandle = nullptr;
|
||||
pipeline_ci.basePipelineIndex = 0;
|
||||
|
||||
VkPipelineShaderStageCreateInfo& stage_ci = pipeline_ci.stage;
|
||||
stage_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stage_ci.pNext = nullptr;
|
||||
stage_ci.flags = 0;
|
||||
stage_ci.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
stage_ci.module = *module;
|
||||
stage_ci.pName = "main";
|
||||
stage_ci.pSpecializationInfo = nullptr;
|
||||
|
||||
pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci);
|
||||
pipeline = device.GetLogical().CreateComputePipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage =
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.module = *module,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
.layout = *layout,
|
||||
.basePipelineHandle = nullptr,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
}
|
||||
|
||||
VKComputePass::~VKComputePass() = default;
|
||||
|
||||
@@ -43,12 +43,13 @@ vk::DescriptorSetLayout VKComputePipeline::CreateDescriptorSetLayout() const {
|
||||
const auto add_bindings = [&](VkDescriptorType descriptor_type, std::size_t num_entries) {
|
||||
// TODO(Rodrigo): Maybe make individual bindings here?
|
||||
for (u32 bindpoint = 0; bindpoint < static_cast<u32>(num_entries); ++bindpoint) {
|
||||
VkDescriptorSetLayoutBinding& entry = bindings.emplace_back();
|
||||
entry.binding = binding++;
|
||||
entry.descriptorType = descriptor_type;
|
||||
entry.descriptorCount = 1;
|
||||
entry.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
entry.pImmutableSamplers = nullptr;
|
||||
bindings.push_back({
|
||||
.binding = binding++,
|
||||
.descriptorType = descriptor_type,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
});
|
||||
}
|
||||
};
|
||||
add_bindings(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, entries.const_buffers.size());
|
||||
@@ -58,25 +59,25 @@ vk::DescriptorSetLayout VKComputePipeline::CreateDescriptorSetLayout() const {
|
||||
add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, entries.storage_texels.size());
|
||||
add_bindings(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, entries.images.size());
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.bindingCount = static_cast<u32>(bindings.size());
|
||||
ci.pBindings = bindings.data();
|
||||
return device.GetLogical().CreateDescriptorSetLayout(ci);
|
||||
return device.GetLogical().CreateDescriptorSetLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.bindingCount = static_cast<u32>(bindings.size()),
|
||||
.pBindings = bindings.data(),
|
||||
});
|
||||
}
|
||||
|
||||
vk::PipelineLayout VKComputePipeline::CreatePipelineLayout() const {
|
||||
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;
|
||||
return device.GetLogical().CreatePipelineLayout(ci);
|
||||
return device.GetLogical().CreatePipelineLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = descriptor_set_layout.address(),
|
||||
.pushConstantRangeCount = 0,
|
||||
.pPushConstantRanges = nullptr,
|
||||
});
|
||||
}
|
||||
|
||||
vk::DescriptorUpdateTemplateKHR VKComputePipeline::CreateDescriptorUpdateTemplate() const {
|
||||
@@ -89,59 +90,63 @@ vk::DescriptorUpdateTemplateKHR VKComputePipeline::CreateDescriptorUpdateTemplat
|
||||
return {};
|
||||
}
|
||||
|
||||
VkDescriptorUpdateTemplateCreateInfoKHR ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.descriptorUpdateEntryCount = static_cast<u32>(template_entries.size());
|
||||
ci.pDescriptorUpdateEntries = template_entries.data();
|
||||
ci.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR;
|
||||
ci.descriptorSetLayout = *descriptor_set_layout;
|
||||
ci.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
ci.pipelineLayout = *layout;
|
||||
ci.set = DESCRIPTOR_SET;
|
||||
return device.GetLogical().CreateDescriptorUpdateTemplateKHR(ci);
|
||||
return device.GetLogical().CreateDescriptorUpdateTemplateKHR({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.descriptorUpdateEntryCount = static_cast<u32>(template_entries.size()),
|
||||
.pDescriptorUpdateEntries = template_entries.data(),
|
||||
.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
|
||||
.descriptorSetLayout = *descriptor_set_layout,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.pipelineLayout = *layout,
|
||||
.set = DESCRIPTOR_SET,
|
||||
});
|
||||
}
|
||||
|
||||
vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const {
|
||||
device.SaveShader(code);
|
||||
|
||||
VkShaderModuleCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.codeSize = code.size() * sizeof(u32);
|
||||
ci.pCode = code.data();
|
||||
return device.GetLogical().CreateShaderModule(ci);
|
||||
return device.GetLogical().CreateShaderModule({
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.codeSize = code.size() * sizeof(u32),
|
||||
.pCode = code.data(),
|
||||
});
|
||||
}
|
||||
|
||||
vk::Pipeline VKComputePipeline::CreatePipeline() const {
|
||||
VkComputePipelineCreateInfo ci;
|
||||
VkPipelineShaderStageCreateInfo& stage_ci = ci.stage;
|
||||
stage_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stage_ci.pNext = nullptr;
|
||||
stage_ci.flags = 0;
|
||||
stage_ci.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
stage_ci.module = *shader_module;
|
||||
stage_ci.pName = "main";
|
||||
stage_ci.pSpecializationInfo = nullptr;
|
||||
|
||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci;
|
||||
subgroup_size_ci.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT;
|
||||
subgroup_size_ci.pNext = nullptr;
|
||||
subgroup_size_ci.requiredSubgroupSize = GuestWarpSize;
|
||||
VkComputePipelineCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage =
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.module = *shader_module,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
.layout = *layout,
|
||||
.basePipelineHandle = nullptr,
|
||||
.basePipelineIndex = 0,
|
||||
};
|
||||
|
||||
const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.requiredSubgroupSize = GuestWarpSize,
|
||||
};
|
||||
|
||||
if (entries.uses_warps && device.IsGuestWarpSizeSupported(VK_SHADER_STAGE_COMPUTE_BIT)) {
|
||||
stage_ci.pNext = &subgroup_size_ci;
|
||||
ci.stage.pNext = &subgroup_size_ci;
|
||||
}
|
||||
|
||||
ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.layout = *layout;
|
||||
ci.basePipelineHandle = nullptr;
|
||||
ci.basePipelineIndex = 0;
|
||||
return device.GetLogical().CreateComputePipeline(ci);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,27 +43,30 @@ vk::DescriptorPool* VKDescriptorPool::AllocateNewPool() {
|
||||
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, num_sets * 64},
|
||||
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, num_sets * 64},
|
||||
{VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, num_sets * 64},
|
||||
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, num_sets * 40}};
|
||||
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, num_sets * 40},
|
||||
};
|
||||
|
||||
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 = num_sets;
|
||||
ci.poolSizeCount = static_cast<u32>(std::size(pool_sizes));
|
||||
ci.pPoolSizes = std::data(pool_sizes);
|
||||
const VkDescriptorPoolCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = num_sets,
|
||||
.poolSizeCount = static_cast<u32>(std::size(pool_sizes)),
|
||||
.pPoolSizes = std::data(pool_sizes),
|
||||
};
|
||||
return &pools.emplace_back(device.GetLogical().CreateDescriptorPool(ci));
|
||||
}
|
||||
|
||||
vk::DescriptorSets VKDescriptorPool::AllocateDescriptors(VkDescriptorSetLayout layout,
|
||||
std::size_t count) {
|
||||
const std::vector layout_copies(count, layout);
|
||||
VkDescriptorSetAllocateInfo ai;
|
||||
ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
ai.pNext = nullptr;
|
||||
ai.descriptorPool = **active_pool;
|
||||
ai.descriptorSetCount = static_cast<u32>(count);
|
||||
ai.pSetLayouts = layout_copies.data();
|
||||
VkDescriptorSetAllocateInfo ai{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.descriptorPool = **active_pool,
|
||||
.descriptorSetCount = static_cast<u32>(count),
|
||||
.pSetLayouts = layout_copies.data(),
|
||||
};
|
||||
|
||||
vk::DescriptorSets sets = active_pool->Allocate(ai);
|
||||
if (!sets.IsOutOfPoolMemory()) {
|
||||
|
||||
@@ -22,14 +22,21 @@ namespace {
|
||||
|
||||
namespace Alternatives {
|
||||
|
||||
constexpr std::array Depth24UnormS8_UINT = {VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VK_FORMAT_D16_UNORM_S8_UINT, VkFormat{}};
|
||||
constexpr std::array Depth16UnormS8_UINT = {VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT, VkFormat{}};
|
||||
constexpr std::array Depth24UnormS8_UINT{
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VK_FORMAT_D16_UNORM_S8_UINT,
|
||||
VkFormat{},
|
||||
};
|
||||
|
||||
constexpr std::array Depth16UnormS8_UINT{
|
||||
VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
||||
VkFormat{},
|
||||
};
|
||||
|
||||
} // namespace Alternatives
|
||||
|
||||
constexpr std::array REQUIRED_EXTENSIONS = {
|
||||
constexpr std::array REQUIRED_EXTENSIONS{
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
|
||||
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
|
||||
@@ -169,97 +176,104 @@ bool VKDevice::Create() {
|
||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||
const std::vector extensions = LoadExtensions();
|
||||
|
||||
VkPhysicalDeviceFeatures2 features2;
|
||||
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
features2.pNext = nullptr;
|
||||
VkPhysicalDeviceFeatures2 features2{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
const void* first_next = &features2;
|
||||
void** next = &features2.pNext;
|
||||
|
||||
auto& features = features2.features;
|
||||
features.robustBufferAccess = false;
|
||||
features.fullDrawIndexUint32 = false;
|
||||
features.imageCubeArray = false;
|
||||
features.independentBlend = true;
|
||||
features.geometryShader = true;
|
||||
features.tessellationShader = true;
|
||||
features.sampleRateShading = false;
|
||||
features.dualSrcBlend = false;
|
||||
features.logicOp = false;
|
||||
features.multiDrawIndirect = false;
|
||||
features.drawIndirectFirstInstance = false;
|
||||
features.depthClamp = true;
|
||||
features.depthBiasClamp = true;
|
||||
features.fillModeNonSolid = false;
|
||||
features.depthBounds = false;
|
||||
features.wideLines = false;
|
||||
features.largePoints = true;
|
||||
features.alphaToOne = false;
|
||||
features.multiViewport = true;
|
||||
features.samplerAnisotropy = true;
|
||||
features.textureCompressionETC2 = false;
|
||||
features.textureCompressionASTC_LDR = is_optimal_astc_supported;
|
||||
features.textureCompressionBC = false;
|
||||
features.occlusionQueryPrecise = true;
|
||||
features.pipelineStatisticsQuery = false;
|
||||
features.vertexPipelineStoresAndAtomics = true;
|
||||
features.fragmentStoresAndAtomics = true;
|
||||
features.shaderTessellationAndGeometryPointSize = false;
|
||||
features.shaderImageGatherExtended = true;
|
||||
features.shaderStorageImageExtendedFormats = false;
|
||||
features.shaderStorageImageMultisample = false;
|
||||
features.shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported;
|
||||
features.shaderStorageImageWriteWithoutFormat = true;
|
||||
features.shaderUniformBufferArrayDynamicIndexing = false;
|
||||
features.shaderSampledImageArrayDynamicIndexing = false;
|
||||
features.shaderStorageBufferArrayDynamicIndexing = false;
|
||||
features.shaderStorageImageArrayDynamicIndexing = false;
|
||||
features.shaderClipDistance = false;
|
||||
features.shaderCullDistance = false;
|
||||
features.shaderFloat64 = false;
|
||||
features.shaderInt64 = false;
|
||||
features.shaderInt16 = false;
|
||||
features.shaderResourceResidency = false;
|
||||
features.shaderResourceMinLod = false;
|
||||
features.sparseBinding = false;
|
||||
features.sparseResidencyBuffer = false;
|
||||
features.sparseResidencyImage2D = false;
|
||||
features.sparseResidencyImage3D = false;
|
||||
features.sparseResidency2Samples = false;
|
||||
features.sparseResidency4Samples = false;
|
||||
features.sparseResidency8Samples = false;
|
||||
features.sparseResidency16Samples = false;
|
||||
features.sparseResidencyAliased = false;
|
||||
features.variableMultisampleRate = false;
|
||||
features.inheritedQueries = false;
|
||||
features2.features = {
|
||||
.robustBufferAccess = false,
|
||||
.fullDrawIndexUint32 = false,
|
||||
.imageCubeArray = false,
|
||||
.independentBlend = true,
|
||||
.geometryShader = true,
|
||||
.tessellationShader = true,
|
||||
.sampleRateShading = false,
|
||||
.dualSrcBlend = false,
|
||||
.logicOp = false,
|
||||
.multiDrawIndirect = false,
|
||||
.drawIndirectFirstInstance = false,
|
||||
.depthClamp = true,
|
||||
.depthBiasClamp = true,
|
||||
.fillModeNonSolid = false,
|
||||
.depthBounds = false,
|
||||
.wideLines = false,
|
||||
.largePoints = true,
|
||||
.alphaToOne = false,
|
||||
.multiViewport = true,
|
||||
.samplerAnisotropy = true,
|
||||
.textureCompressionETC2 = false,
|
||||
.textureCompressionASTC_LDR = is_optimal_astc_supported,
|
||||
.textureCompressionBC = false,
|
||||
.occlusionQueryPrecise = true,
|
||||
.pipelineStatisticsQuery = false,
|
||||
.vertexPipelineStoresAndAtomics = true,
|
||||
.fragmentStoresAndAtomics = true,
|
||||
.shaderTessellationAndGeometryPointSize = false,
|
||||
.shaderImageGatherExtended = true,
|
||||
.shaderStorageImageExtendedFormats = false,
|
||||
.shaderStorageImageMultisample = false,
|
||||
.shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported,
|
||||
.shaderStorageImageWriteWithoutFormat = true,
|
||||
.shaderUniformBufferArrayDynamicIndexing = false,
|
||||
.shaderSampledImageArrayDynamicIndexing = false,
|
||||
.shaderStorageBufferArrayDynamicIndexing = false,
|
||||
.shaderStorageImageArrayDynamicIndexing = false,
|
||||
.shaderClipDistance = false,
|
||||
.shaderCullDistance = false,
|
||||
.shaderFloat64 = false,
|
||||
.shaderInt64 = false,
|
||||
.shaderInt16 = false,
|
||||
.shaderResourceResidency = false,
|
||||
.shaderResourceMinLod = false,
|
||||
.sparseBinding = false,
|
||||
.sparseResidencyBuffer = false,
|
||||
.sparseResidencyImage2D = false,
|
||||
.sparseResidencyImage3D = false,
|
||||
.sparseResidency2Samples = false,
|
||||
.sparseResidency4Samples = false,
|
||||
.sparseResidency8Samples = false,
|
||||
.sparseResidency16Samples = false,
|
||||
.sparseResidencyAliased = false,
|
||||
.variableMultisampleRate = false,
|
||||
.inheritedQueries = false,
|
||||
};
|
||||
|
||||
VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage;
|
||||
bit16_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR;
|
||||
bit16_storage.pNext = nullptr;
|
||||
bit16_storage.storageBuffer16BitAccess = false;
|
||||
bit16_storage.uniformAndStorageBuffer16BitAccess = true;
|
||||
bit16_storage.storagePushConstant16 = false;
|
||||
bit16_storage.storageInputOutput16 = false;
|
||||
VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.storageBuffer16BitAccess = false,
|
||||
.uniformAndStorageBuffer16BitAccess = true,
|
||||
.storagePushConstant16 = false,
|
||||
.storageInputOutput16 = false,
|
||||
};
|
||||
SetNext(next, bit16_storage);
|
||||
|
||||
VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage;
|
||||
bit8_storage.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR;
|
||||
bit8_storage.pNext = nullptr;
|
||||
bit8_storage.storageBuffer8BitAccess = false;
|
||||
bit8_storage.uniformAndStorageBuffer8BitAccess = true;
|
||||
bit8_storage.storagePushConstant8 = false;
|
||||
VkPhysicalDevice8BitStorageFeaturesKHR bit8_storage{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.storageBuffer8BitAccess = false,
|
||||
.uniformAndStorageBuffer8BitAccess = true,
|
||||
.storagePushConstant8 = false,
|
||||
};
|
||||
SetNext(next, bit8_storage);
|
||||
|
||||
VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset;
|
||||
host_query_reset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
|
||||
host_query_reset.hostQueryReset = true;
|
||||
VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT,
|
||||
.hostQueryReset = true,
|
||||
};
|
||||
SetNext(next, host_query_reset);
|
||||
|
||||
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
|
||||
if (is_float16_supported) {
|
||||
float16_int8.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
|
||||
float16_int8.pNext = nullptr;
|
||||
float16_int8.shaderFloat16 = true;
|
||||
float16_int8.shaderInt8 = false;
|
||||
float16_int8 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.shaderFloat16 = true,
|
||||
.shaderInt8 = false,
|
||||
};
|
||||
SetNext(next, float16_int8);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
|
||||
@@ -271,10 +285,11 @@ bool VKDevice::Create() {
|
||||
|
||||
VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR std430_layout;
|
||||
if (khr_uniform_buffer_standard_layout) {
|
||||
std430_layout.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR;
|
||||
std430_layout.pNext = nullptr;
|
||||
std430_layout.uniformBufferStandardLayout = true;
|
||||
std430_layout = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.uniformBufferStandardLayout = true,
|
||||
};
|
||||
SetNext(next, std430_layout);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support packed UBOs");
|
||||
@@ -282,9 +297,11 @@ bool VKDevice::Create() {
|
||||
|
||||
VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8;
|
||||
if (ext_index_type_uint8) {
|
||||
index_type_uint8.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT;
|
||||
index_type_uint8.pNext = nullptr;
|
||||
index_type_uint8.indexTypeUint8 = true;
|
||||
index_type_uint8 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.indexTypeUint8 = true,
|
||||
};
|
||||
SetNext(next, index_type_uint8);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes");
|
||||
@@ -292,11 +309,12 @@ bool VKDevice::Create() {
|
||||
|
||||
VkPhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback;
|
||||
if (ext_transform_feedback) {
|
||||
transform_feedback.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
|
||||
transform_feedback.pNext = nullptr;
|
||||
transform_feedback.transformFeedback = true;
|
||||
transform_feedback.geometryStreams = true;
|
||||
transform_feedback = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.transformFeedback = true,
|
||||
.geometryStreams = true,
|
||||
};
|
||||
SetNext(next, transform_feedback);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks");
|
||||
@@ -304,10 +322,12 @@ bool VKDevice::Create() {
|
||||
|
||||
VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border;
|
||||
if (ext_custom_border_color) {
|
||||
custom_border.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
|
||||
custom_border.pNext = nullptr;
|
||||
custom_border.customBorderColors = VK_TRUE;
|
||||
custom_border.customBorderColorWithoutFormat = VK_TRUE;
|
||||
custom_border = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.customBorderColors = VK_TRUE,
|
||||
.customBorderColorWithoutFormat = VK_TRUE,
|
||||
};
|
||||
SetNext(next, custom_border);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
|
||||
@@ -315,9 +335,11 @@ bool VKDevice::Create() {
|
||||
|
||||
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;
|
||||
dynamic_state = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT,
|
||||
.pNext = nullptr,
|
||||
.extendedDynamicState = VK_TRUE,
|
||||
};
|
||||
SetNext(next, dynamic_state);
|
||||
} else {
|
||||
LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
|
||||
@@ -331,11 +353,13 @@ bool VKDevice::Create() {
|
||||
if (nv_device_diagnostics_config) {
|
||||
nsight_aftermath_tracker.Initialize();
|
||||
|
||||
diagnostics_nv.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
|
||||
diagnostics_nv.pNext = &features2;
|
||||
diagnostics_nv.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
|
||||
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
|
||||
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
|
||||
diagnostics_nv = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV,
|
||||
.pNext = &features2,
|
||||
.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |
|
||||
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |
|
||||
VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV,
|
||||
};
|
||||
first_next = &diagnostics_nv;
|
||||
}
|
||||
|
||||
@@ -704,13 +728,15 @@ void VKDevice::SetupFeatures() {
|
||||
}
|
||||
|
||||
void VKDevice::CollectTelemetryParameters() {
|
||||
VkPhysicalDeviceDriverPropertiesKHR driver;
|
||||
driver.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
|
||||
driver.pNext = nullptr;
|
||||
VkPhysicalDeviceDriverPropertiesKHR driver{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceProperties2KHR properties;
|
||||
properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
|
||||
properties.pNext = &driver;
|
||||
VkPhysicalDeviceProperties2KHR properties{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
|
||||
.pNext = &driver,
|
||||
};
|
||||
physical.GetProperties2KHR(properties);
|
||||
|
||||
driver_id = driver.driverID;
|
||||
@@ -719,22 +745,24 @@ void VKDevice::CollectTelemetryParameters() {
|
||||
const std::vector extensions = physical.EnumerateDeviceExtensionProperties();
|
||||
reported_extensions.reserve(std::size(extensions));
|
||||
for (const auto& extension : extensions) {
|
||||
reported_extensions.push_back(extension.extensionName);
|
||||
reported_extensions.emplace_back(extension.extensionName);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VkDeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const {
|
||||
static constexpr float QUEUE_PRIORITY = 1.0f;
|
||||
|
||||
std::unordered_set<u32> unique_queue_families = {graphics_family, present_family};
|
||||
std::unordered_set<u32> unique_queue_families{graphics_family, present_family};
|
||||
std::vector<VkDeviceQueueCreateInfo> queue_cis;
|
||||
queue_cis.reserve(unique_queue_families.size());
|
||||
|
||||
for (const u32 queue_family : unique_queue_families) {
|
||||
VkDeviceQueueCreateInfo& ci = queue_cis.emplace_back();
|
||||
ci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.queueFamilyIndex = queue_family;
|
||||
auto& ci = queue_cis.emplace_back(VkDeviceQueueCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.queueFamilyIndex = queue_family,
|
||||
});
|
||||
ci.queueCount = 1;
|
||||
ci.pQueuePriorities = &QUEUE_PRIORITY;
|
||||
}
|
||||
|
||||
@@ -28,15 +28,15 @@ namespace {
|
||||
|
||||
template <class StencilFace>
|
||||
VkStencilOpState GetStencilFaceState(const StencilFace& face) {
|
||||
VkStencilOpState state;
|
||||
state.failOp = MaxwellToVK::StencilOp(face.ActionStencilFail());
|
||||
state.passOp = MaxwellToVK::StencilOp(face.ActionDepthPass());
|
||||
state.depthFailOp = MaxwellToVK::StencilOp(face.ActionDepthFail());
|
||||
state.compareOp = MaxwellToVK::ComparisonOp(face.TestFunc());
|
||||
state.compareMask = 0;
|
||||
state.writeMask = 0;
|
||||
state.reference = 0;
|
||||
return state;
|
||||
return {
|
||||
.failOp = MaxwellToVK::StencilOp(face.ActionStencilFail()),
|
||||
.passOp = MaxwellToVK::StencilOp(face.ActionDepthPass()),
|
||||
.depthFailOp = MaxwellToVK::StencilOp(face.ActionDepthFail()),
|
||||
.compareOp = MaxwellToVK::ComparisonOp(face.TestFunc()),
|
||||
.compareMask = 0,
|
||||
.writeMask = 0,
|
||||
.reference = 0,
|
||||
};
|
||||
}
|
||||
|
||||
bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) {
|
||||
@@ -52,20 +52,21 @@ bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) {
|
||||
}
|
||||
|
||||
VkViewportSwizzleNV UnpackViewportSwizzle(u16 swizzle) {
|
||||
union {
|
||||
union Swizzle {
|
||||
u32 raw;
|
||||
BitField<0, 3, Maxwell::ViewportSwizzle> x;
|
||||
BitField<4, 3, Maxwell::ViewportSwizzle> y;
|
||||
BitField<8, 3, Maxwell::ViewportSwizzle> z;
|
||||
BitField<12, 3, Maxwell::ViewportSwizzle> w;
|
||||
} const unpacked{swizzle};
|
||||
};
|
||||
const Swizzle unpacked{swizzle};
|
||||
|
||||
VkViewportSwizzleNV result;
|
||||
result.x = MaxwellToVK::ViewportSwizzle(unpacked.x);
|
||||
result.y = MaxwellToVK::ViewportSwizzle(unpacked.y);
|
||||
result.z = MaxwellToVK::ViewportSwizzle(unpacked.z);
|
||||
result.w = MaxwellToVK::ViewportSwizzle(unpacked.w);
|
||||
return result;
|
||||
return {
|
||||
.x = MaxwellToVK::ViewportSwizzle(unpacked.x),
|
||||
.y = MaxwellToVK::ViewportSwizzle(unpacked.y),
|
||||
.z = MaxwellToVK::ViewportSwizzle(unpacked.z),
|
||||
.w = MaxwellToVK::ViewportSwizzle(unpacked.w),
|
||||
};
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
@@ -100,24 +101,26 @@ VkDescriptorSet VKGraphicsPipeline::CommitDescriptorSet() {
|
||||
|
||||
vk::DescriptorSetLayout VKGraphicsPipeline::CreateDescriptorSetLayout(
|
||||
vk::Span<VkDescriptorSetLayoutBinding> bindings) const {
|
||||
VkDescriptorSetLayoutCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.bindingCount = bindings.size();
|
||||
ci.pBindings = bindings.data();
|
||||
const VkDescriptorSetLayoutCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.bindingCount = bindings.size(),
|
||||
.pBindings = bindings.data(),
|
||||
};
|
||||
return device.GetLogical().CreateDescriptorSetLayout(ci);
|
||||
}
|
||||
|
||||
vk::PipelineLayout VKGraphicsPipeline::CreatePipelineLayout() const {
|
||||
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,
|
||||
};
|
||||
return device.GetLogical().CreatePipelineLayout(ci);
|
||||
}
|
||||
|
||||
@@ -136,26 +139,28 @@ vk::DescriptorUpdateTemplateKHR VKGraphicsPipeline::CreateDescriptorUpdateTempla
|
||||
return {};
|
||||
}
|
||||
|
||||
VkDescriptorUpdateTemplateCreateInfoKHR ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.descriptorUpdateEntryCount = static_cast<u32>(template_entries.size());
|
||||
ci.pDescriptorUpdateEntries = template_entries.data();
|
||||
ci.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR;
|
||||
ci.descriptorSetLayout = *descriptor_set_layout;
|
||||
ci.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
ci.pipelineLayout = *layout;
|
||||
ci.set = DESCRIPTOR_SET;
|
||||
const VkDescriptorUpdateTemplateCreateInfoKHR ci{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.descriptorUpdateEntryCount = static_cast<u32>(template_entries.size()),
|
||||
.pDescriptorUpdateEntries = template_entries.data(),
|
||||
.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR,
|
||||
.descriptorSetLayout = *descriptor_set_layout,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.pipelineLayout = *layout,
|
||||
.set = DESCRIPTOR_SET,
|
||||
};
|
||||
return device.GetLogical().CreateDescriptorUpdateTemplateKHR(ci);
|
||||
}
|
||||
|
||||
std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
|
||||
const SPIRVProgram& program) const {
|
||||
VkShaderModuleCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
VkShaderModuleCreateInfo ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
std::vector<vk::ShaderModule> modules;
|
||||
modules.reserve(Maxwell::MaxShaderStage);
|
||||
@@ -204,15 +209,17 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
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();
|
||||
vertex_binding.binding = static_cast<u32>(index);
|
||||
vertex_binding.stride = binding.stride;
|
||||
vertex_binding.inputRate = rate;
|
||||
vertex_bindings.push_back({
|
||||
.binding = static_cast<u32>(index),
|
||||
.stride = binding.stride,
|
||||
.inputRate = rate,
|
||||
});
|
||||
|
||||
if (instanced) {
|
||||
auto& binding_divisor = vertex_binding_divisors.emplace_back();
|
||||
binding_divisor.binding = static_cast<u32>(index);
|
||||
binding_divisor.divisor = state.binding_divisors[index];
|
||||
vertex_binding_divisors.push_back({
|
||||
.binding = static_cast<u32>(index),
|
||||
.divisor = state.binding_divisors[index],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,116 +234,132 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
// Skip attributes not used by the vertex shaders.
|
||||
continue;
|
||||
}
|
||||
auto& vertex_attribute = vertex_attributes.emplace_back();
|
||||
vertex_attribute.location = static_cast<u32>(index);
|
||||
vertex_attribute.binding = attribute.buffer;
|
||||
vertex_attribute.format = MaxwellToVK::VertexFormat(attribute.Type(), attribute.Size());
|
||||
vertex_attribute.offset = attribute.offset;
|
||||
vertex_attributes.push_back({
|
||||
.location = static_cast<u32>(index),
|
||||
.binding = attribute.buffer,
|
||||
.format = MaxwellToVK::VertexFormat(attribute.Type(), attribute.Size()),
|
||||
.offset = attribute.offset,
|
||||
});
|
||||
}
|
||||
|
||||
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 = static_cast<u32>(vertex_bindings.size());
|
||||
vertex_input_ci.pVertexBindingDescriptions = vertex_bindings.data();
|
||||
vertex_input_ci.vertexAttributeDescriptionCount = static_cast<u32>(vertex_attributes.size());
|
||||
vertex_input_ci.pVertexAttributeDescriptions = vertex_attributes.data();
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.vertexBindingDescriptionCount = static_cast<u32>(vertex_bindings.size()),
|
||||
.pVertexBindingDescriptions = vertex_bindings.data(),
|
||||
.vertexAttributeDescriptionCount = static_cast<u32>(vertex_attributes.size()),
|
||||
.pVertexAttributeDescriptions = vertex_attributes.data(),
|
||||
};
|
||||
|
||||
VkPipelineVertexInputDivisorStateCreateInfoEXT input_divisor_ci;
|
||||
input_divisor_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
|
||||
input_divisor_ci.pNext = nullptr;
|
||||
input_divisor_ci.vertexBindingDivisorCount = static_cast<u32>(vertex_binding_divisors.size());
|
||||
input_divisor_ci.pVertexBindingDivisors = vertex_binding_divisors.data();
|
||||
const VkPipelineVertexInputDivisorStateCreateInfoEXT input_divisor_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.vertexBindingDivisorCount = static_cast<u32>(vertex_binding_divisors.size()),
|
||||
.pVertexBindingDivisors = vertex_binding_divisors.data(),
|
||||
};
|
||||
if (!vertex_binding_divisors.empty()) {
|
||||
vertex_input_ci.pNext = &input_divisor_ci;
|
||||
}
|
||||
|
||||
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 = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
|
||||
input_assembly_ci.primitiveRestartEnable =
|
||||
state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
|
||||
const auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology()),
|
||||
.primitiveRestartEnable = state.primitive_restart_enable != 0 &&
|
||||
SupportsPrimitiveRestart(input_assembly_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 = state.patch_control_points_minus_one.Value() + 1;
|
||||
const VkPipelineTessellationStateCreateInfo tessellation_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.patchControlPoints = state.patch_control_points_minus_one.Value() + 1,
|
||||
};
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_ci;
|
||||
viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewport_ci.pNext = nullptr;
|
||||
viewport_ci.flags = 0;
|
||||
viewport_ci.viewportCount = Maxwell::NumViewports;
|
||||
viewport_ci.pViewports = nullptr;
|
||||
viewport_ci.scissorCount = Maxwell::NumViewports;
|
||||
viewport_ci.pScissors = nullptr;
|
||||
VkPipelineViewportStateCreateInfo viewport_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = Maxwell::NumViewports,
|
||||
.pViewports = nullptr,
|
||||
.scissorCount = Maxwell::NumViewports,
|
||||
.pScissors = nullptr,
|
||||
};
|
||||
|
||||
std::array<VkViewportSwizzleNV, Maxwell::NumViewports> swizzles;
|
||||
std::transform(viewport_swizzles.begin(), viewport_swizzles.end(), swizzles.begin(),
|
||||
UnpackViewportSwizzle);
|
||||
VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci;
|
||||
swizzle_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV;
|
||||
swizzle_ci.pNext = nullptr;
|
||||
swizzle_ci.flags = 0;
|
||||
swizzle_ci.viewportCount = Maxwell::NumViewports;
|
||||
swizzle_ci.pViewportSwizzles = swizzles.data();
|
||||
VkPipelineViewportSwizzleStateCreateInfoNV swizzle_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = Maxwell::NumViewports,
|
||||
.pViewportSwizzles = swizzles.data(),
|
||||
};
|
||||
if (device.IsNvViewportSwizzleSupported()) {
|
||||
viewport_ci.pNext = &swizzle_ci;
|
||||
}
|
||||
|
||||
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 = 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 =
|
||||
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;
|
||||
rasterization_ci.lineWidth = 1.0f;
|
||||
const VkPipelineRasterizationStateCreateInfo rasterization_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthClampEnable =
|
||||
static_cast<VkBool32>(state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
|
||||
.rasterizerDiscardEnable =
|
||||
static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode =
|
||||
dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE,
|
||||
.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
|
||||
.depthBiasEnable = state.depth_bias_enable,
|
||||
.depthBiasConstantFactor = 0.0f,
|
||||
.depthBiasClamp = 0.0f,
|
||||
.depthBiasSlopeFactor = 0.0f,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisample_ci;
|
||||
multisample_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisample_ci.pNext = nullptr;
|
||||
multisample_ci.flags = 0;
|
||||
multisample_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
multisample_ci.sampleShadingEnable = VK_FALSE;
|
||||
multisample_ci.minSampleShading = 0.0f;
|
||||
multisample_ci.pSampleMask = nullptr;
|
||||
multisample_ci.alphaToCoverageEnable = VK_FALSE;
|
||||
multisample_ci.alphaToOneEnable = VK_FALSE;
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_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,
|
||||
};
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_ci;
|
||||
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 = 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;
|
||||
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthTestEnable = dynamic.depth_test_enable,
|
||||
.depthWriteEnable = dynamic.depth_write_enable,
|
||||
.depthCompareOp = dynamic.depth_test_enable
|
||||
? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc())
|
||||
: VK_COMPARE_OP_ALWAYS,
|
||||
.depthBoundsTestEnable = dynamic.depth_bounds_enable,
|
||||
.stencilTestEnable = dynamic.stencil_enable,
|
||||
.front = GetStencilFaceState(dynamic.front),
|
||||
.back = GetStencilFaceState(dynamic.back),
|
||||
.minDepthBounds = 0.0f,
|
||||
.maxDepthBounds = 0.0f,
|
||||
};
|
||||
|
||||
std::array<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
|
||||
const auto num_attachments = static_cast<std::size_t>(renderpass_params.num_color_attachments);
|
||||
for (std::size_t index = 0; index < num_attachments; ++index) {
|
||||
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};
|
||||
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 = state.attachments[index];
|
||||
|
||||
VkColorComponentFlags color_components = 0;
|
||||
@@ -346,35 +369,36 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
}
|
||||
}
|
||||
|
||||
VkPipelineColorBlendAttachmentState& attachment = cb_attachments[index];
|
||||
attachment.blendEnable = blend.enable != 0;
|
||||
attachment.srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor());
|
||||
attachment.dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor());
|
||||
attachment.colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB());
|
||||
attachment.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.SourceAlphaFactor());
|
||||
attachment.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.DestAlphaFactor());
|
||||
attachment.alphaBlendOp = MaxwellToVK::BlendEquation(blend.EquationAlpha());
|
||||
attachment.colorWriteMask = color_components;
|
||||
cb_attachments[index] = {
|
||||
.blendEnable = blend.enable != 0,
|
||||
.srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor()),
|
||||
.dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor()),
|
||||
.colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB()),
|
||||
.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.SourceAlphaFactor()),
|
||||
.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.DestAlphaFactor()),
|
||||
.alphaBlendOp = MaxwellToVK::BlendEquation(blend.EquationAlpha()),
|
||||
.colorWriteMask = color_components,
|
||||
};
|
||||
}
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_ci;
|
||||
color_blend_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
color_blend_ci.pNext = nullptr;
|
||||
color_blend_ci.flags = 0;
|
||||
color_blend_ci.logicOpEnable = VK_FALSE;
|
||||
color_blend_ci.logicOp = VK_LOGIC_OP_COPY;
|
||||
color_blend_ci.attachmentCount = static_cast<u32>(num_attachments);
|
||||
color_blend_ci.pAttachments = cb_attachments.data();
|
||||
std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
|
||||
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 = static_cast<u32>(num_attachments),
|
||||
.pAttachments = cb_attachments.data(),
|
||||
};
|
||||
|
||||
std::vector 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,
|
||||
};
|
||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
||||
static constexpr std::array extended = {
|
||||
static constexpr std::array extended{
|
||||
VK_DYNAMIC_STATE_CULL_MODE_EXT,
|
||||
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
|
||||
VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
|
||||
@@ -389,18 +413,19 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
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;
|
||||
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();
|
||||
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(),
|
||||
};
|
||||
|
||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci;
|
||||
subgroup_size_ci.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT;
|
||||
subgroup_size_ci.pNext = nullptr;
|
||||
subgroup_size_ci.requiredSubgroupSize = GuestWarpSize;
|
||||
const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.requiredSubgroupSize = GuestWarpSize,
|
||||
};
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shader_stages;
|
||||
std::size_t module_index = 0;
|
||||
@@ -408,6 +433,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
if (!program[stage]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VkPipelineShaderStageCreateInfo& stage_ci = shader_stages.emplace_back();
|
||||
stage_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stage_ci.pNext = nullptr;
|
||||
@@ -422,26 +448,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
|
||||
}
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo ci;
|
||||
ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
ci.pNext = nullptr;
|
||||
ci.flags = 0;
|
||||
ci.stageCount = static_cast<u32>(shader_stages.size());
|
||||
ci.pStages = shader_stages.data();
|
||||
ci.pVertexInputState = &vertex_input_ci;
|
||||
ci.pInputAssemblyState = &input_assembly_ci;
|
||||
ci.pTessellationState = &tessellation_ci;
|
||||
ci.pViewportState = &viewport_ci;
|
||||
ci.pRasterizationState = &rasterization_ci;
|
||||
ci.pMultisampleState = &multisample_ci;
|
||||
ci.pDepthStencilState = &depth_stencil_ci;
|
||||
ci.pColorBlendState = &color_blend_ci;
|
||||
ci.pDynamicState = &dynamic_state_ci;
|
||||
ci.layout = *layout;
|
||||
ci.renderPass = renderpass;
|
||||
ci.subpass = 0;
|
||||
ci.basePipelineHandle = nullptr;
|
||||
ci.basePipelineIndex = 0;
|
||||
const VkGraphicsPipelineCreateInfo 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 = &tessellation_ci,
|
||||
.pViewportState = &viewport_ci,
|
||||
.pRasterizationState = &rasterization_ci,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = &depth_stencil_ci,
|
||||
.pColorBlendState = &color_blend_ci,
|
||||
.pDynamicState = &dynamic_state_ci,
|
||||
.layout = *layout,
|
||||
.renderPass = renderpass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = nullptr,
|
||||
.basePipelineIndex = 0,
|
||||
};
|
||||
return device.GetLogical().CreateGraphicsPipeline(ci);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,21 +102,29 @@ bool VKImage::HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num
|
||||
|
||||
void VKImage::CreatePresentView() {
|
||||
// Image type has to be 2D to be presented.
|
||||
VkImageViewCreateInfo image_view_ci;
|
||||
image_view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
image_view_ci.pNext = nullptr;
|
||||
image_view_ci.flags = 0;
|
||||
image_view_ci.image = *image;
|
||||
image_view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
image_view_ci.format = format;
|
||||
image_view_ci.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
image_view_ci.subresourceRange.aspectMask = aspect_mask;
|
||||
image_view_ci.subresourceRange.baseMipLevel = 0;
|
||||
image_view_ci.subresourceRange.levelCount = 1;
|
||||
image_view_ci.subresourceRange.baseArrayLayer = 0;
|
||||
image_view_ci.subresourceRange.layerCount = 1;
|
||||
present_view = device.GetLogical().CreateImageView(image_view_ci);
|
||||
present_view = device.GetLogical().CreateImageView({
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = *image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = format,
|
||||
.components =
|
||||
{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
VKImage::SubrangeState& VKImage::GetSubrangeState(u32 layer, u32 level) noexcept {
|
||||
|
||||
@@ -178,13 +178,12 @@ bool VKMemoryManager::AllocMemory(VkMemoryPropertyFlags wanted_properties, u32 t
|
||||
}();
|
||||
|
||||
// Try to allocate found type.
|
||||
VkMemoryAllocateInfo memory_ai;
|
||||
memory_ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
memory_ai.pNext = nullptr;
|
||||
memory_ai.allocationSize = size;
|
||||
memory_ai.memoryTypeIndex = type;
|
||||
|
||||
vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory(memory_ai);
|
||||
vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.allocationSize = size,
|
||||
.memoryTypeIndex = type,
|
||||
});
|
||||
if (!memory) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Device allocation failed!");
|
||||
return false;
|
||||
|
||||
@@ -88,12 +88,13 @@ void AddBindings(std::vector<VkDescriptorSetLayoutBinding>& bindings, u32& bindi
|
||||
// Combined image samplers can be arrayed.
|
||||
count = container[i].size;
|
||||
}
|
||||
VkDescriptorSetLayoutBinding& entry = bindings.emplace_back();
|
||||
entry.binding = binding++;
|
||||
entry.descriptorType = descriptor_type;
|
||||
entry.descriptorCount = count;
|
||||
entry.stageFlags = stage_flags;
|
||||
entry.pImmutableSamplers = nullptr;
|
||||
bindings.push_back({
|
||||
.binding = binding++,
|
||||
.descriptorType = descriptor_type,
|
||||
.descriptorCount = count,
|
||||
.stageFlags = stage_flags,
|
||||
.pImmutableSamplers = nullptr,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,10 +260,10 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
|
||||
}
|
||||
}
|
||||
|
||||
Specialization specialization;
|
||||
specialization.workgroup_size = key.workgroup_size;
|
||||
specialization.shared_memory_size = key.shared_memory_size;
|
||||
|
||||
const Specialization specialization{
|
||||
.workgroup_size = key.workgroup_size,
|
||||
.shared_memory_size = key.shared_memory_size,
|
||||
};
|
||||
const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute,
|
||||
shader->GetRegistry(), specialization),
|
||||
shader->GetEntries()};
|
||||
@@ -370,13 +371,14 @@ void AddEntry(std::vector<VkDescriptorUpdateTemplateEntry>& template_entries, u3
|
||||
if constexpr (descriptor_type == COMBINED_IMAGE_SAMPLER) {
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const u32 num_samplers = container[i].size;
|
||||
VkDescriptorUpdateTemplateEntry& entry = template_entries.emplace_back();
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = num_samplers;
|
||||
entry.descriptorType = descriptor_type;
|
||||
entry.offset = offset;
|
||||
entry.stride = entry_size;
|
||||
template_entries.push_back({
|
||||
.dstBinding = binding,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = num_samplers,
|
||||
.descriptorType = descriptor_type,
|
||||
.offset = offset,
|
||||
.stride = entry_size,
|
||||
});
|
||||
|
||||
++binding;
|
||||
offset += num_samplers * entry_size;
|
||||
@@ -389,22 +391,24 @@ void AddEntry(std::vector<VkDescriptorUpdateTemplateEntry>& template_entries, u3
|
||||
// Nvidia has a bug where updating multiple texels at once causes the driver to crash.
|
||||
// Note: Fixed in driver Windows 443.24, Linux 440.66.15
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
VkDescriptorUpdateTemplateEntry& entry = template_entries.emplace_back();
|
||||
entry.dstBinding = binding + i;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = 1;
|
||||
entry.descriptorType = descriptor_type;
|
||||
entry.offset = static_cast<std::size_t>(offset + i * entry_size);
|
||||
entry.stride = entry_size;
|
||||
template_entries.push_back({
|
||||
.dstBinding = binding + i,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = descriptor_type,
|
||||
.offset = static_cast<std::size_t>(offset + i * entry_size),
|
||||
.stride = entry_size,
|
||||
});
|
||||
}
|
||||
} else if (count > 0) {
|
||||
VkDescriptorUpdateTemplateEntry& entry = template_entries.emplace_back();
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = count;
|
||||
entry.descriptorType = descriptor_type;
|
||||
entry.offset = offset;
|
||||
entry.stride = entry_size;
|
||||
template_entries.push_back({
|
||||
.dstBinding = binding,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = count,
|
||||
.descriptorType = descriptor_type,
|
||||
.offset = offset,
|
||||
.stride = entry_size,
|
||||
});
|
||||
}
|
||||
offset += count * entry_size;
|
||||
binding += count;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user