diff options
125 files changed, 5019 insertions, 1923 deletions
diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index 52cebaee0..adcecf0ec 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml | |||
| @@ -4,7 +4,7 @@ parameters: | |||
| 4 | version: '' | 4 | version: '' |
| 5 | 5 | ||
| 6 | steps: | 6 | steps: |
| 7 | - script: mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64" --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd .. | 7 | - script: mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd .. |
| 8 | displayName: 'Configure CMake' | 8 | displayName: 'Configure CMake' |
| 9 | - task: MSBuild@1 | 9 | - task: MSBuild@1 |
| 10 | displayName: 'Build' | 10 | displayName: 'Build' |
diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml index 5f2dfb3d8..a90041d28 100644 --- a/.ci/yuzu-mainline-step2.yml +++ b/.ci/yuzu-mainline-step2.yml | |||
| @@ -45,7 +45,7 @@ stages: | |||
| 45 | - job: build | 45 | - job: build |
| 46 | displayName: 'msvc' | 46 | displayName: 'msvc' |
| 47 | pool: | 47 | pool: |
| 48 | vmImage: vs2017-win2016 | 48 | vmImage: windows-2019 |
| 49 | steps: | 49 | steps: |
| 50 | - template: ./templates/sync-source.yml | 50 | - template: ./templates/sync-source.yml |
| 51 | parameters: | 51 | parameters: |
diff --git a/.ci/yuzu-patreon-step2.yml b/.ci/yuzu-patreon-step2.yml index 1b36f63e1..26e287257 100644 --- a/.ci/yuzu-patreon-step2.yml +++ b/.ci/yuzu-patreon-step2.yml | |||
| @@ -22,7 +22,7 @@ stages: | |||
| 22 | - job: build | 22 | - job: build |
| 23 | displayName: 'windows-msvc' | 23 | displayName: 'windows-msvc' |
| 24 | pool: | 24 | pool: |
| 25 | vmImage: vs2017-win2016 | 25 | vmImage: windows-2019 |
| 26 | steps: | 26 | steps: |
| 27 | - template: ./templates/sync-source.yml | 27 | - template: ./templates/sync-source.yml |
| 28 | parameters: | 28 | parameters: |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index be1b019f1..c0fae669e 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -189,7 +189,7 @@ struct UpdateDataHeader { | |||
| 189 | UpdateDataHeader() {} | 189 | UpdateDataHeader() {} |
| 190 | 190 | ||
| 191 | explicit UpdateDataHeader(const AudioRendererParameter& config) { | 191 | explicit UpdateDataHeader(const AudioRendererParameter& config) { |
| 192 | revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision | 192 | revision = Common::MakeMagic('R', 'E', 'V', '8'); // 9.2.0 Revision |
| 193 | behavior_size = 0xb0; | 193 | behavior_size = 0xb0; |
| 194 | memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; | 194 | memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; |
| 195 | voices_size = config.voice_count * 0x10; | 195 | voices_size = config.voice_count * 0x10; |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index fbebed715..eeceaa655 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -106,6 +106,8 @@ add_library(common STATIC | |||
| 106 | common_funcs.h | 106 | common_funcs.h |
| 107 | common_paths.h | 107 | common_paths.h |
| 108 | common_types.h | 108 | common_types.h |
| 109 | dynamic_library.cpp | ||
| 110 | dynamic_library.h | ||
| 109 | file_util.cpp | 111 | file_util.cpp |
| 110 | file_util.h | 112 | file_util.h |
| 111 | hash.h | 113 | hash.h |
diff --git a/src/common/dynamic_library.cpp b/src/common/dynamic_library.cpp new file mode 100644 index 000000000..7ab54e9e4 --- /dev/null +++ b/src/common/dynamic_library.cpp | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | // Copyright 2019 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <string> | ||
| 7 | #include <utility> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include "common/dynamic_library.h" | ||
| 12 | |||
| 13 | #ifdef _WIN32 | ||
| 14 | #include <windows.h> | ||
| 15 | #else | ||
| 16 | #include <dlfcn.h> | ||
| 17 | #endif | ||
| 18 | |||
| 19 | namespace Common { | ||
| 20 | |||
| 21 | DynamicLibrary::DynamicLibrary() = default; | ||
| 22 | |||
| 23 | DynamicLibrary::DynamicLibrary(const char* filename) { | ||
| 24 | Open(filename); | ||
| 25 | } | ||
| 26 | |||
| 27 | DynamicLibrary::DynamicLibrary(DynamicLibrary&& rhs) noexcept | ||
| 28 | : handle{std::exchange(rhs.handle, nullptr)} {} | ||
| 29 | |||
| 30 | DynamicLibrary& DynamicLibrary::operator=(DynamicLibrary&& rhs) noexcept { | ||
| 31 | Close(); | ||
| 32 | handle = std::exchange(rhs.handle, nullptr); | ||
| 33 | return *this; | ||
| 34 | } | ||
| 35 | |||
| 36 | DynamicLibrary::~DynamicLibrary() { | ||
| 37 | Close(); | ||
| 38 | } | ||
| 39 | |||
| 40 | std::string DynamicLibrary::GetUnprefixedFilename(const char* filename) { | ||
| 41 | #if defined(_WIN32) | ||
| 42 | return std::string(filename) + ".dll"; | ||
| 43 | #elif defined(__APPLE__) | ||
| 44 | return std::string(filename) + ".dylib"; | ||
| 45 | #else | ||
| 46 | return std::string(filename) + ".so"; | ||
| 47 | #endif | ||
| 48 | } | ||
| 49 | |||
| 50 | std::string DynamicLibrary::GetVersionedFilename(const char* libname, int major, int minor) { | ||
| 51 | #if defined(_WIN32) | ||
| 52 | if (major >= 0 && minor >= 0) | ||
| 53 | return fmt::format("{}-{}-{}.dll", libname, major, minor); | ||
| 54 | else if (major >= 0) | ||
| 55 | return fmt::format("{}-{}.dll", libname, major); | ||
| 56 | else | ||
| 57 | return fmt::format("{}.dll", libname); | ||
| 58 | #elif defined(__APPLE__) | ||
| 59 | const char* prefix = std::strncmp(libname, "lib", 3) ? "lib" : ""; | ||
| 60 | if (major >= 0 && minor >= 0) | ||
| 61 | return fmt::format("{}{}.{}.{}.dylib", prefix, libname, major, minor); | ||
| 62 | else if (major >= 0) | ||
| 63 | return fmt::format("{}{}.{}.dylib", prefix, libname, major); | ||
| 64 | else | ||
| 65 | return fmt::format("{}{}.dylib", prefix, libname); | ||
| 66 | #else | ||
| 67 | const char* prefix = std::strncmp(libname, "lib", 3) ? "lib" : ""; | ||
| 68 | if (major >= 0 && minor >= 0) | ||
| 69 | return fmt::format("{}{}.so.{}.{}", prefix, libname, major, minor); | ||
| 70 | else if (major >= 0) | ||
| 71 | return fmt::format("{}{}.so.{}", prefix, libname, major); | ||
| 72 | else | ||
| 73 | return fmt::format("{}{}.so", prefix, libname); | ||
| 74 | #endif | ||
| 75 | } | ||
| 76 | |||
| 77 | bool DynamicLibrary::Open(const char* filename) { | ||
| 78 | #ifdef _WIN32 | ||
| 79 | handle = reinterpret_cast<void*>(LoadLibraryA(filename)); | ||
| 80 | #else | ||
| 81 | handle = dlopen(filename, RTLD_NOW); | ||
| 82 | #endif | ||
| 83 | return handle != nullptr; | ||
| 84 | } | ||
| 85 | |||
| 86 | void DynamicLibrary::Close() { | ||
| 87 | if (!IsOpen()) | ||
| 88 | return; | ||
| 89 | |||
| 90 | #ifdef _WIN32 | ||
| 91 | FreeLibrary(reinterpret_cast<HMODULE>(handle)); | ||
| 92 | #else | ||
| 93 | dlclose(handle); | ||
| 94 | #endif | ||
| 95 | handle = nullptr; | ||
| 96 | } | ||
| 97 | |||
| 98 | void* DynamicLibrary::GetSymbolAddress(const char* name) const { | ||
| 99 | #ifdef _WIN32 | ||
| 100 | return reinterpret_cast<void*>(GetProcAddress(reinterpret_cast<HMODULE>(handle), name)); | ||
| 101 | #else | ||
| 102 | return reinterpret_cast<void*>(dlsym(handle, name)); | ||
| 103 | #endif | ||
| 104 | } | ||
| 105 | |||
| 106 | } // namespace Common | ||
diff --git a/src/common/dynamic_library.h b/src/common/dynamic_library.h new file mode 100644 index 000000000..2a06372fd --- /dev/null +++ b/src/common/dynamic_library.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // Copyright 2019 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2+ | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | /** | ||
| 12 | * Provides a platform-independent interface for loading a dynamic library and retrieving symbols. | ||
| 13 | * The interface maintains an internal reference count to allow one handle to be shared between | ||
| 14 | * multiple users. | ||
| 15 | */ | ||
| 16 | class DynamicLibrary final { | ||
| 17 | public: | ||
| 18 | /// Default constructor, does not load a library. | ||
| 19 | explicit DynamicLibrary(); | ||
| 20 | |||
| 21 | /// Automatically loads the specified library. Call IsOpen() to check validity before use. | ||
| 22 | explicit DynamicLibrary(const char* filename); | ||
| 23 | |||
| 24 | /// Moves the library. | ||
| 25 | DynamicLibrary(DynamicLibrary&&) noexcept; | ||
| 26 | DynamicLibrary& operator=(DynamicLibrary&&) noexcept; | ||
| 27 | |||
| 28 | /// Delete copies, we can't copy a dynamic library. | ||
| 29 | DynamicLibrary(const DynamicLibrary&) = delete; | ||
| 30 | DynamicLibrary& operator=(const DynamicLibrary&) = delete; | ||
| 31 | |||
| 32 | /// Closes the library. | ||
| 33 | ~DynamicLibrary(); | ||
| 34 | |||
| 35 | /// Returns the specified library name with the platform-specific suffix added. | ||
| 36 | static std::string GetUnprefixedFilename(const char* filename); | ||
| 37 | |||
| 38 | /// Returns the specified library name in platform-specific format. | ||
| 39 | /// Major/minor versions will not be included if set to -1. | ||
| 40 | /// If libname already contains the "lib" prefix, it will not be added again. | ||
| 41 | /// Windows: LIBNAME-MAJOR-MINOR.dll | ||
| 42 | /// Linux: libLIBNAME.so.MAJOR.MINOR | ||
| 43 | /// Mac: libLIBNAME.MAJOR.MINOR.dylib | ||
| 44 | static std::string GetVersionedFilename(const char* libname, int major = -1, int minor = -1); | ||
| 45 | |||
| 46 | /// Returns true if a module is loaded, otherwise false. | ||
| 47 | bool IsOpen() const { | ||
| 48 | return handle != nullptr; | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Loads (or replaces) the handle with the specified library file name. | ||
| 52 | /// Returns true if the library was loaded and can be used. | ||
| 53 | bool Open(const char* filename); | ||
| 54 | |||
| 55 | /// Unloads the library, any function pointers from this library are no longer valid. | ||
| 56 | void Close(); | ||
| 57 | |||
| 58 | /// Returns the address of the specified symbol (function or variable) as an untyped pointer. | ||
| 59 | /// If the specified symbol does not exist in this library, nullptr is returned. | ||
| 60 | void* GetSymbolAddress(const char* name) const; | ||
| 61 | |||
| 62 | /// Obtains the address of the specified symbol, automatically casting to the correct type. | ||
| 63 | /// Returns true if the symbol was found and assigned, otherwise false. | ||
| 64 | template <typename T> | ||
| 65 | bool GetSymbol(const char* name, T* ptr) const { | ||
| 66 | *ptr = reinterpret_cast<T>(GetSymbolAddress(name)); | ||
| 67 | return *ptr != nullptr; | ||
| 68 | } | ||
| 69 | |||
| 70 | private: | ||
| 71 | /// Platform-dependent data type representing a dynamic library handle. | ||
| 72 | void* handle = nullptr; | ||
| 73 | }; | ||
| 74 | |||
| 75 | } // namespace Common | ||
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 41167f57a..35eee0096 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <limits> | ||
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <sstream> | 8 | #include <sstream> |
| 8 | #include <unordered_map> | 9 | #include <unordered_map> |
| @@ -530,11 +531,11 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) { | |||
| 530 | std::optional<std::string> GetCurrentDir() { | 531 | std::optional<std::string> GetCurrentDir() { |
| 531 | // Get the current working directory (getcwd uses malloc) | 532 | // Get the current working directory (getcwd uses malloc) |
| 532 | #ifdef _WIN32 | 533 | #ifdef _WIN32 |
| 533 | wchar_t* dir; | 534 | wchar_t* dir = _wgetcwd(nullptr, 0); |
| 534 | if (!(dir = _wgetcwd(nullptr, 0))) { | 535 | if (!dir) { |
| 535 | #else | 536 | #else |
| 536 | char* dir; | 537 | char* dir = getcwd(nullptr, 0); |
| 537 | if (!(dir = getcwd(nullptr, 0))) { | 538 | if (!dir) { |
| 538 | #endif | 539 | #endif |
| 539 | LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); | 540 | LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); |
| 540 | return {}; | 541 | return {}; |
| @@ -918,19 +919,22 @@ void IOFile::Swap(IOFile& other) noexcept { | |||
| 918 | 919 | ||
| 919 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { | 920 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { |
| 920 | Close(); | 921 | Close(); |
| 922 | bool m_good; | ||
| 921 | #ifdef _WIN32 | 923 | #ifdef _WIN32 |
| 922 | if (flags != 0) { | 924 | if (flags != 0) { |
| 923 | m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(), | 925 | m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(), |
| 924 | Common::UTF8ToUTF16W(openmode).c_str(), flags); | 926 | Common::UTF8ToUTF16W(openmode).c_str(), flags); |
| 927 | m_good = m_file != nullptr; | ||
| 925 | } else { | 928 | } else { |
| 926 | _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), | 929 | m_good = _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), |
| 927 | Common::UTF8ToUTF16W(openmode).c_str()); | 930 | Common::UTF8ToUTF16W(openmode).c_str()) == 0; |
| 928 | } | 931 | } |
| 929 | #else | 932 | #else |
| 930 | m_file = fopen(filename.c_str(), openmode); | 933 | m_file = std::fopen(filename.c_str(), openmode); |
| 934 | m_good = m_file != nullptr; | ||
| 931 | #endif | 935 | #endif |
| 932 | 936 | ||
| 933 | return IsOpen(); | 937 | return m_good; |
| 934 | } | 938 | } |
| 935 | 939 | ||
| 936 | bool IOFile::Close() { | 940 | bool IOFile::Close() { |
| @@ -956,7 +960,7 @@ u64 IOFile::Tell() const { | |||
| 956 | if (IsOpen()) | 960 | if (IsOpen()) |
| 957 | return ftello(m_file); | 961 | return ftello(m_file); |
| 958 | 962 | ||
| 959 | return -1; | 963 | return std::numeric_limits<u64>::max(); |
| 960 | } | 964 | } |
| 961 | 965 | ||
| 962 | bool IOFile::Flush() { | 966 | bool IOFile::Flush() { |
diff --git a/src/common/thread.cpp b/src/common/thread.cpp index fe7a420cc..0cd2d10bf 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp | |||
| @@ -28,11 +28,8 @@ namespace Common { | |||
| 28 | #ifdef _MSC_VER | 28 | #ifdef _MSC_VER |
| 29 | 29 | ||
| 30 | // Sets the debugger-visible name of the current thread. | 30 | // Sets the debugger-visible name of the current thread. |
| 31 | // Uses undocumented (actually, it is now documented) trick. | 31 | // Uses trick documented in: |
| 32 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp | 32 | // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code |
| 33 | |||
| 34 | // This is implemented much nicer in upcoming msvc++, see: | ||
| 35 | // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx | ||
| 36 | void SetCurrentThreadName(const char* name) { | 33 | void SetCurrentThreadName(const char* name) { |
| 37 | static const DWORD MS_VC_EXCEPTION = 0x406D1388; | 34 | static const DWORD MS_VC_EXCEPTION = 0x406D1388; |
| 38 | 35 | ||
| @@ -47,7 +44,7 @@ void SetCurrentThreadName(const char* name) { | |||
| 47 | 44 | ||
| 48 | info.dwType = 0x1000; | 45 | info.dwType = 0x1000; |
| 49 | info.szName = name; | 46 | info.szName = name; |
| 50 | info.dwThreadID = -1; // dwThreadID; | 47 | info.dwThreadID = std::numeric_limits<DWORD>::max(); |
| 51 | info.dwFlags = 0; | 48 | info.dwFlags = 0; |
| 52 | 49 | ||
| 53 | __try { | 50 | __try { |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b31a0328c..66497a386 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -131,8 +131,6 @@ add_library(core STATIC | |||
| 131 | frontend/framebuffer_layout.cpp | 131 | frontend/framebuffer_layout.cpp |
| 132 | frontend/framebuffer_layout.h | 132 | frontend/framebuffer_layout.h |
| 133 | frontend/input.h | 133 | frontend/input.h |
| 134 | frontend/scope_acquire_context.cpp | ||
| 135 | frontend/scope_acquire_context.h | ||
| 136 | gdbstub/gdbstub.cpp | 134 | gdbstub/gdbstub.cpp |
| 137 | gdbstub/gdbstub.h | 135 | gdbstub/gdbstub.h |
| 138 | hardware_interrupt_manager.cpp | 136 | hardware_interrupt_manager.cpp |
| @@ -287,6 +285,18 @@ add_library(core STATIC | |||
| 287 | hle/service/btm/btm.h | 285 | hle/service/btm/btm.h |
| 288 | hle/service/caps/caps.cpp | 286 | hle/service/caps/caps.cpp |
| 289 | hle/service/caps/caps.h | 287 | hle/service/caps/caps.h |
| 288 | hle/service/caps/caps_a.cpp | ||
| 289 | hle/service/caps/caps_a.h | ||
| 290 | hle/service/caps/caps_c.cpp | ||
| 291 | hle/service/caps/caps_c.h | ||
| 292 | hle/service/caps/caps_u.cpp | ||
| 293 | hle/service/caps/caps_u.h | ||
| 294 | hle/service/caps/caps_sc.cpp | ||
| 295 | hle/service/caps/caps_sc.h | ||
| 296 | hle/service/caps/caps_ss.cpp | ||
| 297 | hle/service/caps/caps_ss.h | ||
| 298 | hle/service/caps/caps_su.cpp | ||
| 299 | hle/service/caps/caps_su.h | ||
| 290 | hle/service/erpt/erpt.cpp | 300 | hle/service/erpt/erpt.cpp |
| 291 | hle/service/erpt/erpt.h | 301 | hle/service/erpt/erpt.h |
| 292 | hle/service/es/es.cpp | 302 | hle/service/es/es.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index d1bc9340d..3bd90d79f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -24,7 +24,6 @@ | |||
| 24 | #include "core/file_sys/sdmc_factory.h" | 24 | #include "core/file_sys/sdmc_factory.h" |
| 25 | #include "core/file_sys/vfs_concat.h" | 25 | #include "core/file_sys/vfs_concat.h" |
| 26 | #include "core/file_sys/vfs_real.h" | 26 | #include "core/file_sys/vfs_real.h" |
| 27 | #include "core/frontend/scope_acquire_context.h" | ||
| 28 | #include "core/gdbstub/gdbstub.h" | 27 | #include "core/gdbstub/gdbstub.h" |
| 29 | #include "core/hardware_interrupt_manager.h" | 28 | #include "core/hardware_interrupt_manager.h" |
| 30 | #include "core/hle/kernel/client_port.h" | 29 | #include "core/hle/kernel/client_port.h" |
| @@ -168,13 +167,12 @@ struct System::Impl { | |||
| 168 | Service::Init(service_manager, system); | 167 | Service::Init(service_manager, system); |
| 169 | GDBStub::DeferStart(); | 168 | GDBStub::DeferStart(); |
| 170 | 169 | ||
| 171 | renderer = VideoCore::CreateRenderer(emu_window, system); | 170 | interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); |
| 172 | if (!renderer->Init()) { | 171 | gpu_core = VideoCore::CreateGPU(emu_window, system); |
| 172 | if (!gpu_core) { | ||
| 173 | return ResultStatus::ErrorVideoCore; | 173 | return ResultStatus::ErrorVideoCore; |
| 174 | } | 174 | } |
| 175 | interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); | 175 | gpu_core->Renderer().Rasterizer().SetupDirtyFlags(); |
| 176 | gpu_core = VideoCore::CreateGPU(system); | ||
| 177 | renderer->Rasterizer().SetupDirtyFlags(); | ||
| 178 | 176 | ||
| 179 | is_powered_on = true; | 177 | is_powered_on = true; |
| 180 | exit_lock = false; | 178 | exit_lock = false; |
| @@ -186,8 +184,6 @@ struct System::Impl { | |||
| 186 | 184 | ||
| 187 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, | 185 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, |
| 188 | const std::string& filepath) { | 186 | const std::string& filepath) { |
| 189 | Core::Frontend::ScopeAcquireContext acquire_context{emu_window}; | ||
| 190 | |||
| 191 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); | 187 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); |
| 192 | if (!app_loader) { | 188 | if (!app_loader) { |
| 193 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | 189 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
| @@ -216,10 +212,6 @@ struct System::Impl { | |||
| 216 | AddGlueRegistrationForProcess(*app_loader, *main_process); | 212 | AddGlueRegistrationForProcess(*app_loader, *main_process); |
| 217 | kernel.MakeCurrentProcess(main_process.get()); | 213 | kernel.MakeCurrentProcess(main_process.get()); |
| 218 | 214 | ||
| 219 | // Main process has been loaded and been made current. | ||
| 220 | // Begin GPU and CPU execution. | ||
| 221 | gpu_core->Start(); | ||
| 222 | |||
| 223 | // Initialize cheat engine | 215 | // Initialize cheat engine |
| 224 | if (cheat_engine) { | 216 | if (cheat_engine) { |
| 225 | cheat_engine->Initialize(); | 217 | cheat_engine->Initialize(); |
| @@ -277,7 +269,6 @@ struct System::Impl { | |||
| 277 | } | 269 | } |
| 278 | 270 | ||
| 279 | // Shutdown emulation session | 271 | // Shutdown emulation session |
| 280 | renderer.reset(); | ||
| 281 | GDBStub::Shutdown(); | 272 | GDBStub::Shutdown(); |
| 282 | Service::Shutdown(); | 273 | Service::Shutdown(); |
| 283 | service_manager.reset(); | 274 | service_manager.reset(); |
| @@ -353,7 +344,6 @@ struct System::Impl { | |||
| 353 | Service::FileSystem::FileSystemController fs_controller; | 344 | Service::FileSystem::FileSystemController fs_controller; |
| 354 | /// AppLoader used to load the current executing application | 345 | /// AppLoader used to load the current executing application |
| 355 | std::unique_ptr<Loader::AppLoader> app_loader; | 346 | std::unique_ptr<Loader::AppLoader> app_loader; |
| 356 | std::unique_ptr<VideoCore::RendererBase> renderer; | ||
| 357 | std::unique_ptr<Tegra::GPU> gpu_core; | 347 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 358 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 348 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; |
| 359 | Memory::Memory memory; | 349 | Memory::Memory memory; |
| @@ -536,11 +526,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const { | |||
| 536 | } | 526 | } |
| 537 | 527 | ||
| 538 | VideoCore::RendererBase& System::Renderer() { | 528 | VideoCore::RendererBase& System::Renderer() { |
| 539 | return *impl->renderer; | 529 | return impl->gpu_core->Renderer(); |
| 540 | } | 530 | } |
| 541 | 531 | ||
| 542 | const VideoCore::RendererBase& System::Renderer() const { | 532 | const VideoCore::RendererBase& System::Renderer() const { |
| 543 | return *impl->renderer; | 533 | return impl->gpu_core->Renderer(); |
| 544 | } | 534 | } |
| 545 | 535 | ||
| 546 | Kernel::KernelCore& System::Kernel() { | 536 | Kernel::KernelCore& System::Kernel() { |
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index c909d1ce4..120032134 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/string_util.h" | ||
| 8 | #include "common/swap.h" | 9 | #include "common/swap.h" |
| 9 | #include "core/file_sys/fsmitm_romfsbuild.h" | 10 | #include "core/file_sys/fsmitm_romfsbuild.h" |
| 10 | #include "core/file_sys/romfs.h" | 11 | #include "core/file_sys/romfs.h" |
| @@ -126,7 +127,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { | |||
| 126 | return out->GetSubdirectories().front(); | 127 | return out->GetSubdirectories().front(); |
| 127 | 128 | ||
| 128 | while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { | 129 | while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { |
| 129 | if (out->GetSubdirectories().front()->GetName() == "data" && | 130 | if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" && |
| 130 | type == RomFSExtractionType::Truncated) | 131 | type == RomFSExtractionType::Truncated) |
| 131 | break; | 132 | break; |
| 132 | out = out->GetSubdirectories().front(); | 133 | out = out->GetSubdirectories().front(); |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 5eb87fb63..13aa14934 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -12,20 +12,49 @@ | |||
| 12 | 12 | ||
| 13 | namespace Core::Frontend { | 13 | namespace Core::Frontend { |
| 14 | 14 | ||
| 15 | /// Information for the Graphics Backends signifying what type of screen pointer is in | ||
| 16 | /// WindowInformation | ||
| 17 | enum class WindowSystemType { | ||
| 18 | Headless, | ||
| 19 | Windows, | ||
| 20 | X11, | ||
| 21 | Wayland, | ||
| 22 | }; | ||
| 23 | |||
| 15 | /** | 24 | /** |
| 16 | * Represents a graphics context that can be used for background computation or drawing. If the | 25 | * Represents a drawing context that supports graphics operations. |
| 17 | * graphics backend doesn't require the context, then the implementation of these methods can be | ||
| 18 | * stubs | ||
| 19 | */ | 26 | */ |
| 20 | class GraphicsContext { | 27 | class GraphicsContext { |
| 21 | public: | 28 | public: |
| 22 | virtual ~GraphicsContext(); | 29 | virtual ~GraphicsContext(); |
| 23 | 30 | ||
| 31 | /// Inform the driver to swap the front/back buffers and present the current image | ||
| 32 | virtual void SwapBuffers() {} | ||
| 33 | |||
| 24 | /// Makes the graphics context current for the caller thread | 34 | /// Makes the graphics context current for the caller thread |
| 25 | virtual void MakeCurrent() = 0; | 35 | virtual void MakeCurrent() {} |
| 26 | 36 | ||
| 27 | /// Releases (dunno if this is the "right" word) the context from the caller thread | 37 | /// Releases (dunno if this is the "right" word) the context from the caller thread |
| 28 | virtual void DoneCurrent() = 0; | 38 | virtual void DoneCurrent() {} |
| 39 | |||
| 40 | class Scoped { | ||
| 41 | public: | ||
| 42 | explicit Scoped(GraphicsContext& context_) : context(context_) { | ||
| 43 | context.MakeCurrent(); | ||
| 44 | } | ||
| 45 | ~Scoped() { | ||
| 46 | context.DoneCurrent(); | ||
| 47 | } | ||
| 48 | |||
| 49 | private: | ||
| 50 | GraphicsContext& context; | ||
| 51 | }; | ||
| 52 | |||
| 53 | /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value | ||
| 54 | /// ends | ||
| 55 | Scoped Acquire() { | ||
| 56 | return Scoped{*this}; | ||
| 57 | } | ||
| 29 | }; | 58 | }; |
| 30 | 59 | ||
| 31 | /** | 60 | /** |
| @@ -46,7 +75,7 @@ public: | |||
| 46 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | 75 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please |
| 47 | * re-read the upper points again and think about it if you don't see this. | 76 | * re-read the upper points again and think about it if you don't see this. |
| 48 | */ | 77 | */ |
| 49 | class EmuWindow : public GraphicsContext { | 78 | class EmuWindow { |
| 50 | public: | 79 | public: |
| 51 | /// Data structure to store emuwindow configuration | 80 | /// Data structure to store emuwindow configuration |
| 52 | struct WindowConfig { | 81 | struct WindowConfig { |
| @@ -56,29 +85,34 @@ public: | |||
| 56 | std::pair<unsigned, unsigned> min_client_area_size; | 85 | std::pair<unsigned, unsigned> min_client_area_size; |
| 57 | }; | 86 | }; |
| 58 | 87 | ||
| 88 | /// Data describing host window system information | ||
| 89 | struct WindowSystemInfo { | ||
| 90 | // Window system type. Determines which GL context or Vulkan WSI is used. | ||
| 91 | WindowSystemType type = WindowSystemType::Headless; | ||
| 92 | |||
| 93 | // Connection to a display server. This is used on X11 and Wayland platforms. | ||
| 94 | void* display_connection = nullptr; | ||
| 95 | |||
| 96 | // Render surface. This is a pointer to the native window handle, which depends | ||
| 97 | // on the platform. e.g. HWND for Windows, Window for X11. If the surface is | ||
| 98 | // set to nullptr, the video backend will run in headless mode. | ||
| 99 | void* render_surface = nullptr; | ||
| 100 | |||
| 101 | // Scale of the render surface. For hidpi systems, this will be >1. | ||
| 102 | float render_surface_scale = 1.0f; | ||
| 103 | }; | ||
| 104 | |||
| 59 | /// Polls window events | 105 | /// Polls window events |
| 60 | virtual void PollEvents() = 0; | 106 | virtual void PollEvents() = 0; |
| 61 | 107 | ||
| 62 | /** | 108 | /** |
| 63 | * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This | 109 | * Returns a GraphicsContext that the frontend provides to be used for rendering. |
| 64 | * context can be used from other threads for background graphics computation. If the frontend | ||
| 65 | * is using a graphics backend that doesn't need anything specific to run on a different thread, | ||
| 66 | * then it can use a stubbed implemenation for GraphicsContext. | ||
| 67 | * | ||
| 68 | * If the return value is null, then the core should assume that the frontend cannot provide a | ||
| 69 | * Shared Context | ||
| 70 | */ | 110 | */ |
| 71 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { | 111 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0; |
| 72 | return nullptr; | ||
| 73 | } | ||
| 74 | 112 | ||
| 75 | /// Returns if window is shown (not minimized) | 113 | /// Returns if window is shown (not minimized) |
| 76 | virtual bool IsShown() const = 0; | 114 | virtual bool IsShown() const = 0; |
| 77 | 115 | ||
| 78 | /// Retrieves Vulkan specific handlers from the window | ||
| 79 | virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 80 | void* surface) const = 0; | ||
| 81 | |||
| 82 | /** | 116 | /** |
| 83 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 117 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
| 84 | * @param framebuffer_x Framebuffer x-coordinate that was pressed | 118 | * @param framebuffer_x Framebuffer x-coordinate that was pressed |
| @@ -116,6 +150,13 @@ public: | |||
| 116 | } | 150 | } |
| 117 | 151 | ||
| 118 | /** | 152 | /** |
| 153 | * Returns system information about the drawing area. | ||
| 154 | */ | ||
| 155 | const WindowSystemInfo& GetWindowInfo() const { | ||
| 156 | return window_info; | ||
| 157 | } | ||
| 158 | |||
| 159 | /** | ||
| 119 | * Gets the framebuffer layout (width, height, and screen regions) | 160 | * Gets the framebuffer layout (width, height, and screen regions) |
| 120 | * @note This method is thread-safe | 161 | * @note This method is thread-safe |
| 121 | */ | 162 | */ |
| @@ -130,7 +171,7 @@ public: | |||
| 130 | void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); | 171 | void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); |
| 131 | 172 | ||
| 132 | protected: | 173 | protected: |
| 133 | EmuWindow(); | 174 | explicit EmuWindow(); |
| 134 | virtual ~EmuWindow(); | 175 | virtual ~EmuWindow(); |
| 135 | 176 | ||
| 136 | /** | 177 | /** |
| @@ -167,6 +208,8 @@ protected: | |||
| 167 | client_area_height = size.second; | 208 | client_area_height = size.second; |
| 168 | } | 209 | } |
| 169 | 210 | ||
| 211 | WindowSystemInfo window_info; | ||
| 212 | |||
| 170 | private: | 213 | private: |
| 171 | /** | 214 | /** |
| 172 | * Handler called when the minimal client area was requested to be changed via SetConfig. | 215 | * Handler called when the minimal client area was requested to be changed via SetConfig. |
diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp deleted file mode 100644 index 878c3157c..000000000 --- a/src/core/frontend/scope_acquire_context.cpp +++ /dev/null | |||
| @@ -1,18 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/frontend/emu_window.h" | ||
| 6 | #include "core/frontend/scope_acquire_context.h" | ||
| 7 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context) | ||
| 11 | : context{context} { | ||
| 12 | context.MakeCurrent(); | ||
| 13 | } | ||
| 14 | ScopeAcquireContext::~ScopeAcquireContext() { | ||
| 15 | context.DoneCurrent(); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/scope_acquire_context.h b/src/core/frontend/scope_acquire_context.h deleted file mode 100644 index 7a65c0623..000000000 --- a/src/core/frontend/scope_acquire_context.h +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Core::Frontend { | ||
| 10 | |||
| 11 | class GraphicsContext; | ||
| 12 | |||
| 13 | /// Helper class to acquire/release window context within a given scope | ||
| 14 | class ScopeAcquireContext : NonCopyable { | ||
| 15 | public: | ||
| 16 | explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context); | ||
| 17 | ~ScopeAcquireContext(); | ||
| 18 | |||
| 19 | private: | ||
| 20 | Core::Frontend::GraphicsContext& context; | ||
| 21 | }; | ||
| 22 | |||
| 23 | } // namespace Core::Frontend | ||
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 907f464ab..26c8a7081 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp | |||
| @@ -2,168 +2,24 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | ||
| 6 | |||
| 7 | #include "core/hle/service/caps/caps.h" | 5 | #include "core/hle/service/caps/caps.h" |
| 6 | #include "core/hle/service/caps/caps_a.h" | ||
| 7 | #include "core/hle/service/caps/caps_c.h" | ||
| 8 | #include "core/hle/service/caps/caps_sc.h" | ||
| 9 | #include "core/hle/service/caps/caps_ss.h" | ||
| 10 | #include "core/hle/service/caps/caps_su.h" | ||
| 11 | #include "core/hle/service/caps/caps_u.h" | ||
| 8 | #include "core/hle/service/service.h" | 12 | #include "core/hle/service/service.h" |
| 9 | #include "core/hle/service/sm/sm.h" | ||
| 10 | 13 | ||
| 11 | namespace Service::Capture { | 14 | namespace Service::Capture { |
| 12 | 15 | ||
| 13 | class CAPS_A final : public ServiceFramework<CAPS_A> { | ||
| 14 | public: | ||
| 15 | explicit CAPS_A() : ServiceFramework{"caps:a"} { | ||
| 16 | // clang-format off | ||
| 17 | static const FunctionInfo functions[] = { | ||
| 18 | {0, nullptr, "GetAlbumFileCount"}, | ||
| 19 | {1, nullptr, "GetAlbumFileList"}, | ||
| 20 | {2, nullptr, "LoadAlbumFile"}, | ||
| 21 | {3, nullptr, "DeleteAlbumFile"}, | ||
| 22 | {4, nullptr, "StorageCopyAlbumFile"}, | ||
| 23 | {5, nullptr, "IsAlbumMounted"}, | ||
| 24 | {6, nullptr, "GetAlbumUsage"}, | ||
| 25 | {7, nullptr, "GetAlbumFileSize"}, | ||
| 26 | {8, nullptr, "LoadAlbumFileThumbnail"}, | ||
| 27 | {9, nullptr, "LoadAlbumScreenShotImage"}, | ||
| 28 | {10, nullptr, "LoadAlbumScreenShotThumbnailImage"}, | ||
| 29 | {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"}, | ||
| 30 | {12, nullptr, "Unknown12"}, | ||
| 31 | {13, nullptr, "Unknown13"}, | ||
| 32 | {14, nullptr, "Unknown14"}, | ||
| 33 | {15, nullptr, "Unknown15"}, | ||
| 34 | {16, nullptr, "Unknown16"}, | ||
| 35 | {17, nullptr, "Unknown17"}, | ||
| 36 | {18, nullptr, "Unknown18"}, | ||
| 37 | {202, nullptr, "SaveEditedScreenShot"}, | ||
| 38 | {301, nullptr, "GetLastThumbnail"}, | ||
| 39 | {401, nullptr, "GetAutoSavingStorage"}, | ||
| 40 | {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, | ||
| 41 | {1001, nullptr, "Unknown1001"}, | ||
| 42 | {1002, nullptr, "Unknown1002"}, | ||
| 43 | {1003, nullptr, "Unknown1003"}, | ||
| 44 | {8001, nullptr, "ForceAlbumUnmounted"}, | ||
| 45 | {8002, nullptr, "ResetAlbumMountStatus"}, | ||
| 46 | {8011, nullptr, "RefreshAlbumCache"}, | ||
| 47 | {8012, nullptr, "GetAlbumCache"}, | ||
| 48 | {8013, nullptr, "Unknown8013"}, | ||
| 49 | {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"}, | ||
| 50 | {10011, nullptr, "SetInternalErrorConversionEnabled"}, | ||
| 51 | {50000, nullptr, "Unknown50000"}, | ||
| 52 | {60002, nullptr, "Unknown60002"}, | ||
| 53 | }; | ||
| 54 | // clang-format on | ||
| 55 | |||
| 56 | RegisterHandlers(functions); | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | class CAPS_C final : public ServiceFramework<CAPS_C> { | ||
| 61 | public: | ||
| 62 | explicit CAPS_C() : ServiceFramework{"caps:c"} { | ||
| 63 | // clang-format off | ||
| 64 | static const FunctionInfo functions[] = { | ||
| 65 | {33, nullptr, "Unknown33"}, | ||
| 66 | {2001, nullptr, "Unknown2001"}, | ||
| 67 | {2002, nullptr, "Unknown2002"}, | ||
| 68 | {2011, nullptr, "Unknown2011"}, | ||
| 69 | {2012, nullptr, "Unknown2012"}, | ||
| 70 | {2013, nullptr, "Unknown2013"}, | ||
| 71 | {2014, nullptr, "Unknown2014"}, | ||
| 72 | {2101, nullptr, "Unknown2101"}, | ||
| 73 | {2102, nullptr, "Unknown2102"}, | ||
| 74 | {2201, nullptr, "Unknown2201"}, | ||
| 75 | {2301, nullptr, "Unknown2301"}, | ||
| 76 | }; | ||
| 77 | // clang-format on | ||
| 78 | |||
| 79 | RegisterHandlers(functions); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | |||
| 83 | class CAPS_SC final : public ServiceFramework<CAPS_SC> { | ||
| 84 | public: | ||
| 85 | explicit CAPS_SC() : ServiceFramework{"caps:sc"} { | ||
| 86 | // clang-format off | ||
| 87 | static const FunctionInfo functions[] = { | ||
| 88 | {1, nullptr, "Unknown1"}, | ||
| 89 | {2, nullptr, "Unknown2"}, | ||
| 90 | {1001, nullptr, "Unknown3"}, | ||
| 91 | {1002, nullptr, "Unknown4"}, | ||
| 92 | {1003, nullptr, "Unknown5"}, | ||
| 93 | {1011, nullptr, "Unknown6"}, | ||
| 94 | {1012, nullptr, "Unknown7"}, | ||
| 95 | {1201, nullptr, "Unknown8"}, | ||
| 96 | {1202, nullptr, "Unknown9"}, | ||
| 97 | {1203, nullptr, "Unknown10"}, | ||
| 98 | }; | ||
| 99 | // clang-format on | ||
| 100 | |||
| 101 | RegisterHandlers(functions); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | class CAPS_SS final : public ServiceFramework<CAPS_SS> { | ||
| 106 | public: | ||
| 107 | explicit CAPS_SS() : ServiceFramework{"caps:ss"} { | ||
| 108 | // clang-format off | ||
| 109 | static const FunctionInfo functions[] = { | ||
| 110 | {201, nullptr, "Unknown1"}, | ||
| 111 | {202, nullptr, "Unknown2"}, | ||
| 112 | {203, nullptr, "Unknown3"}, | ||
| 113 | {204, nullptr, "Unknown4"}, | ||
| 114 | }; | ||
| 115 | // clang-format on | ||
| 116 | |||
| 117 | RegisterHandlers(functions); | ||
| 118 | } | ||
| 119 | }; | ||
| 120 | |||
| 121 | class CAPS_SU final : public ServiceFramework<CAPS_SU> { | ||
| 122 | public: | ||
| 123 | explicit CAPS_SU() : ServiceFramework{"caps:su"} { | ||
| 124 | // clang-format off | ||
| 125 | static const FunctionInfo functions[] = { | ||
| 126 | {201, nullptr, "SaveScreenShot"}, | ||
| 127 | {203, nullptr, "SaveScreenShotEx0"}, | ||
| 128 | }; | ||
| 129 | // clang-format on | ||
| 130 | |||
| 131 | RegisterHandlers(functions); | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | class CAPS_U final : public ServiceFramework<CAPS_U> { | ||
| 136 | public: | ||
| 137 | explicit CAPS_U() : ServiceFramework{"caps:u"} { | ||
| 138 | // clang-format off | ||
| 139 | static const FunctionInfo functions[] = { | ||
| 140 | {32, nullptr, "SetShimLibraryVersion"}, | ||
| 141 | {102, nullptr, "GetAlbumFileListByAruid"}, | ||
| 142 | {103, nullptr, "DeleteAlbumFileByAruid"}, | ||
| 143 | {104, nullptr, "GetAlbumFileSizeByAruid"}, | ||
| 144 | {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, | ||
| 145 | {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, | ||
| 146 | {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, | ||
| 147 | {130, nullptr, "PrecheckToCreateContentsByAruid"}, | ||
| 148 | {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, | ||
| 149 | {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, | ||
| 150 | {142, nullptr, "GetAlbumFileList3AaeAruid"}, | ||
| 151 | {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, | ||
| 152 | {60002, nullptr, "OpenAccessorSessionForApplication"}, | ||
| 153 | }; | ||
| 154 | // clang-format on | ||
| 155 | |||
| 156 | RegisterHandlers(functions); | ||
| 157 | } | ||
| 158 | }; | ||
| 159 | |||
| 160 | void InstallInterfaces(SM::ServiceManager& sm) { | 16 | void InstallInterfaces(SM::ServiceManager& sm) { |
| 161 | std::make_shared<CAPS_A>()->InstallAsService(sm); | 17 | std::make_shared<CAPS_A>()->InstallAsService(sm); |
| 162 | std::make_shared<CAPS_C>()->InstallAsService(sm); | 18 | std::make_shared<CAPS_C>()->InstallAsService(sm); |
| 19 | std::make_shared<CAPS_U>()->InstallAsService(sm); | ||
| 163 | std::make_shared<CAPS_SC>()->InstallAsService(sm); | 20 | std::make_shared<CAPS_SC>()->InstallAsService(sm); |
| 164 | std::make_shared<CAPS_SS>()->InstallAsService(sm); | 21 | std::make_shared<CAPS_SS>()->InstallAsService(sm); |
| 165 | std::make_shared<CAPS_SU>()->InstallAsService(sm); | 22 | std::make_shared<CAPS_SU>()->InstallAsService(sm); |
| 166 | std::make_shared<CAPS_U>()->InstallAsService(sm); | ||
| 167 | } | 23 | } |
| 168 | 24 | ||
| 169 | } // namespace Service::Capture | 25 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 471185dfa..fc70a4c27 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h | |||
| @@ -4,12 +4,83 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 7 | namespace Service::SM { | 9 | namespace Service::SM { |
| 8 | class ServiceManager; | 10 | class ServiceManager; |
| 9 | } | 11 | } |
| 10 | 12 | ||
| 11 | namespace Service::Capture { | 13 | namespace Service::Capture { |
| 12 | 14 | ||
| 15 | enum AlbumImageOrientation { | ||
| 16 | Orientation0 = 0, | ||
| 17 | Orientation1 = 1, | ||
| 18 | Orientation2 = 2, | ||
| 19 | Orientation3 = 3, | ||
| 20 | }; | ||
| 21 | |||
| 22 | enum AlbumReportOption { | ||
| 23 | Disable = 0, | ||
| 24 | Enable = 1, | ||
| 25 | }; | ||
| 26 | |||
| 27 | enum ContentType : u8 { | ||
| 28 | Screenshot = 0, | ||
| 29 | Movie = 1, | ||
| 30 | ExtraMovie = 3, | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum AlbumStorage : u8 { | ||
| 34 | NAND = 0, | ||
| 35 | SD = 1, | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct AlbumFileDateTime { | ||
| 39 | u16 year; | ||
| 40 | u8 month; | ||
| 41 | u8 day; | ||
| 42 | u8 hour; | ||
| 43 | u8 minute; | ||
| 44 | u8 second; | ||
| 45 | u8 uid; | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct AlbumEntry { | ||
| 49 | u64 size; | ||
| 50 | u64 application_id; | ||
| 51 | AlbumFileDateTime datetime; | ||
| 52 | AlbumStorage storage; | ||
| 53 | ContentType content; | ||
| 54 | u8 padding[6]; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct AlbumFileEntry { | ||
| 58 | u64 size; | ||
| 59 | u64 hash; | ||
| 60 | AlbumFileDateTime datetime; | ||
| 61 | AlbumStorage storage; | ||
| 62 | ContentType content; | ||
| 63 | u8 padding[5]; | ||
| 64 | u8 unknown; | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct ApplicationAlbumEntry { | ||
| 68 | u64 size; | ||
| 69 | u64 hash; | ||
| 70 | AlbumFileDateTime datetime; | ||
| 71 | AlbumStorage storage; | ||
| 72 | ContentType content; | ||
| 73 | u8 padding[5]; | ||
| 74 | u8 unknown; | ||
| 75 | }; | ||
| 76 | |||
| 77 | struct ApplicationAlbumFileEntry { | ||
| 78 | ApplicationAlbumEntry entry; | ||
| 79 | AlbumFileDateTime datetime; | ||
| 80 | u64 unknown; | ||
| 81 | }; | ||
| 82 | |||
| 83 | /// Registers all Capture services with the specified service manager. | ||
| 13 | void InstallInterfaces(SM::ServiceManager& sm); | 84 | void InstallInterfaces(SM::ServiceManager& sm); |
| 14 | 85 | ||
| 15 | } // namespace Service::Capture | 86 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp new file mode 100644 index 000000000..88a3fdc05 --- /dev/null +++ b/src/core/hle/service/caps/caps_a.cpp | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/caps/caps_a.h" | ||
| 6 | |||
| 7 | namespace Service::Capture { | ||
| 8 | |||
| 9 | class IAlbumAccessorSession final : public ServiceFramework<IAlbumAccessorSession> { | ||
| 10 | public: | ||
| 11 | explicit IAlbumAccessorSession() : ServiceFramework{"IAlbumAccessorSession"} { | ||
| 12 | // clang-format off | ||
| 13 | static const FunctionInfo functions[] = { | ||
| 14 | {2001, nullptr, "OpenAlbumMovieReadStream"}, | ||
| 15 | {2002, nullptr, "CloseAlbumMovieReadStream"}, | ||
| 16 | {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, | ||
| 17 | {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, | ||
| 18 | {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, | ||
| 19 | {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, | ||
| 20 | {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, | ||
| 21 | {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, | ||
| 22 | }; | ||
| 23 | // clang-format on | ||
| 24 | |||
| 25 | RegisterHandlers(functions); | ||
| 26 | } | ||
| 27 | }; | ||
| 28 | |||
| 29 | CAPS_A::CAPS_A() : ServiceFramework("caps:a") { | ||
| 30 | // clang-format off | ||
| 31 | static const FunctionInfo functions[] = { | ||
| 32 | {0, nullptr, "GetAlbumFileCount"}, | ||
| 33 | {1, nullptr, "GetAlbumFileList"}, | ||
| 34 | {2, nullptr, "LoadAlbumFile"}, | ||
| 35 | {3, nullptr, "DeleteAlbumFile"}, | ||
| 36 | {4, nullptr, "StorageCopyAlbumFile"}, | ||
| 37 | {5, nullptr, "IsAlbumMounted"}, | ||
| 38 | {6, nullptr, "GetAlbumUsage"}, | ||
| 39 | {7, nullptr, "GetAlbumFileSize"}, | ||
| 40 | {8, nullptr, "LoadAlbumFileThumbnail"}, | ||
| 41 | {9, nullptr, "LoadAlbumScreenShotImage"}, | ||
| 42 | {10, nullptr, "LoadAlbumScreenShotThumbnailImage"}, | ||
| 43 | {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"}, | ||
| 44 | {12, nullptr, "LoadAlbumScreenShotImageEx"}, | ||
| 45 | {13, nullptr, "LoadAlbumScreenShotThumbnailImageEx"}, | ||
| 46 | {14, nullptr, "LoadAlbumScreenShotImageEx0"}, | ||
| 47 | {15, nullptr, "GetAlbumUsage3"}, | ||
| 48 | {16, nullptr, "GetAlbumMountResult"}, | ||
| 49 | {17, nullptr, "GetAlbumUsage16"}, | ||
| 50 | {18, nullptr, "Unknown18"}, | ||
| 51 | {100, nullptr, "GetAlbumFileCountEx0"}, | ||
| 52 | {101, nullptr, "GetAlbumFileListEx0"}, | ||
| 53 | {202, nullptr, "SaveEditedScreenShot"}, | ||
| 54 | {301, nullptr, "GetLastThumbnail"}, | ||
| 55 | {302, nullptr, "GetLastOverlayMovieThumbnail"}, | ||
| 56 | {401, nullptr, "GetAutoSavingStorage"}, | ||
| 57 | {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, | ||
| 58 | {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, | ||
| 59 | {1002, nullptr, "LoadAlbumScreenShotImageEx1"}, | ||
| 60 | {1003, nullptr, "LoadAlbumScreenShotThumbnailImageEx1"}, | ||
| 61 | {8001, nullptr, "ForceAlbumUnmounted"}, | ||
| 62 | {8002, nullptr, "ResetAlbumMountStatus"}, | ||
| 63 | {8011, nullptr, "RefreshAlbumCache"}, | ||
| 64 | {8012, nullptr, "GetAlbumCache"}, | ||
| 65 | {8013, nullptr, "GetAlbumCacheEx"}, | ||
| 66 | {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"}, | ||
| 67 | {10011, nullptr, "SetInternalErrorConversionEnabled"}, | ||
| 68 | {50000, nullptr, "LoadMakerNoteInfoForDebug"}, | ||
| 69 | {60002, nullptr, "OpenAccessorSession"}, | ||
| 70 | }; | ||
| 71 | // clang-format on | ||
| 72 | |||
| 73 | RegisterHandlers(functions); | ||
| 74 | } | ||
| 75 | |||
| 76 | CAPS_A::~CAPS_A() = default; | ||
| 77 | |||
| 78 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h new file mode 100644 index 000000000..8de832491 --- /dev/null +++ b/src/core/hle/service/caps/caps_a.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | class HLERequestContext; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::Capture { | ||
| 14 | |||
| 15 | class CAPS_A final : public ServiceFramework<CAPS_A> { | ||
| 16 | public: | ||
| 17 | explicit CAPS_A(); | ||
| 18 | ~CAPS_A() override; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp new file mode 100644 index 000000000..ea6452ffa --- /dev/null +++ b/src/core/hle/service/caps/caps_c.cpp | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/caps/caps_c.h" | ||
| 6 | |||
| 7 | namespace Service::Capture { | ||
| 8 | |||
| 9 | class IAlbumControlSession final : public ServiceFramework<IAlbumControlSession> { | ||
| 10 | public: | ||
| 11 | explicit IAlbumControlSession() : ServiceFramework{"IAlbumControlSession"} { | ||
| 12 | // clang-format off | ||
| 13 | static const FunctionInfo functions[] = { | ||
| 14 | {2001, nullptr, "OpenAlbumMovieReadStream"}, | ||
| 15 | {2002, nullptr, "CloseAlbumMovieReadStream"}, | ||
| 16 | {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, | ||
| 17 | {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, | ||
| 18 | {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, | ||
| 19 | {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, | ||
| 20 | {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, | ||
| 21 | {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, | ||
| 22 | {2401, nullptr, "OpenAlbumMovieWriteStream"}, | ||
| 23 | {2402, nullptr, "FinishAlbumMovieWriteStream"}, | ||
| 24 | {2403, nullptr, "CommitAlbumMovieWriteStream"}, | ||
| 25 | {2404, nullptr, "DiscardAlbumMovieWriteStream"}, | ||
| 26 | {2405, nullptr, "DiscardAlbumMovieWriteStreamNoDelete"}, | ||
| 27 | {2406, nullptr, "CommitAlbumMovieWriteStreamEx"}, | ||
| 28 | {2411, nullptr, "StartAlbumMovieWriteStreamDataSection"}, | ||
| 29 | {2412, nullptr, "EndAlbumMovieWriteStreamDataSection"}, | ||
| 30 | {2413, nullptr, "StartAlbumMovieWriteStreamMetaSection"}, | ||
| 31 | {2414, nullptr, "EndAlbumMovieWriteStreamMetaSection"}, | ||
| 32 | {2421, nullptr, "ReadDataFromAlbumMovieWriteStream"}, | ||
| 33 | {2422, nullptr, "WriteDataToAlbumMovieWriteStream"}, | ||
| 34 | {2424, nullptr, "WriteMetaToAlbumMovieWriteStream"}, | ||
| 35 | {2431, nullptr, "GetAlbumMovieWriteStreamBrokenReason"}, | ||
| 36 | {2433, nullptr, "GetAlbumMovieWriteStreamDataSize"}, | ||
| 37 | {2434, nullptr, "SetAlbumMovieWriteStreamDataSize"}, | ||
| 38 | }; | ||
| 39 | // clang-format on | ||
| 40 | |||
| 41 | RegisterHandlers(functions); | ||
| 42 | } | ||
| 43 | }; | ||
| 44 | |||
| 45 | CAPS_C::CAPS_C() : ServiceFramework("caps:c") { | ||
| 46 | // clang-format off | ||
| 47 | static const FunctionInfo functions[] = { | ||
| 48 | {1, nullptr, "CaptureRawImage"}, | ||
| 49 | {2, nullptr, "CaptureRawImageWithTimeout"}, | ||
| 50 | {33, nullptr, "Unknown33"}, | ||
| 51 | {1001, nullptr, "RequestTakingScreenShot"}, | ||
| 52 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, | ||
| 53 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, | ||
| 54 | {2001, nullptr, "NotifyAlbumStorageIsAvailable"}, | ||
| 55 | {2002, nullptr, "NotifyAlbumStorageIsUnavailable"}, | ||
| 56 | {2011, nullptr, "RegisterAppletResourceUserId"}, | ||
| 57 | {2012, nullptr, "UnregisterAppletResourceUserId"}, | ||
| 58 | {2013, nullptr, "GetApplicationIdFromAruid"}, | ||
| 59 | {2014, nullptr, "CheckApplicationIdRegistered"}, | ||
| 60 | {2101, nullptr, "GenerateCurrentAlbumFileId"}, | ||
| 61 | {2102, nullptr, "GenerateApplicationAlbumEntry"}, | ||
| 62 | {2201, nullptr, "SaveAlbumScreenShotFile"}, | ||
| 63 | {2202, nullptr, "SaveAlbumScreenShotFileEx"}, | ||
| 64 | {2301, nullptr, "SetOverlayScreenShotThumbnailData"}, | ||
| 65 | {2302, nullptr, "SetOverlayMovieThumbnailData"}, | ||
| 66 | {60001, nullptr, "OpenControlSession"}, | ||
| 67 | }; | ||
| 68 | // clang-format on | ||
| 69 | |||
| 70 | RegisterHandlers(functions); | ||
| 71 | } | ||
| 72 | |||
| 73 | CAPS_C::~CAPS_C() = default; | ||
| 74 | |||
| 75 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h new file mode 100644 index 000000000..d07cdb441 --- /dev/null +++ b/src/core/hle/service/caps/caps_c.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | class HLERequestContext; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::Capture { | ||
| 14 | |||
| 15 | class CAPS_C final : public ServiceFramework<CAPS_C> { | ||
| 16 | public: | ||
| 17 | explicit CAPS_C(); | ||
| 18 | ~CAPS_C() override; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp new file mode 100644 index 000000000..d01a8a58e --- /dev/null +++ b/src/core/hle/service/caps/caps_sc.cpp | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/caps/caps_sc.h" | ||
| 6 | |||
| 7 | namespace Service::Capture { | ||
| 8 | |||
| 9 | CAPS_SC::CAPS_SC() : ServiceFramework("caps:sc") { | ||
| 10 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | ||
| 12 | {1, nullptr, "CaptureRawImage"}, | ||
| 13 | {2, nullptr, "CaptureRawImageWithTimeout"}, | ||
| 14 | {3, nullptr, "AttachSharedBuffer"}, | ||
| 15 | {5, nullptr, "CaptureRawImageToAttachedSharedBuffer"}, | ||
| 16 | {210, nullptr, "Unknown210"}, | ||
| 17 | {1001, nullptr, "RequestTakingScreenShot"}, | ||
| 18 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, | ||
| 19 | {1003, nullptr, "RequestTakingScreenShotEx"}, | ||
| 20 | {1004, nullptr, "RequestTakingScreenShotEx1"}, | ||
| 21 | {1009, nullptr, "CancelTakingScreenShot"}, | ||
| 22 | {1010, nullptr, "SetTakingScreenShotCancelState"}, | ||
| 23 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, | ||
| 24 | {1012, nullptr, "NotifyTakingScreenShotFailed"}, | ||
| 25 | {1101, nullptr, "SetupOverlayMovieThumbnail"}, | ||
| 26 | {1106, nullptr, "Unknown1106"}, | ||
| 27 | {1107, nullptr, "Unknown1107"}, | ||
| 28 | {1201, nullptr, "OpenRawScreenShotReadStream"}, | ||
| 29 | {1202, nullptr, "CloseRawScreenShotReadStream"}, | ||
| 30 | {1203, nullptr, "ReadRawScreenShotReadStream"}, | ||
| 31 | {1204, nullptr, "Unknown1204"}, | ||
| 32 | }; | ||
| 33 | // clang-format on | ||
| 34 | |||
| 35 | RegisterHandlers(functions); | ||
| 36 | } | ||
| 37 | |||
| 38 | CAPS_SC::~CAPS_SC() = default; | ||
| 39 | |||
| 40 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h new file mode 100644 index 000000000..9ba372f7a --- /dev/null +++ b/src/core/hle/service/caps/caps_sc.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | class HLERequestContext; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::Capture { | ||
| 14 | |||
| 15 | class CAPS_SC final : public ServiceFramework<CAPS_SC> { | ||
| 16 | public: | ||
| 17 | explicit CAPS_SC(); | ||
| 18 | ~CAPS_SC() override; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp new file mode 100644 index 000000000..eaa3a7494 --- /dev/null +++ b/src/core/hle/service/caps/caps_ss.cpp | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/caps/caps_ss.h" | ||
| 6 | |||
| 7 | namespace Service::Capture { | ||
| 8 | |||
| 9 | CAPS_SS::CAPS_SS() : ServiceFramework("caps:ss") { | ||
| 10 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | ||
| 12 | {201, nullptr, "SaveScreenShot"}, | ||
| 13 | {202, nullptr, "SaveEditedScreenShot"}, | ||
| 14 | {203, nullptr, "SaveScreenShotEx0"}, | ||
| 15 | {204, nullptr, "SaveEditedScreenShotEx0"}, | ||
| 16 | {206, nullptr, "Unknown206"}, | ||
| 17 | {208, nullptr, "SaveScreenShotOfMovieEx1"}, | ||
| 18 | }; | ||
| 19 | // clang-format on | ||
| 20 | |||
| 21 | RegisterHandlers(functions); | ||
| 22 | } | ||
| 23 | |||
| 24 | CAPS_SS::~CAPS_SS() = default; | ||
| 25 | |||
| 26 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h new file mode 100644 index 000000000..e258a6925 --- /dev/null +++ b/src/core/hle/service/caps/caps_ss.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | class HLERequestContext; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::Capture { | ||
| 14 | |||
| 15 | class CAPS_SS final : public ServiceFramework<CAPS_SS> { | ||
| 16 | public: | ||
| 17 | explicit CAPS_SS(); | ||
| 18 | ~CAPS_SS() override; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp new file mode 100644 index 000000000..2b4c2d808 --- /dev/null +++ b/src/core/hle/service/caps/caps_su.cpp | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/caps/caps_su.h" | ||
| 6 | |||
| 7 | namespace Service::Capture { | ||
| 8 | |||
| 9 | CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") { | ||
| 10 | // clang-format off | ||
| 11 | static const FunctionInfo functions[] = { | ||
| 12 | {201, nullptr, "SaveScreenShot"}, | ||
| 13 | {203, nullptr, "SaveScreenShotEx0"}, | ||
| 14 | }; | ||
| 15 | // clang-format on | ||
| 16 | |||
| 17 | RegisterHandlers(functions); | ||
| 18 | } | ||
| 19 | |||
| 20 | CAPS_SU::~CAPS_SU() = default; | ||
| 21 | |||
| 22 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h new file mode 100644 index 000000000..cb11f7c9a --- /dev/null +++ b/src/core/hle/service/caps/caps_su.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | class HLERequestContext; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::Capture { | ||
| 14 | |||
| 15 | class CAPS_SU final : public ServiceFramework<CAPS_SU> { | ||
| 16 | public: | ||
| 17 | explicit CAPS_SU(); | ||
| 18 | ~CAPS_SU() override; | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp new file mode 100644 index 000000000..78bab6ed8 --- /dev/null +++ b/src/core/hle/service/caps/caps_u.cpp | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/service/caps/caps.h" | ||
| 8 | #include "core/hle/service/caps/caps_u.h" | ||
| 9 | |||
| 10 | namespace Service::Capture { | ||
| 11 | |||
| 12 | class IAlbumAccessorApplicationSession final | ||
| 13 | : public ServiceFramework<IAlbumAccessorApplicationSession> { | ||
| 14 | public: | ||
| 15 | explicit IAlbumAccessorApplicationSession() | ||
| 16 | : ServiceFramework{"IAlbumAccessorApplicationSession"} { | ||
| 17 | // clang-format off | ||
| 18 | static const FunctionInfo functions[] = { | ||
| 19 | {2001, nullptr, "OpenAlbumMovieReadStream"}, | ||
| 20 | {2002, nullptr, "CloseAlbumMovieReadStream"}, | ||
| 21 | {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, | ||
| 22 | {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, | ||
| 23 | {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, | ||
| 24 | }; | ||
| 25 | // clang-format on | ||
| 26 | |||
| 27 | RegisterHandlers(functions); | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | CAPS_U::CAPS_U() : ServiceFramework("caps:u") { | ||
| 32 | // clang-format off | ||
| 33 | static const FunctionInfo functions[] = { | ||
| 34 | {31, nullptr, "GetShimLibraryVersion"}, | ||
| 35 | {32, nullptr, "SetShimLibraryVersion"}, | ||
| 36 | {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, | ||
| 37 | {103, nullptr, "DeleteAlbumContentsFileForApplication"}, | ||
| 38 | {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, | ||
| 39 | {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, | ||
| 40 | {110, nullptr, "LoadAlbumContentsFileScreenShotImageForApplication"}, | ||
| 41 | {120, nullptr, "LoadAlbumContentsFileThumbnailImageForApplication"}, | ||
| 42 | {130, nullptr, "PrecheckToCreateContentsForApplication"}, | ||
| 43 | {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, | ||
| 44 | {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, | ||
| 45 | {142, nullptr, "GetAlbumFileList3AaeAruid"}, | ||
| 46 | {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, | ||
| 47 | {60002, nullptr, "OpenAccessorSessionForApplication"}, | ||
| 48 | }; | ||
| 49 | // clang-format on | ||
| 50 | |||
| 51 | RegisterHandlers(functions); | ||
| 52 | } | ||
| 53 | |||
| 54 | CAPS_U::~CAPS_U() = default; | ||
| 55 | |||
| 56 | void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) { | ||
| 57 | // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an | ||
| 58 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total | ||
| 59 | // output entries (which is copied to a s32 by official SW). | ||
| 60 | IPC::RequestParser rp{ctx}; | ||
| 61 | [[maybe_unused]] const auto application_album_file_entries = rp.PopRaw<std::array<u8, 0x30>>(); | ||
| 62 | const auto pid = rp.Pop<s32>(); | ||
| 63 | const auto content_type = rp.PopRaw<ContentType>(); | ||
| 64 | [[maybe_unused]] const auto start_datetime = rp.PopRaw<AlbumFileDateTime>(); | ||
| 65 | [[maybe_unused]] const auto end_datetime = rp.PopRaw<AlbumFileDateTime>(); | ||
| 66 | const auto applet_resource_user_id = rp.Pop<u64>(); | ||
| 67 | LOG_WARNING(Service_Capture, | ||
| 68 | "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, | ||
| 69 | content_type, applet_resource_user_id); | ||
| 70 | |||
| 71 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 72 | rb.Push(RESULT_SUCCESS); | ||
| 73 | rb.Push<s32>(0); | ||
| 74 | } | ||
| 75 | |||
| 76 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h new file mode 100644 index 000000000..e6e0716ff --- /dev/null +++ b/src/core/hle/service/caps/caps_u.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | class HLERequestContext; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Service::Capture { | ||
| 14 | |||
| 15 | class CAPS_U final : public ServiceFramework<CAPS_U> { | ||
| 16 | public: | ||
| 17 | explicit CAPS_U(); | ||
| 18 | ~CAPS_U() override; | ||
| 19 | |||
| 20 | private: | ||
| 21 | void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); | ||
| 22 | }; | ||
| 23 | |||
| 24 | } // namespace Service::Capture | ||
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 157aeec88..647943020 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -342,17 +342,27 @@ public: | |||
| 342 | return; | 342 | return; |
| 343 | } | 343 | } |
| 344 | 344 | ||
| 345 | ASSERT( | 345 | // Mark text and read-only region as ModuleCode |
| 346 | vm_manager | 346 | ASSERT(vm_manager |
| 347 | .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) | 347 | .MirrorMemory(*map_address, nro_address, header.text_size + header.ro_size, |
| 348 | .IsSuccess()); | 348 | Kernel::MemoryState::ModuleCode) |
| 349 | .IsSuccess()); | ||
| 350 | // Mark read/write region as ModuleCodeData, which is necessary if this region is used for | ||
| 351 | // TransferMemory (e.g. Final Fantasy VIII Remastered does this) | ||
| 352 | ASSERT(vm_manager | ||
| 353 | .MirrorMemory(*map_address + header.rw_offset, nro_address + header.rw_offset, | ||
| 354 | header.rw_size, Kernel::MemoryState::ModuleCodeData) | ||
| 355 | .IsSuccess()); | ||
| 356 | // Revoke permissions from the old memory region | ||
| 349 | ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None) | 357 | ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None) |
| 350 | .IsSuccess()); | 358 | .IsSuccess()); |
| 351 | 359 | ||
| 352 | if (bss_size > 0) { | 360 | if (bss_size > 0) { |
| 361 | // Mark BSS region as ModuleCodeData, which is necessary if this region is used for | ||
| 362 | // TransferMemory (e.g. Final Fantasy VIII Remastered does this) | ||
| 353 | ASSERT(vm_manager | 363 | ASSERT(vm_manager |
| 354 | .MirrorMemory(*map_address + nro_size, bss_address, bss_size, | 364 | .MirrorMemory(*map_address + nro_size, bss_address, bss_size, |
| 355 | Kernel::MemoryState::ModuleCode) | 365 | Kernel::MemoryState::ModuleCodeData) |
| 356 | .IsSuccess()); | 366 | .IsSuccess()); |
| 357 | ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None) | 367 | ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None) |
| 358 | .IsSuccess()); | 368 | .IsSuccess()); |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 32b6f4b27..9bcafa5fc 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp | |||
| @@ -28,6 +28,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) | |||
| 28 | buffer.slot = slot; | 28 | buffer.slot = slot; |
| 29 | buffer.igbp_buffer = igbp_buffer; | 29 | buffer.igbp_buffer = igbp_buffer; |
| 30 | buffer.status = Buffer::Status::Free; | 30 | buffer.status = Buffer::Status::Free; |
| 31 | free_buffers.push_back(slot); | ||
| 31 | 32 | ||
| 32 | queue.emplace_back(buffer); | 33 | queue.emplace_back(buffer); |
| 33 | buffer_wait_event.writable->Signal(); | 34 | buffer_wait_event.writable->Signal(); |
| @@ -35,16 +36,37 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) | |||
| 35 | 36 | ||
| 36 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, | 37 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, |
| 37 | u32 height) { | 38 | u32 height) { |
| 38 | auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { | ||
| 39 | // Only consider free buffers. Buffers become free once again after they've been Acquired | ||
| 40 | // and Released by the compositor, see the NVFlinger::Compose method. | ||
| 41 | if (buffer.status != Buffer::Status::Free) { | ||
| 42 | return false; | ||
| 43 | } | ||
| 44 | 39 | ||
| 45 | // Make sure that the parameters match. | 40 | if (free_buffers.empty()) { |
| 46 | return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; | 41 | return {}; |
| 47 | }); | 42 | } |
| 43 | |||
| 44 | auto f_itr = free_buffers.begin(); | ||
| 45 | auto itr = queue.end(); | ||
| 46 | |||
| 47 | while (f_itr != free_buffers.end()) { | ||
| 48 | auto slot = *f_itr; | ||
| 49 | itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { | ||
| 50 | // Only consider free buffers. Buffers become free once again after they've been | ||
| 51 | // Acquired and Released by the compositor, see the NVFlinger::Compose method. | ||
| 52 | if (buffer.status != Buffer::Status::Free) { | ||
| 53 | return false; | ||
| 54 | } | ||
| 55 | |||
| 56 | if (buffer.slot != slot) { | ||
| 57 | return false; | ||
| 58 | } | ||
| 59 | |||
| 60 | // Make sure that the parameters match. | ||
| 61 | return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; | ||
| 62 | }); | ||
| 63 | |||
| 64 | if (itr != queue.end()) { | ||
| 65 | free_buffers.erase(f_itr); | ||
| 66 | break; | ||
| 67 | } | ||
| 68 | ++f_itr; | ||
| 69 | } | ||
| 48 | 70 | ||
| 49 | if (itr == queue.end()) { | 71 | if (itr == queue.end()) { |
| 50 | return {}; | 72 | return {}; |
| @@ -99,6 +121,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) { | |||
| 99 | ASSERT(itr != queue.end()); | 121 | ASSERT(itr != queue.end()); |
| 100 | ASSERT(itr->status == Buffer::Status::Acquired); | 122 | ASSERT(itr->status == Buffer::Status::Acquired); |
| 101 | itr->status = Buffer::Status::Free; | 123 | itr->status = Buffer::Status::Free; |
| 124 | free_buffers.push_back(slot); | ||
| 102 | 125 | ||
| 103 | buffer_wait_event.writable->Signal(); | 126 | buffer_wait_event.writable->Signal(); |
| 104 | } | 127 | } |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index f4bbfd945..f674823b0 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h | |||
| @@ -101,6 +101,7 @@ private: | |||
| 101 | u32 id; | 101 | u32 id; |
| 102 | u64 layer_id; | 102 | u64 layer_id; |
| 103 | 103 | ||
| 104 | std::list<u32> free_buffers; | ||
| 104 | std::vector<Buffer> queue; | 105 | std::vector<Buffer> queue; |
| 105 | std::list<u32> queue_sequence; | 106 | std::list<u32> queue_sequence; |
| 106 | Kernel::EventPair buffer_wait_event; | 107 | Kernel::EventPair buffer_wait_event; |
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 1660bbdb8..f509653a3 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp | |||
| @@ -30,7 +30,7 @@ Time::Time(std::shared_ptr<Module> module, Core::System& system, const char* nam | |||
| 30 | {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, | 30 | {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, |
| 31 | {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, | 31 | {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, |
| 32 | {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, | 32 | {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, |
| 33 | {501, nullptr, "CalculateSpanBetween"}, | 33 | {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"}, |
| 34 | }; | 34 | }; |
| 35 | // clang-format on | 35 | // clang-format on |
| 36 | 36 | ||
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 749b7be70..ce859f18d 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -308,6 +308,35 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques | |||
| 308 | ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); | 308 | ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) { | ||
| 312 | LOG_DEBUG(Service_Time, "called"); | ||
| 313 | |||
| 314 | IPC::RequestParser rp{ctx}; | ||
| 315 | const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>(); | ||
| 316 | const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>(); | ||
| 317 | |||
| 318 | Clock::TimeSpanType time_span_type{}; | ||
| 319 | s64 span{}; | ||
| 320 | if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween( | ||
| 321 | snapshot_b.steady_clock_time_point, span)}; | ||
| 322 | result != RESULT_SUCCESS) { | ||
| 323 | if (snapshot_a.network_time && snapshot_b.network_time) { | ||
| 324 | time_span_type = | ||
| 325 | Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time); | ||
| 326 | } else { | ||
| 327 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 328 | rb.Push(ERROR_TIME_NOT_FOUND); | ||
| 329 | return; | ||
| 330 | } | ||
| 331 | } else { | ||
| 332 | time_span_type = Clock::TimeSpanType::FromSeconds(span); | ||
| 333 | } | ||
| 334 | |||
| 335 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; | ||
| 336 | rb.Push(RESULT_SUCCESS); | ||
| 337 | rb.PushRaw(time_span_type.nanoseconds); | ||
| 338 | } | ||
| 339 | |||
| 311 | void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | 340 | void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { |
| 312 | LOG_DEBUG(Service_Time, "called"); | 341 | LOG_DEBUG(Service_Time, "called"); |
| 313 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 342 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index aadc2df60..351988468 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h | |||
| @@ -32,6 +32,7 @@ public: | |||
| 32 | void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx); | 32 | void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx); |
| 33 | void GetClockSnapshot(Kernel::HLERequestContext& ctx); | 33 | void GetClockSnapshot(Kernel::HLERequestContext& ctx); |
| 34 | void GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx); | 34 | void GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx); |
| 35 | void CalculateSpanBetween(Kernel::HLERequestContext& ctx); | ||
| 35 | void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); | 36 | void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); |
| 36 | 37 | ||
| 37 | private: | 38 | private: |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index f0888327f..6061d37ae 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -242,7 +242,52 @@ struct Memory::Impl { | |||
| 242 | } | 242 | } |
| 243 | case Common::PageType::RasterizerCachedMemory: { | 243 | case Common::PageType::RasterizerCachedMemory: { |
| 244 | const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | 244 | const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); |
| 245 | system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); | 245 | system.GPU().FlushRegion(current_vaddr, copy_amount); |
| 246 | std::memcpy(dest_buffer, host_ptr, copy_amount); | ||
| 247 | break; | ||
| 248 | } | ||
| 249 | default: | ||
| 250 | UNREACHABLE(); | ||
| 251 | } | ||
| 252 | |||
| 253 | page_index++; | ||
| 254 | page_offset = 0; | ||
| 255 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | ||
| 256 | remaining_size -= copy_amount; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | void ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | ||
| 261 | const std::size_t size) { | ||
| 262 | const auto& page_table = process.VMManager().page_table; | ||
| 263 | |||
| 264 | std::size_t remaining_size = size; | ||
| 265 | std::size_t page_index = src_addr >> PAGE_BITS; | ||
| 266 | std::size_t page_offset = src_addr & PAGE_MASK; | ||
| 267 | |||
| 268 | while (remaining_size > 0) { | ||
| 269 | const std::size_t copy_amount = | ||
| 270 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | ||
| 271 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | ||
| 272 | |||
| 273 | switch (page_table.attributes[page_index]) { | ||
| 274 | case Common::PageType::Unmapped: { | ||
| 275 | LOG_ERROR(HW_Memory, | ||
| 276 | "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | ||
| 277 | current_vaddr, src_addr, size); | ||
| 278 | std::memset(dest_buffer, 0, copy_amount); | ||
| 279 | break; | ||
| 280 | } | ||
| 281 | case Common::PageType::Memory: { | ||
| 282 | DEBUG_ASSERT(page_table.pointers[page_index]); | ||
| 283 | |||
| 284 | const u8* const src_ptr = | ||
| 285 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 286 | std::memcpy(dest_buffer, src_ptr, copy_amount); | ||
| 287 | break; | ||
| 288 | } | ||
| 289 | case Common::PageType::RasterizerCachedMemory: { | ||
| 290 | const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||
| 246 | std::memcpy(dest_buffer, host_ptr, copy_amount); | 291 | std::memcpy(dest_buffer, host_ptr, copy_amount); |
| 247 | break; | 292 | break; |
| 248 | } | 293 | } |
| @@ -261,6 +306,10 @@ struct Memory::Impl { | |||
| 261 | ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); | 306 | ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); |
| 262 | } | 307 | } |
| 263 | 308 | ||
| 309 | void ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { | ||
| 310 | ReadBlockUnsafe(*system.CurrentProcess(), src_addr, dest_buffer, size); | ||
| 311 | } | ||
| 312 | |||
| 264 | void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, | 313 | void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, |
| 265 | const std::size_t size) { | 314 | const std::size_t size) { |
| 266 | const auto& page_table = process.VMManager().page_table; | 315 | const auto& page_table = process.VMManager().page_table; |
| @@ -290,7 +339,50 @@ struct Memory::Impl { | |||
| 290 | } | 339 | } |
| 291 | case Common::PageType::RasterizerCachedMemory: { | 340 | case Common::PageType::RasterizerCachedMemory: { |
| 292 | u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | 341 | u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); |
| 293 | system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); | 342 | system.GPU().InvalidateRegion(current_vaddr, copy_amount); |
| 343 | std::memcpy(host_ptr, src_buffer, copy_amount); | ||
| 344 | break; | ||
| 345 | } | ||
| 346 | default: | ||
| 347 | UNREACHABLE(); | ||
| 348 | } | ||
| 349 | |||
| 350 | page_index++; | ||
| 351 | page_offset = 0; | ||
| 352 | src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; | ||
| 353 | remaining_size -= copy_amount; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | void WriteBlockUnsafe(const Kernel::Process& process, const VAddr dest_addr, | ||
| 358 | const void* src_buffer, const std::size_t size) { | ||
| 359 | const auto& page_table = process.VMManager().page_table; | ||
| 360 | std::size_t remaining_size = size; | ||
| 361 | std::size_t page_index = dest_addr >> PAGE_BITS; | ||
| 362 | std::size_t page_offset = dest_addr & PAGE_MASK; | ||
| 363 | |||
| 364 | while (remaining_size > 0) { | ||
| 365 | const std::size_t copy_amount = | ||
| 366 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | ||
| 367 | const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | ||
| 368 | |||
| 369 | switch (page_table.attributes[page_index]) { | ||
| 370 | case Common::PageType::Unmapped: { | ||
| 371 | LOG_ERROR(HW_Memory, | ||
| 372 | "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | ||
| 373 | current_vaddr, dest_addr, size); | ||
| 374 | break; | ||
| 375 | } | ||
| 376 | case Common::PageType::Memory: { | ||
| 377 | DEBUG_ASSERT(page_table.pointers[page_index]); | ||
| 378 | |||
| 379 | u8* const dest_ptr = | ||
| 380 | page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS); | ||
| 381 | std::memcpy(dest_ptr, src_buffer, copy_amount); | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | case Common::PageType::RasterizerCachedMemory: { | ||
| 385 | u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||
| 294 | std::memcpy(host_ptr, src_buffer, copy_amount); | 386 | std::memcpy(host_ptr, src_buffer, copy_amount); |
| 295 | break; | 387 | break; |
| 296 | } | 388 | } |
| @@ -309,6 +401,10 @@ struct Memory::Impl { | |||
| 309 | WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size); | 401 | WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size); |
| 310 | } | 402 | } |
| 311 | 403 | ||
| 404 | void WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { | ||
| 405 | WriteBlockUnsafe(*system.CurrentProcess(), dest_addr, src_buffer, size); | ||
| 406 | } | ||
| 407 | |||
| 312 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { | 408 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { |
| 313 | const auto& page_table = process.VMManager().page_table; | 409 | const auto& page_table = process.VMManager().page_table; |
| 314 | std::size_t remaining_size = size; | 410 | std::size_t remaining_size = size; |
| @@ -337,7 +433,7 @@ struct Memory::Impl { | |||
| 337 | } | 433 | } |
| 338 | case Common::PageType::RasterizerCachedMemory: { | 434 | case Common::PageType::RasterizerCachedMemory: { |
| 339 | u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | 435 | u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); |
| 340 | system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); | 436 | system.GPU().InvalidateRegion(current_vaddr, copy_amount); |
| 341 | std::memset(host_ptr, 0, copy_amount); | 437 | std::memset(host_ptr, 0, copy_amount); |
| 342 | break; | 438 | break; |
| 343 | } | 439 | } |
| @@ -384,7 +480,7 @@ struct Memory::Impl { | |||
| 384 | } | 480 | } |
| 385 | case Common::PageType::RasterizerCachedMemory: { | 481 | case Common::PageType::RasterizerCachedMemory: { |
| 386 | const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | 482 | const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); |
| 387 | system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); | 483 | system.GPU().FlushRegion(current_vaddr, copy_amount); |
| 388 | WriteBlock(process, dest_addr, host_ptr, copy_amount); | 484 | WriteBlock(process, dest_addr, host_ptr, copy_amount); |
| 389 | break; | 485 | break; |
| 390 | } | 486 | } |
| @@ -545,7 +641,7 @@ struct Memory::Impl { | |||
| 545 | break; | 641 | break; |
| 546 | case Common::PageType::RasterizerCachedMemory: { | 642 | case Common::PageType::RasterizerCachedMemory: { |
| 547 | const u8* const host_ptr = GetPointerFromVMA(vaddr); | 643 | const u8* const host_ptr = GetPointerFromVMA(vaddr); |
| 548 | system.GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T)); | 644 | system.GPU().FlushRegion(vaddr, sizeof(T)); |
| 549 | T value; | 645 | T value; |
| 550 | std::memcpy(&value, host_ptr, sizeof(T)); | 646 | std::memcpy(&value, host_ptr, sizeof(T)); |
| 551 | return value; | 647 | return value; |
| @@ -587,7 +683,7 @@ struct Memory::Impl { | |||
| 587 | break; | 683 | break; |
| 588 | case Common::PageType::RasterizerCachedMemory: { | 684 | case Common::PageType::RasterizerCachedMemory: { |
| 589 | u8* const host_ptr{GetPointerFromVMA(vaddr)}; | 685 | u8* const host_ptr{GetPointerFromVMA(vaddr)}; |
| 590 | system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); | 686 | system.GPU().InvalidateRegion(vaddr, sizeof(T)); |
| 591 | std::memcpy(host_ptr, &data, sizeof(T)); | 687 | std::memcpy(host_ptr, &data, sizeof(T)); |
| 592 | break; | 688 | break; |
| 593 | } | 689 | } |
| @@ -696,6 +792,15 @@ void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_ | |||
| 696 | impl->ReadBlock(src_addr, dest_buffer, size); | 792 | impl->ReadBlock(src_addr, dest_buffer, size); |
| 697 | } | 793 | } |
| 698 | 794 | ||
| 795 | void Memory::ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr, | ||
| 796 | void* dest_buffer, const std::size_t size) { | ||
| 797 | impl->ReadBlockUnsafe(process, src_addr, dest_buffer, size); | ||
| 798 | } | ||
| 799 | |||
| 800 | void Memory::ReadBlockUnsafe(const VAddr src_addr, void* dest_buffer, const std::size_t size) { | ||
| 801 | impl->ReadBlockUnsafe(src_addr, dest_buffer, size); | ||
| 802 | } | ||
| 803 | |||
| 699 | void Memory::WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | 804 | void Memory::WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, |
| 700 | std::size_t size) { | 805 | std::size_t size) { |
| 701 | impl->WriteBlock(process, dest_addr, src_buffer, size); | 806 | impl->WriteBlock(process, dest_addr, src_buffer, size); |
| @@ -705,6 +810,16 @@ void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std | |||
| 705 | impl->WriteBlock(dest_addr, src_buffer, size); | 810 | impl->WriteBlock(dest_addr, src_buffer, size); |
| 706 | } | 811 | } |
| 707 | 812 | ||
| 813 | void Memory::WriteBlockUnsafe(const Kernel::Process& process, VAddr dest_addr, | ||
| 814 | const void* src_buffer, std::size_t size) { | ||
| 815 | impl->WriteBlockUnsafe(process, dest_addr, src_buffer, size); | ||
| 816 | } | ||
| 817 | |||
| 818 | void Memory::WriteBlockUnsafe(const VAddr dest_addr, const void* src_buffer, | ||
| 819 | const std::size_t size) { | ||
| 820 | impl->WriteBlockUnsafe(dest_addr, src_buffer, size); | ||
| 821 | } | ||
| 822 | |||
| 708 | void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { | 823 | void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { |
| 709 | impl->ZeroBlock(process, dest_addr, size); | 824 | impl->ZeroBlock(process, dest_addr, size); |
| 710 | } | 825 | } |
diff --git a/src/core/memory.h b/src/core/memory.h index 8913a9da4..b92d678a4 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -295,6 +295,27 @@ public: | |||
| 295 | std::size_t size); | 295 | std::size_t size); |
| 296 | 296 | ||
| 297 | /** | 297 | /** |
| 298 | * Reads a contiguous block of bytes from a specified process' address space. | ||
| 299 | * This unsafe version does not trigger GPU flushing. | ||
| 300 | * | ||
| 301 | * @param process The process to read the data from. | ||
| 302 | * @param src_addr The virtual address to begin reading from. | ||
| 303 | * @param dest_buffer The buffer to place the read bytes into. | ||
| 304 | * @param size The amount of data to read, in bytes. | ||
| 305 | * | ||
| 306 | * @note If a size of 0 is specified, then this function reads nothing and | ||
| 307 | * no attempts to access memory are made at all. | ||
| 308 | * | ||
| 309 | * @pre dest_buffer must be at least size bytes in length, otherwise a | ||
| 310 | * buffer overrun will occur. | ||
| 311 | * | ||
| 312 | * @post The range [dest_buffer, size) contains the read bytes from the | ||
| 313 | * process' address space. | ||
| 314 | */ | ||
| 315 | void ReadBlockUnsafe(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, | ||
| 316 | std::size_t size); | ||
| 317 | |||
| 318 | /** | ||
| 298 | * Reads a contiguous block of bytes from the current process' address space. | 319 | * Reads a contiguous block of bytes from the current process' address space. |
| 299 | * | 320 | * |
| 300 | * @param src_addr The virtual address to begin reading from. | 321 | * @param src_addr The virtual address to begin reading from. |
| @@ -313,6 +334,25 @@ public: | |||
| 313 | void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); | 334 | void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); |
| 314 | 335 | ||
| 315 | /** | 336 | /** |
| 337 | * Reads a contiguous block of bytes from the current process' address space. | ||
| 338 | * This unsafe version does not trigger GPU flushing. | ||
| 339 | * | ||
| 340 | * @param src_addr The virtual address to begin reading from. | ||
| 341 | * @param dest_buffer The buffer to place the read bytes into. | ||
| 342 | * @param size The amount of data to read, in bytes. | ||
| 343 | * | ||
| 344 | * @note If a size of 0 is specified, then this function reads nothing and | ||
| 345 | * no attempts to access memory are made at all. | ||
| 346 | * | ||
| 347 | * @pre dest_buffer must be at least size bytes in length, otherwise a | ||
| 348 | * buffer overrun will occur. | ||
| 349 | * | ||
| 350 | * @post The range [dest_buffer, size) contains the read bytes from the | ||
| 351 | * current process' address space. | ||
| 352 | */ | ||
| 353 | void ReadBlockUnsafe(VAddr src_addr, void* dest_buffer, std::size_t size); | ||
| 354 | |||
| 355 | /** | ||
| 316 | * Writes a range of bytes into a given process' address space at the specified | 356 | * Writes a range of bytes into a given process' address space at the specified |
| 317 | * virtual address. | 357 | * virtual address. |
| 318 | * | 358 | * |
| @@ -336,6 +376,26 @@ public: | |||
| 336 | std::size_t size); | 376 | std::size_t size); |
| 337 | 377 | ||
| 338 | /** | 378 | /** |
| 379 | * Writes a range of bytes into a given process' address space at the specified | ||
| 380 | * virtual address. | ||
| 381 | * This unsafe version does not invalidate GPU Memory. | ||
| 382 | * | ||
| 383 | * @param process The process to write data into the address space of. | ||
| 384 | * @param dest_addr The destination virtual address to begin writing the data at. | ||
| 385 | * @param src_buffer The data to write into the process' address space. | ||
| 386 | * @param size The size of the data to write, in bytes. | ||
| 387 | * | ||
| 388 | * @post The address range [dest_addr, size) in the process' address space | ||
| 389 | * contains the data that was within src_buffer. | ||
| 390 | * | ||
| 391 | * @post If an attempt is made to write into an unmapped region of memory, the writes | ||
| 392 | * will be ignored and an error will be logged. | ||
| 393 | * | ||
| 394 | */ | ||
| 395 | void WriteBlockUnsafe(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | ||
| 396 | std::size_t size); | ||
| 397 | |||
| 398 | /** | ||
| 339 | * Writes a range of bytes into the current process' address space at the specified | 399 | * Writes a range of bytes into the current process' address space at the specified |
| 340 | * virtual address. | 400 | * virtual address. |
| 341 | * | 401 | * |
| @@ -357,6 +417,24 @@ public: | |||
| 357 | void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); | 417 | void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); |
| 358 | 418 | ||
| 359 | /** | 419 | /** |
| 420 | * Writes a range of bytes into the current process' address space at the specified | ||
| 421 | * virtual address. | ||
| 422 | * This unsafe version does not invalidate GPU Memory. | ||
| 423 | * | ||
| 424 | * @param dest_addr The destination virtual address to begin writing the data at. | ||
| 425 | * @param src_buffer The data to write into the current process' address space. | ||
| 426 | * @param size The size of the data to write, in bytes. | ||
| 427 | * | ||
| 428 | * @post The address range [dest_addr, size) in the current process' address space | ||
| 429 | * contains the data that was within src_buffer. | ||
| 430 | * | ||
| 431 | * @post If an attempt is made to write into an unmapped region of memory, the writes | ||
| 432 | * will be ignored and an error will be logged. | ||
| 433 | * | ||
| 434 | */ | ||
| 435 | void WriteBlockUnsafe(VAddr dest_addr, const void* src_buffer, std::size_t size); | ||
| 436 | |||
| 437 | /** | ||
| 360 | * Fills the specified address range within a process' address space with zeroes. | 438 | * Fills the specified address range within a process' address space with zeroes. |
| 361 | * | 439 | * |
| 362 | * @param process The process that will have a portion of its memory zeroed out. | 440 | * @param process The process that will have a portion of its memory zeroed out. |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 91df062d7..f7febd6a2 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -148,6 +148,7 @@ add_library(video_core STATIC | |||
| 148 | textures/convert.h | 148 | textures/convert.h |
| 149 | textures/decoders.cpp | 149 | textures/decoders.cpp |
| 150 | textures/decoders.h | 150 | textures/decoders.h |
| 151 | textures/texture.cpp | ||
| 151 | textures/texture.h | 152 | textures/texture.h |
| 152 | video_core.cpp | 153 | video_core.cpp |
| 153 | video_core.h | 154 | video_core.h |
| @@ -210,6 +211,8 @@ if (ENABLE_VULKAN) | |||
| 210 | renderer_vulkan/vk_texture_cache.h | 211 | renderer_vulkan/vk_texture_cache.h |
| 211 | renderer_vulkan/vk_update_descriptor.cpp | 212 | renderer_vulkan/vk_update_descriptor.cpp |
| 212 | renderer_vulkan/vk_update_descriptor.h | 213 | renderer_vulkan/vk_update_descriptor.h |
| 214 | renderer_vulkan/wrapper.cpp | ||
| 215 | renderer_vulkan/wrapper.h | ||
| 213 | ) | 216 | ) |
| 214 | 217 | ||
| 215 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) | 218 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) |
diff --git a/src/video_core/buffer_cache/buffer_block.h b/src/video_core/buffer_cache/buffer_block.h index 4b9193182..e35ee0b67 100644 --- a/src/video_core/buffer_cache/buffer_block.h +++ b/src/video_core/buffer_cache/buffer_block.h | |||
| @@ -15,37 +15,29 @@ namespace VideoCommon { | |||
| 15 | 15 | ||
| 16 | class BufferBlock { | 16 | class BufferBlock { |
| 17 | public: | 17 | public: |
| 18 | bool Overlaps(const CacheAddr start, const CacheAddr end) const { | 18 | bool Overlaps(const VAddr start, const VAddr end) const { |
| 19 | return (cache_addr < end) && (cache_addr_end > start); | 19 | return (cpu_addr < end) && (cpu_addr_end > start); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | bool IsInside(const CacheAddr other_start, const CacheAddr other_end) const { | 22 | bool IsInside(const VAddr other_start, const VAddr other_end) const { |
| 23 | return cache_addr <= other_start && other_end <= cache_addr_end; | 23 | return cpu_addr <= other_start && other_end <= cpu_addr_end; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | u8* GetWritableHostPtr() const { | 26 | std::size_t GetOffset(const VAddr in_addr) { |
| 27 | return FromCacheAddr(cache_addr); | 27 | return static_cast<std::size_t>(in_addr - cpu_addr); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | u8* GetWritableHostPtr(std::size_t offset) const { | 30 | VAddr GetCpuAddr() const { |
| 31 | return FromCacheAddr(cache_addr + offset); | 31 | return cpu_addr; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | std::size_t GetOffset(const CacheAddr in_addr) { | 34 | VAddr GetCpuAddrEnd() const { |
| 35 | return static_cast<std::size_t>(in_addr - cache_addr); | 35 | return cpu_addr_end; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | CacheAddr GetCacheAddr() const { | 38 | void SetCpuAddr(const VAddr new_addr) { |
| 39 | return cache_addr; | 39 | cpu_addr = new_addr; |
| 40 | } | 40 | cpu_addr_end = new_addr + size; |
| 41 | |||
| 42 | CacheAddr GetCacheAddrEnd() const { | ||
| 43 | return cache_addr_end; | ||
| 44 | } | ||
| 45 | |||
| 46 | void SetCacheAddr(const CacheAddr new_addr) { | ||
| 47 | cache_addr = new_addr; | ||
| 48 | cache_addr_end = new_addr + size; | ||
| 49 | } | 41 | } |
| 50 | 42 | ||
| 51 | std::size_t GetSize() const { | 43 | std::size_t GetSize() const { |
| @@ -61,14 +53,14 @@ public: | |||
| 61 | } | 53 | } |
| 62 | 54 | ||
| 63 | protected: | 55 | protected: |
| 64 | explicit BufferBlock(CacheAddr cache_addr, const std::size_t size) : size{size} { | 56 | explicit BufferBlock(VAddr cpu_addr, const std::size_t size) : size{size} { |
| 65 | SetCacheAddr(cache_addr); | 57 | SetCpuAddr(cpu_addr); |
| 66 | } | 58 | } |
| 67 | ~BufferBlock() = default; | 59 | ~BufferBlock() = default; |
| 68 | 60 | ||
| 69 | private: | 61 | private: |
| 70 | CacheAddr cache_addr{}; | 62 | VAddr cpu_addr{}; |
| 71 | CacheAddr cache_addr_end{}; | 63 | VAddr cpu_addr_end{}; |
| 72 | std::size_t size{}; | 64 | std::size_t size{}; |
| 73 | u64 epoch{}; | 65 | u64 epoch{}; |
| 74 | }; | 66 | }; |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 186aca61d..b57c0d4d4 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "common/alignment.h" | 19 | #include "common/alignment.h" |
| 20 | #include "common/common_types.h" | 20 | #include "common/common_types.h" |
| 21 | #include "core/core.h" | 21 | #include "core/core.h" |
| 22 | #include "core/memory.h" | ||
| 22 | #include "video_core/buffer_cache/buffer_block.h" | 23 | #include "video_core/buffer_cache/buffer_block.h" |
| 23 | #include "video_core/buffer_cache/map_interval.h" | 24 | #include "video_core/buffer_cache/map_interval.h" |
| 24 | #include "video_core/memory_manager.h" | 25 | #include "video_core/memory_manager.h" |
| @@ -37,28 +38,45 @@ public: | |||
| 37 | bool is_written = false, bool use_fast_cbuf = false) { | 38 | bool is_written = false, bool use_fast_cbuf = false) { |
| 38 | std::lock_guard lock{mutex}; | 39 | std::lock_guard lock{mutex}; |
| 39 | 40 | ||
| 40 | auto& memory_manager = system.GPU().MemoryManager(); | 41 | const std::optional<VAddr> cpu_addr_opt = |
| 41 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | 42 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); |
| 42 | if (!host_ptr) { | 43 | |
| 44 | if (!cpu_addr_opt) { | ||
| 43 | return {GetEmptyBuffer(size), 0}; | 45 | return {GetEmptyBuffer(size), 0}; |
| 44 | } | 46 | } |
| 45 | const auto cache_addr = ToCacheAddr(host_ptr); | 47 | |
| 48 | VAddr cpu_addr = *cpu_addr_opt; | ||
| 46 | 49 | ||
| 47 | // Cache management is a big overhead, so only cache entries with a given size. | 50 | // Cache management is a big overhead, so only cache entries with a given size. |
| 48 | // TODO: Figure out which size is the best for given games. | 51 | // TODO: Figure out which size is the best for given games. |
| 49 | constexpr std::size_t max_stream_size = 0x800; | 52 | constexpr std::size_t max_stream_size = 0x800; |
| 50 | if (use_fast_cbuf || size < max_stream_size) { | 53 | if (use_fast_cbuf || size < max_stream_size) { |
| 51 | if (!is_written && !IsRegionWritten(cache_addr, cache_addr + size - 1)) { | 54 | if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) { |
| 55 | auto& memory_manager = system.GPU().MemoryManager(); | ||
| 52 | if (use_fast_cbuf) { | 56 | if (use_fast_cbuf) { |
| 53 | return ConstBufferUpload(host_ptr, size); | 57 | if (memory_manager.IsGranularRange(gpu_addr, size)) { |
| 58 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | ||
| 59 | return ConstBufferUpload(host_ptr, size); | ||
| 60 | } else { | ||
| 61 | staging_buffer.resize(size); | ||
| 62 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); | ||
| 63 | return ConstBufferUpload(staging_buffer.data(), size); | ||
| 64 | } | ||
| 54 | } else { | 65 | } else { |
| 55 | return StreamBufferUpload(host_ptr, size, alignment); | 66 | if (memory_manager.IsGranularRange(gpu_addr, size)) { |
| 67 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | ||
| 68 | return StreamBufferUpload(host_ptr, size, alignment); | ||
| 69 | } else { | ||
| 70 | staging_buffer.resize(size); | ||
| 71 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); | ||
| 72 | return StreamBufferUpload(staging_buffer.data(), size, alignment); | ||
| 73 | } | ||
| 56 | } | 74 | } |
| 57 | } | 75 | } |
| 58 | } | 76 | } |
| 59 | 77 | ||
| 60 | auto block = GetBlock(cache_addr, size); | 78 | auto block = GetBlock(cpu_addr, size); |
| 61 | auto map = MapAddress(block, gpu_addr, cache_addr, size); | 79 | auto map = MapAddress(block, gpu_addr, cpu_addr, size); |
| 62 | if (is_written) { | 80 | if (is_written) { |
| 63 | map->MarkAsModified(true, GetModifiedTicks()); | 81 | map->MarkAsModified(true, GetModifiedTicks()); |
| 64 | if (!map->IsWritten()) { | 82 | if (!map->IsWritten()) { |
| @@ -71,7 +89,7 @@ public: | |||
| 71 | } | 89 | } |
| 72 | } | 90 | } |
| 73 | 91 | ||
| 74 | const u64 offset = static_cast<u64>(block->GetOffset(cache_addr)); | 92 | const u64 offset = static_cast<u64>(block->GetOffset(cpu_addr)); |
| 75 | 93 | ||
| 76 | return {ToHandle(block), offset}; | 94 | return {ToHandle(block), offset}; |
| 77 | } | 95 | } |
| @@ -112,7 +130,7 @@ public: | |||
| 112 | } | 130 | } |
| 113 | 131 | ||
| 114 | /// Write any cached resources overlapping the specified region back to memory | 132 | /// Write any cached resources overlapping the specified region back to memory |
| 115 | void FlushRegion(CacheAddr addr, std::size_t size) { | 133 | void FlushRegion(VAddr addr, std::size_t size) { |
| 116 | std::lock_guard lock{mutex}; | 134 | std::lock_guard lock{mutex}; |
| 117 | 135 | ||
| 118 | std::vector<MapInterval> objects = GetMapsInRange(addr, size); | 136 | std::vector<MapInterval> objects = GetMapsInRange(addr, size); |
| @@ -127,7 +145,7 @@ public: | |||
| 127 | } | 145 | } |
| 128 | 146 | ||
| 129 | /// Mark the specified region as being invalidated | 147 | /// Mark the specified region as being invalidated |
| 130 | void InvalidateRegion(CacheAddr addr, u64 size) { | 148 | void InvalidateRegion(VAddr addr, u64 size) { |
| 131 | std::lock_guard lock{mutex}; | 149 | std::lock_guard lock{mutex}; |
| 132 | 150 | ||
| 133 | std::vector<MapInterval> objects = GetMapsInRange(addr, size); | 151 | std::vector<MapInterval> objects = GetMapsInRange(addr, size); |
| @@ -152,7 +170,7 @@ protected: | |||
| 152 | 170 | ||
| 153 | virtual void WriteBarrier() = 0; | 171 | virtual void WriteBarrier() = 0; |
| 154 | 172 | ||
| 155 | virtual TBuffer CreateBlock(CacheAddr cache_addr, std::size_t size) = 0; | 173 | virtual TBuffer CreateBlock(VAddr cpu_addr, std::size_t size) = 0; |
| 156 | 174 | ||
| 157 | virtual void UploadBlockData(const TBuffer& buffer, std::size_t offset, std::size_t size, | 175 | virtual void UploadBlockData(const TBuffer& buffer, std::size_t offset, std::size_t size, |
| 158 | const u8* data) = 0; | 176 | const u8* data) = 0; |
| @@ -169,20 +187,17 @@ protected: | |||
| 169 | 187 | ||
| 170 | /// Register an object into the cache | 188 | /// Register an object into the cache |
| 171 | void Register(const MapInterval& new_map, bool inherit_written = false) { | 189 | void Register(const MapInterval& new_map, bool inherit_written = false) { |
| 172 | const CacheAddr cache_ptr = new_map->GetStart(); | 190 | const VAddr cpu_addr = new_map->GetStart(); |
| 173 | const std::optional<VAddr> cpu_addr = | 191 | if (!cpu_addr) { |
| 174 | system.GPU().MemoryManager().GpuToCpuAddress(new_map->GetGpuAddress()); | ||
| 175 | if (!cache_ptr || !cpu_addr) { | ||
| 176 | LOG_CRITICAL(HW_GPU, "Failed to register buffer with unmapped gpu_address 0x{:016x}", | 192 | LOG_CRITICAL(HW_GPU, "Failed to register buffer with unmapped gpu_address 0x{:016x}", |
| 177 | new_map->GetGpuAddress()); | 193 | new_map->GetGpuAddress()); |
| 178 | return; | 194 | return; |
| 179 | } | 195 | } |
| 180 | const std::size_t size = new_map->GetEnd() - new_map->GetStart(); | 196 | const std::size_t size = new_map->GetEnd() - new_map->GetStart(); |
| 181 | new_map->SetCpuAddress(*cpu_addr); | ||
| 182 | new_map->MarkAsRegistered(true); | 197 | new_map->MarkAsRegistered(true); |
| 183 | const IntervalType interval{new_map->GetStart(), new_map->GetEnd()}; | 198 | const IntervalType interval{new_map->GetStart(), new_map->GetEnd()}; |
| 184 | mapped_addresses.insert({interval, new_map}); | 199 | mapped_addresses.insert({interval, new_map}); |
| 185 | rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1); | 200 | rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); |
| 186 | if (inherit_written) { | 201 | if (inherit_written) { |
| 187 | MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1); | 202 | MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1); |
| 188 | new_map->MarkAsWritten(true); | 203 | new_map->MarkAsWritten(true); |
| @@ -192,7 +207,7 @@ protected: | |||
| 192 | /// Unregisters an object from the cache | 207 | /// Unregisters an object from the cache |
| 193 | void Unregister(MapInterval& map) { | 208 | void Unregister(MapInterval& map) { |
| 194 | const std::size_t size = map->GetEnd() - map->GetStart(); | 209 | const std::size_t size = map->GetEnd() - map->GetStart(); |
| 195 | rasterizer.UpdatePagesCachedCount(map->GetCpuAddress(), size, -1); | 210 | rasterizer.UpdatePagesCachedCount(map->GetStart(), size, -1); |
| 196 | map->MarkAsRegistered(false); | 211 | map->MarkAsRegistered(false); |
| 197 | if (map->IsWritten()) { | 212 | if (map->IsWritten()) { |
| 198 | UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); | 213 | UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); |
| @@ -202,32 +217,39 @@ protected: | |||
| 202 | } | 217 | } |
| 203 | 218 | ||
| 204 | private: | 219 | private: |
| 205 | MapInterval CreateMap(const CacheAddr start, const CacheAddr end, const GPUVAddr gpu_addr) { | 220 | MapInterval CreateMap(const VAddr start, const VAddr end, const GPUVAddr gpu_addr) { |
| 206 | return std::make_shared<MapIntervalBase>(start, end, gpu_addr); | 221 | return std::make_shared<MapIntervalBase>(start, end, gpu_addr); |
| 207 | } | 222 | } |
| 208 | 223 | ||
| 209 | MapInterval MapAddress(const TBuffer& block, const GPUVAddr gpu_addr, | 224 | MapInterval MapAddress(const TBuffer& block, const GPUVAddr gpu_addr, const VAddr cpu_addr, |
| 210 | const CacheAddr cache_addr, const std::size_t size) { | 225 | const std::size_t size) { |
| 211 | 226 | ||
| 212 | std::vector<MapInterval> overlaps = GetMapsInRange(cache_addr, size); | 227 | std::vector<MapInterval> overlaps = GetMapsInRange(cpu_addr, size); |
| 213 | if (overlaps.empty()) { | 228 | if (overlaps.empty()) { |
| 214 | const CacheAddr cache_addr_end = cache_addr + size; | 229 | auto& memory_manager = system.GPU().MemoryManager(); |
| 215 | MapInterval new_map = CreateMap(cache_addr, cache_addr_end, gpu_addr); | 230 | const VAddr cpu_addr_end = cpu_addr + size; |
| 216 | u8* host_ptr = FromCacheAddr(cache_addr); | 231 | MapInterval new_map = CreateMap(cpu_addr, cpu_addr_end, gpu_addr); |
| 217 | UploadBlockData(block, block->GetOffset(cache_addr), size, host_ptr); | 232 | if (memory_manager.IsGranularRange(gpu_addr, size)) { |
| 233 | u8* host_ptr = memory_manager.GetPointer(gpu_addr); | ||
| 234 | UploadBlockData(block, block->GetOffset(cpu_addr), size, host_ptr); | ||
| 235 | } else { | ||
| 236 | staging_buffer.resize(size); | ||
| 237 | memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); | ||
| 238 | UploadBlockData(block, block->GetOffset(cpu_addr), size, staging_buffer.data()); | ||
| 239 | } | ||
| 218 | Register(new_map); | 240 | Register(new_map); |
| 219 | return new_map; | 241 | return new_map; |
| 220 | } | 242 | } |
| 221 | 243 | ||
| 222 | const CacheAddr cache_addr_end = cache_addr + size; | 244 | const VAddr cpu_addr_end = cpu_addr + size; |
| 223 | if (overlaps.size() == 1) { | 245 | if (overlaps.size() == 1) { |
| 224 | MapInterval& current_map = overlaps[0]; | 246 | MapInterval& current_map = overlaps[0]; |
| 225 | if (current_map->IsInside(cache_addr, cache_addr_end)) { | 247 | if (current_map->IsInside(cpu_addr, cpu_addr_end)) { |
| 226 | return current_map; | 248 | return current_map; |
| 227 | } | 249 | } |
| 228 | } | 250 | } |
| 229 | CacheAddr new_start = cache_addr; | 251 | VAddr new_start = cpu_addr; |
| 230 | CacheAddr new_end = cache_addr_end; | 252 | VAddr new_end = cpu_addr_end; |
| 231 | bool write_inheritance = false; | 253 | bool write_inheritance = false; |
| 232 | bool modified_inheritance = false; | 254 | bool modified_inheritance = false; |
| 233 | // Calculate new buffer parameters | 255 | // Calculate new buffer parameters |
| @@ -237,7 +259,7 @@ private: | |||
| 237 | write_inheritance |= overlap->IsWritten(); | 259 | write_inheritance |= overlap->IsWritten(); |
| 238 | modified_inheritance |= overlap->IsModified(); | 260 | modified_inheritance |= overlap->IsModified(); |
| 239 | } | 261 | } |
| 240 | GPUVAddr new_gpu_addr = gpu_addr + new_start - cache_addr; | 262 | GPUVAddr new_gpu_addr = gpu_addr + new_start - cpu_addr; |
| 241 | for (auto& overlap : overlaps) { | 263 | for (auto& overlap : overlaps) { |
| 242 | Unregister(overlap); | 264 | Unregister(overlap); |
| 243 | } | 265 | } |
| @@ -250,7 +272,7 @@ private: | |||
| 250 | return new_map; | 272 | return new_map; |
| 251 | } | 273 | } |
| 252 | 274 | ||
| 253 | void UpdateBlock(const TBuffer& block, CacheAddr start, CacheAddr end, | 275 | void UpdateBlock(const TBuffer& block, VAddr start, VAddr end, |
| 254 | std::vector<MapInterval>& overlaps) { | 276 | std::vector<MapInterval>& overlaps) { |
| 255 | const IntervalType base_interval{start, end}; | 277 | const IntervalType base_interval{start, end}; |
| 256 | IntervalSet interval_set{}; | 278 | IntervalSet interval_set{}; |
| @@ -262,13 +284,15 @@ private: | |||
| 262 | for (auto& interval : interval_set) { | 284 | for (auto& interval : interval_set) { |
| 263 | std::size_t size = interval.upper() - interval.lower(); | 285 | std::size_t size = interval.upper() - interval.lower(); |
| 264 | if (size > 0) { | 286 | if (size > 0) { |
| 265 | u8* host_ptr = FromCacheAddr(interval.lower()); | 287 | staging_buffer.resize(size); |
| 266 | UploadBlockData(block, block->GetOffset(interval.lower()), size, host_ptr); | 288 | system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size); |
| 289 | UploadBlockData(block, block->GetOffset(interval.lower()), size, | ||
| 290 | staging_buffer.data()); | ||
| 267 | } | 291 | } |
| 268 | } | 292 | } |
| 269 | } | 293 | } |
| 270 | 294 | ||
| 271 | std::vector<MapInterval> GetMapsInRange(CacheAddr addr, std::size_t size) { | 295 | std::vector<MapInterval> GetMapsInRange(VAddr addr, std::size_t size) { |
| 272 | if (size == 0) { | 296 | if (size == 0) { |
| 273 | return {}; | 297 | return {}; |
| 274 | } | 298 | } |
| @@ -290,8 +314,9 @@ private: | |||
| 290 | void FlushMap(MapInterval map) { | 314 | void FlushMap(MapInterval map) { |
| 291 | std::size_t size = map->GetEnd() - map->GetStart(); | 315 | std::size_t size = map->GetEnd() - map->GetStart(); |
| 292 | TBuffer block = blocks[map->GetStart() >> block_page_bits]; | 316 | TBuffer block = blocks[map->GetStart() >> block_page_bits]; |
| 293 | u8* host_ptr = FromCacheAddr(map->GetStart()); | 317 | staging_buffer.resize(size); |
| 294 | DownloadBlockData(block, block->GetOffset(map->GetStart()), size, host_ptr); | 318 | DownloadBlockData(block, block->GetOffset(map->GetStart()), size, staging_buffer.data()); |
| 319 | system.Memory().WriteBlockUnsafe(map->GetStart(), staging_buffer.data(), size); | ||
| 295 | map->MarkAsModified(false, 0); | 320 | map->MarkAsModified(false, 0); |
| 296 | } | 321 | } |
| 297 | 322 | ||
| @@ -316,14 +341,14 @@ private: | |||
| 316 | TBuffer EnlargeBlock(TBuffer buffer) { | 341 | TBuffer EnlargeBlock(TBuffer buffer) { |
| 317 | const std::size_t old_size = buffer->GetSize(); | 342 | const std::size_t old_size = buffer->GetSize(); |
| 318 | const std::size_t new_size = old_size + block_page_size; | 343 | const std::size_t new_size = old_size + block_page_size; |
| 319 | const CacheAddr cache_addr = buffer->GetCacheAddr(); | 344 | const VAddr cpu_addr = buffer->GetCpuAddr(); |
| 320 | TBuffer new_buffer = CreateBlock(cache_addr, new_size); | 345 | TBuffer new_buffer = CreateBlock(cpu_addr, new_size); |
| 321 | CopyBlock(buffer, new_buffer, 0, 0, old_size); | 346 | CopyBlock(buffer, new_buffer, 0, 0, old_size); |
| 322 | buffer->SetEpoch(epoch); | 347 | buffer->SetEpoch(epoch); |
| 323 | pending_destruction.push_back(buffer); | 348 | pending_destruction.push_back(buffer); |
| 324 | const CacheAddr cache_addr_end = cache_addr + new_size - 1; | 349 | const VAddr cpu_addr_end = cpu_addr + new_size - 1; |
| 325 | u64 page_start = cache_addr >> block_page_bits; | 350 | u64 page_start = cpu_addr >> block_page_bits; |
| 326 | const u64 page_end = cache_addr_end >> block_page_bits; | 351 | const u64 page_end = cpu_addr_end >> block_page_bits; |
| 327 | while (page_start <= page_end) { | 352 | while (page_start <= page_end) { |
| 328 | blocks[page_start] = new_buffer; | 353 | blocks[page_start] = new_buffer; |
| 329 | ++page_start; | 354 | ++page_start; |
| @@ -334,9 +359,9 @@ private: | |||
| 334 | TBuffer MergeBlocks(TBuffer first, TBuffer second) { | 359 | TBuffer MergeBlocks(TBuffer first, TBuffer second) { |
| 335 | const std::size_t size_1 = first->GetSize(); | 360 | const std::size_t size_1 = first->GetSize(); |
| 336 | const std::size_t size_2 = second->GetSize(); | 361 | const std::size_t size_2 = second->GetSize(); |
| 337 | const CacheAddr first_addr = first->GetCacheAddr(); | 362 | const VAddr first_addr = first->GetCpuAddr(); |
| 338 | const CacheAddr second_addr = second->GetCacheAddr(); | 363 | const VAddr second_addr = second->GetCpuAddr(); |
| 339 | const CacheAddr new_addr = std::min(first_addr, second_addr); | 364 | const VAddr new_addr = std::min(first_addr, second_addr); |
| 340 | const std::size_t new_size = size_1 + size_2; | 365 | const std::size_t new_size = size_1 + size_2; |
| 341 | TBuffer new_buffer = CreateBlock(new_addr, new_size); | 366 | TBuffer new_buffer = CreateBlock(new_addr, new_size); |
| 342 | CopyBlock(first, new_buffer, 0, new_buffer->GetOffset(first_addr), size_1); | 367 | CopyBlock(first, new_buffer, 0, new_buffer->GetOffset(first_addr), size_1); |
| @@ -345,9 +370,9 @@ private: | |||
| 345 | second->SetEpoch(epoch); | 370 | second->SetEpoch(epoch); |
| 346 | pending_destruction.push_back(first); | 371 | pending_destruction.push_back(first); |
| 347 | pending_destruction.push_back(second); | 372 | pending_destruction.push_back(second); |
| 348 | const CacheAddr cache_addr_end = new_addr + new_size - 1; | 373 | const VAddr cpu_addr_end = new_addr + new_size - 1; |
| 349 | u64 page_start = new_addr >> block_page_bits; | 374 | u64 page_start = new_addr >> block_page_bits; |
| 350 | const u64 page_end = cache_addr_end >> block_page_bits; | 375 | const u64 page_end = cpu_addr_end >> block_page_bits; |
| 351 | while (page_start <= page_end) { | 376 | while (page_start <= page_end) { |
| 352 | blocks[page_start] = new_buffer; | 377 | blocks[page_start] = new_buffer; |
| 353 | ++page_start; | 378 | ++page_start; |
| @@ -355,18 +380,18 @@ private: | |||
| 355 | return new_buffer; | 380 | return new_buffer; |
| 356 | } | 381 | } |
| 357 | 382 | ||
| 358 | TBuffer GetBlock(const CacheAddr cache_addr, const std::size_t size) { | 383 | TBuffer GetBlock(const VAddr cpu_addr, const std::size_t size) { |
| 359 | TBuffer found{}; | 384 | TBuffer found{}; |
| 360 | const CacheAddr cache_addr_end = cache_addr + size - 1; | 385 | const VAddr cpu_addr_end = cpu_addr + size - 1; |
| 361 | u64 page_start = cache_addr >> block_page_bits; | 386 | u64 page_start = cpu_addr >> block_page_bits; |
| 362 | const u64 page_end = cache_addr_end >> block_page_bits; | 387 | const u64 page_end = cpu_addr_end >> block_page_bits; |
| 363 | while (page_start <= page_end) { | 388 | while (page_start <= page_end) { |
| 364 | auto it = blocks.find(page_start); | 389 | auto it = blocks.find(page_start); |
| 365 | if (it == blocks.end()) { | 390 | if (it == blocks.end()) { |
| 366 | if (found) { | 391 | if (found) { |
| 367 | found = EnlargeBlock(found); | 392 | found = EnlargeBlock(found); |
| 368 | } else { | 393 | } else { |
| 369 | const CacheAddr start_addr = (page_start << block_page_bits); | 394 | const VAddr start_addr = (page_start << block_page_bits); |
| 370 | found = CreateBlock(start_addr, block_page_size); | 395 | found = CreateBlock(start_addr, block_page_size); |
| 371 | blocks[page_start] = found; | 396 | blocks[page_start] = found; |
| 372 | } | 397 | } |
| @@ -386,7 +411,7 @@ private: | |||
| 386 | return found; | 411 | return found; |
| 387 | } | 412 | } |
| 388 | 413 | ||
| 389 | void MarkRegionAsWritten(const CacheAddr start, const CacheAddr end) { | 414 | void MarkRegionAsWritten(const VAddr start, const VAddr end) { |
| 390 | u64 page_start = start >> write_page_bit; | 415 | u64 page_start = start >> write_page_bit; |
| 391 | const u64 page_end = end >> write_page_bit; | 416 | const u64 page_end = end >> write_page_bit; |
| 392 | while (page_start <= page_end) { | 417 | while (page_start <= page_end) { |
| @@ -400,7 +425,7 @@ private: | |||
| 400 | } | 425 | } |
| 401 | } | 426 | } |
| 402 | 427 | ||
| 403 | void UnmarkRegionAsWritten(const CacheAddr start, const CacheAddr end) { | 428 | void UnmarkRegionAsWritten(const VAddr start, const VAddr end) { |
| 404 | u64 page_start = start >> write_page_bit; | 429 | u64 page_start = start >> write_page_bit; |
| 405 | const u64 page_end = end >> write_page_bit; | 430 | const u64 page_end = end >> write_page_bit; |
| 406 | while (page_start <= page_end) { | 431 | while (page_start <= page_end) { |
| @@ -416,7 +441,7 @@ private: | |||
| 416 | } | 441 | } |
| 417 | } | 442 | } |
| 418 | 443 | ||
| 419 | bool IsRegionWritten(const CacheAddr start, const CacheAddr end) const { | 444 | bool IsRegionWritten(const VAddr start, const VAddr end) const { |
| 420 | u64 page_start = start >> write_page_bit; | 445 | u64 page_start = start >> write_page_bit; |
| 421 | const u64 page_end = end >> write_page_bit; | 446 | const u64 page_end = end >> write_page_bit; |
| 422 | while (page_start <= page_end) { | 447 | while (page_start <= page_end) { |
| @@ -440,8 +465,8 @@ private: | |||
| 440 | u64 buffer_offset = 0; | 465 | u64 buffer_offset = 0; |
| 441 | u64 buffer_offset_base = 0; | 466 | u64 buffer_offset_base = 0; |
| 442 | 467 | ||
| 443 | using IntervalSet = boost::icl::interval_set<CacheAddr>; | 468 | using IntervalSet = boost::icl::interval_set<VAddr>; |
| 444 | using IntervalCache = boost::icl::interval_map<CacheAddr, MapInterval>; | 469 | using IntervalCache = boost::icl::interval_map<VAddr, MapInterval>; |
| 445 | using IntervalType = typename IntervalCache::interval_type; | 470 | using IntervalType = typename IntervalCache::interval_type; |
| 446 | IntervalCache mapped_addresses; | 471 | IntervalCache mapped_addresses; |
| 447 | 472 | ||
| @@ -456,6 +481,8 @@ private: | |||
| 456 | u64 epoch = 0; | 481 | u64 epoch = 0; |
| 457 | u64 modified_ticks = 0; | 482 | u64 modified_ticks = 0; |
| 458 | 483 | ||
| 484 | std::vector<u8> staging_buffer; | ||
| 485 | |||
| 459 | std::recursive_mutex mutex; | 486 | std::recursive_mutex mutex; |
| 460 | }; | 487 | }; |
| 461 | 488 | ||
diff --git a/src/video_core/buffer_cache/map_interval.h b/src/video_core/buffer_cache/map_interval.h index 3a104d5cd..b0956029d 100644 --- a/src/video_core/buffer_cache/map_interval.h +++ b/src/video_core/buffer_cache/map_interval.h | |||
| @@ -11,7 +11,7 @@ namespace VideoCommon { | |||
| 11 | 11 | ||
| 12 | class MapIntervalBase { | 12 | class MapIntervalBase { |
| 13 | public: | 13 | public: |
| 14 | MapIntervalBase(const CacheAddr start, const CacheAddr end, const GPUVAddr gpu_addr) | 14 | MapIntervalBase(const VAddr start, const VAddr end, const GPUVAddr gpu_addr) |
| 15 | : start{start}, end{end}, gpu_addr{gpu_addr} {} | 15 | : start{start}, end{end}, gpu_addr{gpu_addr} {} |
| 16 | 16 | ||
| 17 | void SetCpuAddress(VAddr new_cpu_addr) { | 17 | void SetCpuAddress(VAddr new_cpu_addr) { |
| @@ -26,7 +26,7 @@ public: | |||
| 26 | return gpu_addr; | 26 | return gpu_addr; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | bool IsInside(const CacheAddr other_start, const CacheAddr other_end) const { | 29 | bool IsInside(const VAddr other_start, const VAddr other_end) const { |
| 30 | return (start <= other_start && other_end <= end); | 30 | return (start <= other_start && other_end <= end); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| @@ -46,11 +46,11 @@ public: | |||
| 46 | return is_registered; | 46 | return is_registered; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | CacheAddr GetStart() const { | 49 | VAddr GetStart() const { |
| 50 | return start; | 50 | return start; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | CacheAddr GetEnd() const { | 53 | VAddr GetEnd() const { |
| 54 | return end; | 54 | return end; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| @@ -76,8 +76,8 @@ public: | |||
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | private: | 78 | private: |
| 79 | CacheAddr start; | 79 | VAddr start; |
| 80 | CacheAddr end; | 80 | VAddr end; |
| 81 | GPUVAddr gpu_addr; | 81 | GPUVAddr gpu_addr; |
| 82 | VAddr cpu_addr{}; | 82 | VAddr cpu_addr{}; |
| 83 | bool is_written{}; | 83 | bool is_written{}; |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index d24c9f657..4637ddabd 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -312,6 +312,35 @@ public: | |||
| 312 | } | 312 | } |
| 313 | }; | 313 | }; |
| 314 | 314 | ||
| 315 | struct MsaaSampleLocation { | ||
| 316 | union { | ||
| 317 | BitField<0, 4, u32> x0; | ||
| 318 | BitField<4, 4, u32> y0; | ||
| 319 | BitField<8, 4, u32> x1; | ||
| 320 | BitField<12, 4, u32> y1; | ||
| 321 | BitField<16, 4, u32> x2; | ||
| 322 | BitField<20, 4, u32> y2; | ||
| 323 | BitField<24, 4, u32> x3; | ||
| 324 | BitField<28, 4, u32> y3; | ||
| 325 | }; | ||
| 326 | |||
| 327 | constexpr std::pair<u32, u32> Location(int index) const { | ||
| 328 | switch (index) { | ||
| 329 | case 0: | ||
| 330 | return {x0, y0}; | ||
| 331 | case 1: | ||
| 332 | return {x1, y1}; | ||
| 333 | case 2: | ||
| 334 | return {x2, y2}; | ||
| 335 | case 3: | ||
| 336 | return {x3, y3}; | ||
| 337 | default: | ||
| 338 | UNREACHABLE(); | ||
| 339 | return {0, 0}; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | }; | ||
| 343 | |||
| 315 | enum class DepthMode : u32 { | 344 | enum class DepthMode : u32 { |
| 316 | MinusOneToOne = 0, | 345 | MinusOneToOne = 0, |
| 317 | ZeroToOne = 1, | 346 | ZeroToOne = 1, |
| @@ -793,7 +822,13 @@ public: | |||
| 793 | 822 | ||
| 794 | u32 rt_separate_frag_data; | 823 | u32 rt_separate_frag_data; |
| 795 | 824 | ||
| 796 | INSERT_UNION_PADDING_WORDS(0xC); | 825 | INSERT_UNION_PADDING_WORDS(0x1); |
| 826 | |||
| 827 | u32 multisample_raster_enable; | ||
| 828 | u32 multisample_raster_samples; | ||
| 829 | std::array<u32, 4> multisample_sample_mask; | ||
| 830 | |||
| 831 | INSERT_UNION_PADDING_WORDS(0x5); | ||
| 797 | 832 | ||
| 798 | struct { | 833 | struct { |
| 799 | u32 address_high; | 834 | u32 address_high; |
| @@ -830,7 +865,16 @@ public: | |||
| 830 | 865 | ||
| 831 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; | 866 | std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format; |
| 832 | 867 | ||
| 833 | INSERT_UNION_PADDING_WORDS(0xF); | 868 | std::array<MsaaSampleLocation, 4> multisample_sample_locations; |
| 869 | |||
| 870 | INSERT_UNION_PADDING_WORDS(0x2); | ||
| 871 | |||
| 872 | union { | ||
| 873 | BitField<0, 1, u32> enable; | ||
| 874 | BitField<4, 3, u32> target; | ||
| 875 | } multisample_coverage_to_color; | ||
| 876 | |||
| 877 | INSERT_UNION_PADDING_WORDS(0x8); | ||
| 834 | 878 | ||
| 835 | struct { | 879 | struct { |
| 836 | union { | 880 | union { |
| @@ -943,7 +987,7 @@ public: | |||
| 943 | 987 | ||
| 944 | CounterReset counter_reset; | 988 | CounterReset counter_reset; |
| 945 | 989 | ||
| 946 | INSERT_UNION_PADDING_WORDS(0x1); | 990 | u32 multisample_enable; |
| 947 | 991 | ||
| 948 | u32 zeta_enable; | 992 | u32 zeta_enable; |
| 949 | 993 | ||
| @@ -1007,7 +1051,11 @@ public: | |||
| 1007 | 1051 | ||
| 1008 | float polygon_offset_units; | 1052 | float polygon_offset_units; |
| 1009 | 1053 | ||
| 1010 | INSERT_UNION_PADDING_WORDS(0x11); | 1054 | INSERT_UNION_PADDING_WORDS(0x4); |
| 1055 | |||
| 1056 | Tegra::Texture::MsaaMode multisample_mode; | ||
| 1057 | |||
| 1058 | INSERT_UNION_PADDING_WORDS(0xC); | ||
| 1011 | 1059 | ||
| 1012 | union { | 1060 | union { |
| 1013 | BitField<2, 1, u32> coord_origin; | 1061 | BitField<2, 1, u32> coord_origin; |
| @@ -1507,12 +1555,17 @@ ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); | |||
| 1507 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); | 1555 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); |
| 1508 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); | 1556 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); |
| 1509 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); | 1557 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); |
| 1510 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); | ||
| 1511 | ASSERT_REG_POSITION(depth_bounds, 0x3E7); | 1558 | ASSERT_REG_POSITION(depth_bounds, 0x3E7); |
| 1559 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); | ||
| 1560 | ASSERT_REG_POSITION(multisample_raster_enable, 0x3ED); | ||
| 1561 | ASSERT_REG_POSITION(multisample_raster_samples, 0x3EE); | ||
| 1562 | ASSERT_REG_POSITION(multisample_sample_mask, 0x3EF); | ||
| 1512 | ASSERT_REG_POSITION(zeta, 0x3F8); | 1563 | ASSERT_REG_POSITION(zeta, 0x3F8); |
| 1513 | ASSERT_REG_POSITION(clear_flags, 0x43E); | 1564 | ASSERT_REG_POSITION(clear_flags, 0x43E); |
| 1514 | ASSERT_REG_POSITION(fill_rectangle, 0x44F); | 1565 | ASSERT_REG_POSITION(fill_rectangle, 0x44F); |
| 1515 | ASSERT_REG_POSITION(vertex_attrib_format, 0x458); | 1566 | ASSERT_REG_POSITION(vertex_attrib_format, 0x458); |
| 1567 | ASSERT_REG_POSITION(multisample_sample_locations, 0x478); | ||
| 1568 | ASSERT_REG_POSITION(multisample_coverage_to_color, 0x47E); | ||
| 1516 | ASSERT_REG_POSITION(rt_control, 0x487); | 1569 | ASSERT_REG_POSITION(rt_control, 0x487); |
| 1517 | ASSERT_REG_POSITION(zeta_width, 0x48a); | 1570 | ASSERT_REG_POSITION(zeta_width, 0x48a); |
| 1518 | ASSERT_REG_POSITION(zeta_height, 0x48b); | 1571 | ASSERT_REG_POSITION(zeta_height, 0x48b); |
| @@ -1545,11 +1598,12 @@ ASSERT_REG_POSITION(samplecnt_enable, 0x545); | |||
| 1545 | ASSERT_REG_POSITION(point_size, 0x546); | 1598 | ASSERT_REG_POSITION(point_size, 0x546); |
| 1546 | ASSERT_REG_POSITION(point_sprite_enable, 0x548); | 1599 | ASSERT_REG_POSITION(point_sprite_enable, 0x548); |
| 1547 | ASSERT_REG_POSITION(counter_reset, 0x54C); | 1600 | ASSERT_REG_POSITION(counter_reset, 0x54C); |
| 1601 | ASSERT_REG_POSITION(multisample_enable, 0x54D); | ||
| 1548 | ASSERT_REG_POSITION(zeta_enable, 0x54E); | 1602 | ASSERT_REG_POSITION(zeta_enable, 0x54E); |
| 1549 | ASSERT_REG_POSITION(multisample_control, 0x54F); | 1603 | ASSERT_REG_POSITION(multisample_control, 0x54F); |
| 1550 | ASSERT_REG_POSITION(condition, 0x554); | 1604 | ASSERT_REG_POSITION(condition, 0x554); |
| 1551 | ASSERT_REG_POSITION(tsc, 0x557); | 1605 | ASSERT_REG_POSITION(tsc, 0x557); |
| 1552 | ASSERT_REG_POSITION(polygon_offset_factor, 0x55b); | 1606 | ASSERT_REG_POSITION(polygon_offset_factor, 0x55B); |
| 1553 | ASSERT_REG_POSITION(tic, 0x55D); | 1607 | ASSERT_REG_POSITION(tic, 0x55D); |
| 1554 | ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); | 1608 | ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); |
| 1555 | ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); | 1609 | ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); |
| @@ -1558,6 +1612,7 @@ ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); | |||
| 1558 | ASSERT_REG_POSITION(stencil_back_func_func, 0x569); | 1612 | ASSERT_REG_POSITION(stencil_back_func_func, 0x569); |
| 1559 | ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); | 1613 | ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); |
| 1560 | ASSERT_REG_POSITION(polygon_offset_units, 0x56F); | 1614 | ASSERT_REG_POSITION(polygon_offset_units, 0x56F); |
| 1615 | ASSERT_REG_POSITION(multisample_mode, 0x574); | ||
| 1561 | ASSERT_REG_POSITION(point_coord_replace, 0x581); | 1616 | ASSERT_REG_POSITION(point_coord_replace, 0x581); |
| 1562 | ASSERT_REG_POSITION(code_address, 0x582); | 1617 | ASSERT_REG_POSITION(code_address, 0x582); |
| 1563 | ASSERT_REG_POSITION(draw, 0x585); | 1618 | ASSERT_REG_POSITION(draw, 0x585); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 7400e1aa9..c66c66f6c 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -231,18 +231,6 @@ enum class AtomicOp : u64 { | |||
| 231 | Or = 6, | 231 | Or = 6, |
| 232 | Xor = 7, | 232 | Xor = 7, |
| 233 | Exch = 8, | 233 | Exch = 8, |
| 234 | }; | ||
| 235 | |||
| 236 | enum class GlobalAtomicOp : u64 { | ||
| 237 | Add = 0, | ||
| 238 | Min = 1, | ||
| 239 | Max = 2, | ||
| 240 | Inc = 3, | ||
| 241 | Dec = 4, | ||
| 242 | And = 5, | ||
| 243 | Or = 6, | ||
| 244 | Xor = 7, | ||
| 245 | Exch = 8, | ||
| 246 | SafeAdd = 10, | 234 | SafeAdd = 10, |
| 247 | }; | 235 | }; |
| 248 | 236 | ||
| @@ -1018,7 +1006,7 @@ union Instruction { | |||
| 1018 | } stg; | 1006 | } stg; |
| 1019 | 1007 | ||
| 1020 | union { | 1008 | union { |
| 1021 | BitField<52, 4, GlobalAtomicOp> operation; | 1009 | BitField<52, 4, AtomicOp> operation; |
| 1022 | BitField<49, 3, GlobalAtomicType> type; | 1010 | BitField<49, 3, GlobalAtomicType> type; |
| 1023 | BitField<28, 20, s64> offset; | 1011 | BitField<28, 20, s64> offset; |
| 1024 | } atom; | 1012 | } atom; |
| @@ -1777,6 +1765,7 @@ public: | |||
| 1777 | BRK, | 1765 | BRK, |
| 1778 | DEPBAR, | 1766 | DEPBAR, |
| 1779 | VOTE, | 1767 | VOTE, |
| 1768 | VOTE_VTG, | ||
| 1780 | SHFL, | 1769 | SHFL, |
| 1781 | FSWZADD, | 1770 | FSWZADD, |
| 1782 | BFE_C, | 1771 | BFE_C, |
| @@ -1823,6 +1812,7 @@ public: | |||
| 1823 | IPA, | 1812 | IPA, |
| 1824 | OUT_R, // Emit vertex/primitive | 1813 | OUT_R, // Emit vertex/primitive |
| 1825 | ISBERD, | 1814 | ISBERD, |
| 1815 | BAR, | ||
| 1826 | MEMBAR, | 1816 | MEMBAR, |
| 1827 | VMAD, | 1817 | VMAD, |
| 1828 | VSETP, | 1818 | VSETP, |
| @@ -1908,7 +1898,7 @@ public: | |||
| 1908 | MOV_C, | 1898 | MOV_C, |
| 1909 | MOV_R, | 1899 | MOV_R, |
| 1910 | MOV_IMM, | 1900 | MOV_IMM, |
| 1911 | MOV_SYS, | 1901 | S2R, |
| 1912 | MOV32_IMM, | 1902 | MOV32_IMM, |
| 1913 | SHL_C, | 1903 | SHL_C, |
| 1914 | SHL_R, | 1904 | SHL_R, |
| @@ -2092,6 +2082,7 @@ private: | |||
| 2092 | INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"), | 2082 | INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"), |
| 2093 | INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), | 2083 | INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), |
| 2094 | INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"), | 2084 | INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"), |
| 2085 | INST("0101000011100---", Id::VOTE_VTG, Type::Warp, "VOTE_VTG"), | ||
| 2095 | INST("1110111100010---", Id::SHFL, Type::Warp, "SHFL"), | 2086 | INST("1110111100010---", Id::SHFL, Type::Warp, "SHFL"), |
| 2096 | INST("0101000011111---", Id::FSWZADD, Type::Warp, "FSWZADD"), | 2087 | INST("0101000011111---", Id::FSWZADD, Type::Warp, "FSWZADD"), |
| 2097 | INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), | 2088 | INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), |
| @@ -2129,6 +2120,7 @@ private: | |||
| 2129 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), | 2120 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), |
| 2130 | INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), | 2121 | INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), |
| 2131 | INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), | 2122 | INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), |
| 2123 | INST("1111000010101---", Id::BAR, Type::Trivial, "BAR"), | ||
| 2132 | INST("1110111110011---", Id::MEMBAR, Type::Trivial, "MEMBAR"), | 2124 | INST("1110111110011---", Id::MEMBAR, Type::Trivial, "MEMBAR"), |
| 2133 | INST("01011111--------", Id::VMAD, Type::Video, "VMAD"), | 2125 | INST("01011111--------", Id::VMAD, Type::Video, "VMAD"), |
| 2134 | INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"), | 2126 | INST("0101000011110---", Id::VSETP, Type::Video, "VSETP"), |
| @@ -2201,7 +2193,7 @@ private: | |||
| 2201 | INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), | 2193 | INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), |
| 2202 | INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), | 2194 | INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), |
| 2203 | INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), | 2195 | INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), |
| 2204 | INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"), | 2196 | INST("1111000011001---", Id::S2R, Type::Trivial, "S2R"), |
| 2205 | INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), | 2197 | INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), |
| 2206 | INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), | 2198 | INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), |
| 2207 | INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), | 2199 | INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), |
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h index bc80661d8..72e2a33d5 100644 --- a/src/video_core/engines/shader_header.h +++ b/src/video_core/engines/shader_header.h | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <optional> | ||
| 9 | |||
| 7 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 8 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| @@ -16,7 +19,7 @@ enum class OutputTopology : u32 { | |||
| 16 | TriangleStrip = 7, | 19 | TriangleStrip = 7, |
| 17 | }; | 20 | }; |
| 18 | 21 | ||
| 19 | enum class AttributeUse : u8 { | 22 | enum class PixelImap : u8 { |
| 20 | Unused = 0, | 23 | Unused = 0, |
| 21 | Constant = 1, | 24 | Constant = 1, |
| 22 | Perspective = 2, | 25 | Perspective = 2, |
| @@ -24,7 +27,7 @@ enum class AttributeUse : u8 { | |||
| 24 | }; | 27 | }; |
| 25 | 28 | ||
| 26 | // Documentation in: | 29 | // Documentation in: |
| 27 | // http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture | 30 | // http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html |
| 28 | struct Header { | 31 | struct Header { |
| 29 | union { | 32 | union { |
| 30 | BitField<0, 5, u32> sph_type; | 33 | BitField<0, 5, u32> sph_type; |
| @@ -59,8 +62,8 @@ struct Header { | |||
| 59 | union { | 62 | union { |
| 60 | BitField<0, 12, u32> max_output_vertices; | 63 | BitField<0, 12, u32> max_output_vertices; |
| 61 | BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders. | 64 | BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders. |
| 62 | BitField<24, 4, u32> reserved; | 65 | BitField<20, 4, u32> reserved; |
| 63 | BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders. | 66 | BitField<24, 8, u32> store_req_end; // NOTE: not used by geometry shaders. |
| 64 | } common4{}; | 67 | } common4{}; |
| 65 | 68 | ||
| 66 | union { | 69 | union { |
| @@ -93,17 +96,20 @@ struct Header { | |||
| 93 | struct { | 96 | struct { |
| 94 | INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA | 97 | INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA |
| 95 | INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB | 98 | INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB |
| 99 | |||
| 96 | union { | 100 | union { |
| 97 | BitField<0, 2, AttributeUse> x; | 101 | BitField<0, 2, PixelImap> x; |
| 98 | BitField<2, 2, AttributeUse> y; | 102 | BitField<2, 2, PixelImap> y; |
| 99 | BitField<4, 2, AttributeUse> w; | 103 | BitField<4, 2, PixelImap> z; |
| 100 | BitField<6, 2, AttributeUse> z; | 104 | BitField<6, 2, PixelImap> w; |
| 101 | u8 raw; | 105 | u8 raw; |
| 102 | } imap_generic_vector[32]; | 106 | } imap_generic_vector[32]; |
| 107 | |||
| 103 | INSERT_UNION_PADDING_BYTES(2); // ImapColor | 108 | INSERT_UNION_PADDING_BYTES(2); // ImapColor |
| 104 | INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC | 109 | INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC |
| 105 | INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10] | 110 | INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10] |
| 106 | INSERT_UNION_PADDING_BYTES(2); // ImapReserved | 111 | INSERT_UNION_PADDING_BYTES(2); // ImapReserved |
| 112 | |||
| 107 | struct { | 113 | struct { |
| 108 | u32 target; | 114 | u32 target; |
| 109 | union { | 115 | union { |
| @@ -112,31 +118,30 @@ struct Header { | |||
| 112 | BitField<2, 30, u32> reserved; | 118 | BitField<2, 30, u32> reserved; |
| 113 | }; | 119 | }; |
| 114 | } omap; | 120 | } omap; |
| 121 | |||
| 115 | bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { | 122 | bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { |
| 116 | const u32 bit = render_target * 4 + component; | 123 | const u32 bit = render_target * 4 + component; |
| 117 | return omap.target & (1 << bit); | 124 | return omap.target & (1 << bit); |
| 118 | } | 125 | } |
| 119 | AttributeUse GetAttributeIndexUse(u32 attribute, u32 index) const { | 126 | |
| 120 | return static_cast<AttributeUse>( | 127 | PixelImap GetPixelImap(u32 attribute) const { |
| 121 | (imap_generic_vector[attribute].raw >> (index * 2)) & 0x03); | 128 | const auto get_index = [this, attribute](u32 index) { |
| 122 | } | 129 | return static_cast<PixelImap>( |
| 123 | AttributeUse GetAttributeUse(u32 attribute) const { | 130 | (imap_generic_vector[attribute].raw >> (index * 2)) & 3); |
| 124 | AttributeUse result = AttributeUse::Unused; | 131 | }; |
| 125 | for (u32 i = 0; i < 4; i++) { | 132 | |
| 126 | const auto index = GetAttributeIndexUse(attribute, i); | 133 | std::optional<PixelImap> result; |
| 127 | if (index == AttributeUse::Unused) { | 134 | for (u32 component = 0; component < 4; ++component) { |
| 128 | continue; | 135 | const PixelImap index = get_index(component); |
| 129 | } | 136 | if (index == PixelImap::Unused) { |
| 130 | if (result == AttributeUse::Unused || result == index) { | ||
| 131 | result = index; | ||
| 132 | continue; | 137 | continue; |
| 133 | } | 138 | } |
| 134 | LOG_CRITICAL(HW_GPU, "Generic Attribute Conflict in Interpolation Mode"); | 139 | if (result && result != index) { |
| 135 | if (index == AttributeUse::Perspective) { | 140 | LOG_CRITICAL(HW_GPU, "Generic attribute conflict in interpolation mode"); |
| 136 | result = index; | ||
| 137 | } | 141 | } |
| 142 | result = index; | ||
| 138 | } | 143 | } |
| 139 | return result; | 144 | return result.value_or(PixelImap::Unused); |
| 140 | } | 145 | } |
| 141 | } ps; | 146 | } ps; |
| 142 | 147 | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index e8f763ce9..8acf2eda2 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/core_timing_util.h" | 9 | #include "core/core_timing_util.h" |
| 10 | #include "core/frontend/emu_window.h" | ||
| 10 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 11 | #include "video_core/engines/fermi_2d.h" | 12 | #include "video_core/engines/fermi_2d.h" |
| 12 | #include "video_core/engines/kepler_compute.h" | 13 | #include "video_core/engines/kepler_compute.h" |
| @@ -16,14 +17,15 @@ | |||
| 16 | #include "video_core/gpu.h" | 17 | #include "video_core/gpu.h" |
| 17 | #include "video_core/memory_manager.h" | 18 | #include "video_core/memory_manager.h" |
| 18 | #include "video_core/renderer_base.h" | 19 | #include "video_core/renderer_base.h" |
| 20 | #include "video_core/video_core.h" | ||
| 19 | 21 | ||
| 20 | namespace Tegra { | 22 | namespace Tegra { |
| 21 | 23 | ||
| 22 | MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); | 24 | MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); |
| 23 | 25 | ||
| 24 | GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) | 26 | GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async) |
| 25 | : system{system}, renderer{renderer}, is_async{is_async} { | 27 | : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { |
| 26 | auto& rasterizer{renderer.Rasterizer()}; | 28 | auto& rasterizer{renderer->Rasterizer()}; |
| 27 | memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); | 29 | memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); |
| 28 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); | 30 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); |
| 29 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); | 31 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); |
| @@ -137,7 +139,7 @@ u64 GPU::GetTicks() const { | |||
| 137 | } | 139 | } |
| 138 | 140 | ||
| 139 | void GPU::FlushCommands() { | 141 | void GPU::FlushCommands() { |
| 140 | renderer.Rasterizer().FlushCommands(); | 142 | renderer->Rasterizer().FlushCommands(); |
| 141 | } | 143 | } |
| 142 | 144 | ||
| 143 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence | 145 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 64acb17df..1a2d747be 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -25,8 +25,11 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) { | |||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | namespace Core { | 27 | namespace Core { |
| 28 | class System; | 28 | namespace Frontend { |
| 29 | class EmuWindow; | ||
| 29 | } | 30 | } |
| 31 | class System; | ||
| 32 | } // namespace Core | ||
| 30 | 33 | ||
| 31 | namespace VideoCore { | 34 | namespace VideoCore { |
| 32 | class RendererBase; | 35 | class RendererBase; |
| @@ -129,7 +132,8 @@ class MemoryManager; | |||
| 129 | 132 | ||
| 130 | class GPU { | 133 | class GPU { |
| 131 | public: | 134 | public: |
| 132 | explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async); | 135 | explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 136 | bool is_async); | ||
| 133 | 137 | ||
| 134 | virtual ~GPU(); | 138 | virtual ~GPU(); |
| 135 | 139 | ||
| @@ -174,6 +178,14 @@ public: | |||
| 174 | /// Returns a reference to the GPU DMA pusher. | 178 | /// Returns a reference to the GPU DMA pusher. |
| 175 | Tegra::DmaPusher& DmaPusher(); | 179 | Tegra::DmaPusher& DmaPusher(); |
| 176 | 180 | ||
| 181 | VideoCore::RendererBase& Renderer() { | ||
| 182 | return *renderer; | ||
| 183 | } | ||
| 184 | |||
| 185 | const VideoCore::RendererBase& Renderer() const { | ||
| 186 | return *renderer; | ||
| 187 | } | ||
| 188 | |||
| 177 | // Waits for the GPU to finish working | 189 | // Waits for the GPU to finish working |
| 178 | virtual void WaitIdle() const = 0; | 190 | virtual void WaitIdle() const = 0; |
| 179 | 191 | ||
| @@ -258,13 +270,13 @@ public: | |||
| 258 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; | 270 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; |
| 259 | 271 | ||
| 260 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 272 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 261 | virtual void FlushRegion(CacheAddr addr, u64 size) = 0; | 273 | virtual void FlushRegion(VAddr addr, u64 size) = 0; |
| 262 | 274 | ||
| 263 | /// Notify rasterizer that any caches of the specified region should be invalidated | 275 | /// Notify rasterizer that any caches of the specified region should be invalidated |
| 264 | virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0; | 276 | virtual void InvalidateRegion(VAddr addr, u64 size) = 0; |
| 265 | 277 | ||
| 266 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | 278 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated |
| 267 | virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0; | 279 | virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; |
| 268 | 280 | ||
| 269 | protected: | 281 | protected: |
| 270 | virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0; | 282 | virtual void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const = 0; |
| @@ -287,7 +299,7 @@ private: | |||
| 287 | protected: | 299 | protected: |
| 288 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; | 300 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; |
| 289 | Core::System& system; | 301 | Core::System& system; |
| 290 | VideoCore::RendererBase& renderer; | 302 | std::unique_ptr<VideoCore::RendererBase> renderer; |
| 291 | 303 | ||
| 292 | private: | 304 | private: |
| 293 | std::unique_ptr<Tegra::MemoryManager> memory_manager; | 305 | std::unique_ptr<Tegra::MemoryManager> memory_manager; |
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 04222d060..cc434faf7 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp | |||
| @@ -10,13 +10,16 @@ | |||
| 10 | 10 | ||
| 11 | namespace VideoCommon { | 11 | namespace VideoCommon { |
| 12 | 12 | ||
| 13 | GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) | 13 | GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, |
| 14 | : GPU(system, renderer, true), gpu_thread{system} {} | 14 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context) |
| 15 | : GPU(system, std::move(renderer_), true), gpu_thread{system}, gpu_context(std::move(context)), | ||
| 16 | cpu_context(renderer->GetRenderWindow().CreateSharedContext()) {} | ||
| 15 | 17 | ||
| 16 | GPUAsynch::~GPUAsynch() = default; | 18 | GPUAsynch::~GPUAsynch() = default; |
| 17 | 19 | ||
| 18 | void GPUAsynch::Start() { | 20 | void GPUAsynch::Start() { |
| 19 | gpu_thread.StartThread(renderer, *dma_pusher); | 21 | cpu_context->MakeCurrent(); |
| 22 | gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher); | ||
| 20 | } | 23 | } |
| 21 | 24 | ||
| 22 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { | 25 | void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { |
| @@ -27,15 +30,15 @@ void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 27 | gpu_thread.SwapBuffers(framebuffer); | 30 | gpu_thread.SwapBuffers(framebuffer); |
| 28 | } | 31 | } |
| 29 | 32 | ||
| 30 | void GPUAsynch::FlushRegion(CacheAddr addr, u64 size) { | 33 | void GPUAsynch::FlushRegion(VAddr addr, u64 size) { |
| 31 | gpu_thread.FlushRegion(addr, size); | 34 | gpu_thread.FlushRegion(addr, size); |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | void GPUAsynch::InvalidateRegion(CacheAddr addr, u64 size) { | 37 | void GPUAsynch::InvalidateRegion(VAddr addr, u64 size) { |
| 35 | gpu_thread.InvalidateRegion(addr, size); | 38 | gpu_thread.InvalidateRegion(addr, size); |
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | void GPUAsynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 41 | void GPUAsynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 39 | gpu_thread.FlushAndInvalidateRegion(addr, size); | 42 | gpu_thread.FlushAndInvalidateRegion(addr, size); |
| 40 | } | 43 | } |
| 41 | 44 | ||
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 1241ade1d..03fd0eef0 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h | |||
| @@ -7,6 +7,10 @@ | |||
| 7 | #include "video_core/gpu.h" | 7 | #include "video_core/gpu.h" |
| 8 | #include "video_core/gpu_thread.h" | 8 | #include "video_core/gpu_thread.h" |
| 9 | 9 | ||
| 10 | namespace Core::Frontend { | ||
| 11 | class GraphicsContext; | ||
| 12 | } | ||
| 13 | |||
| 10 | namespace VideoCore { | 14 | namespace VideoCore { |
| 11 | class RendererBase; | 15 | class RendererBase; |
| 12 | } // namespace VideoCore | 16 | } // namespace VideoCore |
| @@ -16,15 +20,16 @@ namespace VideoCommon { | |||
| 16 | /// Implementation of GPU interface that runs the GPU asynchronously | 20 | /// Implementation of GPU interface that runs the GPU asynchronously |
| 17 | class GPUAsynch final : public Tegra::GPU { | 21 | class GPUAsynch final : public Tegra::GPU { |
| 18 | public: | 22 | public: |
| 19 | explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer); | 23 | explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 24 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context); | ||
| 20 | ~GPUAsynch() override; | 25 | ~GPUAsynch() override; |
| 21 | 26 | ||
| 22 | void Start() override; | 27 | void Start() override; |
| 23 | void PushGPUEntries(Tegra::CommandList&& entries) override; | 28 | void PushGPUEntries(Tegra::CommandList&& entries) override; |
| 24 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 29 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 25 | void FlushRegion(CacheAddr addr, u64 size) override; | 30 | void FlushRegion(VAddr addr, u64 size) override; |
| 26 | void InvalidateRegion(CacheAddr addr, u64 size) override; | 31 | void InvalidateRegion(VAddr addr, u64 size) override; |
| 27 | void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; | 32 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 28 | void WaitIdle() const override; | 33 | void WaitIdle() const override; |
| 29 | 34 | ||
| 30 | protected: | 35 | protected: |
| @@ -32,6 +37,8 @@ protected: | |||
| 32 | 37 | ||
| 33 | private: | 38 | private: |
| 34 | GPUThread::ThreadManager gpu_thread; | 39 | GPUThread::ThreadManager gpu_thread; |
| 40 | std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; | ||
| 41 | std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context; | ||
| 35 | }; | 42 | }; |
| 36 | 43 | ||
| 37 | } // namespace VideoCommon | 44 | } // namespace VideoCommon |
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index d48221077..6f38a672a 100644 --- a/src/video_core/gpu_synch.cpp +++ b/src/video_core/gpu_synch.cpp | |||
| @@ -7,12 +7,15 @@ | |||
| 7 | 7 | ||
| 8 | namespace VideoCommon { | 8 | namespace VideoCommon { |
| 9 | 9 | ||
| 10 | GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) | 10 | GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 11 | : GPU(system, renderer, false) {} | 11 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context) |
| 12 | : GPU(system, std::move(renderer), false), context{std::move(context)} {} | ||
| 12 | 13 | ||
| 13 | GPUSynch::~GPUSynch() = default; | 14 | GPUSynch::~GPUSynch() = default; |
| 14 | 15 | ||
| 15 | void GPUSynch::Start() {} | 16 | void GPUSynch::Start() { |
| 17 | context->MakeCurrent(); | ||
| 18 | } | ||
| 16 | 19 | ||
| 17 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | 20 | void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { |
| 18 | dma_pusher->Push(std::move(entries)); | 21 | dma_pusher->Push(std::move(entries)); |
| @@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) { | |||
| 20 | } | 23 | } |
| 21 | 24 | ||
| 22 | void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 25 | void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 23 | renderer.SwapBuffers(framebuffer); | 26 | renderer->SwapBuffers(framebuffer); |
| 24 | } | 27 | } |
| 25 | 28 | ||
| 26 | void GPUSynch::FlushRegion(CacheAddr addr, u64 size) { | 29 | void GPUSynch::FlushRegion(VAddr addr, u64 size) { |
| 27 | renderer.Rasterizer().FlushRegion(addr, size); | 30 | renderer->Rasterizer().FlushRegion(addr, size); |
| 28 | } | 31 | } |
| 29 | 32 | ||
| 30 | void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) { | 33 | void GPUSynch::InvalidateRegion(VAddr addr, u64 size) { |
| 31 | renderer.Rasterizer().InvalidateRegion(addr, size); | 34 | renderer->Rasterizer().InvalidateRegion(addr, size); |
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 37 | void GPUSynch::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 35 | renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); | 38 | renderer->Rasterizer().FlushAndInvalidateRegion(addr, size); |
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | } // namespace VideoCommon | 41 | } // namespace VideoCommon |
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index c71baee89..4a6e9a01d 100644 --- a/src/video_core/gpu_synch.h +++ b/src/video_core/gpu_synch.h | |||
| @@ -6,6 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include "video_core/gpu.h" | 7 | #include "video_core/gpu.h" |
| 8 | 8 | ||
| 9 | namespace Core::Frontend { | ||
| 10 | class GraphicsContext; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace VideoCore { | 13 | namespace VideoCore { |
| 10 | class RendererBase; | 14 | class RendererBase; |
| 11 | } // namespace VideoCore | 15 | } // namespace VideoCore |
| @@ -15,20 +19,24 @@ namespace VideoCommon { | |||
| 15 | /// Implementation of GPU interface that runs the GPU synchronously | 19 | /// Implementation of GPU interface that runs the GPU synchronously |
| 16 | class GPUSynch final : public Tegra::GPU { | 20 | class GPUSynch final : public Tegra::GPU { |
| 17 | public: | 21 | public: |
| 18 | explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer); | 22 | explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, |
| 23 | std::unique_ptr<Core::Frontend::GraphicsContext>&& context); | ||
| 19 | ~GPUSynch() override; | 24 | ~GPUSynch() override; |
| 20 | 25 | ||
| 21 | void Start() override; | 26 | void Start() override; |
| 22 | void PushGPUEntries(Tegra::CommandList&& entries) override; | 27 | void PushGPUEntries(Tegra::CommandList&& entries) override; |
| 23 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 28 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 24 | void FlushRegion(CacheAddr addr, u64 size) override; | 29 | void FlushRegion(VAddr addr, u64 size) override; |
| 25 | void InvalidateRegion(CacheAddr addr, u64 size) override; | 30 | void InvalidateRegion(VAddr addr, u64 size) override; |
| 26 | void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; | 31 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 27 | void WaitIdle() const override {} | 32 | void WaitIdle() const override {} |
| 28 | 33 | ||
| 29 | protected: | 34 | protected: |
| 30 | void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, | 35 | void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id, |
| 31 | [[maybe_unused]] u32 value) const override {} | 36 | [[maybe_unused]] u32 value) const override {} |
| 37 | |||
| 38 | private: | ||
| 39 | std::unique_ptr<Core::Frontend::GraphicsContext> context; | ||
| 32 | }; | 40 | }; |
| 33 | 41 | ||
| 34 | } // namespace VideoCommon | 42 | } // namespace VideoCommon |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index b1088af3d..10cda686b 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/frontend/scope_acquire_context.h" | 8 | #include "core/frontend/emu_window.h" |
| 9 | #include "video_core/dma_pusher.h" | 9 | #include "video_core/dma_pusher.h" |
| 10 | #include "video_core/gpu.h" | 10 | #include "video_core/gpu.h" |
| 11 | #include "video_core/gpu_thread.h" | 11 | #include "video_core/gpu_thread.h" |
| @@ -14,8 +14,8 @@ | |||
| 14 | namespace VideoCommon::GPUThread { | 14 | namespace VideoCommon::GPUThread { |
| 15 | 15 | ||
| 16 | /// Runs the GPU thread | 16 | /// Runs the GPU thread |
| 17 | static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, | 17 | static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, |
| 18 | SynchState& state) { | 18 | Tegra::DmaPusher& dma_pusher, SynchState& state) { |
| 19 | MicroProfileOnThreadCreate("GpuThread"); | 19 | MicroProfileOnThreadCreate("GpuThread"); |
| 20 | 20 | ||
| 21 | // Wait for first GPU command before acquiring the window context | 21 | // Wait for first GPU command before acquiring the window context |
| @@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p | |||
| 27 | return; | 27 | return; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()}; | 30 | auto current_context = context.Acquire(); |
| 31 | 31 | ||
| 32 | CommandDataContainer next; | 32 | CommandDataContainer next; |
| 33 | while (state.is_running) { | 33 | while (state.is_running) { |
| @@ -62,8 +62,11 @@ ThreadManager::~ThreadManager() { | |||
| 62 | thread.join(); | 62 | thread.join(); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) { | 65 | void ThreadManager::StartThread(VideoCore::RendererBase& renderer, |
| 66 | thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)}; | 66 | Core::Frontend::GraphicsContext& context, |
| 67 | Tegra::DmaPusher& dma_pusher) { | ||
| 68 | thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher), | ||
| 69 | std::ref(state)}; | ||
| 67 | } | 70 | } |
| 68 | 71 | ||
| 69 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { | 72 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { |
| @@ -74,15 +77,15 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 74 | PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt)); | 77 | PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt)); |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 77 | void ThreadManager::FlushRegion(CacheAddr addr, u64 size) { | 80 | void ThreadManager::FlushRegion(VAddr addr, u64 size) { |
| 78 | PushCommand(FlushRegionCommand(addr, size)); | 81 | PushCommand(FlushRegionCommand(addr, size)); |
| 79 | } | 82 | } |
| 80 | 83 | ||
| 81 | void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) { | 84 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { |
| 82 | system.Renderer().Rasterizer().InvalidateRegion(addr, size); | 85 | system.Renderer().Rasterizer().InvalidateRegion(addr, size); |
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 88 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 86 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important | 89 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important |
| 87 | InvalidateRegion(addr, size); | 90 | InvalidateRegion(addr, size); |
| 88 | } | 91 | } |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 882e2d9c7..cd74ad330 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include <optional> | 10 | #include <optional> |
| 11 | #include <thread> | 11 | #include <thread> |
| 12 | #include <variant> | 12 | #include <variant> |
| 13 | |||
| 14 | #include "common/threadsafe_queue.h" | 13 | #include "common/threadsafe_queue.h" |
| 15 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 16 | 15 | ||
| @@ -20,6 +19,9 @@ class DmaPusher; | |||
| 20 | } // namespace Tegra | 19 | } // namespace Tegra |
| 21 | 20 | ||
| 22 | namespace Core { | 21 | namespace Core { |
| 22 | namespace Frontend { | ||
| 23 | class GraphicsContext; | ||
| 24 | } | ||
| 23 | class System; | 25 | class System; |
| 24 | } // namespace Core | 26 | } // namespace Core |
| 25 | 27 | ||
| @@ -45,26 +47,26 @@ struct SwapBuffersCommand final { | |||
| 45 | 47 | ||
| 46 | /// Command to signal to the GPU thread to flush a region | 48 | /// Command to signal to the GPU thread to flush a region |
| 47 | struct FlushRegionCommand final { | 49 | struct FlushRegionCommand final { |
| 48 | explicit constexpr FlushRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {} | 50 | explicit constexpr FlushRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} |
| 49 | 51 | ||
| 50 | CacheAddr addr; | 52 | VAddr addr; |
| 51 | u64 size; | 53 | u64 size; |
| 52 | }; | 54 | }; |
| 53 | 55 | ||
| 54 | /// Command to signal to the GPU thread to invalidate a region | 56 | /// Command to signal to the GPU thread to invalidate a region |
| 55 | struct InvalidateRegionCommand final { | 57 | struct InvalidateRegionCommand final { |
| 56 | explicit constexpr InvalidateRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {} | 58 | explicit constexpr InvalidateRegionCommand(VAddr addr, u64 size) : addr{addr}, size{size} {} |
| 57 | 59 | ||
| 58 | CacheAddr addr; | 60 | VAddr addr; |
| 59 | u64 size; | 61 | u64 size; |
| 60 | }; | 62 | }; |
| 61 | 63 | ||
| 62 | /// Command to signal to the GPU thread to flush and invalidate a region | 64 | /// Command to signal to the GPU thread to flush and invalidate a region |
| 63 | struct FlushAndInvalidateRegionCommand final { | 65 | struct FlushAndInvalidateRegionCommand final { |
| 64 | explicit constexpr FlushAndInvalidateRegionCommand(CacheAddr addr, u64 size) | 66 | explicit constexpr FlushAndInvalidateRegionCommand(VAddr addr, u64 size) |
| 65 | : addr{addr}, size{size} {} | 67 | : addr{addr}, size{size} {} |
| 66 | 68 | ||
| 67 | CacheAddr addr; | 69 | VAddr addr; |
| 68 | u64 size; | 70 | u64 size; |
| 69 | }; | 71 | }; |
| 70 | 72 | ||
| @@ -99,7 +101,8 @@ public: | |||
| 99 | ~ThreadManager(); | 101 | ~ThreadManager(); |
| 100 | 102 | ||
| 101 | /// Creates and starts the GPU thread. | 103 | /// Creates and starts the GPU thread. |
| 102 | void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); | 104 | void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, |
| 105 | Tegra::DmaPusher& dma_pusher); | ||
| 103 | 106 | ||
| 104 | /// Push GPU command entries to be processed | 107 | /// Push GPU command entries to be processed |
| 105 | void SubmitList(Tegra::CommandList&& entries); | 108 | void SubmitList(Tegra::CommandList&& entries); |
| @@ -108,13 +111,13 @@ public: | |||
| 108 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); | 111 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); |
| 109 | 112 | ||
| 110 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 113 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 111 | void FlushRegion(CacheAddr addr, u64 size); | 114 | void FlushRegion(VAddr addr, u64 size); |
| 112 | 115 | ||
| 113 | /// Notify rasterizer that any caches of the specified region should be invalidated | 116 | /// Notify rasterizer that any caches of the specified region should be invalidated |
| 114 | void InvalidateRegion(CacheAddr addr, u64 size); | 117 | void InvalidateRegion(VAddr addr, u64 size); |
| 115 | 118 | ||
| 116 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated | 119 | /// Notify rasterizer that any caches of the specified region should be flushed and invalidated |
| 117 | void FlushAndInvalidateRegion(CacheAddr addr, u64 size); | 120 | void FlushAndInvalidateRegion(VAddr addr, u64 size); |
| 118 | 121 | ||
| 119 | // Wait until the gpu thread is idle. | 122 | // Wait until the gpu thread is idle. |
| 120 | void WaitIdle() const; | 123 | void WaitIdle() const; |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index f5d33f27a..a3389d0d2 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -81,12 +81,11 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | |||
| 81 | ASSERT((gpu_addr & page_mask) == 0); | 81 | ASSERT((gpu_addr & page_mask) == 0); |
| 82 | 82 | ||
| 83 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 83 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 84 | const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))}; | ||
| 85 | const auto cpu_addr = GpuToCpuAddress(gpu_addr); | 84 | const auto cpu_addr = GpuToCpuAddress(gpu_addr); |
| 86 | ASSERT(cpu_addr); | 85 | ASSERT(cpu_addr); |
| 87 | 86 | ||
| 88 | // Flush and invalidate through the GPU interface, to be asynchronous if possible. | 87 | // Flush and invalidate through the GPU interface, to be asynchronous if possible. |
| 89 | system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size); | 88 | system.GPU().FlushAndInvalidateRegion(*cpu_addr, aligned_size); |
| 90 | 89 | ||
| 91 | UnmapRange(gpu_addr, aligned_size); | 90 | UnmapRange(gpu_addr, aligned_size); |
| 92 | ASSERT(system.CurrentProcess() | 91 | ASSERT(system.CurrentProcess() |
| @@ -140,11 +139,11 @@ T MemoryManager::Read(GPUVAddr addr) const { | |||
| 140 | return {}; | 139 | return {}; |
| 141 | } | 140 | } |
| 142 | 141 | ||
| 143 | const u8* page_pointer{page_table.pointers[addr >> page_bits]}; | 142 | const u8* page_pointer{GetPointer(addr)}; |
| 144 | if (page_pointer) { | 143 | if (page_pointer) { |
| 145 | // NOTE: Avoid adding any extra logic to this fast-path block | 144 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 146 | T value; | 145 | T value; |
| 147 | std::memcpy(&value, &page_pointer[addr & page_mask], sizeof(T)); | 146 | std::memcpy(&value, page_pointer, sizeof(T)); |
| 148 | return value; | 147 | return value; |
| 149 | } | 148 | } |
| 150 | 149 | ||
| @@ -167,10 +166,10 @@ void MemoryManager::Write(GPUVAddr addr, T data) { | |||
| 167 | return; | 166 | return; |
| 168 | } | 167 | } |
| 169 | 168 | ||
| 170 | u8* page_pointer{page_table.pointers[addr >> page_bits]}; | 169 | u8* page_pointer{GetPointer(addr)}; |
| 171 | if (page_pointer) { | 170 | if (page_pointer) { |
| 172 | // NOTE: Avoid adding any extra logic to this fast-path block | 171 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 173 | std::memcpy(&page_pointer[addr & page_mask], &data, sizeof(T)); | 172 | std::memcpy(page_pointer, &data, sizeof(T)); |
| 174 | return; | 173 | return; |
| 175 | } | 174 | } |
| 176 | 175 | ||
| @@ -201,9 +200,12 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) { | |||
| 201 | return {}; | 200 | return {}; |
| 202 | } | 201 | } |
| 203 | 202 | ||
| 204 | u8* const page_pointer{page_table.pointers[addr >> page_bits]}; | 203 | auto& memory = system.Memory(); |
| 205 | if (page_pointer != nullptr) { | 204 | |
| 206 | return page_pointer + (addr & page_mask); | 205 | const VAddr page_addr{page_table.backing_addr[addr >> page_bits]}; |
| 206 | |||
| 207 | if (page_addr != 0) { | ||
| 208 | return memory.GetPointer(page_addr + (addr & page_mask)); | ||
| 207 | } | 209 | } |
| 208 | 210 | ||
| 209 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); | 211 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); |
| @@ -215,9 +217,12 @@ const u8* MemoryManager::GetPointer(GPUVAddr addr) const { | |||
| 215 | return {}; | 217 | return {}; |
| 216 | } | 218 | } |
| 217 | 219 | ||
| 218 | const u8* const page_pointer{page_table.pointers[addr >> page_bits]}; | 220 | const auto& memory = system.Memory(); |
| 219 | if (page_pointer != nullptr) { | 221 | |
| 220 | return page_pointer + (addr & page_mask); | 222 | const VAddr page_addr{page_table.backing_addr[addr >> page_bits]}; |
| 223 | |||
| 224 | if (page_addr != 0) { | ||
| 225 | return memory.GetPointer(page_addr + (addr & page_mask)); | ||
| 221 | } | 226 | } |
| 222 | 227 | ||
| 223 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); | 228 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); |
| @@ -238,17 +243,19 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s | |||
| 238 | std::size_t page_index{src_addr >> page_bits}; | 243 | std::size_t page_index{src_addr >> page_bits}; |
| 239 | std::size_t page_offset{src_addr & page_mask}; | 244 | std::size_t page_offset{src_addr & page_mask}; |
| 240 | 245 | ||
| 246 | auto& memory = system.Memory(); | ||
| 247 | |||
| 241 | while (remaining_size > 0) { | 248 | while (remaining_size > 0) { |
| 242 | const std::size_t copy_amount{ | 249 | const std::size_t copy_amount{ |
| 243 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 250 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 244 | 251 | ||
| 245 | switch (page_table.attributes[page_index]) { | 252 | switch (page_table.attributes[page_index]) { |
| 246 | case Common::PageType::Memory: { | 253 | case Common::PageType::Memory: { |
| 247 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | 254 | const VAddr src_addr{page_table.backing_addr[page_index] + page_offset}; |
| 248 | // Flush must happen on the rasterizer interface, such that memory is always synchronous | 255 | // Flush must happen on the rasterizer interface, such that memory is always synchronous |
| 249 | // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. | 256 | // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. |
| 250 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | 257 | rasterizer.FlushRegion(src_addr, copy_amount); |
| 251 | std::memcpy(dest_buffer, src_ptr, copy_amount); | 258 | memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); |
| 252 | break; | 259 | break; |
| 253 | } | 260 | } |
| 254 | default: | 261 | default: |
| @@ -268,13 +275,15 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, | |||
| 268 | std::size_t page_index{src_addr >> page_bits}; | 275 | std::size_t page_index{src_addr >> page_bits}; |
| 269 | std::size_t page_offset{src_addr & page_mask}; | 276 | std::size_t page_offset{src_addr & page_mask}; |
| 270 | 277 | ||
| 278 | auto& memory = system.Memory(); | ||
| 279 | |||
| 271 | while (remaining_size > 0) { | 280 | while (remaining_size > 0) { |
| 272 | const std::size_t copy_amount{ | 281 | const std::size_t copy_amount{ |
| 273 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 282 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 274 | const u8* page_pointer = page_table.pointers[page_index]; | 283 | const u8* page_pointer = page_table.pointers[page_index]; |
| 275 | if (page_pointer) { | 284 | if (page_pointer) { |
| 276 | const u8* src_ptr{page_pointer + page_offset}; | 285 | const VAddr src_addr{page_table.backing_addr[page_index] + page_offset}; |
| 277 | std::memcpy(dest_buffer, src_ptr, copy_amount); | 286 | memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); |
| 278 | } else { | 287 | } else { |
| 279 | std::memset(dest_buffer, 0, copy_amount); | 288 | std::memset(dest_buffer, 0, copy_amount); |
| 280 | } | 289 | } |
| @@ -290,17 +299,19 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const | |||
| 290 | std::size_t page_index{dest_addr >> page_bits}; | 299 | std::size_t page_index{dest_addr >> page_bits}; |
| 291 | std::size_t page_offset{dest_addr & page_mask}; | 300 | std::size_t page_offset{dest_addr & page_mask}; |
| 292 | 301 | ||
| 302 | auto& memory = system.Memory(); | ||
| 303 | |||
| 293 | while (remaining_size > 0) { | 304 | while (remaining_size > 0) { |
| 294 | const std::size_t copy_amount{ | 305 | const std::size_t copy_amount{ |
| 295 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 306 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 296 | 307 | ||
| 297 | switch (page_table.attributes[page_index]) { | 308 | switch (page_table.attributes[page_index]) { |
| 298 | case Common::PageType::Memory: { | 309 | case Common::PageType::Memory: { |
| 299 | u8* dest_ptr{page_table.pointers[page_index] + page_offset}; | 310 | const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset}; |
| 300 | // Invalidate must happen on the rasterizer interface, such that memory is always | 311 | // Invalidate must happen on the rasterizer interface, such that memory is always |
| 301 | // synchronous when it is written (even when in asynchronous GPU mode). | 312 | // synchronous when it is written (even when in asynchronous GPU mode). |
| 302 | rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); | 313 | rasterizer.InvalidateRegion(dest_addr, copy_amount); |
| 303 | std::memcpy(dest_ptr, src_buffer, copy_amount); | 314 | memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); |
| 304 | break; | 315 | break; |
| 305 | } | 316 | } |
| 306 | default: | 317 | default: |
| @@ -320,13 +331,15 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, | |||
| 320 | std::size_t page_index{dest_addr >> page_bits}; | 331 | std::size_t page_index{dest_addr >> page_bits}; |
| 321 | std::size_t page_offset{dest_addr & page_mask}; | 332 | std::size_t page_offset{dest_addr & page_mask}; |
| 322 | 333 | ||
| 334 | auto& memory = system.Memory(); | ||
| 335 | |||
| 323 | while (remaining_size > 0) { | 336 | while (remaining_size > 0) { |
| 324 | const std::size_t copy_amount{ | 337 | const std::size_t copy_amount{ |
| 325 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 338 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 326 | u8* page_pointer = page_table.pointers[page_index]; | 339 | u8* page_pointer = page_table.pointers[page_index]; |
| 327 | if (page_pointer) { | 340 | if (page_pointer) { |
| 328 | u8* dest_ptr{page_pointer + page_offset}; | 341 | const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset}; |
| 329 | std::memcpy(dest_ptr, src_buffer, copy_amount); | 342 | memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); |
| 330 | } | 343 | } |
| 331 | page_index++; | 344 | page_index++; |
| 332 | page_offset = 0; | 345 | page_offset = 0; |
| @@ -336,33 +349,9 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, | |||
| 336 | } | 349 | } |
| 337 | 350 | ||
| 338 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { | 351 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { |
| 339 | std::size_t remaining_size{size}; | 352 | std::vector<u8> tmp_buffer(size); |
| 340 | std::size_t page_index{src_addr >> page_bits}; | 353 | ReadBlock(src_addr, tmp_buffer.data(), size); |
| 341 | std::size_t page_offset{src_addr & page_mask}; | 354 | WriteBlock(dest_addr, tmp_buffer.data(), size); |
| 342 | |||
| 343 | while (remaining_size > 0) { | ||
| 344 | const std::size_t copy_amount{ | ||
| 345 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||
| 346 | |||
| 347 | switch (page_table.attributes[page_index]) { | ||
| 348 | case Common::PageType::Memory: { | ||
| 349 | // Flush must happen on the rasterizer interface, such that memory is always synchronous | ||
| 350 | // when it is copied (even when in asynchronous GPU mode). | ||
| 351 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | ||
| 352 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | ||
| 353 | WriteBlock(dest_addr, src_ptr, copy_amount); | ||
| 354 | break; | ||
| 355 | } | ||
| 356 | default: | ||
| 357 | UNREACHABLE(); | ||
| 358 | } | ||
| 359 | |||
| 360 | page_index++; | ||
| 361 | page_offset = 0; | ||
| 362 | dest_addr += static_cast<VAddr>(copy_amount); | ||
| 363 | src_addr += static_cast<VAddr>(copy_amount); | ||
| 364 | remaining_size -= copy_amount; | ||
| 365 | } | ||
| 366 | } | 355 | } |
| 367 | 356 | ||
| 368 | void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { | 357 | void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { |
| @@ -371,6 +360,12 @@ void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const | |||
| 371 | WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size); | 360 | WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size); |
| 372 | } | 361 | } |
| 373 | 362 | ||
| 363 | bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { | ||
| 364 | const VAddr addr = page_table.backing_addr[gpu_addr >> page_bits]; | ||
| 365 | const std::size_t page = (addr & Memory::PAGE_MASK) + size; | ||
| 366 | return page <= Memory::PAGE_SIZE; | ||
| 367 | } | ||
| 368 | |||
| 374 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, | 369 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, |
| 375 | VAddr backing_addr) { | 370 | VAddr backing_addr) { |
| 376 | LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size, | 371 | LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size, |
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 073bdb491..0d9468535 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -97,6 +97,11 @@ public: | |||
| 97 | void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); | 97 | void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); |
| 98 | void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); | 98 | void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); |
| 99 | 99 | ||
| 100 | /** | ||
| 101 | * IsGranularRange checks if a gpu region can be simply read with a pointer | ||
| 102 | */ | ||
| 103 | bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size); | ||
| 104 | |||
| 100 | private: | 105 | private: |
| 101 | using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; | 106 | using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; |
| 102 | using VMAHandle = VMAMap::const_iterator; | 107 | using VMAHandle = VMAMap::const_iterator; |
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index e66054ed0..5ea2b01f2 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h | |||
| @@ -98,12 +98,12 @@ public: | |||
| 98 | static_cast<QueryCache&>(*this), | 98 | static_cast<QueryCache&>(*this), |
| 99 | VideoCore::QueryType::SamplesPassed}}} {} | 99 | VideoCore::QueryType::SamplesPassed}}} {} |
| 100 | 100 | ||
| 101 | void InvalidateRegion(CacheAddr addr, std::size_t size) { | 101 | void InvalidateRegion(VAddr addr, std::size_t size) { |
| 102 | std::unique_lock lock{mutex}; | 102 | std::unique_lock lock{mutex}; |
| 103 | FlushAndRemoveRegion(addr, size); | 103 | FlushAndRemoveRegion(addr, size); |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | void FlushRegion(CacheAddr addr, std::size_t size) { | 106 | void FlushRegion(VAddr addr, std::size_t size) { |
| 107 | std::unique_lock lock{mutex}; | 107 | std::unique_lock lock{mutex}; |
| 108 | FlushAndRemoveRegion(addr, size); | 108 | FlushAndRemoveRegion(addr, size); |
| 109 | } | 109 | } |
| @@ -117,14 +117,16 @@ public: | |||
| 117 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) { | 117 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) { |
| 118 | std::unique_lock lock{mutex}; | 118 | std::unique_lock lock{mutex}; |
| 119 | auto& memory_manager = system.GPU().MemoryManager(); | 119 | auto& memory_manager = system.GPU().MemoryManager(); |
| 120 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | 120 | const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); |
| 121 | ASSERT(cpu_addr_opt); | ||
| 122 | VAddr cpu_addr = *cpu_addr_opt; | ||
| 121 | 123 | ||
| 122 | CachedQuery* query = TryGet(ToCacheAddr(host_ptr)); | 124 | CachedQuery* query = TryGet(cpu_addr); |
| 123 | if (!query) { | 125 | if (!query) { |
| 124 | const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr); | 126 | ASSERT_OR_EXECUTE(cpu_addr_opt, return;); |
| 125 | ASSERT_OR_EXECUTE(cpu_addr, return;); | 127 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); |
| 126 | 128 | ||
| 127 | query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); | 129 | query = Register(type, cpu_addr, host_ptr, timestamp.has_value()); |
| 128 | } | 130 | } |
| 129 | 131 | ||
| 130 | query->BindCounter(Stream(type).Current(), timestamp); | 132 | query->BindCounter(Stream(type).Current(), timestamp); |
| @@ -173,11 +175,11 @@ protected: | |||
| 173 | 175 | ||
| 174 | private: | 176 | private: |
| 175 | /// Flushes a memory range to guest memory and removes it from the cache. | 177 | /// Flushes a memory range to guest memory and removes it from the cache. |
| 176 | void FlushAndRemoveRegion(CacheAddr addr, std::size_t size) { | 178 | void FlushAndRemoveRegion(VAddr addr, std::size_t size) { |
| 177 | const u64 addr_begin = static_cast<u64>(addr); | 179 | const u64 addr_begin = static_cast<u64>(addr); |
| 178 | const u64 addr_end = addr_begin + static_cast<u64>(size); | 180 | const u64 addr_end = addr_begin + static_cast<u64>(size); |
| 179 | const auto in_range = [addr_begin, addr_end](CachedQuery& query) { | 181 | const auto in_range = [addr_begin, addr_end](CachedQuery& query) { |
| 180 | const u64 cache_begin = query.GetCacheAddr(); | 182 | const u64 cache_begin = query.GetCpuAddr(); |
| 181 | const u64 cache_end = cache_begin + query.SizeInBytes(); | 183 | const u64 cache_end = cache_begin + query.SizeInBytes(); |
| 182 | return cache_begin < addr_end && addr_begin < cache_end; | 184 | return cache_begin < addr_end && addr_begin < cache_end; |
| 183 | }; | 185 | }; |
| @@ -193,7 +195,7 @@ private: | |||
| 193 | if (!in_range(query)) { | 195 | if (!in_range(query)) { |
| 194 | continue; | 196 | continue; |
| 195 | } | 197 | } |
| 196 | rasterizer.UpdatePagesCachedCount(query.CpuAddr(), query.SizeInBytes(), -1); | 198 | rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1); |
| 197 | query.Flush(); | 199 | query.Flush(); |
| 198 | } | 200 | } |
| 199 | contents.erase(std::remove_if(std::begin(contents), std::end(contents), in_range), | 201 | contents.erase(std::remove_if(std::begin(contents), std::end(contents), in_range), |
| @@ -204,22 +206,21 @@ private: | |||
| 204 | /// Registers the passed parameters as cached and returns a pointer to the stored cached query. | 206 | /// Registers the passed parameters as cached and returns a pointer to the stored cached query. |
| 205 | CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { | 207 | CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { |
| 206 | rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); | 208 | rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); |
| 207 | const u64 page = static_cast<u64>(ToCacheAddr(host_ptr)) >> PAGE_SHIFT; | 209 | const u64 page = static_cast<u64>(cpu_addr) >> PAGE_SHIFT; |
| 208 | return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, | 210 | return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, |
| 209 | host_ptr); | 211 | host_ptr); |
| 210 | } | 212 | } |
| 211 | 213 | ||
| 212 | /// Tries to a get a cached query. Returns nullptr on failure. | 214 | /// Tries to a get a cached query. Returns nullptr on failure. |
| 213 | CachedQuery* TryGet(CacheAddr addr) { | 215 | CachedQuery* TryGet(VAddr addr) { |
| 214 | const u64 page = static_cast<u64>(addr) >> PAGE_SHIFT; | 216 | const u64 page = static_cast<u64>(addr) >> PAGE_SHIFT; |
| 215 | const auto it = cached_queries.find(page); | 217 | const auto it = cached_queries.find(page); |
| 216 | if (it == std::end(cached_queries)) { | 218 | if (it == std::end(cached_queries)) { |
| 217 | return nullptr; | 219 | return nullptr; |
| 218 | } | 220 | } |
| 219 | auto& contents = it->second; | 221 | auto& contents = it->second; |
| 220 | const auto found = | 222 | const auto found = std::find_if(std::begin(contents), std::end(contents), |
| 221 | std::find_if(std::begin(contents), std::end(contents), | 223 | [addr](auto& query) { return query.GetCpuAddr() == addr; }); |
| 222 | [addr](auto& query) { return query.GetCacheAddr() == addr; }); | ||
| 223 | return found != std::end(contents) ? &*found : nullptr; | 224 | return found != std::end(contents) ? &*found : nullptr; |
| 224 | } | 225 | } |
| 225 | 226 | ||
| @@ -323,14 +324,10 @@ public: | |||
| 323 | timestamp = timestamp_; | 324 | timestamp = timestamp_; |
| 324 | } | 325 | } |
| 325 | 326 | ||
| 326 | VAddr CpuAddr() const noexcept { | 327 | VAddr GetCpuAddr() const noexcept { |
| 327 | return cpu_addr; | 328 | return cpu_addr; |
| 328 | } | 329 | } |
| 329 | 330 | ||
| 330 | CacheAddr GetCacheAddr() const noexcept { | ||
| 331 | return ToCacheAddr(host_ptr); | ||
| 332 | } | ||
| 333 | |||
| 334 | u64 SizeInBytes() const noexcept { | 331 | u64 SizeInBytes() const noexcept { |
| 335 | return SizeInBytes(timestamp.has_value()); | 332 | return SizeInBytes(timestamp.has_value()); |
| 336 | } | 333 | } |
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h index 6de1597a2..22987751e 100644 --- a/src/video_core/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache.h | |||
| @@ -18,22 +18,14 @@ | |||
| 18 | 18 | ||
| 19 | class RasterizerCacheObject { | 19 | class RasterizerCacheObject { |
| 20 | public: | 20 | public: |
| 21 | explicit RasterizerCacheObject(const u8* host_ptr) | 21 | explicit RasterizerCacheObject(const VAddr cpu_addr) : cpu_addr{cpu_addr} {} |
| 22 | : host_ptr{host_ptr}, cache_addr{ToCacheAddr(host_ptr)} {} | ||
| 23 | 22 | ||
| 24 | virtual ~RasterizerCacheObject(); | 23 | virtual ~RasterizerCacheObject(); |
| 25 | 24 | ||
| 26 | CacheAddr GetCacheAddr() const { | 25 | VAddr GetCpuAddr() const { |
| 27 | return cache_addr; | 26 | return cpu_addr; |
| 28 | } | 27 | } |
| 29 | 28 | ||
| 30 | const u8* GetHostPtr() const { | ||
| 31 | return host_ptr; | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Gets the address of the shader in guest memory, required for cache management | ||
| 35 | virtual VAddr GetCpuAddr() const = 0; | ||
| 36 | |||
| 37 | /// Gets the size of the shader in guest memory, required for cache management | 29 | /// Gets the size of the shader in guest memory, required for cache management |
| 38 | virtual std::size_t GetSizeInBytes() const = 0; | 30 | virtual std::size_t GetSizeInBytes() const = 0; |
| 39 | 31 | ||
| @@ -68,8 +60,7 @@ private: | |||
| 68 | bool is_registered{}; ///< Whether the object is currently registered with the cache | 60 | bool is_registered{}; ///< Whether the object is currently registered with the cache |
| 69 | bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory) | 61 | bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory) |
| 70 | u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing | 62 | u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing |
| 71 | const u8* host_ptr{}; ///< Pointer to the memory backing this cached region | 63 | VAddr cpu_addr{}; ///< Cpu address memory, unique from emulated virtual address space |
| 72 | CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space | ||
| 73 | }; | 64 | }; |
| 74 | 65 | ||
| 75 | template <class T> | 66 | template <class T> |
| @@ -80,7 +71,7 @@ public: | |||
| 80 | explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} | 71 | explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {} |
| 81 | 72 | ||
| 82 | /// Write any cached resources overlapping the specified region back to memory | 73 | /// Write any cached resources overlapping the specified region back to memory |
| 83 | void FlushRegion(CacheAddr addr, std::size_t size) { | 74 | void FlushRegion(VAddr addr, std::size_t size) { |
| 84 | std::lock_guard lock{mutex}; | 75 | std::lock_guard lock{mutex}; |
| 85 | 76 | ||
| 86 | const auto& objects{GetSortedObjectsFromRegion(addr, size)}; | 77 | const auto& objects{GetSortedObjectsFromRegion(addr, size)}; |
| @@ -90,7 +81,7 @@ public: | |||
| 90 | } | 81 | } |
| 91 | 82 | ||
| 92 | /// Mark the specified region as being invalidated | 83 | /// Mark the specified region as being invalidated |
| 93 | void InvalidateRegion(CacheAddr addr, u64 size) { | 84 | void InvalidateRegion(VAddr addr, u64 size) { |
| 94 | std::lock_guard lock{mutex}; | 85 | std::lock_guard lock{mutex}; |
| 95 | 86 | ||
| 96 | const auto& objects{GetSortedObjectsFromRegion(addr, size)}; | 87 | const auto& objects{GetSortedObjectsFromRegion(addr, size)}; |
| @@ -114,27 +105,20 @@ public: | |||
| 114 | 105 | ||
| 115 | protected: | 106 | protected: |
| 116 | /// Tries to get an object from the cache with the specified cache address | 107 | /// Tries to get an object from the cache with the specified cache address |
| 117 | T TryGet(CacheAddr addr) const { | 108 | T TryGet(VAddr addr) const { |
| 118 | const auto iter = map_cache.find(addr); | 109 | const auto iter = map_cache.find(addr); |
| 119 | if (iter != map_cache.end()) | 110 | if (iter != map_cache.end()) |
| 120 | return iter->second; | 111 | return iter->second; |
| 121 | return nullptr; | 112 | return nullptr; |
| 122 | } | 113 | } |
| 123 | 114 | ||
| 124 | T TryGet(const void* addr) const { | ||
| 125 | const auto iter = map_cache.find(ToCacheAddr(addr)); | ||
| 126 | if (iter != map_cache.end()) | ||
| 127 | return iter->second; | ||
| 128 | return nullptr; | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Register an object into the cache | 115 | /// Register an object into the cache |
| 132 | virtual void Register(const T& object) { | 116 | virtual void Register(const T& object) { |
| 133 | std::lock_guard lock{mutex}; | 117 | std::lock_guard lock{mutex}; |
| 134 | 118 | ||
| 135 | object->SetIsRegistered(true); | 119 | object->SetIsRegistered(true); |
| 136 | interval_cache.add({GetInterval(object), ObjectSet{object}}); | 120 | interval_cache.add({GetInterval(object), ObjectSet{object}}); |
| 137 | map_cache.insert({object->GetCacheAddr(), object}); | 121 | map_cache.insert({object->GetCpuAddr(), object}); |
| 138 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), 1); | 122 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), 1); |
| 139 | } | 123 | } |
| 140 | 124 | ||
| @@ -144,7 +128,7 @@ protected: | |||
| 144 | 128 | ||
| 145 | object->SetIsRegistered(false); | 129 | object->SetIsRegistered(false); |
| 146 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1); | 130 | rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1); |
| 147 | const CacheAddr addr = object->GetCacheAddr(); | 131 | const VAddr addr = object->GetCpuAddr(); |
| 148 | interval_cache.subtract({GetInterval(object), ObjectSet{object}}); | 132 | interval_cache.subtract({GetInterval(object), ObjectSet{object}}); |
| 149 | map_cache.erase(addr); | 133 | map_cache.erase(addr); |
| 150 | } | 134 | } |
| @@ -173,7 +157,7 @@ protected: | |||
| 173 | 157 | ||
| 174 | private: | 158 | private: |
| 175 | /// Returns a list of cached objects from the specified memory region, ordered by access time | 159 | /// Returns a list of cached objects from the specified memory region, ordered by access time |
| 176 | std::vector<T> GetSortedObjectsFromRegion(CacheAddr addr, u64 size) { | 160 | std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) { |
| 177 | if (size == 0) { | 161 | if (size == 0) { |
| 178 | return {}; | 162 | return {}; |
| 179 | } | 163 | } |
| @@ -197,13 +181,13 @@ private: | |||
| 197 | } | 181 | } |
| 198 | 182 | ||
| 199 | using ObjectSet = std::set<T>; | 183 | using ObjectSet = std::set<T>; |
| 200 | using ObjectCache = std::unordered_map<CacheAddr, T>; | 184 | using ObjectCache = std::unordered_map<VAddr, T>; |
| 201 | using IntervalCache = boost::icl::interval_map<CacheAddr, ObjectSet>; | 185 | using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>; |
| 202 | using ObjectInterval = typename IntervalCache::interval_type; | 186 | using ObjectInterval = typename IntervalCache::interval_type; |
| 203 | 187 | ||
| 204 | static auto GetInterval(const T& object) { | 188 | static auto GetInterval(const T& object) { |
| 205 | return ObjectInterval::right_open(object->GetCacheAddr(), | 189 | return ObjectInterval::right_open(object->GetCpuAddr(), |
| 206 | object->GetCacheAddr() + object->GetSizeInBytes()); | 190 | object->GetCpuAddr() + object->GetSizeInBytes()); |
| 207 | } | 191 | } |
| 208 | 192 | ||
| 209 | ObjectCache map_cache; | 193 | ObjectCache map_cache; |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 1a68e3caa..8ae5b9c4e 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -53,14 +53,14 @@ public: | |||
| 53 | virtual void FlushAll() = 0; | 53 | virtual void FlushAll() = 0; |
| 54 | 54 | ||
| 55 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 55 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 56 | virtual void FlushRegion(CacheAddr addr, u64 size) = 0; | 56 | virtual void FlushRegion(VAddr addr, u64 size) = 0; |
| 57 | 57 | ||
| 58 | /// Notify rasterizer that any caches of the specified region should be invalidated | 58 | /// Notify rasterizer that any caches of the specified region should be invalidated |
| 59 | virtual void InvalidateRegion(CacheAddr addr, u64 size) = 0; | 59 | virtual void InvalidateRegion(VAddr addr, u64 size) = 0; |
| 60 | 60 | ||
| 61 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 61 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 62 | /// and invalidated | 62 | /// and invalidated |
| 63 | virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0; | 63 | virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; |
| 64 | 64 | ||
| 65 | /// Notify the rasterizer to send all written commands to the host GPU. | 65 | /// Notify the rasterizer to send all written commands to the host GPU. |
| 66 | virtual void FlushCommands() = 0; | 66 | virtual void FlushCommands() = 0; |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5ec99a126..1d85219b6 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -46,7 +46,8 @@ public: | |||
| 46 | 46 | ||
| 47 | /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer | 47 | /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer |
| 48 | /// specific implementation) | 48 | /// specific implementation) |
| 49 | virtual void TryPresent(int timeout_ms) = 0; | 49 | /// Returns true if a frame was drawn |
| 50 | virtual bool TryPresent(int timeout_ms) = 0; | ||
| 50 | 51 | ||
| 51 | // Getter/setter functions: | 52 | // Getter/setter functions: |
| 52 | // ------------------------ | 53 | // ------------------------ |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 0375fca17..4eb37a96c 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -21,8 +21,8 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; | |||
| 21 | 21 | ||
| 22 | MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); | 22 | MICROPROFILE_DEFINE(OpenGL_Buffer_Download, "OpenGL", "Buffer Download", MP_RGB(192, 192, 128)); |
| 23 | 23 | ||
| 24 | CachedBufferBlock::CachedBufferBlock(CacheAddr cache_addr, const std::size_t size) | 24 | CachedBufferBlock::CachedBufferBlock(VAddr cpu_addr, const std::size_t size) |
| 25 | : VideoCommon::BufferBlock{cache_addr, size} { | 25 | : VideoCommon::BufferBlock{cpu_addr, size} { |
| 26 | gl_buffer.Create(); | 26 | gl_buffer.Create(); |
| 27 | glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); | 27 | glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); |
| 28 | } | 28 | } |
| @@ -47,8 +47,8 @@ OGLBufferCache::~OGLBufferCache() { | |||
| 47 | glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); | 47 | glDeleteBuffers(static_cast<GLsizei>(std::size(cbufs)), std::data(cbufs)); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | Buffer OGLBufferCache::CreateBlock(CacheAddr cache_addr, std::size_t size) { | 50 | Buffer OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { |
| 51 | return std::make_shared<CachedBufferBlock>(cache_addr, size); | 51 | return std::make_shared<CachedBufferBlock>(cpu_addr, size); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | void OGLBufferCache::WriteBarrier() { | 54 | void OGLBufferCache::WriteBarrier() { |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 8c7145443..d94a11252 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -31,7 +31,7 @@ using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuf | |||
| 31 | 31 | ||
| 32 | class CachedBufferBlock : public VideoCommon::BufferBlock { | 32 | class CachedBufferBlock : public VideoCommon::BufferBlock { |
| 33 | public: | 33 | public: |
| 34 | explicit CachedBufferBlock(CacheAddr cache_addr, const std::size_t size); | 34 | explicit CachedBufferBlock(VAddr cpu_addr, const std::size_t size); |
| 35 | ~CachedBufferBlock(); | 35 | ~CachedBufferBlock(); |
| 36 | 36 | ||
| 37 | const GLuint* GetHandle() const { | 37 | const GLuint* GetHandle() const { |
| @@ -55,7 +55,7 @@ public: | |||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | protected: | 57 | protected: |
| 58 | Buffer CreateBlock(CacheAddr cache_addr, std::size_t size) override; | 58 | Buffer CreateBlock(VAddr cpu_addr, std::size_t size) override; |
| 59 | 59 | ||
| 60 | void WriteBarrier() override; | 60 | void WriteBarrier() override; |
| 61 | 61 | ||
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 1a2e2a9f7..c286502ba 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp | |||
| @@ -131,6 +131,31 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin | |||
| 131 | return bindings; | 131 | return bindings; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | bool IsASTCSupported() { | ||
| 135 | static constexpr std::array formats = { | ||
| 136 | GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR, | ||
| 137 | GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR, | ||
| 138 | GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x5_KHR, | ||
| 139 | GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x8_KHR, | ||
| 140 | GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_COMPRESSED_RGBA_ASTC_10x6_KHR, | ||
| 141 | GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_COMPRESSED_RGBA_ASTC_10x10_KHR, | ||
| 142 | GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_COMPRESSED_RGBA_ASTC_12x12_KHR, | ||
| 143 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, | ||
| 144 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, | ||
| 145 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, | ||
| 146 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, | ||
| 147 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, | ||
| 148 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, | ||
| 149 | GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, | ||
| 150 | }; | ||
| 151 | return std::find_if_not(formats.begin(), formats.end(), [](GLenum format) { | ||
| 152 | GLint supported; | ||
| 153 | glGetInternalformativ(GL_TEXTURE_2D, format, GL_INTERNALFORMAT_SUPPORTED, 1, | ||
| 154 | &supported); | ||
| 155 | return supported == GL_TRUE; | ||
| 156 | }) == formats.end(); | ||
| 157 | } | ||
| 158 | |||
| 134 | } // Anonymous namespace | 159 | } // Anonymous namespace |
| 135 | 160 | ||
| 136 | Device::Device() : base_bindings{BuildBaseBindings()} { | 161 | Device::Device() : base_bindings{BuildBaseBindings()} { |
| @@ -152,6 +177,7 @@ Device::Device() : base_bindings{BuildBaseBindings()} { | |||
| 152 | has_shader_ballot = GLAD_GL_ARB_shader_ballot; | 177 | has_shader_ballot = GLAD_GL_ARB_shader_ballot; |
| 153 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; | 178 | has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; |
| 154 | has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); | 179 | has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); |
| 180 | has_astc = IsASTCSupported(); | ||
| 155 | has_variable_aoffi = TestVariableAoffi(); | 181 | has_variable_aoffi = TestVariableAoffi(); |
| 156 | has_component_indexing_bug = is_amd; | 182 | has_component_indexing_bug = is_amd; |
| 157 | has_precise_bug = TestPreciseBug(); | 183 | has_precise_bug = TestPreciseBug(); |
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index d73b099d0..a55050cb5 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h | |||
| @@ -64,6 +64,10 @@ public: | |||
| 64 | return has_image_load_formatted; | 64 | return has_image_load_formatted; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | bool HasASTC() const { | ||
| 68 | return has_astc; | ||
| 69 | } | ||
| 70 | |||
| 67 | bool HasVariableAoffi() const { | 71 | bool HasVariableAoffi() const { |
| 68 | return has_variable_aoffi; | 72 | return has_variable_aoffi; |
| 69 | } | 73 | } |
| @@ -97,6 +101,7 @@ private: | |||
| 97 | bool has_shader_ballot{}; | 101 | bool has_shader_ballot{}; |
| 98 | bool has_vertex_viewport_layer{}; | 102 | bool has_vertex_viewport_layer{}; |
| 99 | bool has_image_load_formatted{}; | 103 | bool has_image_load_formatted{}; |
| 104 | bool has_astc{}; | ||
| 100 | bool has_variable_aoffi{}; | 105 | bool has_variable_aoffi{}; |
| 101 | bool has_component_indexing_bug{}; | 106 | bool has_component_indexing_bug{}; |
| 102 | bool has_precise_bug{}; | 107 | bool has_precise_bug{}; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 826eee7df..368f399df 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -386,11 +386,14 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using | |||
| 386 | texture_cache.GuardRenderTargets(true); | 386 | texture_cache.GuardRenderTargets(true); |
| 387 | View color_surface; | 387 | View color_surface; |
| 388 | if (using_color_fb) { | 388 | if (using_color_fb) { |
| 389 | color_surface = texture_cache.GetColorBufferSurface(regs.clear_buffers.RT, false); | 389 | const std::size_t index = regs.clear_buffers.RT; |
| 390 | color_surface = texture_cache.GetColorBufferSurface(index, true); | ||
| 391 | texture_cache.MarkColorBufferInUse(index); | ||
| 390 | } | 392 | } |
| 391 | View depth_surface; | 393 | View depth_surface; |
| 392 | if (using_depth_fb || using_stencil_fb) { | 394 | if (using_depth_fb || using_stencil_fb) { |
| 393 | depth_surface = texture_cache.GetDepthBufferSurface(false); | 395 | depth_surface = texture_cache.GetDepthBufferSurface(true); |
| 396 | texture_cache.MarkDepthBufferInUse(); | ||
| 394 | } | 397 | } |
| 395 | texture_cache.GuardRenderTargets(false); | 398 | texture_cache.GuardRenderTargets(false); |
| 396 | 399 | ||
| @@ -444,6 +447,7 @@ void RasterizerOpenGL::Clear() { | |||
| 444 | } | 447 | } |
| 445 | 448 | ||
| 446 | SyncRasterizeEnable(); | 449 | SyncRasterizeEnable(); |
| 450 | SyncStencilTestState(); | ||
| 447 | 451 | ||
| 448 | if (regs.clear_flags.scissor) { | 452 | if (regs.clear_flags.scissor) { |
| 449 | SyncScissorTest(); | 453 | SyncScissorTest(); |
| @@ -652,9 +656,9 @@ void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCore::QueryType type, | |||
| 652 | 656 | ||
| 653 | void RasterizerOpenGL::FlushAll() {} | 657 | void RasterizerOpenGL::FlushAll() {} |
| 654 | 658 | ||
| 655 | void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) { | 659 | void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { |
| 656 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 660 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 657 | if (!addr || !size) { | 661 | if (addr == 0 || size == 0) { |
| 658 | return; | 662 | return; |
| 659 | } | 663 | } |
| 660 | texture_cache.FlushRegion(addr, size); | 664 | texture_cache.FlushRegion(addr, size); |
| @@ -662,9 +666,9 @@ void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) { | |||
| 662 | query_cache.FlushRegion(addr, size); | 666 | query_cache.FlushRegion(addr, size); |
| 663 | } | 667 | } |
| 664 | 668 | ||
| 665 | void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { | 669 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { |
| 666 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 670 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 667 | if (!addr || !size) { | 671 | if (addr == 0 || size == 0) { |
| 668 | return; | 672 | return; |
| 669 | } | 673 | } |
| 670 | texture_cache.InvalidateRegion(addr, size); | 674 | texture_cache.InvalidateRegion(addr, size); |
| @@ -673,7 +677,7 @@ void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { | |||
| 673 | query_cache.InvalidateRegion(addr, size); | 677 | query_cache.InvalidateRegion(addr, size); |
| 674 | } | 678 | } |
| 675 | 679 | ||
| 676 | void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 680 | void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 677 | if (Settings::values.use_accurate_gpu_emulation) { | 681 | if (Settings::values.use_accurate_gpu_emulation) { |
| 678 | FlushRegion(addr, size); | 682 | FlushRegion(addr, size); |
| 679 | } | 683 | } |
| @@ -712,8 +716,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, | |||
| 712 | 716 | ||
| 713 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 717 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 714 | 718 | ||
| 715 | const auto surface{ | 719 | const auto surface{texture_cache.TryFindFramebufferSurface(framebuffer_addr)}; |
| 716 | texture_cache.TryFindFramebufferSurface(system.Memory().GetPointer(framebuffer_addr))}; | ||
| 717 | if (!surface) { | 720 | if (!surface) { |
| 718 | return {}; | 721 | return {}; |
| 719 | } | 722 | } |
| @@ -1052,12 +1055,8 @@ void RasterizerOpenGL::SyncStencilTestState() { | |||
| 1052 | flags[Dirty::StencilTest] = false; | 1055 | flags[Dirty::StencilTest] = false; |
| 1053 | 1056 | ||
| 1054 | const auto& regs = gpu.regs; | 1057 | const auto& regs = gpu.regs; |
| 1055 | if (!regs.stencil_enable) { | 1058 | oglEnable(GL_STENCIL_TEST, regs.stencil_enable); |
| 1056 | glDisable(GL_STENCIL_TEST); | ||
| 1057 | return; | ||
| 1058 | } | ||
| 1059 | 1059 | ||
| 1060 | glEnable(GL_STENCIL_TEST); | ||
| 1061 | glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), | 1060 | glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), |
| 1062 | regs.stencil_front_func_ref, regs.stencil_front_func_mask); | 1061 | regs.stencil_front_func_ref, regs.stencil_front_func_mask); |
| 1063 | glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), | 1062 | glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 2d3be2437..212dad852 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -65,9 +65,9 @@ public: | |||
| 65 | void ResetCounter(VideoCore::QueryType type) override; | 65 | void ResetCounter(VideoCore::QueryType type) override; |
| 66 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; | 66 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; |
| 67 | void FlushAll() override; | 67 | void FlushAll() override; |
| 68 | void FlushRegion(CacheAddr addr, u64 size) override; | 68 | void FlushRegion(VAddr addr, u64 size) override; |
| 69 | void InvalidateRegion(CacheAddr addr, u64 size) override; | 69 | void InvalidateRegion(VAddr addr, u64 size) override; |
| 70 | void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; | 70 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 71 | void FlushCommands() override; | 71 | void FlushCommands() override; |
| 72 | void TickFrame() override; | 72 | void TickFrame() override; |
| 73 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, | 73 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index e3d31c3eb..6d2ff20f9 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -214,11 +214,11 @@ std::unordered_set<GLenum> GetSupportedFormats() { | |||
| 214 | 214 | ||
| 215 | } // Anonymous namespace | 215 | } // Anonymous namespace |
| 216 | 216 | ||
| 217 | CachedShader::CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes, | 217 | CachedShader::CachedShader(VAddr cpu_addr, std::size_t size_in_bytes, |
| 218 | std::shared_ptr<VideoCommon::Shader::Registry> registry, | 218 | std::shared_ptr<VideoCommon::Shader::Registry> registry, |
| 219 | ShaderEntries entries, std::shared_ptr<OGLProgram> program) | 219 | ShaderEntries entries, std::shared_ptr<OGLProgram> program) |
| 220 | : RasterizerCacheObject{host_ptr}, registry{std::move(registry)}, entries{std::move(entries)}, | 220 | : RasterizerCacheObject{cpu_addr}, registry{std::move(registry)}, entries{std::move(entries)}, |
| 221 | cpu_addr{cpu_addr}, size_in_bytes{size_in_bytes}, program{std::move(program)} {} | 221 | size_in_bytes{size_in_bytes}, program{std::move(program)} {} |
| 222 | 222 | ||
| 223 | CachedShader::~CachedShader() = default; | 223 | CachedShader::~CachedShader() = default; |
| 224 | 224 | ||
| @@ -254,9 +254,8 @@ Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params, | |||
| 254 | entry.bindless_samplers = registry->GetBindlessSamplers(); | 254 | entry.bindless_samplers = registry->GetBindlessSamplers(); |
| 255 | params.disk_cache.SaveEntry(std::move(entry)); | 255 | params.disk_cache.SaveEntry(std::move(entry)); |
| 256 | 256 | ||
| 257 | return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr, | 257 | return std::shared_ptr<CachedShader>(new CachedShader( |
| 258 | size_in_bytes, std::move(registry), | 258 | params.cpu_addr, size_in_bytes, std::move(registry), MakeEntries(ir), std::move(program))); |
| 259 | MakeEntries(ir), std::move(program))); | ||
| 260 | } | 259 | } |
| 261 | 260 | ||
| 262 | Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) { | 261 | Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, ProgramCode code) { |
| @@ -279,17 +278,16 @@ Shader CachedShader::CreateKernelFromMemory(const ShaderParameters& params, Prog | |||
| 279 | entry.bindless_samplers = registry->GetBindlessSamplers(); | 278 | entry.bindless_samplers = registry->GetBindlessSamplers(); |
| 280 | params.disk_cache.SaveEntry(std::move(entry)); | 279 | params.disk_cache.SaveEntry(std::move(entry)); |
| 281 | 280 | ||
| 282 | return std::shared_ptr<CachedShader>(new CachedShader(params.host_ptr, params.cpu_addr, | 281 | return std::shared_ptr<CachedShader>(new CachedShader( |
| 283 | size_in_bytes, std::move(registry), | 282 | params.cpu_addr, size_in_bytes, std::move(registry), MakeEntries(ir), std::move(program))); |
| 284 | MakeEntries(ir), std::move(program))); | ||
| 285 | } | 283 | } |
| 286 | 284 | ||
| 287 | Shader CachedShader::CreateFromCache(const ShaderParameters& params, | 285 | Shader CachedShader::CreateFromCache(const ShaderParameters& params, |
| 288 | const PrecompiledShader& precompiled_shader, | 286 | const PrecompiledShader& precompiled_shader, |
| 289 | std::size_t size_in_bytes) { | 287 | std::size_t size_in_bytes) { |
| 290 | return std::shared_ptr<CachedShader>(new CachedShader( | 288 | return std::shared_ptr<CachedShader>( |
| 291 | params.host_ptr, params.cpu_addr, size_in_bytes, precompiled_shader.registry, | 289 | new CachedShader(params.cpu_addr, size_in_bytes, precompiled_shader.registry, |
| 292 | precompiled_shader.entries, precompiled_shader.program)); | 290 | precompiled_shader.entries, precompiled_shader.program)); |
| 293 | } | 291 | } |
| 294 | 292 | ||
| 295 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, | 293 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, |
| @@ -327,8 +325,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 327 | 325 | ||
| 328 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, | 326 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, |
| 329 | std::size_t end) { | 327 | std::size_t end) { |
| 330 | context->MakeCurrent(); | 328 | const auto scope = context->Acquire(); |
| 331 | SCOPE_EXIT({ return context->DoneCurrent(); }); | ||
| 332 | 329 | ||
| 333 | for (std::size_t i = begin; i < end; ++i) { | 330 | for (std::size_t i = begin; i < end; ++i) { |
| 334 | if (stop_loading) { | 331 | if (stop_loading) { |
| @@ -450,12 +447,14 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 450 | const GPUVAddr address{GetShaderAddress(system, program)}; | 447 | const GPUVAddr address{GetShaderAddress(system, program)}; |
| 451 | 448 | ||
| 452 | // Look up shader in the cache based on address | 449 | // Look up shader in the cache based on address |
| 453 | const auto host_ptr{memory_manager.GetPointer(address)}; | 450 | const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; |
| 454 | Shader shader{TryGet(host_ptr)}; | 451 | Shader shader{cpu_addr ? TryGet(*cpu_addr) : nullptr}; |
| 455 | if (shader) { | 452 | if (shader) { |
| 456 | return last_shaders[static_cast<std::size_t>(program)] = shader; | 453 | return last_shaders[static_cast<std::size_t>(program)] = shader; |
| 457 | } | 454 | } |
| 458 | 455 | ||
| 456 | const auto host_ptr{memory_manager.GetPointer(address)}; | ||
| 457 | |||
| 459 | // No shader found - create a new one | 458 | // No shader found - create a new one |
| 460 | ProgramCode code{GetShaderCode(memory_manager, address, host_ptr)}; | 459 | ProgramCode code{GetShaderCode(memory_manager, address, host_ptr)}; |
| 461 | ProgramCode code_b; | 460 | ProgramCode code_b; |
| @@ -466,9 +465,9 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 466 | 465 | ||
| 467 | const auto unique_identifier = GetUniqueIdentifier( | 466 | const auto unique_identifier = GetUniqueIdentifier( |
| 468 | GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); | 467 | GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b); |
| 469 | const auto cpu_addr{*memory_manager.GpuToCpuAddress(address)}; | 468 | |
| 470 | const ShaderParameters params{system, disk_cache, device, | 469 | const ShaderParameters params{system, disk_cache, device, |
| 471 | cpu_addr, host_ptr, unique_identifier}; | 470 | *cpu_addr, host_ptr, unique_identifier}; |
| 472 | 471 | ||
| 473 | const auto found = runtime_cache.find(unique_identifier); | 472 | const auto found = runtime_cache.find(unique_identifier); |
| 474 | if (found == runtime_cache.end()) { | 473 | if (found == runtime_cache.end()) { |
| @@ -485,18 +484,20 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 485 | 484 | ||
| 486 | Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { | 485 | Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { |
| 487 | auto& memory_manager{system.GPU().MemoryManager()}; | 486 | auto& memory_manager{system.GPU().MemoryManager()}; |
| 488 | const auto host_ptr{memory_manager.GetPointer(code_addr)}; | 487 | const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)}; |
| 489 | auto kernel = TryGet(host_ptr); | 488 | |
| 489 | auto kernel = cpu_addr ? TryGet(*cpu_addr) : nullptr; | ||
| 490 | if (kernel) { | 490 | if (kernel) { |
| 491 | return kernel; | 491 | return kernel; |
| 492 | } | 492 | } |
| 493 | 493 | ||
| 494 | const auto host_ptr{memory_manager.GetPointer(code_addr)}; | ||
| 494 | // No kernel found, create a new one | 495 | // No kernel found, create a new one |
| 495 | auto code{GetShaderCode(memory_manager, code_addr, host_ptr)}; | 496 | auto code{GetShaderCode(memory_manager, code_addr, host_ptr)}; |
| 496 | const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; | 497 | const auto unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)}; |
| 497 | const auto cpu_addr{*memory_manager.GpuToCpuAddress(code_addr)}; | 498 | |
| 498 | const ShaderParameters params{system, disk_cache, device, | 499 | const ShaderParameters params{system, disk_cache, device, |
| 499 | cpu_addr, host_ptr, unique_identifier}; | 500 | *cpu_addr, host_ptr, unique_identifier}; |
| 500 | 501 | ||
| 501 | const auto found = runtime_cache.find(unique_identifier); | 502 | const auto found = runtime_cache.find(unique_identifier); |
| 502 | if (found == runtime_cache.end()) { | 503 | if (found == runtime_cache.end()) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 4935019fc..c836df5bd 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -65,11 +65,6 @@ public: | |||
| 65 | /// Gets the GL program handle for the shader | 65 | /// Gets the GL program handle for the shader |
| 66 | GLuint GetHandle() const; | 66 | GLuint GetHandle() const; |
| 67 | 67 | ||
| 68 | /// Returns the guest CPU address of the shader | ||
| 69 | VAddr GetCpuAddr() const override { | ||
| 70 | return cpu_addr; | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Returns the size in bytes of the shader | 68 | /// Returns the size in bytes of the shader |
| 74 | std::size_t GetSizeInBytes() const override { | 69 | std::size_t GetSizeInBytes() const override { |
| 75 | return size_in_bytes; | 70 | return size_in_bytes; |
| @@ -90,13 +85,12 @@ public: | |||
| 90 | std::size_t size_in_bytes); | 85 | std::size_t size_in_bytes); |
| 91 | 86 | ||
| 92 | private: | 87 | private: |
| 93 | explicit CachedShader(const u8* host_ptr, VAddr cpu_addr, std::size_t size_in_bytes, | 88 | explicit CachedShader(VAddr cpu_addr, std::size_t size_in_bytes, |
| 94 | std::shared_ptr<VideoCommon::Shader::Registry> registry, | 89 | std::shared_ptr<VideoCommon::Shader::Registry> registry, |
| 95 | ShaderEntries entries, std::shared_ptr<OGLProgram> program); | 90 | ShaderEntries entries, std::shared_ptr<OGLProgram> program); |
| 96 | 91 | ||
| 97 | std::shared_ptr<VideoCommon::Shader::Registry> registry; | 92 | std::shared_ptr<VideoCommon::Shader::Registry> registry; |
| 98 | ShaderEntries entries; | 93 | ShaderEntries entries; |
| 99 | VAddr cpu_addr = 0; | ||
| 100 | std::size_t size_in_bytes = 0; | 94 | std::size_t size_in_bytes = 0; |
| 101 | std::shared_ptr<OGLProgram> program; | 95 | std::shared_ptr<OGLProgram> program; |
| 102 | }; | 96 | }; |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8aa4a7ac9..160ae4340 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -31,11 +31,11 @@ namespace { | |||
| 31 | 31 | ||
| 32 | using Tegra::Engines::ShaderType; | 32 | using Tegra::Engines::ShaderType; |
| 33 | using Tegra::Shader::Attribute; | 33 | using Tegra::Shader::Attribute; |
| 34 | using Tegra::Shader::AttributeUse; | ||
| 35 | using Tegra::Shader::Header; | 34 | using Tegra::Shader::Header; |
| 36 | using Tegra::Shader::IpaInterpMode; | 35 | using Tegra::Shader::IpaInterpMode; |
| 37 | using Tegra::Shader::IpaMode; | 36 | using Tegra::Shader::IpaMode; |
| 38 | using Tegra::Shader::IpaSampleMode; | 37 | using Tegra::Shader::IpaSampleMode; |
| 38 | using Tegra::Shader::PixelImap; | ||
| 39 | using Tegra::Shader::Register; | 39 | using Tegra::Shader::Register; |
| 40 | using VideoCommon::Shader::BuildTransformFeedback; | 40 | using VideoCommon::Shader::BuildTransformFeedback; |
| 41 | using VideoCommon::Shader::Registry; | 41 | using VideoCommon::Shader::Registry; |
| @@ -702,20 +702,19 @@ private: | |||
| 702 | code.AddNewLine(); | 702 | code.AddNewLine(); |
| 703 | } | 703 | } |
| 704 | 704 | ||
| 705 | std::string GetInputFlags(AttributeUse attribute) { | 705 | const char* GetInputFlags(PixelImap attribute) { |
| 706 | switch (attribute) { | 706 | switch (attribute) { |
| 707 | case AttributeUse::Perspective: | 707 | case PixelImap::Perspective: |
| 708 | // Default, Smooth | 708 | return "smooth"; |
| 709 | return {}; | 709 | case PixelImap::Constant: |
| 710 | case AttributeUse::Constant: | 710 | return "flat"; |
| 711 | return "flat "; | 711 | case PixelImap::ScreenLinear: |
| 712 | case AttributeUse::ScreenLinear: | 712 | return "noperspective"; |
| 713 | return "noperspective "; | 713 | case PixelImap::Unused: |
| 714 | default: | 714 | break; |
| 715 | case AttributeUse::Unused: | ||
| 716 | UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute)); | ||
| 717 | return {}; | ||
| 718 | } | 715 | } |
| 716 | UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<int>(attribute)); | ||
| 717 | return {}; | ||
| 719 | } | 718 | } |
| 720 | 719 | ||
| 721 | void DeclareInputAttributes() { | 720 | void DeclareInputAttributes() { |
| @@ -749,8 +748,8 @@ private: | |||
| 749 | 748 | ||
| 750 | std::string suffix; | 749 | std::string suffix; |
| 751 | if (stage == ShaderType::Fragment) { | 750 | if (stage == ShaderType::Fragment) { |
| 752 | const auto input_mode{header.ps.GetAttributeUse(location)}; | 751 | const auto input_mode{header.ps.GetPixelImap(location)}; |
| 753 | if (skip_unused && input_mode == AttributeUse::Unused) { | 752 | if (input_mode == PixelImap::Unused) { |
| 754 | return; | 753 | return; |
| 755 | } | 754 | } |
| 756 | suffix = GetInputFlags(input_mode); | 755 | suffix = GetInputFlags(input_mode); |
| @@ -927,7 +926,7 @@ private: | |||
| 927 | const u32 address{generic_base + index * generic_stride + element * element_stride}; | 926 | const u32 address{generic_base + index * generic_stride + element * element_stride}; |
| 928 | 927 | ||
| 929 | const bool declared = stage != ShaderType::Fragment || | 928 | const bool declared = stage != ShaderType::Fragment || |
| 930 | header.ps.GetAttributeUse(index) != AttributeUse::Unused; | 929 | header.ps.GetPixelImap(index) != PixelImap::Unused; |
| 931 | const std::string value = | 930 | const std::string value = |
| 932 | declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f"; | 931 | declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f"; |
| 933 | code.AddLine("case 0x{:X}U: return {};", address, value); | 932 | code.AddLine("case 0x{:X}U: return {};", address, value); |
| @@ -1142,8 +1141,7 @@ private: | |||
| 1142 | GetSwizzle(element)), | 1141 | GetSwizzle(element)), |
| 1143 | Type::Float}; | 1142 | Type::Float}; |
| 1144 | case ShaderType::Fragment: | 1143 | case ShaderType::Fragment: |
| 1145 | return {element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)), | 1144 | return {"gl_FragCoord"s + GetSwizzle(element), Type::Float}; |
| 1146 | Type::Float}; | ||
| 1147 | default: | 1145 | default: |
| 1148 | UNREACHABLE(); | 1146 | UNREACHABLE(); |
| 1149 | } | 1147 | } |
| @@ -2114,6 +2112,10 @@ private: | |||
| 2114 | 2112 | ||
| 2115 | template <const std::string_view& opname, Type type> | 2113 | template <const std::string_view& opname, Type type> |
| 2116 | Expression Atomic(Operation operation) { | 2114 | Expression Atomic(Operation operation) { |
| 2115 | if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) { | ||
| 2116 | UNIMPLEMENTED_MSG("Unimplemented Min & Max for atomic operations"); | ||
| 2117 | return {}; | ||
| 2118 | } | ||
| 2117 | return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), | 2119 | return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), |
| 2118 | Visit(operation[1]).As(type)), | 2120 | Visit(operation[1]).As(type)), |
| 2119 | type}; | 2121 | type}; |
| @@ -2307,6 +2309,8 @@ private: | |||
| 2307 | ~Func() = delete; | 2309 | ~Func() = delete; |
| 2308 | 2310 | ||
| 2309 | static constexpr std::string_view Add = "Add"; | 2311 | static constexpr std::string_view Add = "Add"; |
| 2312 | static constexpr std::string_view Min = "Min"; | ||
| 2313 | static constexpr std::string_view Max = "Max"; | ||
| 2310 | static constexpr std::string_view And = "And"; | 2314 | static constexpr std::string_view And = "And"; |
| 2311 | static constexpr std::string_view Or = "Or"; | 2315 | static constexpr std::string_view Or = "Or"; |
| 2312 | static constexpr std::string_view Xor = "Xor"; | 2316 | static constexpr std::string_view Xor = "Xor"; |
| @@ -2457,7 +2461,21 @@ private: | |||
| 2457 | &GLSLDecompiler::AtomicImage<Func::Xor>, | 2461 | &GLSLDecompiler::AtomicImage<Func::Xor>, |
| 2458 | &GLSLDecompiler::AtomicImage<Func::Exchange>, | 2462 | &GLSLDecompiler::AtomicImage<Func::Exchange>, |
| 2459 | 2463 | ||
| 2464 | &GLSLDecompiler::Atomic<Func::Exchange, Type::Uint>, | ||
| 2460 | &GLSLDecompiler::Atomic<Func::Add, Type::Uint>, | 2465 | &GLSLDecompiler::Atomic<Func::Add, Type::Uint>, |
| 2466 | &GLSLDecompiler::Atomic<Func::Min, Type::Uint>, | ||
| 2467 | &GLSLDecompiler::Atomic<Func::Max, Type::Uint>, | ||
| 2468 | &GLSLDecompiler::Atomic<Func::And, Type::Uint>, | ||
| 2469 | &GLSLDecompiler::Atomic<Func::Or, Type::Uint>, | ||
| 2470 | &GLSLDecompiler::Atomic<Func::Xor, Type::Uint>, | ||
| 2471 | |||
| 2472 | &GLSLDecompiler::Atomic<Func::Exchange, Type::Int>, | ||
| 2473 | &GLSLDecompiler::Atomic<Func::Add, Type::Int>, | ||
| 2474 | &GLSLDecompiler::Atomic<Func::Min, Type::Int>, | ||
| 2475 | &GLSLDecompiler::Atomic<Func::Max, Type::Int>, | ||
| 2476 | &GLSLDecompiler::Atomic<Func::And, Type::Int>, | ||
| 2477 | &GLSLDecompiler::Atomic<Func::Or, Type::Int>, | ||
| 2478 | &GLSLDecompiler::Atomic<Func::Xor, Type::Int>, | ||
| 2461 | 2479 | ||
| 2462 | &GLSLDecompiler::Branch, | 2480 | &GLSLDecompiler::Branch, |
| 2463 | &GLSLDecompiler::BranchIndirect, | 2481 | &GLSLDecompiler::BranchIndirect, |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index f424e3000..36590a6d0 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -24,7 +24,6 @@ using Tegra::Texture::SwizzleSource; | |||
| 24 | using VideoCore::MortonSwizzleMode; | 24 | using VideoCore::MortonSwizzleMode; |
| 25 | 25 | ||
| 26 | using VideoCore::Surface::PixelFormat; | 26 | using VideoCore::Surface::PixelFormat; |
| 27 | using VideoCore::Surface::SurfaceCompression; | ||
| 28 | using VideoCore::Surface::SurfaceTarget; | 27 | using VideoCore::Surface::SurfaceTarget; |
| 29 | using VideoCore::Surface::SurfaceType; | 28 | using VideoCore::Surface::SurfaceType; |
| 30 | 29 | ||
| @@ -37,102 +36,100 @@ namespace { | |||
| 37 | 36 | ||
| 38 | struct FormatTuple { | 37 | struct FormatTuple { |
| 39 | GLint internal_format; | 38 | GLint internal_format; |
| 40 | GLenum format; | 39 | GLenum format = GL_NONE; |
| 41 | GLenum type; | 40 | GLenum type = GL_NONE; |
| 42 | bool compressed; | ||
| 43 | }; | 41 | }; |
| 44 | 42 | ||
| 45 | constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{ | 43 | constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{ |
| 46 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // ABGR8U | 44 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // ABGR8U |
| 47 | {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, false}, // ABGR8S | 45 | {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE}, // ABGR8S |
| 48 | {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, false}, // ABGR8UI | 46 | {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // ABGR8UI |
| 49 | {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false}, // B5G6R5U | 47 | {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // B5G6R5U |
| 50 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false}, // A2B10G10R10U | 48 | {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10U |
| 51 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5U | 49 | {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5U |
| 52 | {GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8U | 50 | {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8U |
| 53 | {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, false}, // R8UI | 51 | {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // R8UI |
| 54 | {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F | 52 | {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16F |
| 55 | {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, false}, // RGBA16U | 53 | {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // RGBA16U |
| 56 | {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, false}, // RGBA16S | 54 | {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT}, // RGBA16S |
| 57 | {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, false}, // RGBA16UI | 55 | {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // RGBA16UI |
| 58 | {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F | 56 | {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, // R11FG11FB10F |
| 59 | {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, false}, // RGBA32UI | 57 | {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, // RGBA32UI |
| 60 | {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1 | 58 | {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}, // DXT1 |
| 61 | {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23 | 59 | {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}, // DXT23 |
| 62 | {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45 | 60 | {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}, // DXT45 |
| 63 | {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1 | 61 | {GL_COMPRESSED_RED_RGTC1}, // DXN1 |
| 64 | {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN2UNORM | 62 | {GL_COMPRESSED_RG_RGTC2}, // DXN2UNORM |
| 65 | {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, true}, // DXN2SNORM | 63 | {GL_COMPRESSED_SIGNED_RG_RGTC2}, // DXN2SNORM |
| 66 | {GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // BC7U | 64 | {GL_COMPRESSED_RGBA_BPTC_UNORM}, // BC7U |
| 67 | {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // BC6H_UF16 | 65 | {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UF16 |
| 68 | {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // BC6H_SF16 | 66 | {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SF16 |
| 69 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_4X4 | 67 | {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4 |
| 70 | {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, false}, // BGRA8 | 68 | {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 |
| 71 | {GL_RGBA32F, GL_RGBA, GL_FLOAT, false}, // RGBA32F | 69 | {GL_RGBA32F, GL_RGBA, GL_FLOAT}, // RGBA32F |
| 72 | {GL_RG32F, GL_RG, GL_FLOAT, false}, // RG32F | 70 | {GL_RG32F, GL_RG, GL_FLOAT}, // RG32F |
| 73 | {GL_R32F, GL_RED, GL_FLOAT, false}, // R32F | 71 | {GL_R32F, GL_RED, GL_FLOAT}, // R32F |
| 74 | {GL_R16F, GL_RED, GL_HALF_FLOAT, false}, // R16F | 72 | {GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16F |
| 75 | {GL_R16, GL_RED, GL_UNSIGNED_SHORT, false}, // R16U | 73 | {GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16U |
| 76 | {GL_R16_SNORM, GL_RED, GL_SHORT, false}, // R16S | 74 | {GL_R16_SNORM, GL_RED, GL_SHORT}, // R16S |
| 77 | {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, false}, // R16UI | 75 | {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16UI |
| 78 | {GL_R16I, GL_RED_INTEGER, GL_SHORT, false}, // R16I | 76 | {GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16I |
| 79 | {GL_RG16, GL_RG, GL_UNSIGNED_SHORT, false}, // RG16 | 77 | {GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // RG16 |
| 80 | {GL_RG16F, GL_RG, GL_HALF_FLOAT, false}, // RG16F | 78 | {GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16F |
| 81 | {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, false}, // RG16UI | 79 | {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // RG16UI |
| 82 | {GL_RG16I, GL_RG_INTEGER, GL_SHORT, false}, // RG16I | 80 | {GL_RG16I, GL_RG_INTEGER, GL_SHORT}, // RG16I |
| 83 | {GL_RG16_SNORM, GL_RG, GL_SHORT, false}, // RG16S | 81 | {GL_RG16_SNORM, GL_RG, GL_SHORT}, // RG16S |
| 84 | {GL_RGB32F, GL_RGB, GL_FLOAT, false}, // RGB32F | 82 | {GL_RGB32F, GL_RGB, GL_FLOAT}, // RGB32F |
| 85 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // RGBA8_SRGB | 83 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // RGBA8_SRGB |
| 86 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false}, // RG8U | 84 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // RG8U |
| 87 | {GL_RG8_SNORM, GL_RG, GL_BYTE, false}, // RG8S | 85 | {GL_RG8_SNORM, GL_RG, GL_BYTE}, // RG8S |
| 88 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false}, // RG32UI | 86 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, // RG32UI |
| 89 | {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBX16F | 87 | {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT}, // RGBX16F |
| 90 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false}, // R32UI | 88 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32UI |
| 91 | {GL_R32I, GL_RED_INTEGER, GL_INT, false}, // R32I | 89 | {GL_R32I, GL_RED_INTEGER, GL_INT}, // R32I |
| 92 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8 | 90 | {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8 |
| 93 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5 | 91 | {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5 |
| 94 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4 | 92 | {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4 |
| 95 | {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, false}, // BGRA8 | 93 | {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 |
| 96 | // Compressed sRGB formats | 94 | // Compressed sRGB formats |
| 97 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1_SRGB | 95 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // DXT1_SRGB |
| 98 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23_SRGB | 96 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // DXT23_SRGB |
| 99 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45_SRGB | 97 | {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // DXT45_SRGB |
| 100 | {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // BC7U_SRGB | 98 | {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7U_SRGB |
| 101 | {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV, false}, // R4G4B4A4U | 99 | {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // R4G4B4A4U |
| 102 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_4X4_SRGB | 100 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB |
| 103 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8_SRGB | 101 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB |
| 104 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5_SRGB | 102 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB |
| 105 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4_SRGB | 103 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}, // ASTC_2D_5X4_SRGB |
| 106 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X5 | 104 | {GL_COMPRESSED_RGBA_ASTC_5x5_KHR}, // ASTC_2D_5X5 |
| 107 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X5_SRGB | 105 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}, // ASTC_2D_5X5_SRGB |
| 108 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X8 | 106 | {GL_COMPRESSED_RGBA_ASTC_10x8_KHR}, // ASTC_2D_10X8 |
| 109 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X8_SRGB | 107 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB |
| 110 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X6 | 108 | {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6 |
| 111 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X6_SRGB | 109 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB |
| 112 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X10 | 110 | {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10 |
| 113 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X10_SRGB | 111 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB |
| 114 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_12X12 | 112 | {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12 |
| 115 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_12X12_SRGB | 113 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB |
| 116 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X6 | 114 | {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6 |
| 117 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X6_SRGB | 115 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB |
| 118 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X5 | 116 | {GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5 |
| 119 | {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X5_SRGB | 117 | {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB |
| 120 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV, false}, // E5B9G9R9F | 118 | {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9F |
| 121 | 119 | ||
| 122 | // Depth formats | 120 | // Depth formats |
| 123 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, false}, // Z32F | 121 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // Z32F |
| 124 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, false}, // Z16 | 122 | {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // Z16 |
| 125 | 123 | ||
| 126 | // DepthStencil formats | 124 | // DepthStencil formats |
| 127 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false}, // Z24S8 | 125 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // Z24S8 |
| 128 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false}, // S8Z24 | 126 | {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8Z24 |
| 129 | {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, false}, // Z32FS8 | 127 | {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // Z32FS8 |
| 130 | }}; | 128 | }}; |
| 131 | 129 | ||
| 132 | const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { | 130 | const FormatTuple& GetFormatTuple(PixelFormat pixel_format) { |
| 133 | ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size()); | 131 | ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size()); |
| 134 | const auto& format{tex_format_tuples[static_cast<std::size_t>(pixel_format)]}; | 132 | return tex_format_tuples[static_cast<std::size_t>(pixel_format)]; |
| 135 | return format; | ||
| 136 | } | 133 | } |
| 137 | 134 | ||
| 138 | GLenum GetTextureTarget(const SurfaceTarget& target) { | 135 | GLenum GetTextureTarget(const SurfaceTarget& target) { |
| @@ -242,13 +239,20 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte | |||
| 242 | 239 | ||
| 243 | } // Anonymous namespace | 240 | } // Anonymous namespace |
| 244 | 241 | ||
| 245 | CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) | 242 | CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params, |
| 246 | : VideoCommon::SurfaceBase<View>(gpu_addr, params) { | 243 | bool is_astc_supported) |
| 247 | const auto& tuple{GetFormatTuple(params.pixel_format)}; | 244 | : VideoCommon::SurfaceBase<View>(gpu_addr, params, is_astc_supported) { |
| 248 | internal_format = tuple.internal_format; | 245 | if (is_converted) { |
| 249 | format = tuple.format; | 246 | internal_format = params.srgb_conversion ? GL_SRGB8_ALPHA8 : GL_RGBA8; |
| 250 | type = tuple.type; | 247 | format = GL_RGBA; |
| 251 | is_compressed = tuple.compressed; | 248 | type = GL_UNSIGNED_BYTE; |
| 249 | } else { | ||
| 250 | const auto& tuple{GetFormatTuple(params.pixel_format)}; | ||
| 251 | internal_format = tuple.internal_format; | ||
| 252 | format = tuple.format; | ||
| 253 | type = tuple.type; | ||
| 254 | is_compressed = params.IsCompressed(); | ||
| 255 | } | ||
| 252 | target = GetTextureTarget(params.target); | 256 | target = GetTextureTarget(params.target); |
| 253 | texture = CreateTexture(params, target, internal_format, texture_buffer); | 257 | texture = CreateTexture(params, target, internal_format, texture_buffer); |
| 254 | DecorateSurfaceName(); | 258 | DecorateSurfaceName(); |
| @@ -264,7 +268,7 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { | |||
| 264 | 268 | ||
| 265 | if (params.IsBuffer()) { | 269 | if (params.IsBuffer()) { |
| 266 | glGetNamedBufferSubData(texture_buffer.handle, 0, | 270 | glGetNamedBufferSubData(texture_buffer.handle, 0, |
| 267 | static_cast<GLsizeiptr>(params.GetHostSizeInBytes()), | 271 | static_cast<GLsizeiptr>(params.GetHostSizeInBytes(false)), |
| 268 | staging_buffer.data()); | 272 | staging_buffer.data()); |
| 269 | return; | 273 | return; |
| 270 | } | 274 | } |
| @@ -272,9 +276,10 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { | |||
| 272 | SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); | 276 | SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); |
| 273 | 277 | ||
| 274 | for (u32 level = 0; level < params.emulated_levels; ++level) { | 278 | for (u32 level = 0; level < params.emulated_levels; ++level) { |
| 275 | glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level))); | 279 | glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level, is_converted))); |
| 276 | glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); | 280 | glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); |
| 277 | const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level); | 281 | const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted); |
| 282 | |||
| 278 | u8* const mip_data = staging_buffer.data() + mip_offset; | 283 | u8* const mip_data = staging_buffer.data() + mip_offset; |
| 279 | const GLsizei size = static_cast<GLsizei>(params.GetHostMipmapSize(level)); | 284 | const GLsizei size = static_cast<GLsizei>(params.GetHostMipmapSize(level)); |
| 280 | if (is_compressed) { | 285 | if (is_compressed) { |
| @@ -294,14 +299,10 @@ void CachedSurface::UploadTexture(const std::vector<u8>& staging_buffer) { | |||
| 294 | } | 299 | } |
| 295 | 300 | ||
| 296 | void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& staging_buffer) { | 301 | void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& staging_buffer) { |
| 297 | glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level))); | 302 | glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level, is_converted))); |
| 298 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); | 303 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); |
| 299 | 304 | ||
| 300 | auto compression_type = params.GetCompressionType(); | 305 | const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted); |
| 301 | |||
| 302 | const std::size_t mip_offset = compression_type == SurfaceCompression::Converted | ||
| 303 | ? params.GetConvertedMipmapOffset(level) | ||
| 304 | : params.GetHostMipmapLevelOffset(level); | ||
| 305 | const u8* buffer{staging_buffer.data() + mip_offset}; | 306 | const u8* buffer{staging_buffer.data() + mip_offset}; |
| 306 | if (is_compressed) { | 307 | if (is_compressed) { |
| 307 | const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))}; | 308 | const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))}; |
| @@ -482,7 +483,7 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const { | |||
| 482 | TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system, | 483 | TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system, |
| 483 | VideoCore::RasterizerInterface& rasterizer, | 484 | VideoCore::RasterizerInterface& rasterizer, |
| 484 | const Device& device, StateTracker& state_tracker) | 485 | const Device& device, StateTracker& state_tracker) |
| 485 | : TextureCacheBase{system, rasterizer}, state_tracker{state_tracker} { | 486 | : TextureCacheBase{system, rasterizer, device.HasASTC()}, state_tracker{state_tracker} { |
| 486 | src_framebuffer.Create(); | 487 | src_framebuffer.Create(); |
| 487 | dst_framebuffer.Create(); | 488 | dst_framebuffer.Create(); |
| 488 | } | 489 | } |
| @@ -490,7 +491,7 @@ TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system, | |||
| 490 | TextureCacheOpenGL::~TextureCacheOpenGL() = default; | 491 | TextureCacheOpenGL::~TextureCacheOpenGL() = default; |
| 491 | 492 | ||
| 492 | Surface TextureCacheOpenGL::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) { | 493 | Surface TextureCacheOpenGL::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) { |
| 493 | return std::make_shared<CachedSurface>(gpu_addr, params); | 494 | return std::make_shared<CachedSurface>(gpu_addr, params, is_astc_supported); |
| 494 | } | 495 | } |
| 495 | 496 | ||
| 496 | void TextureCacheOpenGL::ImageCopy(Surface& src_surface, Surface& dst_surface, | 497 | void TextureCacheOpenGL::ImageCopy(Surface& src_surface, Surface& dst_surface, |
| @@ -596,7 +597,7 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface) | |||
| 596 | 597 | ||
| 597 | glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); | 598 | glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); |
| 598 | 599 | ||
| 599 | if (source_format.compressed) { | 600 | if (src_surface->IsCompressed()) { |
| 600 | glGetCompressedTextureImage(src_surface->GetTexture(), 0, static_cast<GLsizei>(source_size), | 601 | glGetCompressedTextureImage(src_surface->GetTexture(), 0, static_cast<GLsizei>(source_size), |
| 601 | nullptr); | 602 | nullptr); |
| 602 | } else { | 603 | } else { |
| @@ -610,7 +611,7 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface) | |||
| 610 | const GLsizei width = static_cast<GLsizei>(dst_params.width); | 611 | const GLsizei width = static_cast<GLsizei>(dst_params.width); |
| 611 | const GLsizei height = static_cast<GLsizei>(dst_params.height); | 612 | const GLsizei height = static_cast<GLsizei>(dst_params.height); |
| 612 | const GLsizei depth = static_cast<GLsizei>(dst_params.depth); | 613 | const GLsizei depth = static_cast<GLsizei>(dst_params.depth); |
| 613 | if (dest_format.compressed) { | 614 | if (dst_surface->IsCompressed()) { |
| 614 | LOG_CRITICAL(HW_GPU, "Compressed buffer copy is unimplemented!"); | 615 | LOG_CRITICAL(HW_GPU, "Compressed buffer copy is unimplemented!"); |
| 615 | UNREACHABLE(); | 616 | UNREACHABLE(); |
| 616 | } else { | 617 | } else { |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 6658c6ffd..02d9981a1 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h | |||
| @@ -37,7 +37,7 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> { | |||
| 37 | friend CachedSurfaceView; | 37 | friend CachedSurfaceView; |
| 38 | 38 | ||
| 39 | public: | 39 | public: |
| 40 | explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params); | 40 | explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params, bool is_astc_supported); |
| 41 | ~CachedSurface(); | 41 | ~CachedSurface(); |
| 42 | 42 | ||
| 43 | void UploadTexture(const std::vector<u8>& staging_buffer) override; | 43 | void UploadTexture(const std::vector<u8>& staging_buffer) override; |
| @@ -51,6 +51,10 @@ public: | |||
| 51 | return texture.handle; | 51 | return texture.handle; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | bool IsCompressed() const { | ||
| 55 | return is_compressed; | ||
| 56 | } | ||
| 57 | |||
| 54 | protected: | 58 | protected: |
| 55 | void DecorateSurfaceName() override; | 59 | void DecorateSurfaceName() override; |
| 56 | 60 | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index fca5e3ec0..f1a28cc21 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -30,8 +30,6 @@ namespace OpenGL { | |||
| 30 | 30 | ||
| 31 | namespace { | 31 | namespace { |
| 32 | 32 | ||
| 33 | // If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have | ||
| 34 | // to wait on available presentation frames. | ||
| 35 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; | 33 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; |
| 36 | 34 | ||
| 37 | struct Frame { | 35 | struct Frame { |
| @@ -214,7 +212,7 @@ public: | |||
| 214 | std::deque<Frame*> present_queue; | 212 | std::deque<Frame*> present_queue; |
| 215 | Frame* previous_frame{}; | 213 | Frame* previous_frame{}; |
| 216 | 214 | ||
| 217 | FrameMailbox() : has_debug_tool{HasDebugTool()} { | 215 | FrameMailbox() { |
| 218 | for (auto& frame : swap_chain) { | 216 | for (auto& frame : swap_chain) { |
| 219 | free_queue.push(&frame); | 217 | free_queue.push(&frame); |
| 220 | } | 218 | } |
| @@ -285,13 +283,9 @@ public: | |||
| 285 | std::unique_lock lock{swap_chain_lock}; | 283 | std::unique_lock lock{swap_chain_lock}; |
| 286 | present_queue.push_front(frame); | 284 | present_queue.push_front(frame); |
| 287 | present_cv.notify_one(); | 285 | present_cv.notify_one(); |
| 288 | |||
| 289 | DebugNotifyNextFrame(); | ||
| 290 | } | 286 | } |
| 291 | 287 | ||
| 292 | Frame* TryGetPresentFrame(int timeout_ms) { | 288 | Frame* TryGetPresentFrame(int timeout_ms) { |
| 293 | DebugWaitForNextFrame(); | ||
| 294 | |||
| 295 | std::unique_lock lock{swap_chain_lock}; | 289 | std::unique_lock lock{swap_chain_lock}; |
| 296 | // wait for new entries in the present_queue | 290 | // wait for new entries in the present_queue |
| 297 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), | 291 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), |
| @@ -317,38 +311,12 @@ public: | |||
| 317 | previous_frame = frame; | 311 | previous_frame = frame; |
| 318 | return frame; | 312 | return frame; |
| 319 | } | 313 | } |
| 320 | |||
| 321 | private: | ||
| 322 | std::mutex debug_synch_mutex; | ||
| 323 | std::condition_variable debug_synch_condition; | ||
| 324 | std::atomic_int frame_for_debug{}; | ||
| 325 | const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step | ||
| 326 | |||
| 327 | /// Signal that a new frame is available (called from GPU thread) | ||
| 328 | void DebugNotifyNextFrame() { | ||
| 329 | if (!has_debug_tool) { | ||
| 330 | return; | ||
| 331 | } | ||
| 332 | frame_for_debug++; | ||
| 333 | std::lock_guard lock{debug_synch_mutex}; | ||
| 334 | debug_synch_condition.notify_one(); | ||
| 335 | } | ||
| 336 | |||
| 337 | /// Wait for a new frame to be available (called from presentation thread) | ||
| 338 | void DebugWaitForNextFrame() { | ||
| 339 | if (!has_debug_tool) { | ||
| 340 | return; | ||
| 341 | } | ||
| 342 | const int last_frame = frame_for_debug; | ||
| 343 | std::unique_lock lock{debug_synch_mutex}; | ||
| 344 | debug_synch_condition.wait(lock, | ||
| 345 | [this, last_frame] { return frame_for_debug > last_frame; }); | ||
| 346 | } | ||
| 347 | }; | 314 | }; |
| 348 | 315 | ||
| 349 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) | 316 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, |
| 317 | Core::Frontend::GraphicsContext& context) | ||
| 350 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, | 318 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, |
| 351 | frame_mailbox{std::make_unique<FrameMailbox>()} {} | 319 | frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {} |
| 352 | 320 | ||
| 353 | RendererOpenGL::~RendererOpenGL() = default; | 321 | RendererOpenGL::~RendererOpenGL() = default; |
| 354 | 322 | ||
| @@ -356,8 +324,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12 | |||
| 356 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); | 324 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); |
| 357 | 325 | ||
| 358 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 326 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 359 | render_window.PollEvents(); | ||
| 360 | |||
| 361 | if (!framebuffer) { | 327 | if (!framebuffer) { |
| 362 | return; | 328 | return; |
| 363 | } | 329 | } |
| @@ -413,6 +379,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 413 | m_current_frame++; | 379 | m_current_frame++; |
| 414 | rasterizer->TickFrame(); | 380 | rasterizer->TickFrame(); |
| 415 | } | 381 | } |
| 382 | |||
| 383 | render_window.PollEvents(); | ||
| 384 | if (has_debug_tool) { | ||
| 385 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||
| 386 | Present(0); | ||
| 387 | context.SwapBuffers(); | ||
| 388 | } | ||
| 416 | } | 389 | } |
| 417 | 390 | ||
| 418 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { | 391 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { |
| @@ -480,6 +453,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||
| 480 | } | 453 | } |
| 481 | 454 | ||
| 482 | void RendererOpenGL::InitOpenGLObjects() { | 455 | void RendererOpenGL::InitOpenGLObjects() { |
| 456 | frame_mailbox = std::make_unique<FrameMailbox>(); | ||
| 457 | |||
| 483 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, | 458 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, |
| 484 | 0.0f); | 459 | 0.0f); |
| 485 | 460 | ||
| @@ -692,12 +667,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 692 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 667 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 693 | } | 668 | } |
| 694 | 669 | ||
| 695 | void RendererOpenGL::TryPresent(int timeout_ms) { | 670 | bool RendererOpenGL::TryPresent(int timeout_ms) { |
| 671 | if (has_debug_tool) { | ||
| 672 | LOG_DEBUG(Render_OpenGL, | ||
| 673 | "Skipping presentation because we are presenting on the main context"); | ||
| 674 | return false; | ||
| 675 | } | ||
| 676 | return Present(timeout_ms); | ||
| 677 | } | ||
| 678 | |||
| 679 | bool RendererOpenGL::Present(int timeout_ms) { | ||
| 696 | const auto& layout = render_window.GetFramebufferLayout(); | 680 | const auto& layout = render_window.GetFramebufferLayout(); |
| 697 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); | 681 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); |
| 698 | if (!frame) { | 682 | if (!frame) { |
| 699 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); | 683 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); |
| 700 | return; | 684 | return false; |
| 701 | } | 685 | } |
| 702 | 686 | ||
| 703 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a | 687 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a |
| @@ -725,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) { | |||
| 725 | glFlush(); | 709 | glFlush(); |
| 726 | 710 | ||
| 727 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | 711 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| 712 | return true; | ||
| 728 | } | 713 | } |
| 729 | 714 | ||
| 730 | void RendererOpenGL::RenderScreenshot() { | 715 | void RendererOpenGL::RenderScreenshot() { |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 33073ce5b..50b647661 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -55,13 +55,14 @@ class FrameMailbox; | |||
| 55 | 55 | ||
| 56 | class RendererOpenGL final : public VideoCore::RendererBase { | 56 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 57 | public: | 57 | public: |
| 58 | explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); | 58 | explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, |
| 59 | Core::Frontend::GraphicsContext& context); | ||
| 59 | ~RendererOpenGL() override; | 60 | ~RendererOpenGL() override; |
| 60 | 61 | ||
| 61 | bool Init() override; | 62 | bool Init() override; |
| 62 | void ShutDown() override; | 63 | void ShutDown() override; |
| 63 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 64 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 64 | void TryPresent(int timeout_ms) override; | 65 | bool TryPresent(int timeout_ms) override; |
| 65 | 66 | ||
| 66 | private: | 67 | private: |
| 67 | /// Initializes the OpenGL state and creates persistent objects. | 68 | /// Initializes the OpenGL state and creates persistent objects. |
| @@ -89,8 +90,11 @@ private: | |||
| 89 | 90 | ||
| 90 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); | 91 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); |
| 91 | 92 | ||
| 93 | bool Present(int timeout_ms); | ||
| 94 | |||
| 92 | Core::Frontend::EmuWindow& emu_window; | 95 | Core::Frontend::EmuWindow& emu_window; |
| 93 | Core::System& system; | 96 | Core::System& system; |
| 97 | Core::Frontend::GraphicsContext& context; | ||
| 94 | 98 | ||
| 95 | StateTracker state_tracker{system}; | 99 | StateTracker state_tracker{system}; |
| 96 | 100 | ||
| @@ -115,6 +119,8 @@ private: | |||
| 115 | 119 | ||
| 116 | /// Frame presentation mailbox | 120 | /// Frame presentation mailbox |
| 117 | std::unique_ptr<FrameMailbox> frame_mailbox; | 121 | std::unique_ptr<FrameMailbox> frame_mailbox; |
| 122 | |||
| 123 | bool has_debug_tool = false; | ||
| 118 | }; | 124 | }; |
| 119 | 125 | ||
| 120 | } // namespace OpenGL | 126 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_vulkan/declarations.h b/src/video_core/renderer_vulkan/declarations.h index 323bf6b39..89a035ca4 100644 --- a/src/video_core/renderer_vulkan/declarations.h +++ b/src/video_core/renderer_vulkan/declarations.h | |||
| @@ -39,6 +39,7 @@ using UniqueFence = UniqueHandle<vk::Fence>; | |||
| 39 | using UniqueFramebuffer = UniqueHandle<vk::Framebuffer>; | 39 | using UniqueFramebuffer = UniqueHandle<vk::Framebuffer>; |
| 40 | using UniqueImage = UniqueHandle<vk::Image>; | 40 | using UniqueImage = UniqueHandle<vk::Image>; |
| 41 | using UniqueImageView = UniqueHandle<vk::ImageView>; | 41 | using UniqueImageView = UniqueHandle<vk::ImageView>; |
| 42 | using UniqueInstance = UniqueHandle<vk::Instance>; | ||
| 42 | using UniqueIndirectCommandsLayoutNVX = UniqueHandle<vk::IndirectCommandsLayoutNVX>; | 43 | using UniqueIndirectCommandsLayoutNVX = UniqueHandle<vk::IndirectCommandsLayoutNVX>; |
| 43 | using UniqueObjectTableNVX = UniqueHandle<vk::ObjectTableNVX>; | 44 | using UniqueObjectTableNVX = UniqueHandle<vk::ObjectTableNVX>; |
| 44 | using UniquePipeline = UniqueHandle<vk::Pipeline>; | 45 | using UniquePipeline = UniqueHandle<vk::Pipeline>; |
| @@ -50,6 +51,7 @@ using UniqueSampler = UniqueHandle<vk::Sampler>; | |||
| 50 | using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>; | 51 | using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>; |
| 51 | using UniqueSemaphore = UniqueHandle<vk::Semaphore>; | 52 | using UniqueSemaphore = UniqueHandle<vk::Semaphore>; |
| 52 | using UniqueShaderModule = UniqueHandle<vk::ShaderModule>; | 53 | using UniqueShaderModule = UniqueHandle<vk::ShaderModule>; |
| 54 | using UniqueSurfaceKHR = UniqueHandle<vk::SurfaceKHR>; | ||
| 53 | using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>; | 55 | using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>; |
| 54 | using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>; | 56 | using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>; |
| 55 | using UniqueDebugReportCallbackEXT = UniqueHandle<vk::DebugReportCallbackEXT>; | 57 | using UniqueDebugReportCallbackEXT = UniqueHandle<vk::DebugReportCallbackEXT>; |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 42bb01418..9cdb4b627 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -2,13 +2,18 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <cstring> | ||
| 5 | #include <memory> | 8 | #include <memory> |
| 6 | #include <optional> | 9 | #include <optional> |
| 10 | #include <string> | ||
| 7 | #include <vector> | 11 | #include <vector> |
| 8 | 12 | ||
| 9 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| 10 | 14 | ||
| 11 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| 16 | #include "common/dynamic_library.h" | ||
| 12 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| 13 | #include "common/telemetry.h" | 18 | #include "common/telemetry.h" |
| 14 | #include "core/core.h" | 19 | #include "core/core.h" |
| @@ -30,15 +35,30 @@ | |||
| 30 | #include "video_core/renderer_vulkan/vk_state_tracker.h" | 35 | #include "video_core/renderer_vulkan/vk_state_tracker.h" |
| 31 | #include "video_core/renderer_vulkan/vk_swapchain.h" | 36 | #include "video_core/renderer_vulkan/vk_swapchain.h" |
| 32 | 37 | ||
| 38 | // Include these late to avoid changing Vulkan-Hpp's dynamic dispatcher size | ||
| 39 | #ifdef _WIN32 | ||
| 40 | #include <windows.h> | ||
| 41 | // ensure include order | ||
| 42 | #include <vulkan/vulkan_win32.h> | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #ifdef __linux__ | ||
| 46 | #include <X11/Xlib.h> | ||
| 47 | #include <vulkan/vulkan_wayland.h> | ||
| 48 | #include <vulkan/vulkan_xlib.h> | ||
| 49 | #endif | ||
| 50 | |||
| 33 | namespace Vulkan { | 51 | namespace Vulkan { |
| 34 | 52 | ||
| 35 | namespace { | 53 | namespace { |
| 36 | 54 | ||
| 55 | using Core::Frontend::WindowSystemType; | ||
| 56 | |||
| 37 | VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, | 57 | VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, |
| 38 | VkDebugUtilsMessageTypeFlagsEXT type, | 58 | VkDebugUtilsMessageTypeFlagsEXT type, |
| 39 | const VkDebugUtilsMessengerCallbackDataEXT* data, | 59 | const VkDebugUtilsMessengerCallbackDataEXT* data, |
| 40 | [[maybe_unused]] void* user_data) { | 60 | [[maybe_unused]] void* user_data) { |
| 41 | const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_}; | 61 | const auto severity{static_cast<vk::DebugUtilsMessageSeverityFlagBitsEXT>(severity_)}; |
| 42 | const char* message{data->pMessage}; | 62 | const char* message{data->pMessage}; |
| 43 | 63 | ||
| 44 | if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) { | 64 | if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) { |
| @@ -53,6 +73,110 @@ VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, | |||
| 53 | return VK_FALSE; | 73 | return VK_FALSE; |
| 54 | } | 74 | } |
| 55 | 75 | ||
| 76 | Common::DynamicLibrary OpenVulkanLibrary() { | ||
| 77 | Common::DynamicLibrary library; | ||
| 78 | #ifdef __APPLE__ | ||
| 79 | // Check if a path to a specific Vulkan library has been specified. | ||
| 80 | char* libvulkan_env = getenv("LIBVULKAN_PATH"); | ||
| 81 | if (!libvulkan_env || !library.Open(libvulkan_env)) { | ||
| 82 | // Use the libvulkan.dylib from the application bundle. | ||
| 83 | std::string filename = File::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; | ||
| 84 | library.Open(filename.c_str()); | ||
| 85 | } | ||
| 86 | #else | ||
| 87 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); | ||
| 88 | if (!library.Open(filename.c_str())) { | ||
| 89 | // Android devices may not have libvulkan.so.1, only libvulkan.so. | ||
| 90 | filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); | ||
| 91 | library.Open(filename.c_str()); | ||
| 92 | } | ||
| 93 | #endif | ||
| 94 | return library; | ||
| 95 | } | ||
| 96 | |||
| 97 | UniqueInstance CreateInstance(Common::DynamicLibrary& library, vk::DispatchLoaderDynamic& dld, | ||
| 98 | WindowSystemType window_type = WindowSystemType::Headless, | ||
| 99 | bool enable_layers = false) { | ||
| 100 | if (!library.IsOpen()) { | ||
| 101 | LOG_ERROR(Render_Vulkan, "Vulkan library not available"); | ||
| 102 | return UniqueInstance{}; | ||
| 103 | } | ||
| 104 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | ||
| 105 | if (!library.GetSymbol("vkGetInstanceProcAddr", &vkGetInstanceProcAddr)) { | ||
| 106 | LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan"); | ||
| 107 | return UniqueInstance{}; | ||
| 108 | } | ||
| 109 | dld.init(vkGetInstanceProcAddr); | ||
| 110 | |||
| 111 | std::vector<const char*> extensions; | ||
| 112 | extensions.reserve(4); | ||
| 113 | switch (window_type) { | ||
| 114 | case Core::Frontend::WindowSystemType::Headless: | ||
| 115 | break; | ||
| 116 | #ifdef _WIN32 | ||
| 117 | case Core::Frontend::WindowSystemType::Windows: | ||
| 118 | extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); | ||
| 119 | break; | ||
| 120 | #endif | ||
| 121 | #ifdef __linux__ | ||
| 122 | case Core::Frontend::WindowSystemType::X11: | ||
| 123 | extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); | ||
| 124 | break; | ||
| 125 | case Core::Frontend::WindowSystemType::Wayland: | ||
| 126 | extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); | ||
| 127 | break; | ||
| 128 | #endif | ||
| 129 | default: | ||
| 130 | LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | if (window_type != Core::Frontend::WindowSystemType::Headless) { | ||
| 134 | extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); | ||
| 135 | } | ||
| 136 | if (enable_layers) { | ||
| 137 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 138 | } | ||
| 139 | |||
| 140 | u32 num_properties; | ||
| 141 | if (vk::enumerateInstanceExtensionProperties(nullptr, &num_properties, nullptr, dld) != | ||
| 142 | vk::Result::eSuccess) { | ||
| 143 | LOG_ERROR(Render_Vulkan, "Failed to query number of extension properties"); | ||
| 144 | return UniqueInstance{}; | ||
| 145 | } | ||
| 146 | std::vector<vk::ExtensionProperties> properties(num_properties); | ||
| 147 | if (vk::enumerateInstanceExtensionProperties(nullptr, &num_properties, properties.data(), | ||
| 148 | dld) != vk::Result::eSuccess) { | ||
| 149 | LOG_ERROR(Render_Vulkan, "Failed to query extension properties"); | ||
| 150 | return UniqueInstance{}; | ||
| 151 | } | ||
| 152 | |||
| 153 | for (const char* extension : extensions) { | ||
| 154 | const auto it = | ||
| 155 | std::find_if(properties.begin(), properties.end(), [extension](const auto& prop) { | ||
| 156 | return !std::strcmp(extension, prop.extensionName); | ||
| 157 | }); | ||
| 158 | if (it == properties.end()) { | ||
| 159 | LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension); | ||
| 160 | return UniqueInstance{}; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | const vk::ApplicationInfo application_info("yuzu Emulator", VK_MAKE_VERSION(0, 1, 0), | ||
| 165 | "yuzu Emulator", VK_MAKE_VERSION(0, 1, 0), | ||
| 166 | VK_API_VERSION_1_1); | ||
| 167 | const std::array layers = {"VK_LAYER_LUNARG_standard_validation"}; | ||
| 168 | const vk::InstanceCreateInfo instance_ci( | ||
| 169 | {}, &application_info, enable_layers ? static_cast<u32>(layers.size()) : 0, layers.data(), | ||
| 170 | static_cast<u32>(extensions.size()), extensions.data()); | ||
| 171 | vk::Instance unsafe_instance; | ||
| 172 | if (vk::createInstance(&instance_ci, nullptr, &unsafe_instance, dld) != vk::Result::eSuccess) { | ||
| 173 | LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance"); | ||
| 174 | return UniqueInstance{}; | ||
| 175 | } | ||
| 176 | dld.init(unsafe_instance); | ||
| 177 | return UniqueInstance(unsafe_instance, {nullptr, dld}); | ||
| 178 | } | ||
| 179 | |||
| 56 | std::string GetReadableVersion(u32 version) { | 180 | std::string GetReadableVersion(u32 version) { |
| 57 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), | 181 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), |
| 58 | VK_VERSION_PATCH(version)); | 182 | VK_VERSION_PATCH(version)); |
| @@ -141,32 +265,18 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 141 | render_window.PollEvents(); | 265 | render_window.PollEvents(); |
| 142 | } | 266 | } |
| 143 | 267 | ||
| 144 | void RendererVulkan::TryPresent(int /*timeout_ms*/) { | 268 | bool RendererVulkan::TryPresent(int /*timeout_ms*/) { |
| 145 | // TODO (bunnei): ImplementMe | 269 | // TODO (bunnei): ImplementMe |
| 270 | return true; | ||
| 146 | } | 271 | } |
| 147 | 272 | ||
| 148 | bool RendererVulkan::Init() { | 273 | bool RendererVulkan::Init() { |
| 149 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; | 274 | library = OpenVulkanLibrary(); |
| 150 | render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); | 275 | instance = CreateInstance(library, dld, render_window.GetWindowInfo().type, |
| 151 | const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr); | 276 | Settings::values.renderer_debug); |
| 152 | 277 | if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) { | |
| 153 | std::optional<vk::DebugUtilsMessengerEXT> callback; | ||
| 154 | if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) { | ||
| 155 | callback = CreateDebugCallback(dldi); | ||
| 156 | if (!callback) { | ||
| 157 | return false; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | if (!PickDevices(dldi)) { | ||
| 162 | if (callback) { | ||
| 163 | instance.destroy(*callback, nullptr, dldi); | ||
| 164 | } | ||
| 165 | return false; | 278 | return false; |
| 166 | } | 279 | } |
| 167 | debug_callback = UniqueDebugUtilsMessengerEXT( | ||
| 168 | *callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>( | ||
| 169 | instance, nullptr, device->GetDispatchLoader())); | ||
| 170 | 280 | ||
| 171 | Report(); | 281 | Report(); |
| 172 | 282 | ||
| @@ -175,7 +285,7 @@ bool RendererVulkan::Init() { | |||
| 175 | resource_manager = std::make_unique<VKResourceManager>(*device); | 285 | resource_manager = std::make_unique<VKResourceManager>(*device); |
| 176 | 286 | ||
| 177 | const auto& framebuffer = render_window.GetFramebufferLayout(); | 287 | const auto& framebuffer = render_window.GetFramebufferLayout(); |
| 178 | swapchain = std::make_unique<VKSwapchain>(surface, *device); | 288 | swapchain = std::make_unique<VKSwapchain>(*surface, *device); |
| 179 | swapchain->Create(framebuffer.width, framebuffer.height, false); | 289 | swapchain->Create(framebuffer.width, framebuffer.height, false); |
| 180 | 290 | ||
| 181 | state_tracker = std::make_unique<StateTracker>(system); | 291 | state_tracker = std::make_unique<StateTracker>(system); |
| @@ -212,8 +322,10 @@ void RendererVulkan::ShutDown() { | |||
| 212 | device.reset(); | 322 | device.reset(); |
| 213 | } | 323 | } |
| 214 | 324 | ||
| 215 | std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback( | 325 | bool RendererVulkan::CreateDebugCallback() { |
| 216 | const vk::DispatchLoaderDynamic& dldi) { | 326 | if (!Settings::values.renderer_debug) { |
| 327 | return true; | ||
| 328 | } | ||
| 217 | const vk::DebugUtilsMessengerCreateInfoEXT callback_ci( | 329 | const vk::DebugUtilsMessengerCreateInfoEXT callback_ci( |
| 218 | {}, | 330 | {}, |
| 219 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | | 331 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | |
| @@ -224,32 +336,88 @@ std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback( | |||
| 224 | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | | 336 | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | |
| 225 | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, | 337 | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, |
| 226 | &DebugCallback, nullptr); | 338 | &DebugCallback, nullptr); |
| 227 | vk::DebugUtilsMessengerEXT callback; | 339 | vk::DebugUtilsMessengerEXT unsafe_callback; |
| 228 | if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) != | 340 | if (instance->createDebugUtilsMessengerEXT(&callback_ci, nullptr, &unsafe_callback, dld) != |
| 229 | vk::Result::eSuccess) { | 341 | vk::Result::eSuccess) { |
| 230 | LOG_ERROR(Render_Vulkan, "Failed to create debug callback"); | 342 | LOG_ERROR(Render_Vulkan, "Failed to create debug callback"); |
| 231 | return {}; | 343 | return false; |
| 344 | } | ||
| 345 | debug_callback = UniqueDebugUtilsMessengerEXT(unsafe_callback, {*instance, nullptr, dld}); | ||
| 346 | return true; | ||
| 347 | } | ||
| 348 | |||
| 349 | bool RendererVulkan::CreateSurface() { | ||
| 350 | [[maybe_unused]] const auto& window_info = render_window.GetWindowInfo(); | ||
| 351 | VkSurfaceKHR unsafe_surface = nullptr; | ||
| 352 | |||
| 353 | #ifdef _WIN32 | ||
| 354 | if (window_info.type == Core::Frontend::WindowSystemType::Windows) { | ||
| 355 | const HWND hWnd = static_cast<HWND>(window_info.render_surface); | ||
| 356 | const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, | ||
| 357 | nullptr, 0, nullptr, hWnd}; | ||
| 358 | const auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>( | ||
| 359 | dld.vkGetInstanceProcAddr(*instance, "vkCreateWin32SurfaceKHR")); | ||
| 360 | if (!vkCreateWin32SurfaceKHR || vkCreateWin32SurfaceKHR(instance.get(), &win32_ci, nullptr, | ||
| 361 | &unsafe_surface) != VK_SUCCESS) { | ||
| 362 | LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface"); | ||
| 363 | return false; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | #endif | ||
| 367 | #ifdef __linux__ | ||
| 368 | if (window_info.type == Core::Frontend::WindowSystemType::X11) { | ||
| 369 | const VkXlibSurfaceCreateInfoKHR xlib_ci{ | ||
| 370 | VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, | ||
| 371 | static_cast<Display*>(window_info.display_connection), | ||
| 372 | reinterpret_cast<Window>(window_info.render_surface)}; | ||
| 373 | const auto vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>( | ||
| 374 | dld.vkGetInstanceProcAddr(*instance, "vkCreateXlibSurfaceKHR")); | ||
| 375 | if (!vkCreateXlibSurfaceKHR || vkCreateXlibSurfaceKHR(instance.get(), &xlib_ci, nullptr, | ||
| 376 | &unsafe_surface) != VK_SUCCESS) { | ||
| 377 | LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface"); | ||
| 378 | return false; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | if (window_info.type == Core::Frontend::WindowSystemType::Wayland) { | ||
| 382 | const VkWaylandSurfaceCreateInfoKHR wayland_ci{ | ||
| 383 | VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0, | ||
| 384 | static_cast<wl_display*>(window_info.display_connection), | ||
| 385 | static_cast<wl_surface*>(window_info.render_surface)}; | ||
| 386 | const auto vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>( | ||
| 387 | dld.vkGetInstanceProcAddr(*instance, "vkCreateWaylandSurfaceKHR")); | ||
| 388 | if (!vkCreateWaylandSurfaceKHR || | ||
| 389 | vkCreateWaylandSurfaceKHR(instance.get(), &wayland_ci, nullptr, &unsafe_surface) != | ||
| 390 | VK_SUCCESS) { | ||
| 391 | LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface"); | ||
| 392 | return false; | ||
| 393 | } | ||
| 394 | } | ||
| 395 | #endif | ||
| 396 | if (!unsafe_surface) { | ||
| 397 | LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); | ||
| 398 | return false; | ||
| 232 | } | 399 | } |
| 233 | return callback; | 400 | |
| 401 | surface = UniqueSurfaceKHR(unsafe_surface, {*instance, nullptr, dld}); | ||
| 402 | return true; | ||
| 234 | } | 403 | } |
| 235 | 404 | ||
| 236 | bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) { | 405 | bool RendererVulkan::PickDevices() { |
| 237 | const auto devices = instance.enumeratePhysicalDevices(dldi); | 406 | const auto devices = instance->enumeratePhysicalDevices(dld); |
| 238 | 407 | ||
| 239 | // TODO(Rodrigo): Choose device from config file | ||
| 240 | const s32 device_index = Settings::values.vulkan_device; | 408 | const s32 device_index = Settings::values.vulkan_device; |
| 241 | if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) { | 409 | if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) { |
| 242 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); | 410 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); |
| 243 | return false; | 411 | return false; |
| 244 | } | 412 | } |
| 245 | const vk::PhysicalDevice physical_device = devices[device_index]; | 413 | const vk::PhysicalDevice physical_device = devices[static_cast<std::size_t>(device_index)]; |
| 246 | 414 | ||
| 247 | if (!VKDevice::IsSuitable(dldi, physical_device, surface)) { | 415 | if (!VKDevice::IsSuitable(physical_device, *surface, dld)) { |
| 248 | return false; | 416 | return false; |
| 249 | } | 417 | } |
| 250 | 418 | ||
| 251 | device = std::make_unique<VKDevice>(dldi, physical_device, surface); | 419 | device = std::make_unique<VKDevice>(dld, physical_device, *surface); |
| 252 | return device->Create(dldi, instance); | 420 | return device->Create(*instance); |
| 253 | } | 421 | } |
| 254 | 422 | ||
| 255 | void RendererVulkan::Report() const { | 423 | void RendererVulkan::Report() const { |
| @@ -275,4 +443,33 @@ void RendererVulkan::Report() const { | |||
| 275 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | 443 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |
| 276 | } | 444 | } |
| 277 | 445 | ||
| 446 | std::vector<std::string> RendererVulkan::EnumerateDevices() { | ||
| 447 | // Avoid putting DispatchLoaderDynamic, it's too large | ||
| 448 | auto dld_memory = std::make_unique<vk::DispatchLoaderDynamic>(); | ||
| 449 | auto& dld = *dld_memory; | ||
| 450 | |||
| 451 | Common::DynamicLibrary library = OpenVulkanLibrary(); | ||
| 452 | UniqueInstance instance = CreateInstance(library, dld); | ||
| 453 | if (!instance) { | ||
| 454 | return {}; | ||
| 455 | } | ||
| 456 | |||
| 457 | u32 num_devices; | ||
| 458 | if (instance->enumeratePhysicalDevices(&num_devices, nullptr, dld) != vk::Result::eSuccess) { | ||
| 459 | return {}; | ||
| 460 | } | ||
| 461 | std::vector<vk::PhysicalDevice> devices(num_devices); | ||
| 462 | if (instance->enumeratePhysicalDevices(&num_devices, devices.data(), dld) != | ||
| 463 | vk::Result::eSuccess) { | ||
| 464 | return {}; | ||
| 465 | } | ||
| 466 | |||
| 467 | std::vector<std::string> names; | ||
| 468 | names.reserve(num_devices); | ||
| 469 | for (auto& device : devices) { | ||
| 470 | names.push_back(device.getProperties(dld).deviceName); | ||
| 471 | } | ||
| 472 | return names; | ||
| 473 | } | ||
| 474 | |||
| 278 | } // namespace Vulkan | 475 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 3da08d2e4..42e253de5 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -6,8 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <optional> | 8 | #include <optional> |
| 9 | #include <string> | ||
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | 11 | ||
| 12 | #include "common/dynamic_library.h" | ||
| 13 | |||
| 11 | #include "video_core/renderer_base.h" | 14 | #include "video_core/renderer_base.h" |
| 12 | #include "video_core/renderer_vulkan/declarations.h" | 15 | #include "video_core/renderer_vulkan/declarations.h" |
| 13 | 16 | ||
| @@ -42,20 +45,26 @@ public: | |||
| 42 | bool Init() override; | 45 | bool Init() override; |
| 43 | void ShutDown() override; | 46 | void ShutDown() override; |
| 44 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 47 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 45 | void TryPresent(int timeout_ms) override; | 48 | bool TryPresent(int timeout_ms) override; |
| 49 | |||
| 50 | static std::vector<std::string> EnumerateDevices(); | ||
| 46 | 51 | ||
| 47 | private: | 52 | private: |
| 48 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( | 53 | bool CreateDebugCallback(); |
| 49 | const vk::DispatchLoaderDynamic& dldi); | ||
| 50 | 54 | ||
| 51 | bool PickDevices(const vk::DispatchLoaderDynamic& dldi); | 55 | bool CreateSurface(); |
| 56 | |||
| 57 | bool PickDevices(); | ||
| 52 | 58 | ||
| 53 | void Report() const; | 59 | void Report() const; |
| 54 | 60 | ||
| 55 | Core::System& system; | 61 | Core::System& system; |
| 56 | 62 | ||
| 57 | vk::Instance instance; | 63 | Common::DynamicLibrary library; |
| 58 | vk::SurfaceKHR surface; | 64 | vk::DispatchLoaderDynamic dld; |
| 65 | |||
| 66 | UniqueInstance instance; | ||
| 67 | UniqueSurfaceKHR surface; | ||
| 59 | 68 | ||
| 60 | VKScreenInfo screen_info; | 69 | VKScreenInfo screen_info; |
| 61 | 70 | ||
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 1ba544943..326d74f29 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp | |||
| @@ -42,8 +42,8 @@ auto CreateStreamBuffer(const VKDevice& device, VKScheduler& scheduler) { | |||
| 42 | } // Anonymous namespace | 42 | } // Anonymous namespace |
| 43 | 43 | ||
| 44 | CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, | 44 | CachedBufferBlock::CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, |
| 45 | CacheAddr cache_addr, std::size_t size) | 45 | VAddr cpu_addr, std::size_t size) |
| 46 | : VideoCommon::BufferBlock{cache_addr, size} { | 46 | : VideoCommon::BufferBlock{cpu_addr, size} { |
| 47 | const vk::BufferCreateInfo buffer_ci({}, static_cast<vk::DeviceSize>(size), | 47 | const vk::BufferCreateInfo buffer_ci({}, static_cast<vk::DeviceSize>(size), |
| 48 | BufferUsage | vk::BufferUsageFlagBits::eTransferSrc | | 48 | BufferUsage | vk::BufferUsageFlagBits::eTransferSrc | |
| 49 | vk::BufferUsageFlagBits::eTransferDst, | 49 | vk::BufferUsageFlagBits::eTransferDst, |
| @@ -68,8 +68,8 @@ VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::S | |||
| 68 | 68 | ||
| 69 | VKBufferCache::~VKBufferCache() = default; | 69 | VKBufferCache::~VKBufferCache() = default; |
| 70 | 70 | ||
| 71 | Buffer VKBufferCache::CreateBlock(CacheAddr cache_addr, std::size_t size) { | 71 | Buffer VKBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { |
| 72 | return std::make_shared<CachedBufferBlock>(device, memory_manager, cache_addr, size); | 72 | return std::make_shared<CachedBufferBlock>(device, memory_manager, cpu_addr, size); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | const vk::Buffer* VKBufferCache::ToHandle(const Buffer& buffer) { | 75 | const vk::Buffer* VKBufferCache::ToHandle(const Buffer& buffer) { |
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 3f38eed0c..508214618 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h | |||
| @@ -30,7 +30,7 @@ class VKScheduler; | |||
| 30 | class CachedBufferBlock final : public VideoCommon::BufferBlock { | 30 | class CachedBufferBlock final : public VideoCommon::BufferBlock { |
| 31 | public: | 31 | public: |
| 32 | explicit CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, | 32 | explicit CachedBufferBlock(const VKDevice& device, VKMemoryManager& memory_manager, |
| 33 | CacheAddr cache_addr, std::size_t size); | 33 | VAddr cpu_addr, std::size_t size); |
| 34 | ~CachedBufferBlock(); | 34 | ~CachedBufferBlock(); |
| 35 | 35 | ||
| 36 | const vk::Buffer* GetHandle() const { | 36 | const vk::Buffer* GetHandle() const { |
| @@ -55,7 +55,7 @@ public: | |||
| 55 | protected: | 55 | protected: |
| 56 | void WriteBarrier() override {} | 56 | void WriteBarrier() override {} |
| 57 | 57 | ||
| 58 | Buffer CreateBlock(CacheAddr cache_addr, std::size_t size) override; | 58 | Buffer CreateBlock(VAddr cpu_addr, std::size_t size) override; |
| 59 | 59 | ||
| 60 | const vk::Buffer* ToHandle(const Buffer& buffer) override; | 60 | const vk::Buffer* ToHandle(const Buffer& buffer) override; |
| 61 | 61 | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 28d2fbc4f..6f4ae9132 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <string_view> | 10 | #include <string_view> |
| 11 | #include <thread> | 11 | #include <thread> |
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | |||
| 13 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 14 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 15 | #include "video_core/renderer_vulkan/declarations.h" | 16 | #include "video_core/renderer_vulkan/declarations.h" |
| @@ -35,20 +36,20 @@ void SetNext(void**& next, T& data) { | |||
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | template <typename T> | 38 | template <typename T> |
| 38 | T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) { | 39 | T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) { |
| 39 | vk::PhysicalDeviceFeatures2 features; | 40 | vk::PhysicalDeviceFeatures2 features; |
| 40 | T extension_features; | 41 | T extension_features; |
| 41 | features.pNext = &extension_features; | 42 | features.pNext = &extension_features; |
| 42 | physical.getFeatures2(&features, dldi); | 43 | physical.getFeatures2(&features, dld); |
| 43 | return extension_features; | 44 | return extension_features; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | template <typename T> | 47 | template <typename T> |
| 47 | T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) { | 48 | T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) { |
| 48 | vk::PhysicalDeviceProperties2 properties; | 49 | vk::PhysicalDeviceProperties2 properties; |
| 49 | T extension_properties; | 50 | T extension_properties; |
| 50 | properties.pNext = &extension_properties; | 51 | properties.pNext = &extension_properties; |
| 51 | physical.getProperties2(&properties, dldi); | 52 | physical.getProperties2(&properties, dld); |
| 52 | return extension_properties; | 53 | return extension_properties; |
| 53 | } | 54 | } |
| 54 | 55 | ||
| @@ -78,19 +79,19 @@ vk::FormatFeatureFlags GetFormatFeatures(vk::FormatProperties properties, Format | |||
| 78 | 79 | ||
| 79 | } // Anonymous namespace | 80 | } // Anonymous namespace |
| 80 | 81 | ||
| 81 | VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, | 82 | VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical, |
| 82 | vk::SurfaceKHR surface) | 83 | vk::SurfaceKHR surface) |
| 83 | : physical{physical}, properties{physical.getProperties(dldi)}, | 84 | : dld{dld}, physical{physical}, properties{physical.getProperties(dld)}, |
| 84 | format_properties{GetFormatProperties(dldi, physical)} { | 85 | format_properties{GetFormatProperties(dld, physical)} { |
| 85 | SetupFamilies(dldi, surface); | 86 | SetupFamilies(surface); |
| 86 | SetupFeatures(dldi); | 87 | SetupFeatures(); |
| 87 | } | 88 | } |
| 88 | 89 | ||
| 89 | VKDevice::~VKDevice() = default; | 90 | VKDevice::~VKDevice() = default; |
| 90 | 91 | ||
| 91 | bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) { | 92 | bool VKDevice::Create(vk::Instance instance) { |
| 92 | const auto queue_cis = GetDeviceQueueCreateInfos(); | 93 | const auto queue_cis = GetDeviceQueueCreateInfos(); |
| 93 | const std::vector extensions = LoadExtensions(dldi); | 94 | const std::vector extensions = LoadExtensions(); |
| 94 | 95 | ||
| 95 | vk::PhysicalDeviceFeatures2 features2; | 96 | vk::PhysicalDeviceFeatures2 features2; |
| 96 | void** next = &features2.pNext; | 97 | void** next = &features2.pNext; |
| @@ -165,15 +166,13 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan | |||
| 165 | nullptr); | 166 | nullptr); |
| 166 | device_ci.pNext = &features2; | 167 | device_ci.pNext = &features2; |
| 167 | 168 | ||
| 168 | vk::Device dummy_logical; | 169 | vk::Device unsafe_logical; |
| 169 | if (physical.createDevice(&device_ci, nullptr, &dummy_logical, dldi) != vk::Result::eSuccess) { | 170 | if (physical.createDevice(&device_ci, nullptr, &unsafe_logical, dld) != vk::Result::eSuccess) { |
| 170 | LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!"); | 171 | LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!"); |
| 171 | return false; | 172 | return false; |
| 172 | } | 173 | } |
| 173 | 174 | dld.init(instance, dld.vkGetInstanceProcAddr, unsafe_logical); | |
| 174 | dld.init(instance, dldi.vkGetInstanceProcAddr, dummy_logical, dldi.vkGetDeviceProcAddr); | 175 | logical = UniqueDevice(unsafe_logical, {nullptr, dld}); |
| 175 | logical = UniqueDevice( | ||
| 176 | dummy_logical, vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dld)); | ||
| 177 | 176 | ||
| 178 | CollectTelemetryParameters(); | 177 | CollectTelemetryParameters(); |
| 179 | 178 | ||
| @@ -235,20 +234,23 @@ void VKDevice::ReportLoss() const { | |||
| 235 | // *(VKGraphicsPipeline*)data[0] | 234 | // *(VKGraphicsPipeline*)data[0] |
| 236 | } | 235 | } |
| 237 | 236 | ||
| 238 | bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features, | 237 | bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const { |
| 239 | const vk::DispatchLoaderDynamic& dldi) const { | ||
| 240 | // Disable for now to avoid converting ASTC twice. | 238 | // Disable for now to avoid converting ASTC twice. |
| 241 | return false; | ||
| 242 | static constexpr std::array astc_formats = { | 239 | static constexpr std::array astc_formats = { |
| 243 | vk::Format::eAstc4x4SrgbBlock, vk::Format::eAstc8x8SrgbBlock, | 240 | vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock, |
| 244 | vk::Format::eAstc8x5SrgbBlock, vk::Format::eAstc5x4SrgbBlock, | 241 | vk::Format::eAstc5x4UnormBlock, vk::Format::eAstc5x4SrgbBlock, |
| 245 | vk::Format::eAstc5x5UnormBlock, vk::Format::eAstc5x5SrgbBlock, | 242 | vk::Format::eAstc5x5UnormBlock, vk::Format::eAstc5x5SrgbBlock, |
| 246 | vk::Format::eAstc10x8UnormBlock, vk::Format::eAstc10x8SrgbBlock, | 243 | vk::Format::eAstc6x5UnormBlock, vk::Format::eAstc6x5SrgbBlock, |
| 247 | vk::Format::eAstc6x6UnormBlock, vk::Format::eAstc6x6SrgbBlock, | 244 | vk::Format::eAstc6x6UnormBlock, vk::Format::eAstc6x6SrgbBlock, |
| 248 | vk::Format::eAstc10x10UnormBlock, vk::Format::eAstc10x10SrgbBlock, | 245 | vk::Format::eAstc8x5UnormBlock, vk::Format::eAstc8x5SrgbBlock, |
| 249 | vk::Format::eAstc12x12UnormBlock, vk::Format::eAstc12x12SrgbBlock, | ||
| 250 | vk::Format::eAstc8x6UnormBlock, vk::Format::eAstc8x6SrgbBlock, | 246 | vk::Format::eAstc8x6UnormBlock, vk::Format::eAstc8x6SrgbBlock, |
| 251 | vk::Format::eAstc6x5UnormBlock, vk::Format::eAstc6x5SrgbBlock}; | 247 | vk::Format::eAstc8x8UnormBlock, vk::Format::eAstc8x8SrgbBlock, |
| 248 | vk::Format::eAstc10x5UnormBlock, vk::Format::eAstc10x5SrgbBlock, | ||
| 249 | vk::Format::eAstc10x6UnormBlock, vk::Format::eAstc10x6SrgbBlock, | ||
| 250 | vk::Format::eAstc10x8UnormBlock, vk::Format::eAstc10x8SrgbBlock, | ||
| 251 | vk::Format::eAstc10x10UnormBlock, vk::Format::eAstc10x10SrgbBlock, | ||
| 252 | vk::Format::eAstc12x10UnormBlock, vk::Format::eAstc12x10SrgbBlock, | ||
| 253 | vk::Format::eAstc12x12UnormBlock, vk::Format::eAstc12x12SrgbBlock}; | ||
| 252 | if (!features.textureCompressionASTC_LDR) { | 254 | if (!features.textureCompressionASTC_LDR) { |
| 253 | return false; | 255 | return false; |
| 254 | } | 256 | } |
| @@ -257,7 +259,7 @@ bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features | |||
| 257 | vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc | | 259 | vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc | |
| 258 | vk::FormatFeatureFlagBits::eTransferDst}; | 260 | vk::FormatFeatureFlagBits::eTransferDst}; |
| 259 | for (const auto format : astc_formats) { | 261 | for (const auto format : astc_formats) { |
| 260 | const auto format_properties{physical.getFormatProperties(format, dldi)}; | 262 | const auto format_properties{physical.getFormatProperties(format, dld)}; |
| 261 | if (!(format_properties.optimalTilingFeatures & format_feature_usage)) { | 263 | if (!(format_properties.optimalTilingFeatures & format_feature_usage)) { |
| 262 | return false; | 264 | return false; |
| 263 | } | 265 | } |
| @@ -276,11 +278,9 @@ bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlag | |||
| 276 | return (supported_usage & wanted_usage) == wanted_usage; | 278 | return (supported_usage & wanted_usage) == wanted_usage; |
| 277 | } | 279 | } |
| 278 | 280 | ||
| 279 | bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, | 281 | bool VKDevice::IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface, |
| 280 | vk::SurfaceKHR surface) { | 282 | const vk::DispatchLoaderDynamic& dld) { |
| 281 | bool is_suitable = true; | 283 | static constexpr std::array required_extensions = { |
| 282 | |||
| 283 | constexpr std::array required_extensions = { | ||
| 284 | VK_KHR_SWAPCHAIN_EXTENSION_NAME, | 284 | VK_KHR_SWAPCHAIN_EXTENSION_NAME, |
| 285 | VK_KHR_16BIT_STORAGE_EXTENSION_NAME, | 285 | VK_KHR_16BIT_STORAGE_EXTENSION_NAME, |
| 286 | VK_KHR_8BIT_STORAGE_EXTENSION_NAME, | 286 | VK_KHR_8BIT_STORAGE_EXTENSION_NAME, |
| @@ -290,9 +290,10 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 290 | VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, | 290 | VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, |
| 291 | VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, | 291 | VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, |
| 292 | }; | 292 | }; |
| 293 | bool is_suitable = true; | ||
| 293 | std::bitset<required_extensions.size()> available_extensions{}; | 294 | std::bitset<required_extensions.size()> available_extensions{}; |
| 294 | 295 | ||
| 295 | for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) { | 296 | for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dld)) { |
| 296 | for (std::size_t i = 0; i < required_extensions.size(); ++i) { | 297 | for (std::size_t i = 0; i < required_extensions.size(); ++i) { |
| 297 | if (available_extensions[i]) { | 298 | if (available_extensions[i]) { |
| 298 | continue; | 299 | continue; |
| @@ -312,7 +313,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 312 | } | 313 | } |
| 313 | 314 | ||
| 314 | bool has_graphics{}, has_present{}; | 315 | bool has_graphics{}, has_present{}; |
| 315 | const auto queue_family_properties = physical.getQueueFamilyProperties(dldi); | 316 | const auto queue_family_properties = physical.getQueueFamilyProperties(dld); |
| 316 | for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { | 317 | for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { |
| 317 | const auto& family = queue_family_properties[i]; | 318 | const auto& family = queue_family_properties[i]; |
| 318 | if (family.queueCount == 0) { | 319 | if (family.queueCount == 0) { |
| @@ -320,7 +321,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 320 | } | 321 | } |
| 321 | has_graphics |= | 322 | has_graphics |= |
| 322 | (family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlagBits>(0); | 323 | (family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlagBits>(0); |
| 323 | has_present |= physical.getSurfaceSupportKHR(i, surface, dldi) != 0; | 324 | has_present |= physical.getSurfaceSupportKHR(i, surface, dld) != 0; |
| 324 | } | 325 | } |
| 325 | if (!has_graphics || !has_present) { | 326 | if (!has_graphics || !has_present) { |
| 326 | LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue"); | 327 | LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue"); |
| @@ -328,7 +329,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 328 | } | 329 | } |
| 329 | 330 | ||
| 330 | // TODO(Rodrigo): Check if the device matches all requeriments. | 331 | // TODO(Rodrigo): Check if the device matches all requeriments. |
| 331 | const auto properties{physical.getProperties(dldi)}; | 332 | const auto properties{physical.getProperties(dld)}; |
| 332 | const auto& limits{properties.limits}; | 333 | const auto& limits{properties.limits}; |
| 333 | 334 | ||
| 334 | constexpr u32 required_ubo_size = 65536; | 335 | constexpr u32 required_ubo_size = 65536; |
| @@ -345,7 +346,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 345 | is_suitable = false; | 346 | is_suitable = false; |
| 346 | } | 347 | } |
| 347 | 348 | ||
| 348 | const auto features{physical.getFeatures(dldi)}; | 349 | const auto features{physical.getFeatures(dld)}; |
| 349 | const std::array feature_report = { | 350 | const std::array feature_report = { |
| 350 | std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), | 351 | std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), |
| 351 | std::make_pair(features.independentBlend, "independentBlend"), | 352 | std::make_pair(features.independentBlend, "independentBlend"), |
| @@ -377,7 +378,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 377 | return is_suitable; | 378 | return is_suitable; |
| 378 | } | 379 | } |
| 379 | 380 | ||
| 380 | std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynamic& dldi) { | 381 | std::vector<const char*> VKDevice::LoadExtensions() { |
| 381 | std::vector<const char*> extensions; | 382 | std::vector<const char*> extensions; |
| 382 | const auto Test = [&](const vk::ExtensionProperties& extension, | 383 | const auto Test = [&](const vk::ExtensionProperties& extension, |
| 383 | std::optional<std::reference_wrapper<bool>> status, const char* name, | 384 | std::optional<std::reference_wrapper<bool>> status, const char* name, |
| @@ -408,7 +409,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 408 | bool has_khr_shader_float16_int8{}; | 409 | bool has_khr_shader_float16_int8{}; |
| 409 | bool has_ext_subgroup_size_control{}; | 410 | bool has_ext_subgroup_size_control{}; |
| 410 | bool has_ext_transform_feedback{}; | 411 | bool has_ext_transform_feedback{}; |
| 411 | for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) { | 412 | for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dld)) { |
| 412 | Test(extension, khr_uniform_buffer_standard_layout, | 413 | Test(extension, khr_uniform_buffer_standard_layout, |
| 413 | VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true); | 414 | VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true); |
| 414 | Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, | 415 | Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, |
| @@ -430,15 +431,15 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 430 | 431 | ||
| 431 | if (has_khr_shader_float16_int8) { | 432 | if (has_khr_shader_float16_int8) { |
| 432 | is_float16_supported = | 433 | is_float16_supported = |
| 433 | GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dldi).shaderFloat16; | 434 | GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dld).shaderFloat16; |
| 434 | extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); | 435 | extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); |
| 435 | } | 436 | } |
| 436 | 437 | ||
| 437 | if (has_ext_subgroup_size_control) { | 438 | if (has_ext_subgroup_size_control) { |
| 438 | const auto features = | 439 | const auto features = |
| 439 | GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dldi); | 440 | GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dld); |
| 440 | const auto properties = | 441 | const auto properties = |
| 441 | GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dldi); | 442 | GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dld); |
| 442 | 443 | ||
| 443 | is_warp_potentially_bigger = properties.maxSubgroupSize > GuestWarpSize; | 444 | is_warp_potentially_bigger = properties.maxSubgroupSize > GuestWarpSize; |
| 444 | 445 | ||
| @@ -453,9 +454,9 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 453 | 454 | ||
| 454 | if (has_ext_transform_feedback) { | 455 | if (has_ext_transform_feedback) { |
| 455 | const auto features = | 456 | const auto features = |
| 456 | GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dldi); | 457 | GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dld); |
| 457 | const auto properties = | 458 | const auto properties = |
| 458 | GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dldi); | 459 | GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dld); |
| 459 | 460 | ||
| 460 | if (features.transformFeedback && features.geometryStreams && | 461 | if (features.transformFeedback && features.geometryStreams && |
| 461 | properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers && | 462 | properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers && |
| @@ -468,10 +469,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 468 | return extensions; | 469 | return extensions; |
| 469 | } | 470 | } |
| 470 | 471 | ||
| 471 | void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface) { | 472 | void VKDevice::SetupFamilies(vk::SurfaceKHR surface) { |
| 472 | std::optional<u32> graphics_family_, present_family_; | 473 | std::optional<u32> graphics_family_, present_family_; |
| 473 | 474 | ||
| 474 | const auto queue_family_properties = physical.getQueueFamilyProperties(dldi); | 475 | const auto queue_family_properties = physical.getQueueFamilyProperties(dld); |
| 475 | for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { | 476 | for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) { |
| 476 | if (graphics_family_ && present_family_) | 477 | if (graphics_family_ && present_family_) |
| 477 | break; | 478 | break; |
| @@ -480,10 +481,12 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK | |||
| 480 | if (queue_family.queueCount == 0) | 481 | if (queue_family.queueCount == 0) |
| 481 | continue; | 482 | continue; |
| 482 | 483 | ||
| 483 | if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) | 484 | if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) { |
| 484 | graphics_family_ = i; | 485 | graphics_family_ = i; |
| 485 | if (physical.getSurfaceSupportKHR(i, surface, dldi)) | 486 | } |
| 487 | if (physical.getSurfaceSupportKHR(i, surface, dld)) { | ||
| 486 | present_family_ = i; | 488 | present_family_ = i; |
| 489 | } | ||
| 487 | } | 490 | } |
| 488 | ASSERT(graphics_family_ && present_family_); | 491 | ASSERT(graphics_family_ && present_family_); |
| 489 | 492 | ||
| @@ -491,10 +494,10 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK | |||
| 491 | present_family = *present_family_; | 494 | present_family = *present_family_; |
| 492 | } | 495 | } |
| 493 | 496 | ||
| 494 | void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) { | 497 | void VKDevice::SetupFeatures() { |
| 495 | const auto supported_features{physical.getFeatures(dldi)}; | 498 | const auto supported_features{physical.getFeatures(dld)}; |
| 496 | is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat; | 499 | is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat; |
| 497 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi); | 500 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features); |
| 498 | } | 501 | } |
| 499 | 502 | ||
| 500 | void VKDevice::CollectTelemetryParameters() { | 503 | void VKDevice::CollectTelemetryParameters() { |
| @@ -522,7 +525,7 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con | |||
| 522 | } | 525 | } |
| 523 | 526 | ||
| 524 | std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties( | 527 | std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties( |
| 525 | const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) { | 528 | const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical) { |
| 526 | static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32, | 529 | static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32, |
| 527 | vk::Format::eA8B8G8R8UintPack32, | 530 | vk::Format::eA8B8G8R8UintPack32, |
| 528 | vk::Format::eA8B8G8R8SnormPack32, | 531 | vk::Format::eA8B8G8R8SnormPack32, |
| @@ -572,28 +575,38 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti | |||
| 572 | vk::Format::eBc2SrgbBlock, | 575 | vk::Format::eBc2SrgbBlock, |
| 573 | vk::Format::eBc3SrgbBlock, | 576 | vk::Format::eBc3SrgbBlock, |
| 574 | vk::Format::eBc7SrgbBlock, | 577 | vk::Format::eBc7SrgbBlock, |
| 578 | vk::Format::eAstc4x4UnormBlock, | ||
| 575 | vk::Format::eAstc4x4SrgbBlock, | 579 | vk::Format::eAstc4x4SrgbBlock, |
| 576 | vk::Format::eAstc8x8SrgbBlock, | 580 | vk::Format::eAstc5x4UnormBlock, |
| 577 | vk::Format::eAstc8x5SrgbBlock, | ||
| 578 | vk::Format::eAstc5x4SrgbBlock, | 581 | vk::Format::eAstc5x4SrgbBlock, |
| 579 | vk::Format::eAstc5x5UnormBlock, | 582 | vk::Format::eAstc5x5UnormBlock, |
| 580 | vk::Format::eAstc5x5SrgbBlock, | 583 | vk::Format::eAstc5x5SrgbBlock, |
| 581 | vk::Format::eAstc10x8UnormBlock, | 584 | vk::Format::eAstc6x5UnormBlock, |
| 582 | vk::Format::eAstc10x8SrgbBlock, | 585 | vk::Format::eAstc6x5SrgbBlock, |
| 583 | vk::Format::eAstc6x6UnormBlock, | 586 | vk::Format::eAstc6x6UnormBlock, |
| 584 | vk::Format::eAstc6x6SrgbBlock, | 587 | vk::Format::eAstc6x6SrgbBlock, |
| 588 | vk::Format::eAstc8x5UnormBlock, | ||
| 589 | vk::Format::eAstc8x5SrgbBlock, | ||
| 590 | vk::Format::eAstc8x6UnormBlock, | ||
| 591 | vk::Format::eAstc8x6SrgbBlock, | ||
| 592 | vk::Format::eAstc8x8UnormBlock, | ||
| 593 | vk::Format::eAstc8x8SrgbBlock, | ||
| 594 | vk::Format::eAstc10x5UnormBlock, | ||
| 595 | vk::Format::eAstc10x5SrgbBlock, | ||
| 596 | vk::Format::eAstc10x6UnormBlock, | ||
| 597 | vk::Format::eAstc10x6SrgbBlock, | ||
| 598 | vk::Format::eAstc10x8UnormBlock, | ||
| 599 | vk::Format::eAstc10x8SrgbBlock, | ||
| 585 | vk::Format::eAstc10x10UnormBlock, | 600 | vk::Format::eAstc10x10UnormBlock, |
| 586 | vk::Format::eAstc10x10SrgbBlock, | 601 | vk::Format::eAstc10x10SrgbBlock, |
| 602 | vk::Format::eAstc12x10UnormBlock, | ||
| 603 | vk::Format::eAstc12x10SrgbBlock, | ||
| 587 | vk::Format::eAstc12x12UnormBlock, | 604 | vk::Format::eAstc12x12UnormBlock, |
| 588 | vk::Format::eAstc12x12SrgbBlock, | 605 | vk::Format::eAstc12x12SrgbBlock, |
| 589 | vk::Format::eAstc8x6UnormBlock, | ||
| 590 | vk::Format::eAstc8x6SrgbBlock, | ||
| 591 | vk::Format::eAstc6x5UnormBlock, | ||
| 592 | vk::Format::eAstc6x5SrgbBlock, | ||
| 593 | vk::Format::eE5B9G9R9UfloatPack32}; | 606 | vk::Format::eE5B9G9R9UfloatPack32}; |
| 594 | std::unordered_map<vk::Format, vk::FormatProperties> format_properties; | 607 | std::unordered_map<vk::Format, vk::FormatProperties> format_properties; |
| 595 | for (const auto format : formats) { | 608 | for (const auto format : formats) { |
| 596 | format_properties.emplace(format, physical.getFormatProperties(format, dldi)); | 609 | format_properties.emplace(format, physical.getFormatProperties(format, dld)); |
| 597 | } | 610 | } |
| 598 | return format_properties; | 611 | return format_properties; |
| 599 | } | 612 | } |
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index 6e656517f..d9d809852 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h | |||
| @@ -22,12 +22,12 @@ const u32 GuestWarpSize = 32; | |||
| 22 | /// Handles data specific to a physical device. | 22 | /// Handles data specific to a physical device. |
| 23 | class VKDevice final { | 23 | class VKDevice final { |
| 24 | public: | 24 | public: |
| 25 | explicit VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, | 25 | explicit VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical, |
| 26 | vk::SurfaceKHR surface); | 26 | vk::SurfaceKHR surface); |
| 27 | ~VKDevice(); | 27 | ~VKDevice(); |
| 28 | 28 | ||
| 29 | /// Initializes the device. Returns true on success. | 29 | /// Initializes the device. Returns true on success. |
| 30 | bool Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance); | 30 | bool Create(vk::Instance instance); |
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * Returns a format supported by the device for the passed requeriments. | 33 | * Returns a format supported by the device for the passed requeriments. |
| @@ -188,18 +188,18 @@ public: | |||
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | /// Checks if the physical device is suitable. | 190 | /// Checks if the physical device is suitable. |
| 191 | static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, | 191 | static bool IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface, |
| 192 | vk::SurfaceKHR surface); | 192 | const vk::DispatchLoaderDynamic& dld); |
| 193 | 193 | ||
| 194 | private: | 194 | private: |
| 195 | /// Loads extensions into a vector and stores available ones in this object. | 195 | /// Loads extensions into a vector and stores available ones in this object. |
| 196 | std::vector<const char*> LoadExtensions(const vk::DispatchLoaderDynamic& dldi); | 196 | std::vector<const char*> LoadExtensions(); |
| 197 | 197 | ||
| 198 | /// Sets up queue families. | 198 | /// Sets up queue families. |
| 199 | void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface); | 199 | void SetupFamilies(vk::SurfaceKHR surface); |
| 200 | 200 | ||
| 201 | /// Sets up device features. | 201 | /// Sets up device features. |
| 202 | void SetupFeatures(const vk::DispatchLoaderDynamic& dldi); | 202 | void SetupFeatures(); |
| 203 | 203 | ||
| 204 | /// Collects telemetry information from the device. | 204 | /// Collects telemetry information from the device. |
| 205 | void CollectTelemetryParameters(); | 205 | void CollectTelemetryParameters(); |
| @@ -208,8 +208,7 @@ private: | |||
| 208 | std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const; | 208 | std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const; |
| 209 | 209 | ||
| 210 | /// Returns true if ASTC textures are natively supported. | 210 | /// Returns true if ASTC textures are natively supported. |
| 211 | bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features, | 211 | bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const; |
| 212 | const vk::DispatchLoaderDynamic& dldi) const; | ||
| 213 | 212 | ||
| 214 | /// Returns true if a format is supported. | 213 | /// Returns true if a format is supported. |
| 215 | bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage, | 214 | bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage, |
| @@ -217,10 +216,10 @@ private: | |||
| 217 | 216 | ||
| 218 | /// Returns the device properties for Vulkan formats. | 217 | /// Returns the device properties for Vulkan formats. |
| 219 | static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties( | 218 | static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties( |
| 220 | const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical); | 219 | const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical); |
| 221 | 220 | ||
| 222 | const vk::PhysicalDevice physical; ///< Physical device. | ||
| 223 | vk::DispatchLoaderDynamic dld; ///< Device function pointers. | 221 | vk::DispatchLoaderDynamic dld; ///< Device function pointers. |
| 222 | vk::PhysicalDevice physical; ///< Physical device. | ||
| 224 | vk::PhysicalDeviceProperties properties; ///< Device properties. | 223 | vk::PhysicalDeviceProperties properties; ///< Device properties. |
| 225 | UniqueDevice logical; ///< Logical device. | 224 | UniqueDevice logical; ///< Logical device. |
| 226 | vk::Queue graphics_queue; ///< Main graphics queue. | 225 | vk::Queue graphics_queue; ///< Main graphics queue. |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 557b9d662..c2a426aeb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -158,11 +158,11 @@ u32 FillDescriptorLayout(const ShaderEntries& entries, | |||
| 158 | } // Anonymous namespace | 158 | } // Anonymous namespace |
| 159 | 159 | ||
| 160 | CachedShader::CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, | 160 | CachedShader::CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, |
| 161 | GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, | 161 | GPUVAddr gpu_addr, VAddr cpu_addr, ProgramCode program_code, |
| 162 | ProgramCode program_code, u32 main_offset) | 162 | u32 main_offset) |
| 163 | : RasterizerCacheObject{host_ptr}, gpu_addr{gpu_addr}, cpu_addr{cpu_addr}, | 163 | : RasterizerCacheObject{cpu_addr}, gpu_addr{gpu_addr}, program_code{std::move(program_code)}, |
| 164 | program_code{std::move(program_code)}, registry{stage, GetEngine(system, stage)}, | 164 | registry{stage, GetEngine(system, stage)}, shader_ir{this->program_code, main_offset, |
| 165 | shader_ir{this->program_code, main_offset, compiler_settings, registry}, | 165 | compiler_settings, registry}, |
| 166 | entries{GenerateShaderEntries(shader_ir)} {} | 166 | entries{GenerateShaderEntries(shader_ir)} {} |
| 167 | 167 | ||
| 168 | CachedShader::~CachedShader() = default; | 168 | CachedShader::~CachedShader() = default; |
| @@ -201,19 +201,19 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { | |||
| 201 | 201 | ||
| 202 | auto& memory_manager{system.GPU().MemoryManager()}; | 202 | auto& memory_manager{system.GPU().MemoryManager()}; |
| 203 | const GPUVAddr program_addr{GetShaderAddress(system, program)}; | 203 | const GPUVAddr program_addr{GetShaderAddress(system, program)}; |
| 204 | const auto host_ptr{memory_manager.GetPointer(program_addr)}; | 204 | const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); |
| 205 | auto shader = TryGet(host_ptr); | 205 | ASSERT(cpu_addr); |
| 206 | auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr; | ||
| 206 | if (!shader) { | 207 | if (!shader) { |
| 208 | const auto host_ptr{memory_manager.GetPointer(program_addr)}; | ||
| 209 | |||
| 207 | // No shader found - create a new one | 210 | // No shader found - create a new one |
| 208 | constexpr u32 stage_offset = 10; | 211 | constexpr u32 stage_offset = 10; |
| 209 | const auto stage = static_cast<Tegra::Engines::ShaderType>(index == 0 ? 0 : index - 1); | 212 | const auto stage = static_cast<Tegra::Engines::ShaderType>(index == 0 ? 0 : index - 1); |
| 210 | auto code = GetShaderCode(memory_manager, program_addr, host_ptr, false); | 213 | auto code = GetShaderCode(memory_manager, program_addr, host_ptr, false); |
| 211 | 214 | ||
| 212 | const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); | ||
| 213 | ASSERT(cpu_addr); | ||
| 214 | |||
| 215 | shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr, | 215 | shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr, |
| 216 | host_ptr, std::move(code), stage_offset); | 216 | std::move(code), stage_offset); |
| 217 | Register(shader); | 217 | Register(shader); |
| 218 | } | 218 | } |
| 219 | shaders[index] = std::move(shader); | 219 | shaders[index] = std::move(shader); |
| @@ -253,18 +253,19 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach | |||
| 253 | 253 | ||
| 254 | auto& memory_manager = system.GPU().MemoryManager(); | 254 | auto& memory_manager = system.GPU().MemoryManager(); |
| 255 | const auto program_addr = key.shader; | 255 | const auto program_addr = key.shader; |
| 256 | const auto host_ptr = memory_manager.GetPointer(program_addr); | ||
| 257 | 256 | ||
| 258 | auto shader = TryGet(host_ptr); | 257 | const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); |
| 258 | ASSERT(cpu_addr); | ||
| 259 | |||
| 260 | auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr; | ||
| 259 | if (!shader) { | 261 | if (!shader) { |
| 260 | // No shader found - create a new one | 262 | // No shader found - create a new one |
| 261 | const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); | 263 | const auto host_ptr = memory_manager.GetPointer(program_addr); |
| 262 | ASSERT(cpu_addr); | ||
| 263 | 264 | ||
| 264 | auto code = GetShaderCode(memory_manager, program_addr, host_ptr, true); | 265 | auto code = GetShaderCode(memory_manager, program_addr, host_ptr, true); |
| 265 | constexpr u32 kernel_main_offset = 0; | 266 | constexpr u32 kernel_main_offset = 0; |
| 266 | shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, | 267 | shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, |
| 267 | program_addr, *cpu_addr, host_ptr, std::move(code), | 268 | program_addr, *cpu_addr, std::move(code), |
| 268 | kernel_main_offset); | 269 | kernel_main_offset); |
| 269 | Register(shader); | 270 | Register(shader); |
| 270 | } | 271 | } |
| @@ -345,8 +346,9 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 345 | } | 346 | } |
| 346 | 347 | ||
| 347 | const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum); | 348 | const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum); |
| 348 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | 349 | const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr); |
| 349 | const auto shader = TryGet(host_ptr); | 350 | ASSERT(cpu_addr); |
| 351 | const auto shader = TryGet(*cpu_addr); | ||
| 350 | ASSERT(shader); | 352 | ASSERT(shader); |
| 351 | 353 | ||
| 352 | const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 | 354 | const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index c4c112290..27c01732f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -113,17 +113,13 @@ namespace Vulkan { | |||
| 113 | class CachedShader final : public RasterizerCacheObject { | 113 | class CachedShader final : public RasterizerCacheObject { |
| 114 | public: | 114 | public: |
| 115 | explicit CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, | 115 | explicit CachedShader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, |
| 116 | VAddr cpu_addr, u8* host_ptr, ProgramCode program_code, u32 main_offset); | 116 | VAddr cpu_addr, ProgramCode program_code, u32 main_offset); |
| 117 | ~CachedShader(); | 117 | ~CachedShader(); |
| 118 | 118 | ||
| 119 | GPUVAddr GetGpuAddr() const { | 119 | GPUVAddr GetGpuAddr() const { |
| 120 | return gpu_addr; | 120 | return gpu_addr; |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | VAddr GetCpuAddr() const override { | ||
| 124 | return cpu_addr; | ||
| 125 | } | ||
| 126 | |||
| 127 | std::size_t GetSizeInBytes() const override { | 123 | std::size_t GetSizeInBytes() const override { |
| 128 | return program_code.size() * sizeof(u64); | 124 | return program_code.size() * sizeof(u64); |
| 129 | } | 125 | } |
| @@ -149,7 +145,6 @@ private: | |||
| 149 | Tegra::Engines::ShaderType stage); | 145 | Tegra::Engines::ShaderType stage); |
| 150 | 146 | ||
| 151 | GPUVAddr gpu_addr{}; | 147 | GPUVAddr gpu_addr{}; |
| 152 | VAddr cpu_addr{}; | ||
| 153 | ProgramCode program_code; | 148 | ProgramCode program_code; |
| 154 | VideoCommon::Shader::Registry registry; | 149 | VideoCommon::Shader::Registry registry; |
| 155 | VideoCommon::Shader::ShaderIR shader_ir; | 150 | VideoCommon::Shader::ShaderIR shader_ir; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 58c69b786..0a2ea4fd4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -495,20 +495,26 @@ void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCore::QueryType type, | |||
| 495 | 495 | ||
| 496 | void RasterizerVulkan::FlushAll() {} | 496 | void RasterizerVulkan::FlushAll() {} |
| 497 | 497 | ||
| 498 | void RasterizerVulkan::FlushRegion(CacheAddr addr, u64 size) { | 498 | void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { |
| 499 | if (addr == 0 || size == 0) { | ||
| 500 | return; | ||
| 501 | } | ||
| 499 | texture_cache.FlushRegion(addr, size); | 502 | texture_cache.FlushRegion(addr, size); |
| 500 | buffer_cache.FlushRegion(addr, size); | 503 | buffer_cache.FlushRegion(addr, size); |
| 501 | query_cache.FlushRegion(addr, size); | 504 | query_cache.FlushRegion(addr, size); |
| 502 | } | 505 | } |
| 503 | 506 | ||
| 504 | void RasterizerVulkan::InvalidateRegion(CacheAddr addr, u64 size) { | 507 | void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { |
| 508 | if (addr == 0 || size == 0) { | ||
| 509 | return; | ||
| 510 | } | ||
| 505 | texture_cache.InvalidateRegion(addr, size); | 511 | texture_cache.InvalidateRegion(addr, size); |
| 506 | pipeline_cache.InvalidateRegion(addr, size); | 512 | pipeline_cache.InvalidateRegion(addr, size); |
| 507 | buffer_cache.InvalidateRegion(addr, size); | 513 | buffer_cache.InvalidateRegion(addr, size); |
| 508 | query_cache.InvalidateRegion(addr, size); | 514 | query_cache.InvalidateRegion(addr, size); |
| 509 | } | 515 | } |
| 510 | 516 | ||
| 511 | void RasterizerVulkan::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 517 | void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 512 | FlushRegion(addr, size); | 518 | FlushRegion(addr, size); |
| 513 | InvalidateRegion(addr, size); | 519 | InvalidateRegion(addr, size); |
| 514 | } | 520 | } |
| @@ -540,8 +546,7 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, | |||
| 540 | return false; | 546 | return false; |
| 541 | } | 547 | } |
| 542 | 548 | ||
| 543 | const u8* host_ptr{system.Memory().GetPointer(framebuffer_addr)}; | 549 | const auto surface{texture_cache.TryFindFramebufferSurface(framebuffer_addr)}; |
| 544 | const auto surface{texture_cache.TryFindFramebufferSurface(host_ptr)}; | ||
| 545 | if (!surface) { | 550 | if (!surface) { |
| 546 | return false; | 551 | return false; |
| 547 | } | 552 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 3185868e9..f642dde76 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -118,9 +118,9 @@ public: | |||
| 118 | void ResetCounter(VideoCore::QueryType type) override; | 118 | void ResetCounter(VideoCore::QueryType type) override; |
| 119 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; | 119 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; |
| 120 | void FlushAll() override; | 120 | void FlushAll() override; |
| 121 | void FlushRegion(CacheAddr addr, u64 size) override; | 121 | void FlushRegion(VAddr addr, u64 size) override; |
| 122 | void InvalidateRegion(CacheAddr addr, u64 size) override; | 122 | void InvalidateRegion(VAddr addr, u64 size) override; |
| 123 | void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; | 123 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 124 | void FlushCommands() override; | 124 | void FlushCommands() override; |
| 125 | void TickFrame() override; | 125 | void TickFrame() override; |
| 126 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, | 126 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 51ecb5567..b9f9e2714 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -35,7 +35,7 @@ namespace { | |||
| 35 | using Sirit::Id; | 35 | using Sirit::Id; |
| 36 | using Tegra::Engines::ShaderType; | 36 | using Tegra::Engines::ShaderType; |
| 37 | using Tegra::Shader::Attribute; | 37 | using Tegra::Shader::Attribute; |
| 38 | using Tegra::Shader::AttributeUse; | 38 | using Tegra::Shader::PixelImap; |
| 39 | using Tegra::Shader::Register; | 39 | using Tegra::Shader::Register; |
| 40 | using namespace VideoCommon::Shader; | 40 | using namespace VideoCommon::Shader; |
| 41 | 41 | ||
| @@ -752,16 +752,16 @@ private: | |||
| 752 | if (stage != ShaderType::Fragment) { | 752 | if (stage != ShaderType::Fragment) { |
| 753 | continue; | 753 | continue; |
| 754 | } | 754 | } |
| 755 | switch (header.ps.GetAttributeUse(location)) { | 755 | switch (header.ps.GetPixelImap(location)) { |
| 756 | case AttributeUse::Constant: | 756 | case PixelImap::Constant: |
| 757 | Decorate(id, spv::Decoration::Flat); | 757 | Decorate(id, spv::Decoration::Flat); |
| 758 | break; | 758 | break; |
| 759 | case AttributeUse::ScreenLinear: | 759 | case PixelImap::Perspective: |
| 760 | Decorate(id, spv::Decoration::NoPerspective); | ||
| 761 | break; | ||
| 762 | case AttributeUse::Perspective: | ||
| 763 | // Default | 760 | // Default |
| 764 | break; | 761 | break; |
| 762 | case PixelImap::ScreenLinear: | ||
| 763 | Decorate(id, spv::Decoration::NoPerspective); | ||
| 764 | break; | ||
| 765 | default: | 765 | default: |
| 766 | UNREACHABLE_MSG("Unused attribute being fetched"); | 766 | UNREACHABLE_MSG("Unused attribute being fetched"); |
| 767 | } | 767 | } |
| @@ -1145,9 +1145,6 @@ private: | |||
| 1145 | switch (attribute) { | 1145 | switch (attribute) { |
| 1146 | case Attribute::Index::Position: { | 1146 | case Attribute::Index::Position: { |
| 1147 | if (stage == ShaderType::Fragment) { | 1147 | if (stage == ShaderType::Fragment) { |
| 1148 | if (element == 3) { | ||
| 1149 | return {Constant(t_float, 1.0f), Type::Float}; | ||
| 1150 | } | ||
| 1151 | return {OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)), | 1148 | return {OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)), |
| 1152 | Type::Float}; | 1149 | Type::Float}; |
| 1153 | } | 1150 | } |
| @@ -1941,7 +1938,11 @@ private: | |||
| 1941 | return {}; | 1938 | return {}; |
| 1942 | } | 1939 | } |
| 1943 | 1940 | ||
| 1944 | Expression AtomicAdd(Operation operation) { | 1941 | template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, |
| 1942 | Type value_type = result_type> | ||
| 1943 | Expression Atomic(Operation operation) { | ||
| 1944 | const Id type_def = GetTypeDefinition(result_type); | ||
| 1945 | |||
| 1945 | Id pointer; | 1946 | Id pointer; |
| 1946 | if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { | 1947 | if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { |
| 1947 | pointer = GetSharedMemoryPointer(*smem); | 1948 | pointer = GetSharedMemoryPointer(*smem); |
| @@ -1949,14 +1950,15 @@ private: | |||
| 1949 | pointer = GetGlobalMemoryPointer(*gmem); | 1950 | pointer = GetGlobalMemoryPointer(*gmem); |
| 1950 | } else { | 1951 | } else { |
| 1951 | UNREACHABLE(); | 1952 | UNREACHABLE(); |
| 1952 | return {Constant(t_uint, 0), Type::Uint}; | 1953 | return {Constant(type_def, 0), result_type}; |
| 1953 | } | 1954 | } |
| 1954 | 1955 | ||
| 1956 | const Id value = As(Visit(operation[1]), value_type); | ||
| 1957 | |||
| 1955 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); | 1958 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); |
| 1956 | const Id semantics = Constant(t_uint, 0U); | 1959 | const Id semantics = Constant(type_def, 0); |
| 1957 | 1960 | ||
| 1958 | const Id value = AsUint(Visit(operation[1])); | 1961 | return {(this->*func)(type_def, pointer, scope, semantics, value), result_type}; |
| 1959 | return {OpAtomicIAdd(t_uint, pointer, scope, semantics, value), Type::Uint}; | ||
| 1960 | } | 1962 | } |
| 1961 | 1963 | ||
| 1962 | Expression Branch(Operation operation) { | 1964 | Expression Branch(Operation operation) { |
| @@ -2545,7 +2547,21 @@ private: | |||
| 2545 | &SPIRVDecompiler::AtomicImageXor, | 2547 | &SPIRVDecompiler::AtomicImageXor, |
| 2546 | &SPIRVDecompiler::AtomicImageExchange, | 2548 | &SPIRVDecompiler::AtomicImageExchange, |
| 2547 | 2549 | ||
| 2548 | &SPIRVDecompiler::AtomicAdd, | 2550 | &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Uint>, |
| 2551 | &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Uint>, | ||
| 2552 | &SPIRVDecompiler::Atomic<&Module::OpAtomicUMin, Type::Uint>, | ||
| 2553 | &SPIRVDecompiler::Atomic<&Module::OpAtomicUMax, Type::Uint>, | ||
| 2554 | &SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Uint>, | ||
| 2555 | &SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Uint>, | ||
| 2556 | &SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Uint>, | ||
| 2557 | |||
| 2558 | &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Int>, | ||
| 2559 | &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Int>, | ||
| 2560 | &SPIRVDecompiler::Atomic<&Module::OpAtomicSMin, Type::Int>, | ||
| 2561 | &SPIRVDecompiler::Atomic<&Module::OpAtomicSMax, Type::Int>, | ||
| 2562 | &SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Int>, | ||
| 2563 | &SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Int>, | ||
| 2564 | &SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Int>, | ||
| 2549 | 2565 | ||
| 2550 | &SPIRVDecompiler::Branch, | 2566 | &SPIRVDecompiler::Branch, |
| 2551 | &SPIRVDecompiler::BranchIndirect, | 2567 | &SPIRVDecompiler::BranchIndirect, |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 26175921b..5b9b39670 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -35,7 +35,6 @@ using VideoCore::MortonSwizzleMode; | |||
| 35 | 35 | ||
| 36 | using Tegra::Texture::SwizzleSource; | 36 | using Tegra::Texture::SwizzleSource; |
| 37 | using VideoCore::Surface::PixelFormat; | 37 | using VideoCore::Surface::PixelFormat; |
| 38 | using VideoCore::Surface::SurfaceCompression; | ||
| 39 | using VideoCore::Surface::SurfaceTarget; | 38 | using VideoCore::Surface::SurfaceTarget; |
| 40 | 39 | ||
| 41 | namespace { | 40 | namespace { |
| @@ -96,9 +95,10 @@ vk::ImageViewType GetImageViewType(SurfaceTarget target) { | |||
| 96 | return {}; | 95 | return {}; |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) { | 98 | UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params, |
| 99 | std::size_t host_memory_size) { | ||
| 100 | // TODO(Rodrigo): Move texture buffer creation to the buffer cache | 100 | // TODO(Rodrigo): Move texture buffer creation to the buffer cache |
| 101 | const vk::BufferCreateInfo buffer_ci({}, params.GetHostSizeInBytes(), | 101 | const vk::BufferCreateInfo buffer_ci({}, host_memory_size, |
| 102 | vk::BufferUsageFlagBits::eUniformTexelBuffer | | 102 | vk::BufferUsageFlagBits::eUniformTexelBuffer | |
| 103 | vk::BufferUsageFlagBits::eTransferSrc | | 103 | vk::BufferUsageFlagBits::eTransferSrc | |
| 104 | vk::BufferUsageFlagBits::eTransferDst, | 104 | vk::BufferUsageFlagBits::eTransferDst, |
| @@ -110,12 +110,13 @@ UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) { | |||
| 110 | 110 | ||
| 111 | vk::BufferViewCreateInfo GenerateBufferViewCreateInfo(const VKDevice& device, | 111 | vk::BufferViewCreateInfo GenerateBufferViewCreateInfo(const VKDevice& device, |
| 112 | const SurfaceParams& params, | 112 | const SurfaceParams& params, |
| 113 | vk::Buffer buffer) { | 113 | vk::Buffer buffer, |
| 114 | std::size_t host_memory_size) { | ||
| 114 | ASSERT(params.IsBuffer()); | 115 | ASSERT(params.IsBuffer()); |
| 115 | 116 | ||
| 116 | const auto format = | 117 | const auto format = |
| 117 | MaxwellToVK::SurfaceFormat(device, FormatType::Buffer, params.pixel_format).format; | 118 | MaxwellToVK::SurfaceFormat(device, FormatType::Buffer, params.pixel_format).format; |
| 118 | return vk::BufferViewCreateInfo({}, buffer, format, 0, params.GetHostSizeInBytes()); | 119 | return vk::BufferViewCreateInfo({}, buffer, format, 0, host_memory_size); |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | vk::ImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceParams& params) { | 122 | vk::ImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceParams& params) { |
| @@ -169,14 +170,15 @@ CachedSurface::CachedSurface(Core::System& system, const VKDevice& device, | |||
| 169 | VKResourceManager& resource_manager, VKMemoryManager& memory_manager, | 170 | VKResourceManager& resource_manager, VKMemoryManager& memory_manager, |
| 170 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool, | 171 | VKScheduler& scheduler, VKStagingBufferPool& staging_pool, |
| 171 | GPUVAddr gpu_addr, const SurfaceParams& params) | 172 | GPUVAddr gpu_addr, const SurfaceParams& params) |
| 172 | : SurfaceBase<View>{gpu_addr, params}, system{system}, device{device}, | 173 | : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, system{system}, |
| 173 | resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, | 174 | device{device}, resource_manager{resource_manager}, |
| 174 | staging_pool{staging_pool} { | 175 | memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} { |
| 175 | if (params.IsBuffer()) { | 176 | if (params.IsBuffer()) { |
| 176 | buffer = CreateBuffer(device, params); | 177 | buffer = CreateBuffer(device, params, host_memory_size); |
| 177 | commit = memory_manager.Commit(*buffer, false); | 178 | commit = memory_manager.Commit(*buffer, false); |
| 178 | 179 | ||
| 179 | const auto buffer_view_ci = GenerateBufferViewCreateInfo(device, params, *buffer); | 180 | const auto buffer_view_ci = |
| 181 | GenerateBufferViewCreateInfo(device, params, *buffer, host_memory_size); | ||
| 180 | format = buffer_view_ci.format; | 182 | format = buffer_view_ci.format; |
| 181 | 183 | ||
| 182 | const auto dev = device.GetLogical(); | 184 | const auto dev = device.GetLogical(); |
| @@ -255,7 +257,7 @@ void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) { | |||
| 255 | std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size); | 257 | std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size); |
| 256 | 258 | ||
| 257 | scheduler.Record([src_buffer = *src_buffer.handle, dst_buffer = *buffer, | 259 | scheduler.Record([src_buffer = *src_buffer.handle, dst_buffer = *buffer, |
| 258 | size = params.GetHostSizeInBytes()](auto cmdbuf, auto& dld) { | 260 | size = host_memory_size](auto cmdbuf, auto& dld) { |
| 259 | const vk::BufferCopy copy(0, 0, size); | 261 | const vk::BufferCopy copy(0, 0, size); |
| 260 | cmdbuf.copyBuffer(src_buffer, dst_buffer, {copy}, dld); | 262 | cmdbuf.copyBuffer(src_buffer, dst_buffer, {copy}, dld); |
| 261 | 263 | ||
| @@ -299,10 +301,7 @@ void CachedSurface::UploadImage(const std::vector<u8>& staging_buffer) { | |||
| 299 | 301 | ||
| 300 | vk::BufferImageCopy CachedSurface::GetBufferImageCopy(u32 level) const { | 302 | vk::BufferImageCopy CachedSurface::GetBufferImageCopy(u32 level) const { |
| 301 | const u32 vk_depth = params.target == SurfaceTarget::Texture3D ? params.GetMipDepth(level) : 1; | 303 | const u32 vk_depth = params.target == SurfaceTarget::Texture3D ? params.GetMipDepth(level) : 1; |
| 302 | const auto compression_type = params.GetCompressionType(); | 304 | const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted); |
| 303 | const std::size_t mip_offset = compression_type == SurfaceCompression::Converted | ||
| 304 | ? params.GetConvertedMipmapOffset(level) | ||
| 305 | : params.GetHostMipmapLevelOffset(level); | ||
| 306 | 305 | ||
| 307 | return vk::BufferImageCopy( | 306 | return vk::BufferImageCopy( |
| 308 | mip_offset, 0, 0, | 307 | mip_offset, 0, 0, |
| @@ -390,8 +389,9 @@ VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterf | |||
| 390 | const VKDevice& device, VKResourceManager& resource_manager, | 389 | const VKDevice& device, VKResourceManager& resource_manager, |
| 391 | VKMemoryManager& memory_manager, VKScheduler& scheduler, | 390 | VKMemoryManager& memory_manager, VKScheduler& scheduler, |
| 392 | VKStagingBufferPool& staging_pool) | 391 | VKStagingBufferPool& staging_pool) |
| 393 | : TextureCache(system, rasterizer), device{device}, resource_manager{resource_manager}, | 392 | : TextureCache(system, rasterizer, device.IsOptimalAstcSupported()), device{device}, |
| 394 | memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {} | 393 | resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, |
| 394 | staging_pool{staging_pool} {} | ||
| 395 | 395 | ||
| 396 | VKTextureCache::~VKTextureCache() = default; | 396 | VKTextureCache::~VKTextureCache() = default; |
| 397 | 397 | ||
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp new file mode 100644 index 000000000..9b94dfff1 --- /dev/null +++ b/src/video_core/renderer_vulkan/wrapper.cpp | |||
| @@ -0,0 +1,750 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <exception> | ||
| 6 | #include <memory> | ||
| 7 | #include <optional> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 14 | |||
| 15 | namespace Vulkan::vk { | ||
| 16 | |||
| 17 | namespace { | ||
| 18 | |||
| 19 | template <typename T> | ||
| 20 | bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name, | ||
| 21 | VkInstance instance = nullptr) noexcept { | ||
| 22 | result = reinterpret_cast<T>(dld.vkGetInstanceProcAddr(instance, proc_name)); | ||
| 23 | return result != nullptr; | ||
| 24 | } | ||
| 25 | |||
| 26 | template <typename T> | ||
| 27 | void Proc(T& result, const DeviceDispatch& dld, const char* proc_name, VkDevice device) noexcept { | ||
| 28 | result = reinterpret_cast<T>(dld.vkGetDeviceProcAddr(device, proc_name)); | ||
| 29 | } | ||
| 30 | |||
| 31 | void Load(VkDevice device, DeviceDispatch& dld) noexcept { | ||
| 32 | #define X(name) Proc(dld.name, dld, #name, device) | ||
| 33 | X(vkAcquireNextImageKHR); | ||
| 34 | X(vkAllocateCommandBuffers); | ||
| 35 | X(vkAllocateDescriptorSets); | ||
| 36 | X(vkAllocateMemory); | ||
| 37 | X(vkBeginCommandBuffer); | ||
| 38 | X(vkBindBufferMemory); | ||
| 39 | X(vkBindImageMemory); | ||
| 40 | X(vkCmdBeginQuery); | ||
| 41 | X(vkCmdBeginRenderPass); | ||
| 42 | X(vkCmdBeginTransformFeedbackEXT); | ||
| 43 | X(vkCmdBindDescriptorSets); | ||
| 44 | X(vkCmdBindIndexBuffer); | ||
| 45 | X(vkCmdBindPipeline); | ||
| 46 | X(vkCmdBindTransformFeedbackBuffersEXT); | ||
| 47 | X(vkCmdBindVertexBuffers); | ||
| 48 | X(vkCmdBlitImage); | ||
| 49 | X(vkCmdClearAttachments); | ||
| 50 | X(vkCmdCopyBuffer); | ||
| 51 | X(vkCmdCopyBufferToImage); | ||
| 52 | X(vkCmdCopyImage); | ||
| 53 | X(vkCmdCopyImageToBuffer); | ||
| 54 | X(vkCmdDispatch); | ||
| 55 | X(vkCmdDraw); | ||
| 56 | X(vkCmdDrawIndexed); | ||
| 57 | X(vkCmdEndQuery); | ||
| 58 | X(vkCmdEndRenderPass); | ||
| 59 | X(vkCmdEndTransformFeedbackEXT); | ||
| 60 | X(vkCmdFillBuffer); | ||
| 61 | X(vkCmdPipelineBarrier); | ||
| 62 | X(vkCmdPushConstants); | ||
| 63 | X(vkCmdSetBlendConstants); | ||
| 64 | X(vkCmdSetCheckpointNV); | ||
| 65 | X(vkCmdSetDepthBias); | ||
| 66 | X(vkCmdSetDepthBounds); | ||
| 67 | X(vkCmdSetScissor); | ||
| 68 | X(vkCmdSetStencilCompareMask); | ||
| 69 | X(vkCmdSetStencilReference); | ||
| 70 | X(vkCmdSetStencilWriteMask); | ||
| 71 | X(vkCmdSetViewport); | ||
| 72 | X(vkCreateBuffer); | ||
| 73 | X(vkCreateBufferView); | ||
| 74 | X(vkCreateCommandPool); | ||
| 75 | X(vkCreateComputePipelines); | ||
| 76 | X(vkCreateDescriptorPool); | ||
| 77 | X(vkCreateDescriptorSetLayout); | ||
| 78 | X(vkCreateDescriptorUpdateTemplateKHR); | ||
| 79 | X(vkCreateFence); | ||
| 80 | X(vkCreateFramebuffer); | ||
| 81 | X(vkCreateGraphicsPipelines); | ||
| 82 | X(vkCreateImage); | ||
| 83 | X(vkCreateImageView); | ||
| 84 | X(vkCreatePipelineLayout); | ||
| 85 | X(vkCreateQueryPool); | ||
| 86 | X(vkCreateRenderPass); | ||
| 87 | X(vkCreateSampler); | ||
| 88 | X(vkCreateSemaphore); | ||
| 89 | X(vkCreateShaderModule); | ||
| 90 | X(vkCreateSwapchainKHR); | ||
| 91 | X(vkDestroyBuffer); | ||
| 92 | X(vkDestroyBufferView); | ||
| 93 | X(vkDestroyCommandPool); | ||
| 94 | X(vkDestroyDescriptorPool); | ||
| 95 | X(vkDestroyDescriptorSetLayout); | ||
| 96 | X(vkDestroyDescriptorUpdateTemplateKHR); | ||
| 97 | X(vkDestroyFence); | ||
| 98 | X(vkDestroyFramebuffer); | ||
| 99 | X(vkDestroyImage); | ||
| 100 | X(vkDestroyImageView); | ||
| 101 | X(vkDestroyPipeline); | ||
| 102 | X(vkDestroyPipelineLayout); | ||
| 103 | X(vkDestroyQueryPool); | ||
| 104 | X(vkDestroyRenderPass); | ||
| 105 | X(vkDestroySampler); | ||
| 106 | X(vkDestroySemaphore); | ||
| 107 | X(vkDestroyShaderModule); | ||
| 108 | X(vkDestroySwapchainKHR); | ||
| 109 | X(vkDeviceWaitIdle); | ||
| 110 | X(vkEndCommandBuffer); | ||
| 111 | X(vkFreeCommandBuffers); | ||
| 112 | X(vkFreeDescriptorSets); | ||
| 113 | X(vkFreeMemory); | ||
| 114 | X(vkGetBufferMemoryRequirements); | ||
| 115 | X(vkGetDeviceQueue); | ||
| 116 | X(vkGetFenceStatus); | ||
| 117 | X(vkGetImageMemoryRequirements); | ||
| 118 | X(vkGetQueryPoolResults); | ||
| 119 | X(vkGetQueueCheckpointDataNV); | ||
| 120 | X(vkMapMemory); | ||
| 121 | X(vkQueueSubmit); | ||
| 122 | X(vkResetFences); | ||
| 123 | X(vkResetQueryPoolEXT); | ||
| 124 | X(vkUnmapMemory); | ||
| 125 | X(vkUpdateDescriptorSetWithTemplateKHR); | ||
| 126 | X(vkUpdateDescriptorSets); | ||
| 127 | X(vkWaitForFences); | ||
| 128 | #undef X | ||
| 129 | } | ||
| 130 | |||
| 131 | } // Anonymous namespace | ||
| 132 | |||
| 133 | bool Load(InstanceDispatch& dld) noexcept { | ||
| 134 | #define X(name) Proc(dld.name, dld, #name) | ||
| 135 | return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties); | ||
| 136 | #undef X | ||
| 137 | } | ||
| 138 | |||
| 139 | bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { | ||
| 140 | #define X(name) Proc(dld.name, dld, #name, instance) | ||
| 141 | // These functions may fail to load depending on the enabled extensions. | ||
| 142 | // Don't return a failure on these. | ||
| 143 | X(vkCreateDebugUtilsMessengerEXT); | ||
| 144 | X(vkDestroyDebugUtilsMessengerEXT); | ||
| 145 | X(vkDestroySurfaceKHR); | ||
| 146 | X(vkGetPhysicalDeviceFeatures2KHR); | ||
| 147 | X(vkGetPhysicalDeviceProperties2KHR); | ||
| 148 | X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); | ||
| 149 | X(vkGetPhysicalDeviceSurfaceFormatsKHR); | ||
| 150 | X(vkGetPhysicalDeviceSurfacePresentModesKHR); | ||
| 151 | X(vkGetPhysicalDeviceSurfaceSupportKHR); | ||
| 152 | X(vkGetSwapchainImagesKHR); | ||
| 153 | X(vkQueuePresentKHR); | ||
| 154 | |||
| 155 | return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) && | ||
| 156 | X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) && | ||
| 157 | X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) && | ||
| 158 | X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) && | ||
| 159 | X(vkGetPhysicalDeviceQueueFamilyProperties); | ||
| 160 | #undef X | ||
| 161 | } | ||
| 162 | |||
| 163 | const char* Exception::what() const noexcept { | ||
| 164 | return ToString(result); | ||
| 165 | } | ||
| 166 | |||
| 167 | const char* ToString(VkResult result) noexcept { | ||
| 168 | switch (result) { | ||
| 169 | case VkResult::VK_SUCCESS: | ||
| 170 | return "VK_SUCCESS"; | ||
| 171 | case VkResult::VK_NOT_READY: | ||
| 172 | return "VK_NOT_READY"; | ||
| 173 | case VkResult::VK_TIMEOUT: | ||
| 174 | return "VK_TIMEOUT"; | ||
| 175 | case VkResult::VK_EVENT_SET: | ||
| 176 | return "VK_EVENT_SET"; | ||
| 177 | case VkResult::VK_EVENT_RESET: | ||
| 178 | return "VK_EVENT_RESET"; | ||
| 179 | case VkResult::VK_INCOMPLETE: | ||
| 180 | return "VK_INCOMPLETE"; | ||
| 181 | case VkResult::VK_ERROR_OUT_OF_HOST_MEMORY: | ||
| 182 | return "VK_ERROR_OUT_OF_HOST_MEMORY"; | ||
| 183 | case VkResult::VK_ERROR_OUT_OF_DEVICE_MEMORY: | ||
| 184 | return "VK_ERROR_OUT_OF_DEVICE_MEMORY"; | ||
| 185 | case VkResult::VK_ERROR_INITIALIZATION_FAILED: | ||
| 186 | return "VK_ERROR_INITIALIZATION_FAILED"; | ||
| 187 | case VkResult::VK_ERROR_DEVICE_LOST: | ||
| 188 | return "VK_ERROR_DEVICE_LOST"; | ||
| 189 | case VkResult::VK_ERROR_MEMORY_MAP_FAILED: | ||
| 190 | return "VK_ERROR_MEMORY_MAP_FAILED"; | ||
| 191 | case VkResult::VK_ERROR_LAYER_NOT_PRESENT: | ||
| 192 | return "VK_ERROR_LAYER_NOT_PRESENT"; | ||
| 193 | case VkResult::VK_ERROR_EXTENSION_NOT_PRESENT: | ||
| 194 | return "VK_ERROR_EXTENSION_NOT_PRESENT"; | ||
| 195 | case VkResult::VK_ERROR_FEATURE_NOT_PRESENT: | ||
| 196 | return "VK_ERROR_FEATURE_NOT_PRESENT"; | ||
| 197 | case VkResult::VK_ERROR_INCOMPATIBLE_DRIVER: | ||
| 198 | return "VK_ERROR_INCOMPATIBLE_DRIVER"; | ||
| 199 | case VkResult::VK_ERROR_TOO_MANY_OBJECTS: | ||
| 200 | return "VK_ERROR_TOO_MANY_OBJECTS"; | ||
| 201 | case VkResult::VK_ERROR_FORMAT_NOT_SUPPORTED: | ||
| 202 | return "VK_ERROR_FORMAT_NOT_SUPPORTED"; | ||
| 203 | case VkResult::VK_ERROR_FRAGMENTED_POOL: | ||
| 204 | return "VK_ERROR_FRAGMENTED_POOL"; | ||
| 205 | case VkResult::VK_ERROR_OUT_OF_POOL_MEMORY: | ||
| 206 | return "VK_ERROR_OUT_OF_POOL_MEMORY"; | ||
| 207 | case VkResult::VK_ERROR_INVALID_EXTERNAL_HANDLE: | ||
| 208 | return "VK_ERROR_INVALID_EXTERNAL_HANDLE"; | ||
| 209 | case VkResult::VK_ERROR_SURFACE_LOST_KHR: | ||
| 210 | return "VK_ERROR_SURFACE_LOST_KHR"; | ||
| 211 | case VkResult::VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: | ||
| 212 | return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"; | ||
| 213 | case VkResult::VK_SUBOPTIMAL_KHR: | ||
| 214 | return "VK_SUBOPTIMAL_KHR"; | ||
| 215 | case VkResult::VK_ERROR_OUT_OF_DATE_KHR: | ||
| 216 | return "VK_ERROR_OUT_OF_DATE_KHR"; | ||
| 217 | case VkResult::VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: | ||
| 218 | return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR"; | ||
| 219 | case VkResult::VK_ERROR_VALIDATION_FAILED_EXT: | ||
| 220 | return "VK_ERROR_VALIDATION_FAILED_EXT"; | ||
| 221 | case VkResult::VK_ERROR_INVALID_SHADER_NV: | ||
| 222 | return "VK_ERROR_INVALID_SHADER_NV"; | ||
| 223 | case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: | ||
| 224 | return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; | ||
| 225 | case VkResult::VK_ERROR_FRAGMENTATION_EXT: | ||
| 226 | return "VK_ERROR_FRAGMENTATION_EXT"; | ||
| 227 | case VkResult::VK_ERROR_NOT_PERMITTED_EXT: | ||
| 228 | return "VK_ERROR_NOT_PERMITTED_EXT"; | ||
| 229 | case VkResult::VK_ERROR_INVALID_DEVICE_ADDRESS_EXT: | ||
| 230 | return "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT"; | ||
| 231 | case VkResult::VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: | ||
| 232 | return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; | ||
| 233 | } | ||
| 234 | return "Unknown"; | ||
| 235 | } | ||
| 236 | |||
| 237 | void Destroy(VkInstance instance, const InstanceDispatch& dld) noexcept { | ||
| 238 | dld.vkDestroyInstance(instance, nullptr); | ||
| 239 | } | ||
| 240 | |||
| 241 | void Destroy(VkDevice device, const InstanceDispatch& dld) noexcept { | ||
| 242 | dld.vkDestroyDevice(device, nullptr); | ||
| 243 | } | ||
| 244 | |||
| 245 | void Destroy(VkDevice device, VkBuffer handle, const DeviceDispatch& dld) noexcept { | ||
| 246 | dld.vkDestroyBuffer(device, handle, nullptr); | ||
| 247 | } | ||
| 248 | |||
| 249 | void Destroy(VkDevice device, VkBufferView handle, const DeviceDispatch& dld) noexcept { | ||
| 250 | dld.vkDestroyBufferView(device, handle, nullptr); | ||
| 251 | } | ||
| 252 | |||
| 253 | void Destroy(VkDevice device, VkCommandPool handle, const DeviceDispatch& dld) noexcept { | ||
| 254 | dld.vkDestroyCommandPool(device, handle, nullptr); | ||
| 255 | } | ||
| 256 | |||
| 257 | void Destroy(VkDevice device, VkDescriptorPool handle, const DeviceDispatch& dld) noexcept { | ||
| 258 | dld.vkDestroyDescriptorPool(device, handle, nullptr); | ||
| 259 | } | ||
| 260 | |||
| 261 | void Destroy(VkDevice device, VkDescriptorSetLayout handle, const DeviceDispatch& dld) noexcept { | ||
| 262 | dld.vkDestroyDescriptorSetLayout(device, handle, nullptr); | ||
| 263 | } | ||
| 264 | |||
| 265 | void Destroy(VkDevice device, VkDescriptorUpdateTemplateKHR handle, | ||
| 266 | const DeviceDispatch& dld) noexcept { | ||
| 267 | dld.vkDestroyDescriptorUpdateTemplateKHR(device, handle, nullptr); | ||
| 268 | } | ||
| 269 | |||
| 270 | void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept { | ||
| 271 | dld.vkFreeMemory(device, handle, nullptr); | ||
| 272 | } | ||
| 273 | |||
| 274 | void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept { | ||
| 275 | dld.vkDestroyFence(device, handle, nullptr); | ||
| 276 | } | ||
| 277 | |||
| 278 | void Destroy(VkDevice device, VkFramebuffer handle, const DeviceDispatch& dld) noexcept { | ||
| 279 | dld.vkDestroyFramebuffer(device, handle, nullptr); | ||
| 280 | } | ||
| 281 | |||
| 282 | void Destroy(VkDevice device, VkImage handle, const DeviceDispatch& dld) noexcept { | ||
| 283 | dld.vkDestroyImage(device, handle, nullptr); | ||
| 284 | } | ||
| 285 | |||
| 286 | void Destroy(VkDevice device, VkImageView handle, const DeviceDispatch& dld) noexcept { | ||
| 287 | dld.vkDestroyImageView(device, handle, nullptr); | ||
| 288 | } | ||
| 289 | |||
| 290 | void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noexcept { | ||
| 291 | dld.vkDestroyPipeline(device, handle, nullptr); | ||
| 292 | } | ||
| 293 | |||
| 294 | void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { | ||
| 295 | dld.vkDestroyPipelineLayout(device, handle, nullptr); | ||
| 296 | } | ||
| 297 | |||
| 298 | void Destroy(VkDevice device, VkQueryPool handle, const DeviceDispatch& dld) noexcept { | ||
| 299 | dld.vkDestroyQueryPool(device, handle, nullptr); | ||
| 300 | } | ||
| 301 | |||
| 302 | void Destroy(VkDevice device, VkRenderPass handle, const DeviceDispatch& dld) noexcept { | ||
| 303 | dld.vkDestroyRenderPass(device, handle, nullptr); | ||
| 304 | } | ||
| 305 | |||
| 306 | void Destroy(VkDevice device, VkSampler handle, const DeviceDispatch& dld) noexcept { | ||
| 307 | dld.vkDestroySampler(device, handle, nullptr); | ||
| 308 | } | ||
| 309 | |||
| 310 | void Destroy(VkDevice device, VkSwapchainKHR handle, const DeviceDispatch& dld) noexcept { | ||
| 311 | dld.vkDestroySwapchainKHR(device, handle, nullptr); | ||
| 312 | } | ||
| 313 | |||
| 314 | void Destroy(VkDevice device, VkSemaphore handle, const DeviceDispatch& dld) noexcept { | ||
| 315 | dld.vkDestroySemaphore(device, handle, nullptr); | ||
| 316 | } | ||
| 317 | |||
| 318 | void Destroy(VkDevice device, VkShaderModule handle, const DeviceDispatch& dld) noexcept { | ||
| 319 | dld.vkDestroyShaderModule(device, handle, nullptr); | ||
| 320 | } | ||
| 321 | |||
| 322 | void Destroy(VkInstance instance, VkDebugUtilsMessengerEXT handle, | ||
| 323 | const InstanceDispatch& dld) noexcept { | ||
| 324 | dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr); | ||
| 325 | } | ||
| 326 | |||
| 327 | void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept { | ||
| 328 | dld.vkDestroySurfaceKHR(instance, handle, nullptr); | ||
| 329 | } | ||
| 330 | |||
| 331 | VkResult Free(VkDevice device, VkDescriptorPool handle, Span<VkDescriptorSet> sets, | ||
| 332 | const DeviceDispatch& dld) noexcept { | ||
| 333 | return dld.vkFreeDescriptorSets(device, handle, sets.size(), sets.data()); | ||
| 334 | } | ||
| 335 | |||
| 336 | VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffers, | ||
| 337 | const DeviceDispatch& dld) noexcept { | ||
| 338 | dld.vkFreeCommandBuffers(device, handle, buffers.size(), buffers.data()); | ||
| 339 | return VK_SUCCESS; | ||
| 340 | } | ||
| 341 | |||
| 342 | Instance Instance::Create(Span<const char*> layers, Span<const char*> extensions, | ||
| 343 | InstanceDispatch& dld) noexcept { | ||
| 344 | VkApplicationInfo application_info; | ||
| 345 | application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | ||
| 346 | application_info.pNext = nullptr; | ||
| 347 | application_info.pApplicationName = "yuzu Emulator"; | ||
| 348 | application_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 349 | application_info.pEngineName = "yuzu Emulator"; | ||
| 350 | application_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 351 | application_info.apiVersion = VK_API_VERSION_1_1; | ||
| 352 | |||
| 353 | VkInstanceCreateInfo ci; | ||
| 354 | ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | ||
| 355 | ci.pNext = nullptr; | ||
| 356 | ci.flags = 0; | ||
| 357 | ci.pApplicationInfo = &application_info; | ||
| 358 | ci.enabledLayerCount = layers.size(); | ||
| 359 | ci.ppEnabledLayerNames = layers.data(); | ||
| 360 | ci.enabledExtensionCount = extensions.size(); | ||
| 361 | ci.ppEnabledExtensionNames = extensions.data(); | ||
| 362 | |||
| 363 | VkInstance instance; | ||
| 364 | if (dld.vkCreateInstance(&ci, nullptr, &instance) != VK_SUCCESS) { | ||
| 365 | // Failed to create the instance. | ||
| 366 | return {}; | ||
| 367 | } | ||
| 368 | if (!Proc(dld.vkDestroyInstance, dld, "vkDestroyInstance", instance)) { | ||
| 369 | // We successfully created an instance but the destroy function couldn't be loaded. | ||
| 370 | // This is a good moment to panic. | ||
| 371 | return {}; | ||
| 372 | } | ||
| 373 | |||
| 374 | return Instance(instance, dld); | ||
| 375 | } | ||
| 376 | |||
| 377 | std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices() { | ||
| 378 | u32 num; | ||
| 379 | if (dld->vkEnumeratePhysicalDevices(handle, &num, nullptr) != VK_SUCCESS) { | ||
| 380 | return std::nullopt; | ||
| 381 | } | ||
| 382 | std::vector<VkPhysicalDevice> physical_devices(num); | ||
| 383 | if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) { | ||
| 384 | return std::nullopt; | ||
| 385 | } | ||
| 386 | return physical_devices; | ||
| 387 | } | ||
| 388 | |||
| 389 | DebugCallback Instance::TryCreateDebugCallback( | ||
| 390 | PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept { | ||
| 391 | VkDebugUtilsMessengerCreateInfoEXT ci; | ||
| 392 | ci.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; | ||
| 393 | ci.pNext = nullptr; | ||
| 394 | ci.flags = 0; | ||
| 395 | ci.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | | ||
| 396 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | | ||
| 397 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | | ||
| 398 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; | ||
| 399 | ci.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | | ||
| 400 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | | ||
| 401 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; | ||
| 402 | ci.pfnUserCallback = callback; | ||
| 403 | ci.pUserData = nullptr; | ||
| 404 | |||
| 405 | VkDebugUtilsMessengerEXT messenger; | ||
| 406 | if (dld->vkCreateDebugUtilsMessengerEXT(handle, &ci, nullptr, &messenger) != VK_SUCCESS) { | ||
| 407 | return {}; | ||
| 408 | } | ||
| 409 | return DebugCallback(messenger, handle, *dld); | ||
| 410 | } | ||
| 411 | |||
| 412 | std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const { | ||
| 413 | if (!dld.vkGetQueueCheckpointDataNV) { | ||
| 414 | return {}; | ||
| 415 | } | ||
| 416 | u32 num; | ||
| 417 | dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr); | ||
| 418 | std::vector<VkCheckpointDataNV> checkpoints(num); | ||
| 419 | dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data()); | ||
| 420 | return checkpoints; | ||
| 421 | } | ||
| 422 | |||
| 423 | void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { | ||
| 424 | Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); | ||
| 425 | } | ||
| 426 | |||
| 427 | void Image::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { | ||
| 428 | Check(dld->vkBindImageMemory(owner, handle, memory, offset)); | ||
| 429 | } | ||
| 430 | |||
| 431 | DescriptorSets DescriptorPool::Allocate(const VkDescriptorSetAllocateInfo& ai) const { | ||
| 432 | const std::size_t num = ai.descriptorSetCount; | ||
| 433 | std::unique_ptr sets = std::make_unique<VkDescriptorSet[]>(num); | ||
| 434 | switch (const VkResult result = dld->vkAllocateDescriptorSets(owner, &ai, sets.get())) { | ||
| 435 | case VK_SUCCESS: | ||
| 436 | return DescriptorSets(std::move(sets), num, owner, handle, *dld); | ||
| 437 | case VK_ERROR_OUT_OF_POOL_MEMORY: | ||
| 438 | return {}; | ||
| 439 | default: | ||
| 440 | throw Exception(result); | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | CommandBuffers CommandPool::Allocate(std::size_t num_buffers, VkCommandBufferLevel level) const { | ||
| 445 | VkCommandBufferAllocateInfo ai; | ||
| 446 | ai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | ||
| 447 | ai.pNext = nullptr; | ||
| 448 | ai.commandPool = handle; | ||
| 449 | ai.level = level; | ||
| 450 | ai.commandBufferCount = static_cast<u32>(num_buffers); | ||
| 451 | |||
| 452 | std::unique_ptr buffers = std::make_unique<VkCommandBuffer[]>(num_buffers); | ||
| 453 | switch (const VkResult result = dld->vkAllocateCommandBuffers(owner, &ai, buffers.get())) { | ||
| 454 | case VK_SUCCESS: | ||
| 455 | return CommandBuffers(std::move(buffers), num_buffers, owner, handle, *dld); | ||
| 456 | case VK_ERROR_OUT_OF_POOL_MEMORY: | ||
| 457 | return {}; | ||
| 458 | default: | ||
| 459 | throw Exception(result); | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | std::vector<VkImage> SwapchainKHR::GetImages() const { | ||
| 464 | u32 num; | ||
| 465 | Check(dld->vkGetSwapchainImagesKHR(owner, handle, &num, nullptr)); | ||
| 466 | std::vector<VkImage> images(num); | ||
| 467 | Check(dld->vkGetSwapchainImagesKHR(owner, handle, &num, images.data())); | ||
| 468 | return images; | ||
| 469 | } | ||
| 470 | |||
| 471 | Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, | ||
| 472 | Span<const char*> enabled_extensions, | ||
| 473 | const VkPhysicalDeviceFeatures2& enabled_features, | ||
| 474 | DeviceDispatch& dld) noexcept { | ||
| 475 | VkDeviceCreateInfo ci; | ||
| 476 | ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | ||
| 477 | ci.pNext = &enabled_features; | ||
| 478 | ci.flags = 0; | ||
| 479 | ci.queueCreateInfoCount = queues_ci.size(); | ||
| 480 | ci.pQueueCreateInfos = queues_ci.data(); | ||
| 481 | ci.enabledLayerCount = 0; | ||
| 482 | ci.ppEnabledLayerNames = nullptr; | ||
| 483 | ci.enabledExtensionCount = enabled_extensions.size(); | ||
| 484 | ci.ppEnabledExtensionNames = enabled_extensions.data(); | ||
| 485 | ci.pEnabledFeatures = nullptr; | ||
| 486 | |||
| 487 | VkDevice device; | ||
| 488 | if (dld.vkCreateDevice(physical_device, &ci, nullptr, &device) != VK_SUCCESS) { | ||
| 489 | return {}; | ||
| 490 | } | ||
| 491 | Load(device, dld); | ||
| 492 | return Device(device, dld); | ||
| 493 | } | ||
| 494 | |||
| 495 | Queue Device::GetQueue(u32 family_index) const noexcept { | ||
| 496 | VkQueue queue; | ||
| 497 | dld->vkGetDeviceQueue(handle, family_index, 0, &queue); | ||
| 498 | return Queue(queue, *dld); | ||
| 499 | } | ||
| 500 | |||
| 501 | Buffer Device::CreateBuffer(const VkBufferCreateInfo& ci) const { | ||
| 502 | VkBuffer object; | ||
| 503 | Check(dld->vkCreateBuffer(handle, &ci, nullptr, &object)); | ||
| 504 | return Buffer(object, handle, *dld); | ||
| 505 | } | ||
| 506 | |||
| 507 | BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const { | ||
| 508 | VkBufferView object; | ||
| 509 | Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object)); | ||
| 510 | return BufferView(object, handle, *dld); | ||
| 511 | } | ||
| 512 | |||
| 513 | Image Device::CreateImage(const VkImageCreateInfo& ci) const { | ||
| 514 | VkImage object; | ||
| 515 | Check(dld->vkCreateImage(handle, &ci, nullptr, &object)); | ||
| 516 | return Image(object, handle, *dld); | ||
| 517 | } | ||
| 518 | |||
| 519 | ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const { | ||
| 520 | VkImageView object; | ||
| 521 | Check(dld->vkCreateImageView(handle, &ci, nullptr, &object)); | ||
| 522 | return ImageView(object, handle, *dld); | ||
| 523 | } | ||
| 524 | |||
| 525 | Semaphore Device::CreateSemaphore() const { | ||
| 526 | VkSemaphoreCreateInfo ci; | ||
| 527 | ci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | ||
| 528 | ci.pNext = nullptr; | ||
| 529 | ci.flags = 0; | ||
| 530 | |||
| 531 | VkSemaphore object; | ||
| 532 | Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object)); | ||
| 533 | return Semaphore(object, handle, *dld); | ||
| 534 | } | ||
| 535 | |||
| 536 | Fence Device::CreateFence(const VkFenceCreateInfo& ci) const { | ||
| 537 | VkFence object; | ||
| 538 | Check(dld->vkCreateFence(handle, &ci, nullptr, &object)); | ||
| 539 | return Fence(object, handle, *dld); | ||
| 540 | } | ||
| 541 | |||
| 542 | DescriptorPool Device::CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const { | ||
| 543 | VkDescriptorPool object; | ||
| 544 | Check(dld->vkCreateDescriptorPool(handle, &ci, nullptr, &object)); | ||
| 545 | return DescriptorPool(object, handle, *dld); | ||
| 546 | } | ||
| 547 | |||
| 548 | RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const { | ||
| 549 | VkRenderPass object; | ||
| 550 | Check(dld->vkCreateRenderPass(handle, &ci, nullptr, &object)); | ||
| 551 | return RenderPass(object, handle, *dld); | ||
| 552 | } | ||
| 553 | |||
| 554 | DescriptorSetLayout Device::CreateDescriptorSetLayout( | ||
| 555 | const VkDescriptorSetLayoutCreateInfo& ci) const { | ||
| 556 | VkDescriptorSetLayout object; | ||
| 557 | Check(dld->vkCreateDescriptorSetLayout(handle, &ci, nullptr, &object)); | ||
| 558 | return DescriptorSetLayout(object, handle, *dld); | ||
| 559 | } | ||
| 560 | |||
| 561 | PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { | ||
| 562 | VkPipelineLayout object; | ||
| 563 | Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object)); | ||
| 564 | return PipelineLayout(object, handle, *dld); | ||
| 565 | } | ||
| 566 | |||
| 567 | Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { | ||
| 568 | VkPipeline object; | ||
| 569 | Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object)); | ||
| 570 | return Pipeline(object, handle, *dld); | ||
| 571 | } | ||
| 572 | |||
| 573 | Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { | ||
| 574 | VkPipeline object; | ||
| 575 | Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object)); | ||
| 576 | return Pipeline(object, handle, *dld); | ||
| 577 | } | ||
| 578 | |||
| 579 | Sampler Device::CreateSampler(const VkSamplerCreateInfo& ci) const { | ||
| 580 | VkSampler object; | ||
| 581 | Check(dld->vkCreateSampler(handle, &ci, nullptr, &object)); | ||
| 582 | return Sampler(object, handle, *dld); | ||
| 583 | } | ||
| 584 | |||
| 585 | Framebuffer Device::CreateFramebuffer(const VkFramebufferCreateInfo& ci) const { | ||
| 586 | VkFramebuffer object; | ||
| 587 | Check(dld->vkCreateFramebuffer(handle, &ci, nullptr, &object)); | ||
| 588 | return Framebuffer(object, handle, *dld); | ||
| 589 | } | ||
| 590 | |||
| 591 | CommandPool Device::CreateCommandPool(const VkCommandPoolCreateInfo& ci) const { | ||
| 592 | VkCommandPool object; | ||
| 593 | Check(dld->vkCreateCommandPool(handle, &ci, nullptr, &object)); | ||
| 594 | return CommandPool(object, handle, *dld); | ||
| 595 | } | ||
| 596 | |||
| 597 | DescriptorUpdateTemplateKHR Device::CreateDescriptorUpdateTemplateKHR( | ||
| 598 | const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const { | ||
| 599 | VkDescriptorUpdateTemplateKHR object; | ||
| 600 | Check(dld->vkCreateDescriptorUpdateTemplateKHR(handle, &ci, nullptr, &object)); | ||
| 601 | return DescriptorUpdateTemplateKHR(object, handle, *dld); | ||
| 602 | } | ||
| 603 | |||
| 604 | QueryPool Device::CreateQueryPool(const VkQueryPoolCreateInfo& ci) const { | ||
| 605 | VkQueryPool object; | ||
| 606 | Check(dld->vkCreateQueryPool(handle, &ci, nullptr, &object)); | ||
| 607 | return QueryPool(object, handle, *dld); | ||
| 608 | } | ||
| 609 | |||
| 610 | ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) const { | ||
| 611 | VkShaderModule object; | ||
| 612 | Check(dld->vkCreateShaderModule(handle, &ci, nullptr, &object)); | ||
| 613 | return ShaderModule(object, handle, *dld); | ||
| 614 | } | ||
| 615 | |||
| 616 | SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const { | ||
| 617 | VkSwapchainKHR object; | ||
| 618 | Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object)); | ||
| 619 | return SwapchainKHR(object, handle, *dld); | ||
| 620 | } | ||
| 621 | |||
| 622 | DeviceMemory Device::TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept { | ||
| 623 | VkDeviceMemory memory; | ||
| 624 | if (dld->vkAllocateMemory(handle, &ai, nullptr, &memory) != VK_SUCCESS) { | ||
| 625 | return {}; | ||
| 626 | } | ||
| 627 | return DeviceMemory(memory, handle, *dld); | ||
| 628 | } | ||
| 629 | |||
| 630 | DeviceMemory Device::AllocateMemory(const VkMemoryAllocateInfo& ai) const { | ||
| 631 | VkDeviceMemory memory; | ||
| 632 | Check(dld->vkAllocateMemory(handle, &ai, nullptr, &memory)); | ||
| 633 | return DeviceMemory(memory, handle, *dld); | ||
| 634 | } | ||
| 635 | |||
| 636 | VkMemoryRequirements Device::GetBufferMemoryRequirements(VkBuffer buffer) const noexcept { | ||
| 637 | VkMemoryRequirements requirements; | ||
| 638 | dld->vkGetBufferMemoryRequirements(handle, buffer, &requirements); | ||
| 639 | return requirements; | ||
| 640 | } | ||
| 641 | |||
| 642 | VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noexcept { | ||
| 643 | VkMemoryRequirements requirements; | ||
| 644 | dld->vkGetImageMemoryRequirements(handle, image, &requirements); | ||
| 645 | return requirements; | ||
| 646 | } | ||
| 647 | |||
| 648 | void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, | ||
| 649 | Span<VkCopyDescriptorSet> copies) const noexcept { | ||
| 650 | dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data()); | ||
| 651 | } | ||
| 652 | |||
| 653 | VkPhysicalDeviceProperties PhysicalDevice::GetProperties() const noexcept { | ||
| 654 | VkPhysicalDeviceProperties properties; | ||
| 655 | dld->vkGetPhysicalDeviceProperties(physical_device, &properties); | ||
| 656 | return properties; | ||
| 657 | } | ||
| 658 | |||
| 659 | void PhysicalDevice::GetProperties2KHR(VkPhysicalDeviceProperties2KHR& properties) const noexcept { | ||
| 660 | dld->vkGetPhysicalDeviceProperties2KHR(physical_device, &properties); | ||
| 661 | } | ||
| 662 | |||
| 663 | VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept { | ||
| 664 | VkPhysicalDeviceFeatures2KHR features2; | ||
| 665 | features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; | ||
| 666 | features2.pNext = nullptr; | ||
| 667 | dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features2); | ||
| 668 | return features2.features; | ||
| 669 | } | ||
| 670 | |||
| 671 | void PhysicalDevice::GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR& features) const noexcept { | ||
| 672 | dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features); | ||
| 673 | } | ||
| 674 | |||
| 675 | VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const noexcept { | ||
| 676 | VkFormatProperties properties; | ||
| 677 | dld->vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties); | ||
| 678 | return properties; | ||
| 679 | } | ||
| 680 | |||
| 681 | std::vector<VkExtensionProperties> PhysicalDevice::EnumerateDeviceExtensionProperties() const { | ||
| 682 | u32 num; | ||
| 683 | dld->vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &num, nullptr); | ||
| 684 | std::vector<VkExtensionProperties> properties(num); | ||
| 685 | dld->vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &num, properties.data()); | ||
| 686 | return properties; | ||
| 687 | } | ||
| 688 | |||
| 689 | std::vector<VkQueueFamilyProperties> PhysicalDevice::GetQueueFamilyProperties() const { | ||
| 690 | u32 num; | ||
| 691 | dld->vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &num, nullptr); | ||
| 692 | std::vector<VkQueueFamilyProperties> properties(num); | ||
| 693 | dld->vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &num, properties.data()); | ||
| 694 | return properties; | ||
| 695 | } | ||
| 696 | |||
| 697 | bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR surface) const { | ||
| 698 | VkBool32 supported; | ||
| 699 | Check(dld->vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_index, surface, | ||
| 700 | &supported)); | ||
| 701 | return supported == VK_TRUE; | ||
| 702 | } | ||
| 703 | |||
| 704 | VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const | ||
| 705 | noexcept { | ||
| 706 | VkSurfaceCapabilitiesKHR capabilities; | ||
| 707 | Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities)); | ||
| 708 | return capabilities; | ||
| 709 | } | ||
| 710 | |||
| 711 | std::vector<VkSurfaceFormatKHR> PhysicalDevice::GetSurfaceFormatsKHR(VkSurfaceKHR surface) const { | ||
| 712 | u32 num; | ||
| 713 | Check(dld->vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &num, nullptr)); | ||
| 714 | std::vector<VkSurfaceFormatKHR> formats(num); | ||
| 715 | Check( | ||
| 716 | dld->vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &num, formats.data())); | ||
| 717 | return formats; | ||
| 718 | } | ||
| 719 | |||
| 720 | std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR( | ||
| 721 | VkSurfaceKHR surface) const { | ||
| 722 | u32 num; | ||
| 723 | Check(dld->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &num, nullptr)); | ||
| 724 | std::vector<VkPresentModeKHR> modes(num); | ||
| 725 | Check(dld->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &num, | ||
| 726 | modes.data())); | ||
| 727 | return modes; | ||
| 728 | } | ||
| 729 | |||
| 730 | VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept { | ||
| 731 | VkPhysicalDeviceMemoryProperties properties; | ||
| 732 | dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties); | ||
| 733 | return properties; | ||
| 734 | } | ||
| 735 | |||
| 736 | std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( | ||
| 737 | const InstanceDispatch& dld) { | ||
| 738 | u32 num; | ||
| 739 | if (dld.vkEnumerateInstanceExtensionProperties(nullptr, &num, nullptr) != VK_SUCCESS) { | ||
| 740 | return std::nullopt; | ||
| 741 | } | ||
| 742 | std::vector<VkExtensionProperties> properties(num); | ||
| 743 | if (dld.vkEnumerateInstanceExtensionProperties(nullptr, &num, properties.data()) != | ||
| 744 | VK_SUCCESS) { | ||
| 745 | return std::nullopt; | ||
| 746 | } | ||
| 747 | return properties; | ||
| 748 | } | ||
| 749 | |||
| 750 | } // namespace Vulkan::vk | ||
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h new file mode 100644 index 000000000..fb3657819 --- /dev/null +++ b/src/video_core/renderer_vulkan/wrapper.h | |||
| @@ -0,0 +1,987 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <exception> | ||
| 8 | #include <iterator> | ||
| 9 | #include <limits> | ||
| 10 | #include <memory> | ||
| 11 | #include <optional> | ||
| 12 | #include <type_traits> | ||
| 13 | #include <utility> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 16 | #define VK_NO_PROTOTYPES | ||
| 17 | #include <vulkan/vulkan.h> | ||
| 18 | |||
| 19 | #include "common/common_types.h" | ||
| 20 | |||
| 21 | namespace Vulkan::vk { | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Span for Vulkan arrays. | ||
| 25 | * Based on std::span but optimized for array access instead of iterators. | ||
| 26 | * Size returns uint32_t instead of size_t to ease interaction with Vulkan functions. | ||
| 27 | */ | ||
| 28 | template <typename T> | ||
| 29 | class Span { | ||
| 30 | public: | ||
| 31 | using value_type = T; | ||
| 32 | using size_type = u32; | ||
| 33 | using difference_type = std::ptrdiff_t; | ||
| 34 | using reference = const T&; | ||
| 35 | using const_reference = const T&; | ||
| 36 | using pointer = const T*; | ||
| 37 | using const_pointer = const T*; | ||
| 38 | using iterator = const T*; | ||
| 39 | using const_iterator = const T*; | ||
| 40 | |||
| 41 | /// Construct an empty span. | ||
| 42 | constexpr Span() noexcept = default; | ||
| 43 | |||
| 44 | /// Construct a span from a single element. | ||
| 45 | constexpr Span(const T& value) noexcept : ptr{&value}, num{1} {} | ||
| 46 | |||
| 47 | /// Construct a span from a range. | ||
| 48 | template <typename Range> | ||
| 49 | // requires std::data(const Range&) | ||
| 50 | // requires std::size(const Range&) | ||
| 51 | constexpr Span(const Range& range) : ptr{std::data(range)}, num{std::size(range)} {} | ||
| 52 | |||
| 53 | /// Construct a span from a pointer and a size. | ||
| 54 | /// This is inteded for subranges. | ||
| 55 | constexpr Span(const T* ptr, std::size_t num) noexcept : ptr{ptr}, num{num} {} | ||
| 56 | |||
| 57 | /// Returns the data pointer by the span. | ||
| 58 | constexpr const T* data() const noexcept { | ||
| 59 | return ptr; | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Returns the number of elements in the span. | ||
| 63 | /// @note Returns a 32 bits integer because most Vulkan functions expect this type. | ||
| 64 | constexpr u32 size() const noexcept { | ||
| 65 | return static_cast<u32>(num); | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Returns true when the span is empty. | ||
| 69 | constexpr bool empty() const noexcept { | ||
| 70 | return num == 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Returns a reference to the element in the passed index. | ||
| 74 | /// @pre: index < size() | ||
| 75 | constexpr const T& operator[](std::size_t index) const noexcept { | ||
| 76 | return ptr[index]; | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Returns an iterator to the beginning of the span. | ||
| 80 | constexpr const T* begin() const noexcept { | ||
| 81 | return ptr; | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Returns an iterator to the end of the span. | ||
| 85 | constexpr const T* end() const noexcept { | ||
| 86 | return ptr + num; | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Returns an iterator to the beginning of the span. | ||
| 90 | constexpr const T* cbegin() const noexcept { | ||
| 91 | return ptr; | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Returns an iterator to the end of the span. | ||
| 95 | constexpr const T* cend() const noexcept { | ||
| 96 | return ptr + num; | ||
| 97 | } | ||
| 98 | |||
| 99 | private: | ||
| 100 | const T* ptr = nullptr; | ||
| 101 | std::size_t num = 0; | ||
| 102 | }; | ||
| 103 | |||
| 104 | /// Vulkan exception generated from a VkResult. | ||
| 105 | class Exception final : public std::exception { | ||
| 106 | public: | ||
| 107 | /// Construct the exception with a result. | ||
| 108 | /// @pre result != VK_SUCCESS | ||
| 109 | explicit Exception(VkResult result_) : result{result_} {} | ||
| 110 | virtual ~Exception() = default; | ||
| 111 | |||
| 112 | const char* what() const noexcept override; | ||
| 113 | |||
| 114 | private: | ||
| 115 | VkResult result; | ||
| 116 | }; | ||
| 117 | |||
| 118 | /// Converts a VkResult enum into a rodata string | ||
| 119 | const char* ToString(VkResult) noexcept; | ||
| 120 | |||
| 121 | /// Throws a Vulkan exception if result is not success. | ||
| 122 | inline void Check(VkResult result) { | ||
| 123 | if (result != VK_SUCCESS) { | ||
| 124 | throw Exception(result); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Throws a Vulkan exception if result is an error. | ||
| 129 | /// @return result | ||
| 130 | inline VkResult Filter(VkResult result) { | ||
| 131 | if (result < 0) { | ||
| 132 | throw Exception(result); | ||
| 133 | } | ||
| 134 | return result; | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Table holding Vulkan instance function pointers. | ||
| 138 | struct InstanceDispatch { | ||
| 139 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | ||
| 140 | |||
| 141 | PFN_vkCreateInstance vkCreateInstance; | ||
| 142 | PFN_vkDestroyInstance vkDestroyInstance; | ||
| 143 | PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; | ||
| 144 | |||
| 145 | PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; | ||
| 146 | PFN_vkCreateDevice vkCreateDevice; | ||
| 147 | PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT; | ||
| 148 | PFN_vkDestroyDevice vkDestroyDevice; | ||
| 149 | PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; | ||
| 150 | PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; | ||
| 151 | PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; | ||
| 152 | PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; | ||
| 153 | PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; | ||
| 154 | PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; | ||
| 155 | PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; | ||
| 156 | PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; | ||
| 157 | PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; | ||
| 158 | PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; | ||
| 159 | PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; | ||
| 160 | PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; | ||
| 161 | PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; | ||
| 162 | PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; | ||
| 163 | PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; | ||
| 164 | PFN_vkQueuePresentKHR vkQueuePresentKHR; | ||
| 165 | }; | ||
| 166 | |||
| 167 | /// Table holding Vulkan device function pointers. | ||
| 168 | struct DeviceDispatch : public InstanceDispatch { | ||
| 169 | PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; | ||
| 170 | PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; | ||
| 171 | PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; | ||
| 172 | PFN_vkAllocateMemory vkAllocateMemory; | ||
| 173 | PFN_vkBeginCommandBuffer vkBeginCommandBuffer; | ||
| 174 | PFN_vkBindBufferMemory vkBindBufferMemory; | ||
| 175 | PFN_vkBindImageMemory vkBindImageMemory; | ||
| 176 | PFN_vkCmdBeginQuery vkCmdBeginQuery; | ||
| 177 | PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; | ||
| 178 | PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT; | ||
| 179 | PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; | ||
| 180 | PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; | ||
| 181 | PFN_vkCmdBindPipeline vkCmdBindPipeline; | ||
| 182 | PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT; | ||
| 183 | PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; | ||
| 184 | PFN_vkCmdBlitImage vkCmdBlitImage; | ||
| 185 | PFN_vkCmdClearAttachments vkCmdClearAttachments; | ||
| 186 | PFN_vkCmdCopyBuffer vkCmdCopyBuffer; | ||
| 187 | PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; | ||
| 188 | PFN_vkCmdCopyImage vkCmdCopyImage; | ||
| 189 | PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; | ||
| 190 | PFN_vkCmdDispatch vkCmdDispatch; | ||
| 191 | PFN_vkCmdDraw vkCmdDraw; | ||
| 192 | PFN_vkCmdDrawIndexed vkCmdDrawIndexed; | ||
| 193 | PFN_vkCmdEndQuery vkCmdEndQuery; | ||
| 194 | PFN_vkCmdEndRenderPass vkCmdEndRenderPass; | ||
| 195 | PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT; | ||
| 196 | PFN_vkCmdFillBuffer vkCmdFillBuffer; | ||
| 197 | PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; | ||
| 198 | PFN_vkCmdPushConstants vkCmdPushConstants; | ||
| 199 | PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; | ||
| 200 | PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV; | ||
| 201 | PFN_vkCmdSetDepthBias vkCmdSetDepthBias; | ||
| 202 | PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; | ||
| 203 | PFN_vkCmdSetScissor vkCmdSetScissor; | ||
| 204 | PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; | ||
| 205 | PFN_vkCmdSetStencilReference vkCmdSetStencilReference; | ||
| 206 | PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; | ||
| 207 | PFN_vkCmdSetViewport vkCmdSetViewport; | ||
| 208 | PFN_vkCreateBuffer vkCreateBuffer; | ||
| 209 | PFN_vkCreateBufferView vkCreateBufferView; | ||
| 210 | PFN_vkCreateCommandPool vkCreateCommandPool; | ||
| 211 | PFN_vkCreateComputePipelines vkCreateComputePipelines; | ||
| 212 | PFN_vkCreateDescriptorPool vkCreateDescriptorPool; | ||
| 213 | PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; | ||
| 214 | PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; | ||
| 215 | PFN_vkCreateFence vkCreateFence; | ||
| 216 | PFN_vkCreateFramebuffer vkCreateFramebuffer; | ||
| 217 | PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; | ||
| 218 | PFN_vkCreateImage vkCreateImage; | ||
| 219 | PFN_vkCreateImageView vkCreateImageView; | ||
| 220 | PFN_vkCreatePipelineLayout vkCreatePipelineLayout; | ||
| 221 | PFN_vkCreateQueryPool vkCreateQueryPool; | ||
| 222 | PFN_vkCreateRenderPass vkCreateRenderPass; | ||
| 223 | PFN_vkCreateSampler vkCreateSampler; | ||
| 224 | PFN_vkCreateSemaphore vkCreateSemaphore; | ||
| 225 | PFN_vkCreateShaderModule vkCreateShaderModule; | ||
| 226 | PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; | ||
| 227 | PFN_vkDestroyBuffer vkDestroyBuffer; | ||
| 228 | PFN_vkDestroyBufferView vkDestroyBufferView; | ||
| 229 | PFN_vkDestroyCommandPool vkDestroyCommandPool; | ||
| 230 | PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; | ||
| 231 | PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; | ||
| 232 | PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; | ||
| 233 | PFN_vkDestroyFence vkDestroyFence; | ||
| 234 | PFN_vkDestroyFramebuffer vkDestroyFramebuffer; | ||
| 235 | PFN_vkDestroyImage vkDestroyImage; | ||
| 236 | PFN_vkDestroyImageView vkDestroyImageView; | ||
| 237 | PFN_vkDestroyPipeline vkDestroyPipeline; | ||
| 238 | PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; | ||
| 239 | PFN_vkDestroyQueryPool vkDestroyQueryPool; | ||
| 240 | PFN_vkDestroyRenderPass vkDestroyRenderPass; | ||
| 241 | PFN_vkDestroySampler vkDestroySampler; | ||
| 242 | PFN_vkDestroySemaphore vkDestroySemaphore; | ||
| 243 | PFN_vkDestroyShaderModule vkDestroyShaderModule; | ||
| 244 | PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; | ||
| 245 | PFN_vkDeviceWaitIdle vkDeviceWaitIdle; | ||
| 246 | PFN_vkEndCommandBuffer vkEndCommandBuffer; | ||
| 247 | PFN_vkFreeCommandBuffers vkFreeCommandBuffers; | ||
| 248 | PFN_vkFreeDescriptorSets vkFreeDescriptorSets; | ||
| 249 | PFN_vkFreeMemory vkFreeMemory; | ||
| 250 | PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; | ||
| 251 | PFN_vkGetDeviceQueue vkGetDeviceQueue; | ||
| 252 | PFN_vkGetFenceStatus vkGetFenceStatus; | ||
| 253 | PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; | ||
| 254 | PFN_vkGetQueryPoolResults vkGetQueryPoolResults; | ||
| 255 | PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV; | ||
| 256 | PFN_vkMapMemory vkMapMemory; | ||
| 257 | PFN_vkQueueSubmit vkQueueSubmit; | ||
| 258 | PFN_vkResetFences vkResetFences; | ||
| 259 | PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT; | ||
| 260 | PFN_vkUnmapMemory vkUnmapMemory; | ||
| 261 | PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; | ||
| 262 | PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; | ||
| 263 | PFN_vkWaitForFences vkWaitForFences; | ||
| 264 | }; | ||
| 265 | |||
| 266 | /// Loads instance agnostic function pointers. | ||
| 267 | /// @return True on success, false on error. | ||
| 268 | bool Load(InstanceDispatch&) noexcept; | ||
| 269 | |||
| 270 | /// Loads instance function pointers. | ||
| 271 | /// @return True on success, false on error. | ||
| 272 | bool Load(VkInstance, InstanceDispatch&) noexcept; | ||
| 273 | |||
| 274 | void Destroy(VkInstance, const InstanceDispatch&) noexcept; | ||
| 275 | void Destroy(VkDevice, const InstanceDispatch&) noexcept; | ||
| 276 | |||
| 277 | void Destroy(VkDevice, VkBuffer, const DeviceDispatch&) noexcept; | ||
| 278 | void Destroy(VkDevice, VkBufferView, const DeviceDispatch&) noexcept; | ||
| 279 | void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept; | ||
| 280 | void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept; | ||
| 281 | void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; | ||
| 282 | void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; | ||
| 283 | void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; | ||
| 284 | void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; | ||
| 285 | void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; | ||
| 286 | void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; | ||
| 287 | void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; | ||
| 288 | void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; | ||
| 289 | void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; | ||
| 290 | void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; | ||
| 291 | void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; | ||
| 292 | void Destroy(VkDevice, VkSampler, const DeviceDispatch&) noexcept; | ||
| 293 | void Destroy(VkDevice, VkSwapchainKHR, const DeviceDispatch&) noexcept; | ||
| 294 | void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept; | ||
| 295 | void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept; | ||
| 296 | void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept; | ||
| 297 | void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept; | ||
| 298 | |||
| 299 | VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept; | ||
| 300 | VkResult Free(VkDevice, VkCommandPool, Span<VkCommandBuffer>, const DeviceDispatch&) noexcept; | ||
| 301 | |||
| 302 | template <typename Type, typename OwnerType, typename Dispatch> | ||
| 303 | class Handle; | ||
| 304 | |||
| 305 | /// Handle with an owning type. | ||
| 306 | /// Analogue to std::unique_ptr. | ||
| 307 | template <typename Type, typename OwnerType, typename Dispatch> | ||
| 308 | class Handle { | ||
| 309 | public: | ||
| 310 | /// Construct a handle and hold it's ownership. | ||
| 311 | explicit Handle(Type handle_, OwnerType owner_, const Dispatch& dld_) noexcept | ||
| 312 | : handle{handle_}, owner{owner_}, dld{&dld_} {} | ||
| 313 | |||
| 314 | /// Construct an empty handle. | ||
| 315 | Handle() = default; | ||
| 316 | |||
| 317 | /// Copying Vulkan objects is not supported and will never be. | ||
| 318 | Handle(const Handle&) = delete; | ||
| 319 | Handle& operator=(const Handle&) = delete; | ||
| 320 | |||
| 321 | /// Construct a handle transfering the ownership from another handle. | ||
| 322 | Handle(Handle&& rhs) noexcept | ||
| 323 | : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, dld{rhs.dld} {} | ||
| 324 | |||
| 325 | /// Assign the current handle transfering the ownership from another handle. | ||
| 326 | /// Destroys any previously held object. | ||
| 327 | Handle& operator=(Handle&& rhs) noexcept { | ||
| 328 | Release(); | ||
| 329 | handle = std::exchange(rhs.handle, nullptr); | ||
| 330 | owner = rhs.owner; | ||
| 331 | dld = rhs.dld; | ||
| 332 | return *this; | ||
| 333 | } | ||
| 334 | |||
| 335 | /// Destroys the current handle if it existed. | ||
| 336 | ~Handle() noexcept { | ||
| 337 | Release(); | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Destroys any held object. | ||
| 341 | void reset() noexcept { | ||
| 342 | Release(); | ||
| 343 | handle = nullptr; | ||
| 344 | } | ||
| 345 | |||
| 346 | /// Returns the address of the held object. | ||
| 347 | /// Intended for Vulkan structures that expect a pointer to an array. | ||
| 348 | const Type* address() const noexcept { | ||
| 349 | return std::addressof(handle); | ||
| 350 | } | ||
| 351 | |||
| 352 | /// Returns the held Vulkan handle. | ||
| 353 | Type operator*() const noexcept { | ||
| 354 | return handle; | ||
| 355 | } | ||
| 356 | |||
| 357 | /// Returns true when there's a held object. | ||
| 358 | explicit operator bool() const noexcept { | ||
| 359 | return handle != nullptr; | ||
| 360 | } | ||
| 361 | |||
| 362 | protected: | ||
| 363 | Type handle = nullptr; | ||
| 364 | OwnerType owner = nullptr; | ||
| 365 | const Dispatch* dld = nullptr; | ||
| 366 | |||
| 367 | private: | ||
| 368 | /// Destroys the held object if it exists. | ||
| 369 | void Release() noexcept { | ||
| 370 | if (handle) { | ||
| 371 | Destroy(owner, handle, *dld); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | }; | ||
| 375 | |||
| 376 | /// Dummy type used to specify a handle has no owner. | ||
| 377 | struct NoOwner {}; | ||
| 378 | |||
| 379 | /// Handle without an owning type. | ||
| 380 | /// Analogue to std::unique_ptr | ||
| 381 | template <typename Type, typename Dispatch> | ||
| 382 | class Handle<Type, NoOwner, Dispatch> { | ||
| 383 | public: | ||
| 384 | /// Construct a handle and hold it's ownership. | ||
| 385 | explicit Handle(Type handle_, const Dispatch& dld_) noexcept : handle{handle_}, dld{&dld_} {} | ||
| 386 | |||
| 387 | /// Construct an empty handle. | ||
| 388 | Handle() noexcept = default; | ||
| 389 | |||
| 390 | /// Copying Vulkan objects is not supported and will never be. | ||
| 391 | Handle(const Handle&) = delete; | ||
| 392 | Handle& operator=(const Handle&) = delete; | ||
| 393 | |||
| 394 | /// Construct a handle transfering ownership from another handle. | ||
| 395 | Handle(Handle&& rhs) noexcept : handle{std::exchange(rhs.handle, nullptr)}, dld{rhs.dld} {} | ||
| 396 | |||
| 397 | /// Assign the current handle transfering the ownership from another handle. | ||
| 398 | /// Destroys any previously held object. | ||
| 399 | Handle& operator=(Handle&& rhs) noexcept { | ||
| 400 | Release(); | ||
| 401 | handle = std::exchange(rhs.handle, nullptr); | ||
| 402 | dld = rhs.dld; | ||
| 403 | return *this; | ||
| 404 | } | ||
| 405 | |||
| 406 | /// Destroys the current handle if it existed. | ||
| 407 | ~Handle() noexcept { | ||
| 408 | Release(); | ||
| 409 | } | ||
| 410 | |||
| 411 | /// Destroys any held object. | ||
| 412 | void reset() noexcept { | ||
| 413 | Release(); | ||
| 414 | handle = nullptr; | ||
| 415 | } | ||
| 416 | |||
| 417 | /// Returns the address of the held object. | ||
| 418 | /// Intended for Vulkan structures that expect a pointer to an array. | ||
| 419 | const Type* address() const noexcept { | ||
| 420 | return std::addressof(handle); | ||
| 421 | } | ||
| 422 | |||
| 423 | /// Returns the held Vulkan handle. | ||
| 424 | Type operator*() const noexcept { | ||
| 425 | return handle; | ||
| 426 | } | ||
| 427 | |||
| 428 | /// Returns true when there's a held object. | ||
| 429 | operator bool() const noexcept { | ||
| 430 | return handle != nullptr; | ||
| 431 | } | ||
| 432 | |||
| 433 | protected: | ||
| 434 | Type handle = nullptr; | ||
| 435 | const Dispatch* dld = nullptr; | ||
| 436 | |||
| 437 | private: | ||
| 438 | /// Destroys the held object if it exists. | ||
| 439 | void Release() noexcept { | ||
| 440 | if (handle) { | ||
| 441 | Destroy(handle, *dld); | ||
| 442 | } | ||
| 443 | } | ||
| 444 | }; | ||
| 445 | |||
| 446 | /// Array of a pool allocation. | ||
| 447 | /// Analogue to std::vector | ||
| 448 | template <typename AllocationType, typename PoolType> | ||
| 449 | class PoolAllocations { | ||
| 450 | public: | ||
| 451 | /// Construct an empty allocation. | ||
| 452 | PoolAllocations() = default; | ||
| 453 | |||
| 454 | /// Construct an allocation. Errors are reported through IsOutOfPoolMemory(). | ||
| 455 | explicit PoolAllocations(std::unique_ptr<AllocationType[]> allocations, std::size_t num, | ||
| 456 | VkDevice device, PoolType pool, const DeviceDispatch& dld) noexcept | ||
| 457 | : allocations{std::move(allocations)}, num{num}, device{device}, pool{pool}, dld{&dld} {} | ||
| 458 | |||
| 459 | /// Copying Vulkan allocations is not supported and will never be. | ||
| 460 | PoolAllocations(const PoolAllocations&) = delete; | ||
| 461 | PoolAllocations& operator=(const PoolAllocations&) = delete; | ||
| 462 | |||
| 463 | /// Construct an allocation transfering ownership from another allocation. | ||
| 464 | PoolAllocations(PoolAllocations&& rhs) noexcept | ||
| 465 | : allocations{std::move(rhs.allocations)}, num{rhs.num}, device{rhs.device}, pool{rhs.pool}, | ||
| 466 | dld{rhs.dld} {} | ||
| 467 | |||
| 468 | /// Assign an allocation transfering ownership from another allocation. | ||
| 469 | /// Releases any previously held allocation. | ||
| 470 | PoolAllocations& operator=(PoolAllocations&& rhs) noexcept { | ||
| 471 | Release(); | ||
| 472 | allocations = std::move(rhs.allocations); | ||
| 473 | num = rhs.num; | ||
| 474 | device = rhs.device; | ||
| 475 | pool = rhs.pool; | ||
| 476 | dld = rhs.dld; | ||
| 477 | return *this; | ||
| 478 | } | ||
| 479 | |||
| 480 | /// Destroys any held allocation. | ||
| 481 | ~PoolAllocations() { | ||
| 482 | Release(); | ||
| 483 | } | ||
| 484 | |||
| 485 | /// Returns the number of allocations. | ||
| 486 | std::size_t size() const noexcept { | ||
| 487 | return num; | ||
| 488 | } | ||
| 489 | |||
| 490 | /// Returns a pointer to the array of allocations. | ||
| 491 | AllocationType const* data() const noexcept { | ||
| 492 | return allocations.get(); | ||
| 493 | } | ||
| 494 | |||
| 495 | /// Returns the allocation in the specified index. | ||
| 496 | /// @pre index < size() | ||
| 497 | AllocationType operator[](std::size_t index) const noexcept { | ||
| 498 | return allocations[index]; | ||
| 499 | } | ||
| 500 | |||
| 501 | /// True when a pool fails to construct. | ||
| 502 | bool IsOutOfPoolMemory() const noexcept { | ||
| 503 | return !device; | ||
| 504 | } | ||
| 505 | |||
| 506 | private: | ||
| 507 | /// Destroys the held allocations if they exist. | ||
| 508 | void Release() noexcept { | ||
| 509 | if (!allocations) { | ||
| 510 | return; | ||
| 511 | } | ||
| 512 | const Span<AllocationType> span(allocations.get(), num); | ||
| 513 | const VkResult result = Free(device, pool, span, *dld); | ||
| 514 | // There's no way to report errors from a destructor. | ||
| 515 | if (result != VK_SUCCESS) { | ||
| 516 | std::terminate(); | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | std::unique_ptr<AllocationType[]> allocations; | ||
| 521 | std::size_t num = 0; | ||
| 522 | VkDevice device = nullptr; | ||
| 523 | PoolType pool = nullptr; | ||
| 524 | const DeviceDispatch* dld = nullptr; | ||
| 525 | }; | ||
| 526 | |||
| 527 | using BufferView = Handle<VkBufferView, VkDevice, DeviceDispatch>; | ||
| 528 | using DebugCallback = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; | ||
| 529 | using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; | ||
| 530 | using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; | ||
| 531 | using Framebuffer = Handle<VkFramebuffer, VkDevice, DeviceDispatch>; | ||
| 532 | using ImageView = Handle<VkImageView, VkDevice, DeviceDispatch>; | ||
| 533 | using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; | ||
| 534 | using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>; | ||
| 535 | using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>; | ||
| 536 | using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>; | ||
| 537 | using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>; | ||
| 538 | using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>; | ||
| 539 | using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>; | ||
| 540 | using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>; | ||
| 541 | |||
| 542 | using DescriptorSets = PoolAllocations<VkDescriptorSet, VkDescriptorPool>; | ||
| 543 | using CommandBuffers = PoolAllocations<VkCommandBuffer, VkCommandPool>; | ||
| 544 | |||
| 545 | /// Vulkan instance owning handle. | ||
| 546 | class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> { | ||
| 547 | using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle; | ||
| 548 | |||
| 549 | public: | ||
| 550 | /// Creates a Vulkan instance. Use "operator bool" for error handling. | ||
| 551 | static Instance Create(Span<const char*> layers, Span<const char*> extensions, | ||
| 552 | InstanceDispatch& dld) noexcept; | ||
| 553 | |||
| 554 | /// Enumerates physical devices. | ||
| 555 | /// @return Physical devices and an empty handle on failure. | ||
| 556 | std::optional<std::vector<VkPhysicalDevice>> EnumeratePhysicalDevices(); | ||
| 557 | |||
| 558 | /// Tries to create a debug callback messenger. Returns an empty handle on failure. | ||
| 559 | DebugCallback TryCreateDebugCallback(PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept; | ||
| 560 | }; | ||
| 561 | |||
| 562 | class Queue { | ||
| 563 | public: | ||
| 564 | /// Construct an empty queue handle. | ||
| 565 | constexpr Queue() noexcept = default; | ||
| 566 | |||
| 567 | /// Construct a queue handle. | ||
| 568 | constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} | ||
| 569 | |||
| 570 | /// Returns the checkpoint data. | ||
| 571 | /// @note Returns an empty vector when the function pointer is not present. | ||
| 572 | std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const; | ||
| 573 | |||
| 574 | void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const { | ||
| 575 | Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence)); | ||
| 576 | } | ||
| 577 | |||
| 578 | VkResult Present(const VkPresentInfoKHR& present_info) const noexcept { | ||
| 579 | return dld->vkQueuePresentKHR(queue, &present_info); | ||
| 580 | } | ||
| 581 | |||
| 582 | private: | ||
| 583 | VkQueue queue = nullptr; | ||
| 584 | const DeviceDispatch* dld = nullptr; | ||
| 585 | }; | ||
| 586 | |||
| 587 | class Buffer : public Handle<VkBuffer, VkDevice, DeviceDispatch> { | ||
| 588 | using Handle<VkBuffer, VkDevice, DeviceDispatch>::Handle; | ||
| 589 | |||
| 590 | public: | ||
| 591 | /// Attaches a memory allocation. | ||
| 592 | void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const; | ||
| 593 | }; | ||
| 594 | |||
| 595 | class Image : public Handle<VkImage, VkDevice, DeviceDispatch> { | ||
| 596 | using Handle<VkImage, VkDevice, DeviceDispatch>::Handle; | ||
| 597 | |||
| 598 | public: | ||
| 599 | /// Attaches a memory allocation. | ||
| 600 | void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const; | ||
| 601 | }; | ||
| 602 | |||
| 603 | class DeviceMemory : public Handle<VkDeviceMemory, VkDevice, DeviceDispatch> { | ||
| 604 | using Handle<VkDeviceMemory, VkDevice, DeviceDispatch>::Handle; | ||
| 605 | |||
| 606 | public: | ||
| 607 | u8* Map(VkDeviceSize offset, VkDeviceSize size) const { | ||
| 608 | void* data; | ||
| 609 | Check(dld->vkMapMemory(owner, handle, offset, size, 0, &data)); | ||
| 610 | return static_cast<u8*>(data); | ||
| 611 | } | ||
| 612 | |||
| 613 | void Unmap() const noexcept { | ||
| 614 | dld->vkUnmapMemory(owner, handle); | ||
| 615 | } | ||
| 616 | }; | ||
| 617 | |||
| 618 | class Fence : public Handle<VkFence, VkDevice, DeviceDispatch> { | ||
| 619 | using Handle<VkFence, VkDevice, DeviceDispatch>::Handle; | ||
| 620 | |||
| 621 | public: | ||
| 622 | VkResult Wait(u64 timeout = std::numeric_limits<u64>::max()) const noexcept { | ||
| 623 | return dld->vkWaitForFences(owner, 1, &handle, true, timeout); | ||
| 624 | } | ||
| 625 | |||
| 626 | VkResult GetStatus() const noexcept { | ||
| 627 | return dld->vkGetFenceStatus(owner, handle); | ||
| 628 | } | ||
| 629 | |||
| 630 | void Reset() const { | ||
| 631 | Check(dld->vkResetFences(owner, 1, &handle)); | ||
| 632 | } | ||
| 633 | }; | ||
| 634 | |||
| 635 | class DescriptorPool : public Handle<VkDescriptorPool, VkDevice, DeviceDispatch> { | ||
| 636 | using Handle<VkDescriptorPool, VkDevice, DeviceDispatch>::Handle; | ||
| 637 | |||
| 638 | public: | ||
| 639 | DescriptorSets Allocate(const VkDescriptorSetAllocateInfo& ai) const; | ||
| 640 | }; | ||
| 641 | |||
| 642 | class CommandPool : public Handle<VkCommandPool, VkDevice, DeviceDispatch> { | ||
| 643 | using Handle<VkCommandPool, VkDevice, DeviceDispatch>::Handle; | ||
| 644 | |||
| 645 | public: | ||
| 646 | CommandBuffers Allocate(std::size_t num_buffers, | ||
| 647 | VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) const; | ||
| 648 | }; | ||
| 649 | |||
| 650 | class SwapchainKHR : public Handle<VkSwapchainKHR, VkDevice, DeviceDispatch> { | ||
| 651 | using Handle<VkSwapchainKHR, VkDevice, DeviceDispatch>::Handle; | ||
| 652 | |||
| 653 | public: | ||
| 654 | std::vector<VkImage> GetImages() const; | ||
| 655 | }; | ||
| 656 | |||
| 657 | class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { | ||
| 658 | using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle; | ||
| 659 | |||
| 660 | public: | ||
| 661 | static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, | ||
| 662 | Span<const char*> enabled_extensions, | ||
| 663 | const VkPhysicalDeviceFeatures2& enabled_features, | ||
| 664 | DeviceDispatch& dld) noexcept; | ||
| 665 | |||
| 666 | Queue GetQueue(u32 family_index) const noexcept; | ||
| 667 | |||
| 668 | Buffer CreateBuffer(const VkBufferCreateInfo& ci) const; | ||
| 669 | |||
| 670 | BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; | ||
| 671 | |||
| 672 | Image CreateImage(const VkImageCreateInfo& ci) const; | ||
| 673 | |||
| 674 | ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; | ||
| 675 | |||
| 676 | Semaphore CreateSemaphore() const; | ||
| 677 | |||
| 678 | Fence CreateFence(const VkFenceCreateInfo& ci) const; | ||
| 679 | |||
| 680 | DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const; | ||
| 681 | |||
| 682 | RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; | ||
| 683 | |||
| 684 | DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; | ||
| 685 | |||
| 686 | PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; | ||
| 687 | |||
| 688 | Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const; | ||
| 689 | |||
| 690 | Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const; | ||
| 691 | |||
| 692 | Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; | ||
| 693 | |||
| 694 | Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const; | ||
| 695 | |||
| 696 | CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; | ||
| 697 | |||
| 698 | DescriptorUpdateTemplateKHR CreateDescriptorUpdateTemplateKHR( | ||
| 699 | const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const; | ||
| 700 | |||
| 701 | QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const; | ||
| 702 | |||
| 703 | ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; | ||
| 704 | |||
| 705 | SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; | ||
| 706 | |||
| 707 | DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; | ||
| 708 | |||
| 709 | DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const; | ||
| 710 | |||
| 711 | VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer) const noexcept; | ||
| 712 | |||
| 713 | VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept; | ||
| 714 | |||
| 715 | void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, | ||
| 716 | Span<VkCopyDescriptorSet> copies) const noexcept; | ||
| 717 | |||
| 718 | void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplateKHR update_template, | ||
| 719 | const void* data) const noexcept { | ||
| 720 | dld->vkUpdateDescriptorSetWithTemplateKHR(handle, set, update_template, data); | ||
| 721 | } | ||
| 722 | |||
| 723 | VkResult AcquireNextImageKHR(VkSwapchainKHR swapchain, u64 timeout, VkSemaphore semaphore, | ||
| 724 | VkFence fence, u32* image_index) const noexcept { | ||
| 725 | return dld->vkAcquireNextImageKHR(handle, swapchain, timeout, semaphore, fence, | ||
| 726 | image_index); | ||
| 727 | } | ||
| 728 | |||
| 729 | VkResult WaitIdle() const noexcept { | ||
| 730 | return dld->vkDeviceWaitIdle(handle); | ||
| 731 | } | ||
| 732 | |||
| 733 | void ResetQueryPoolEXT(VkQueryPool query_pool, u32 first, u32 count) const noexcept { | ||
| 734 | dld->vkResetQueryPoolEXT(handle, query_pool, first, count); | ||
| 735 | } | ||
| 736 | |||
| 737 | void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, | ||
| 738 | void* data, VkDeviceSize stride, VkQueryResultFlags flags) const { | ||
| 739 | Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, | ||
| 740 | flags)); | ||
| 741 | } | ||
| 742 | |||
| 743 | template <typename T> | ||
| 744 | T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const { | ||
| 745 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 746 | T value; | ||
| 747 | GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags); | ||
| 748 | return value; | ||
| 749 | } | ||
| 750 | }; | ||
| 751 | |||
| 752 | class PhysicalDevice { | ||
| 753 | public: | ||
| 754 | constexpr PhysicalDevice() noexcept = default; | ||
| 755 | |||
| 756 | constexpr PhysicalDevice(VkPhysicalDevice physical_device, const InstanceDispatch& dld) noexcept | ||
| 757 | : physical_device{physical_device}, dld{&dld} {} | ||
| 758 | |||
| 759 | constexpr operator VkPhysicalDevice() const noexcept { | ||
| 760 | return physical_device; | ||
| 761 | } | ||
| 762 | |||
| 763 | VkPhysicalDeviceProperties GetProperties() const noexcept; | ||
| 764 | |||
| 765 | void GetProperties2KHR(VkPhysicalDeviceProperties2KHR&) const noexcept; | ||
| 766 | |||
| 767 | VkPhysicalDeviceFeatures GetFeatures() const noexcept; | ||
| 768 | |||
| 769 | void GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR&) const noexcept; | ||
| 770 | |||
| 771 | VkFormatProperties GetFormatProperties(VkFormat) const noexcept; | ||
| 772 | |||
| 773 | std::vector<VkExtensionProperties> EnumerateDeviceExtensionProperties() const; | ||
| 774 | |||
| 775 | std::vector<VkQueueFamilyProperties> GetQueueFamilyProperties() const; | ||
| 776 | |||
| 777 | bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; | ||
| 778 | |||
| 779 | VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const noexcept; | ||
| 780 | |||
| 781 | std::vector<VkSurfaceFormatKHR> GetSurfaceFormatsKHR(VkSurfaceKHR) const; | ||
| 782 | |||
| 783 | std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const; | ||
| 784 | |||
| 785 | VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept; | ||
| 786 | |||
| 787 | private: | ||
| 788 | VkPhysicalDevice physical_device = nullptr; | ||
| 789 | const InstanceDispatch* dld = nullptr; | ||
| 790 | }; | ||
| 791 | |||
| 792 | class CommandBuffer { | ||
| 793 | public: | ||
| 794 | CommandBuffer() noexcept = default; | ||
| 795 | |||
| 796 | explicit CommandBuffer(VkCommandBuffer handle, const DeviceDispatch& dld) noexcept | ||
| 797 | : handle{handle}, dld{&dld} {} | ||
| 798 | |||
| 799 | const VkCommandBuffer* address() const noexcept { | ||
| 800 | return &handle; | ||
| 801 | } | ||
| 802 | |||
| 803 | void Begin(const VkCommandBufferBeginInfo& begin_info) const { | ||
| 804 | Check(dld->vkBeginCommandBuffer(handle, &begin_info)); | ||
| 805 | } | ||
| 806 | |||
| 807 | void End() const { | ||
| 808 | Check(dld->vkEndCommandBuffer(handle)); | ||
| 809 | } | ||
| 810 | |||
| 811 | void BeginRenderPass(const VkRenderPassBeginInfo& renderpass_bi, | ||
| 812 | VkSubpassContents contents) const noexcept { | ||
| 813 | dld->vkCmdBeginRenderPass(handle, &renderpass_bi, contents); | ||
| 814 | } | ||
| 815 | |||
| 816 | void EndRenderPass() const noexcept { | ||
| 817 | dld->vkCmdEndRenderPass(handle); | ||
| 818 | } | ||
| 819 | |||
| 820 | void BeginQuery(VkQueryPool query_pool, u32 query, VkQueryControlFlags flags) const noexcept { | ||
| 821 | dld->vkCmdBeginQuery(handle, query_pool, query, flags); | ||
| 822 | } | ||
| 823 | |||
| 824 | void EndQuery(VkQueryPool query_pool, u32 query) const noexcept { | ||
| 825 | dld->vkCmdEndQuery(handle, query_pool, query); | ||
| 826 | } | ||
| 827 | |||
| 828 | void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first, | ||
| 829 | Span<VkDescriptorSet> sets, Span<u32> dynamic_offsets) const noexcept { | ||
| 830 | dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(), | ||
| 831 | dynamic_offsets.size(), dynamic_offsets.data()); | ||
| 832 | } | ||
| 833 | |||
| 834 | void BindPipeline(VkPipelineBindPoint bind_point, VkPipeline pipeline) const noexcept { | ||
| 835 | dld->vkCmdBindPipeline(handle, bind_point, pipeline); | ||
| 836 | } | ||
| 837 | |||
| 838 | void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) const | ||
| 839 | noexcept { | ||
| 840 | dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type); | ||
| 841 | } | ||
| 842 | |||
| 843 | void BindVertexBuffers(u32 first, u32 count, const VkBuffer* buffers, | ||
| 844 | const VkDeviceSize* offsets) const noexcept { | ||
| 845 | dld->vkCmdBindVertexBuffers(handle, first, count, buffers, offsets); | ||
| 846 | } | ||
| 847 | |||
| 848 | void BindVertexBuffer(u32 binding, VkBuffer buffer, VkDeviceSize offset) const noexcept { | ||
| 849 | BindVertexBuffers(binding, 1, &buffer, &offset); | ||
| 850 | } | ||
| 851 | |||
| 852 | void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance) const | ||
| 853 | noexcept { | ||
| 854 | dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance); | ||
| 855 | } | ||
| 856 | |||
| 857 | void DrawIndexed(u32 index_count, u32 instance_count, u32 first_index, u32 vertex_offset, | ||
| 858 | u32 first_instance) const noexcept { | ||
| 859 | dld->vkCmdDrawIndexed(handle, index_count, instance_count, first_index, vertex_offset, | ||
| 860 | first_instance); | ||
| 861 | } | ||
| 862 | |||
| 863 | void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const | ||
| 864 | noexcept { | ||
| 865 | dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), | ||
| 866 | rects.data()); | ||
| 867 | } | ||
| 868 | |||
| 869 | void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, | ||
| 870 | VkImageLayout dst_layout, Span<VkImageBlit> regions, VkFilter filter) const | ||
| 871 | noexcept { | ||
| 872 | dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), | ||
| 873 | regions.data(), filter); | ||
| 874 | } | ||
| 875 | |||
| 876 | void Dispatch(u32 x, u32 y, u32 z) const noexcept { | ||
| 877 | dld->vkCmdDispatch(handle, x, y, z); | ||
| 878 | } | ||
| 879 | |||
| 880 | void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, | ||
| 881 | VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, | ||
| 882 | Span<VkBufferMemoryBarrier> buffer_barriers, | ||
| 883 | Span<VkImageMemoryBarrier> image_barriers) const noexcept { | ||
| 884 | dld->vkCmdPipelineBarrier(handle, src_stage_mask, dst_stage_mask, dependency_flags, | ||
| 885 | memory_barriers.size(), memory_barriers.data(), | ||
| 886 | buffer_barriers.size(), buffer_barriers.data(), | ||
| 887 | image_barriers.size(), image_barriers.data()); | ||
| 888 | } | ||
| 889 | |||
| 890 | void CopyBufferToImage(VkBuffer src_buffer, VkImage dst_image, VkImageLayout dst_image_layout, | ||
| 891 | Span<VkBufferImageCopy> regions) const noexcept { | ||
| 892 | dld->vkCmdCopyBufferToImage(handle, src_buffer, dst_image, dst_image_layout, regions.size(), | ||
| 893 | regions.data()); | ||
| 894 | } | ||
| 895 | |||
| 896 | void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, Span<VkBufferCopy> regions) const | ||
| 897 | noexcept { | ||
| 898 | dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data()); | ||
| 899 | } | ||
| 900 | |||
| 901 | void CopyImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, | ||
| 902 | VkImageLayout dst_layout, Span<VkImageCopy> regions) const noexcept { | ||
| 903 | dld->vkCmdCopyImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), | ||
| 904 | regions.data()); | ||
| 905 | } | ||
| 906 | |||
| 907 | void CopyImageToBuffer(VkImage src_image, VkImageLayout src_layout, VkBuffer dst_buffer, | ||
| 908 | Span<VkBufferImageCopy> regions) const noexcept { | ||
| 909 | dld->vkCmdCopyImageToBuffer(handle, src_image, src_layout, dst_buffer, regions.size(), | ||
| 910 | regions.data()); | ||
| 911 | } | ||
| 912 | |||
| 913 | void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, u32 data) const | ||
| 914 | noexcept { | ||
| 915 | dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data); | ||
| 916 | } | ||
| 917 | |||
| 918 | void PushConstants(VkPipelineLayout layout, VkShaderStageFlags flags, u32 offset, u32 size, | ||
| 919 | const void* values) const noexcept { | ||
| 920 | dld->vkCmdPushConstants(handle, layout, flags, offset, size, values); | ||
| 921 | } | ||
| 922 | |||
| 923 | void SetCheckpointNV(const void* checkpoint_marker) const noexcept { | ||
| 924 | dld->vkCmdSetCheckpointNV(handle, checkpoint_marker); | ||
| 925 | } | ||
| 926 | |||
| 927 | void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept { | ||
| 928 | dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data()); | ||
| 929 | } | ||
| 930 | |||
| 931 | void SetScissor(u32 first, Span<VkRect2D> scissors) const noexcept { | ||
| 932 | dld->vkCmdSetScissor(handle, first, scissors.size(), scissors.data()); | ||
| 933 | } | ||
| 934 | |||
| 935 | void SetBlendConstants(const float blend_constants[4]) const noexcept { | ||
| 936 | dld->vkCmdSetBlendConstants(handle, blend_constants); | ||
| 937 | } | ||
| 938 | |||
| 939 | void SetStencilCompareMask(VkStencilFaceFlags face_mask, u32 compare_mask) const noexcept { | ||
| 940 | dld->vkCmdSetStencilCompareMask(handle, face_mask, compare_mask); | ||
| 941 | } | ||
| 942 | |||
| 943 | void SetStencilReference(VkStencilFaceFlags face_mask, u32 reference) const noexcept { | ||
| 944 | dld->vkCmdSetStencilReference(handle, face_mask, reference); | ||
| 945 | } | ||
| 946 | |||
| 947 | void SetStencilWriteMask(VkStencilFaceFlags face_mask, u32 write_mask) const noexcept { | ||
| 948 | dld->vkCmdSetStencilWriteMask(handle, face_mask, write_mask); | ||
| 949 | } | ||
| 950 | |||
| 951 | void SetDepthBias(float constant_factor, float clamp, float slope_factor) const noexcept { | ||
| 952 | dld->vkCmdSetDepthBias(handle, constant_factor, clamp, slope_factor); | ||
| 953 | } | ||
| 954 | |||
| 955 | void SetDepthBounds(float min_depth_bounds, float max_depth_bounds) const noexcept { | ||
| 956 | dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds); | ||
| 957 | } | ||
| 958 | |||
| 959 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, | ||
| 960 | const VkDeviceSize* offsets, | ||
| 961 | const VkDeviceSize* sizes) const noexcept { | ||
| 962 | dld->vkCmdBindTransformFeedbackBuffersEXT(handle, first, count, buffers, offsets, sizes); | ||
| 963 | } | ||
| 964 | |||
| 965 | void BeginTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count, | ||
| 966 | const VkBuffer* counter_buffers, | ||
| 967 | const VkDeviceSize* counter_buffer_offsets) const noexcept { | ||
| 968 | dld->vkCmdBeginTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, | ||
| 969 | counter_buffers, counter_buffer_offsets); | ||
| 970 | } | ||
| 971 | |||
| 972 | void EndTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count, | ||
| 973 | const VkBuffer* counter_buffers, | ||
| 974 | const VkDeviceSize* counter_buffer_offsets) const noexcept { | ||
| 975 | dld->vkCmdEndTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, | ||
| 976 | counter_buffers, counter_buffer_offsets); | ||
| 977 | } | ||
| 978 | |||
| 979 | private: | ||
| 980 | VkCommandBuffer handle; | ||
| 981 | const DeviceDispatch* dld; | ||
| 982 | }; | ||
| 983 | |||
| 984 | std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( | ||
| 985 | const InstanceDispatch& dld); | ||
| 986 | |||
| 987 | } // namespace Vulkan::vk | ||
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp index 2fe787d6f..0f4c3103a 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp | |||
| @@ -235,34 +235,30 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) { | |||
| 235 | case OpCode::Id::LEA_IMM: | 235 | case OpCode::Id::LEA_IMM: |
| 236 | case OpCode::Id::LEA_RZ: | 236 | case OpCode::Id::LEA_RZ: |
| 237 | case OpCode::Id::LEA_HI: { | 237 | case OpCode::Id::LEA_HI: { |
| 238 | const auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> { | 238 | auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> { |
| 239 | switch (opcode->get().GetId()) { | 239 | switch (opcode->get().GetId()) { |
| 240 | case OpCode::Id::LEA_R2: { | 240 | case OpCode::Id::LEA_R2: { |
| 241 | return {GetRegister(instr.gpr20), GetRegister(instr.gpr39), | 241 | return {GetRegister(instr.gpr20), GetRegister(instr.gpr39), |
| 242 | Immediate(static_cast<u32>(instr.lea.r2.entry_a))}; | 242 | Immediate(static_cast<u32>(instr.lea.r2.entry_a))}; |
| 243 | } | 243 | } |
| 244 | |||
| 245 | case OpCode::Id::LEA_R1: { | 244 | case OpCode::Id::LEA_R1: { |
| 246 | const bool neg = instr.lea.r1.neg != 0; | 245 | const bool neg = instr.lea.r1.neg != 0; |
| 247 | return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), | 246 | return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), |
| 248 | GetRegister(instr.gpr20), | 247 | GetRegister(instr.gpr20), |
| 249 | Immediate(static_cast<u32>(instr.lea.r1.entry_a))}; | 248 | Immediate(static_cast<u32>(instr.lea.r1.entry_a))}; |
| 250 | } | 249 | } |
| 251 | |||
| 252 | case OpCode::Id::LEA_IMM: { | 250 | case OpCode::Id::LEA_IMM: { |
| 253 | const bool neg = instr.lea.imm.neg != 0; | 251 | const bool neg = instr.lea.imm.neg != 0; |
| 254 | return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)), | 252 | return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)), |
| 255 | GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), | 253 | GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), |
| 256 | Immediate(static_cast<u32>(instr.lea.imm.entry_b))}; | 254 | Immediate(static_cast<u32>(instr.lea.imm.entry_b))}; |
| 257 | } | 255 | } |
| 258 | |||
| 259 | case OpCode::Id::LEA_RZ: { | 256 | case OpCode::Id::LEA_RZ: { |
| 260 | const bool neg = instr.lea.rz.neg != 0; | 257 | const bool neg = instr.lea.rz.neg != 0; |
| 261 | return {GetConstBuffer(instr.lea.rz.cb_index, instr.lea.rz.cb_offset), | 258 | return {GetConstBuffer(instr.lea.rz.cb_index, instr.lea.rz.cb_offset), |
| 262 | GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), | 259 | GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), |
| 263 | Immediate(static_cast<u32>(instr.lea.rz.entry_a))}; | 260 | Immediate(static_cast<u32>(instr.lea.rz.entry_a))}; |
| 264 | } | 261 | } |
| 265 | |||
| 266 | case OpCode::Id::LEA_HI: | 262 | case OpCode::Id::LEA_HI: |
| 267 | default: | 263 | default: |
| 268 | UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName()); | 264 | UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName()); |
| @@ -275,12 +271,9 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) { | |||
| 275 | UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex), | 271 | UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex), |
| 276 | "Unhandled LEA Predicate"); | 272 | "Unhandled LEA Predicate"); |
| 277 | 273 | ||
| 278 | const Node shifted_c = | 274 | Node value = Operation(OperationCode::ILogicalShiftLeft, std::move(op_a), std::move(op_c)); |
| 279 | Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, Immediate(1), op_c); | 275 | value = Operation(OperationCode::IAdd, std::move(op_b), std::move(value)); |
| 280 | const Node mul_bc = Operation(OperationCode::IMul, NO_PRECISE, op_b, shifted_c); | 276 | SetRegister(bb, instr.gpr0, std::move(value)); |
| 281 | const Node value = Operation(OperationCode::IAdd, NO_PRECISE, op_a, mul_bc); | ||
| 282 | |||
| 283 | SetRegister(bb, instr.gpr0, value); | ||
| 284 | 277 | ||
| 285 | break; | 278 | break; |
| 286 | } | 279 | } |
diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp index 6ead42070..c72690b2b 100644 --- a/src/video_core/shader/decode/conversion.cpp +++ b/src/video_core/shader/decode/conversion.cpp | |||
| @@ -138,18 +138,23 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 138 | 138 | ||
| 139 | value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); | 139 | value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); |
| 140 | 140 | ||
| 141 | value = [&]() { | 141 | value = [&] { |
| 142 | if (instr.conversion.src_size != instr.conversion.dst_size) { | ||
| 143 | // Rounding operations only matter when the source and destination conversion size | ||
| 144 | // is the same. | ||
| 145 | return value; | ||
| 146 | } | ||
| 142 | switch (instr.conversion.f2f.GetRoundingMode()) { | 147 | switch (instr.conversion.f2f.GetRoundingMode()) { |
| 143 | case Tegra::Shader::F2fRoundingOp::None: | 148 | case Tegra::Shader::F2fRoundingOp::None: |
| 144 | return value; | 149 | return value; |
| 145 | case Tegra::Shader::F2fRoundingOp::Round: | 150 | case Tegra::Shader::F2fRoundingOp::Round: |
| 146 | return Operation(OperationCode::FRoundEven, PRECISE, value); | 151 | return Operation(OperationCode::FRoundEven, value); |
| 147 | case Tegra::Shader::F2fRoundingOp::Floor: | 152 | case Tegra::Shader::F2fRoundingOp::Floor: |
| 148 | return Operation(OperationCode::FFloor, PRECISE, value); | 153 | return Operation(OperationCode::FFloor, value); |
| 149 | case Tegra::Shader::F2fRoundingOp::Ceil: | 154 | case Tegra::Shader::F2fRoundingOp::Ceil: |
| 150 | return Operation(OperationCode::FCeil, PRECISE, value); | 155 | return Operation(OperationCode::FCeil, value); |
| 151 | case Tegra::Shader::F2fRoundingOp::Trunc: | 156 | case Tegra::Shader::F2fRoundingOp::Trunc: |
| 152 | return Operation(OperationCode::FTrunc, PRECISE, value); | 157 | return Operation(OperationCode::FTrunc, value); |
| 153 | default: | 158 | default: |
| 154 | UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", | 159 | UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", |
| 155 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); | 160 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); |
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index d2fe4ec5d..0dd7a1196 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp | |||
| @@ -13,13 +13,247 @@ | |||
| 13 | #include "video_core/engines/shader_bytecode.h" | 13 | #include "video_core/engines/shader_bytecode.h" |
| 14 | #include "video_core/shader/node_helper.h" | 14 | #include "video_core/shader/node_helper.h" |
| 15 | #include "video_core/shader/shader_ir.h" | 15 | #include "video_core/shader/shader_ir.h" |
| 16 | #include "video_core/textures/texture.h" | ||
| 16 | 17 | ||
| 17 | namespace VideoCommon::Shader { | 18 | namespace VideoCommon::Shader { |
| 18 | 19 | ||
| 19 | using Tegra::Shader::Instruction; | 20 | using Tegra::Shader::Instruction; |
| 20 | using Tegra::Shader::OpCode; | 21 | using Tegra::Shader::OpCode; |
| 22 | using Tegra::Shader::PredCondition; | ||
| 23 | using Tegra::Shader::StoreType; | ||
| 24 | using Tegra::Texture::ComponentType; | ||
| 25 | using Tegra::Texture::TextureFormat; | ||
| 26 | using Tegra::Texture::TICEntry; | ||
| 21 | 27 | ||
| 22 | namespace { | 28 | namespace { |
| 29 | |||
| 30 | ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, | ||
| 31 | std::size_t component) { | ||
| 32 | const TextureFormat format{descriptor.format}; | ||
| 33 | switch (format) { | ||
| 34 | case TextureFormat::R16_G16_B16_A16: | ||
| 35 | case TextureFormat::R32_G32_B32_A32: | ||
| 36 | case TextureFormat::R32_G32_B32: | ||
| 37 | case TextureFormat::R32_G32: | ||
| 38 | case TextureFormat::R16_G16: | ||
| 39 | case TextureFormat::R32: | ||
| 40 | case TextureFormat::R16: | ||
| 41 | case TextureFormat::R8: | ||
| 42 | case TextureFormat::R1: | ||
| 43 | if (component == 0) { | ||
| 44 | return descriptor.r_type; | ||
| 45 | } | ||
| 46 | if (component == 1) { | ||
| 47 | return descriptor.g_type; | ||
| 48 | } | ||
| 49 | if (component == 2) { | ||
| 50 | return descriptor.b_type; | ||
| 51 | } | ||
| 52 | if (component == 3) { | ||
| 53 | return descriptor.a_type; | ||
| 54 | } | ||
| 55 | break; | ||
| 56 | case TextureFormat::A8R8G8B8: | ||
| 57 | if (component == 0) { | ||
| 58 | return descriptor.a_type; | ||
| 59 | } | ||
| 60 | if (component == 1) { | ||
| 61 | return descriptor.r_type; | ||
| 62 | } | ||
| 63 | if (component == 2) { | ||
| 64 | return descriptor.g_type; | ||
| 65 | } | ||
| 66 | if (component == 3) { | ||
| 67 | return descriptor.b_type; | ||
| 68 | } | ||
| 69 | break; | ||
| 70 | case TextureFormat::A2B10G10R10: | ||
| 71 | case TextureFormat::A4B4G4R4: | ||
| 72 | case TextureFormat::A5B5G5R1: | ||
| 73 | case TextureFormat::A1B5G5R5: | ||
| 74 | if (component == 0) { | ||
| 75 | return descriptor.a_type; | ||
| 76 | } | ||
| 77 | if (component == 1) { | ||
| 78 | return descriptor.b_type; | ||
| 79 | } | ||
| 80 | if (component == 2) { | ||
| 81 | return descriptor.g_type; | ||
| 82 | } | ||
| 83 | if (component == 3) { | ||
| 84 | return descriptor.r_type; | ||
| 85 | } | ||
| 86 | break; | ||
| 87 | case TextureFormat::R32_B24G8: | ||
| 88 | if (component == 0) { | ||
| 89 | return descriptor.r_type; | ||
| 90 | } | ||
| 91 | if (component == 1) { | ||
| 92 | return descriptor.b_type; | ||
| 93 | } | ||
| 94 | if (component == 2) { | ||
| 95 | return descriptor.g_type; | ||
| 96 | } | ||
| 97 | break; | ||
| 98 | case TextureFormat::B5G6R5: | ||
| 99 | case TextureFormat::B6G5R5: | ||
| 100 | if (component == 0) { | ||
| 101 | return descriptor.b_type; | ||
| 102 | } | ||
| 103 | if (component == 1) { | ||
| 104 | return descriptor.g_type; | ||
| 105 | } | ||
| 106 | if (component == 2) { | ||
| 107 | return descriptor.r_type; | ||
| 108 | } | ||
| 109 | break; | ||
| 110 | case TextureFormat::G8R24: | ||
| 111 | case TextureFormat::G24R8: | ||
| 112 | case TextureFormat::G8R8: | ||
| 113 | case TextureFormat::G4R4: | ||
| 114 | if (component == 0) { | ||
| 115 | return descriptor.g_type; | ||
| 116 | } | ||
| 117 | if (component == 1) { | ||
| 118 | return descriptor.r_type; | ||
| 119 | } | ||
| 120 | break; | ||
| 121 | } | ||
| 122 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | ||
| 123 | return ComponentType::FLOAT; | ||
| 124 | } | ||
| 125 | |||
| 126 | bool IsComponentEnabled(std::size_t component_mask, std::size_t component) { | ||
| 127 | constexpr u8 R = 0b0001; | ||
| 128 | constexpr u8 G = 0b0010; | ||
| 129 | constexpr u8 B = 0b0100; | ||
| 130 | constexpr u8 A = 0b1000; | ||
| 131 | constexpr std::array<u8, 16> mask = { | ||
| 132 | 0, (R), (G), (R | G), (B), (R | B), (G | B), (R | G | B), | ||
| 133 | (A), (R | A), (G | A), (R | G | A), (B | A), (R | B | A), (G | B | A), (R | G | B | A)}; | ||
| 134 | return std::bitset<4>{mask.at(component_mask)}.test(component); | ||
| 135 | } | ||
| 136 | |||
| 137 | u32 GetComponentSize(TextureFormat format, std::size_t component) { | ||
| 138 | switch (format) { | ||
| 139 | case TextureFormat::R32_G32_B32_A32: | ||
| 140 | return 32; | ||
| 141 | case TextureFormat::R16_G16_B16_A16: | ||
| 142 | return 16; | ||
| 143 | case TextureFormat::R32_G32_B32: | ||
| 144 | return component <= 2 ? 32 : 0; | ||
| 145 | case TextureFormat::R32_G32: | ||
| 146 | return component <= 1 ? 32 : 0; | ||
| 147 | case TextureFormat::R16_G16: | ||
| 148 | return component <= 1 ? 16 : 0; | ||
| 149 | case TextureFormat::R32: | ||
| 150 | return component == 0 ? 32 : 0; | ||
| 151 | case TextureFormat::R16: | ||
| 152 | return component == 0 ? 16 : 0; | ||
| 153 | case TextureFormat::R8: | ||
| 154 | return component == 0 ? 8 : 0; | ||
| 155 | case TextureFormat::R1: | ||
| 156 | return component == 0 ? 1 : 0; | ||
| 157 | case TextureFormat::A8R8G8B8: | ||
| 158 | return 8; | ||
| 159 | case TextureFormat::A2B10G10R10: | ||
| 160 | return (component == 3 || component == 2 || component == 1) ? 10 : 2; | ||
| 161 | case TextureFormat::A4B4G4R4: | ||
| 162 | return 4; | ||
| 163 | case TextureFormat::A5B5G5R1: | ||
| 164 | return (component == 0 || component == 1 || component == 2) ? 5 : 1; | ||
| 165 | case TextureFormat::A1B5G5R5: | ||
| 166 | return (component == 1 || component == 2 || component == 3) ? 5 : 1; | ||
| 167 | case TextureFormat::R32_B24G8: | ||
| 168 | if (component == 0) { | ||
| 169 | return 32; | ||
| 170 | } | ||
| 171 | if (component == 1) { | ||
| 172 | return 24; | ||
| 173 | } | ||
| 174 | if (component == 2) { | ||
| 175 | return 8; | ||
| 176 | } | ||
| 177 | return 0; | ||
| 178 | case TextureFormat::B5G6R5: | ||
| 179 | if (component == 0 || component == 2) { | ||
| 180 | return 5; | ||
| 181 | } | ||
| 182 | if (component == 1) { | ||
| 183 | return 6; | ||
| 184 | } | ||
| 185 | return 0; | ||
| 186 | case TextureFormat::B6G5R5: | ||
| 187 | if (component == 1 || component == 2) { | ||
| 188 | return 5; | ||
| 189 | } | ||
| 190 | if (component == 0) { | ||
| 191 | return 6; | ||
| 192 | } | ||
| 193 | return 0; | ||
| 194 | case TextureFormat::G8R24: | ||
| 195 | if (component == 0) { | ||
| 196 | return 8; | ||
| 197 | } | ||
| 198 | if (component == 1) { | ||
| 199 | return 24; | ||
| 200 | } | ||
| 201 | return 0; | ||
| 202 | case TextureFormat::G24R8: | ||
| 203 | if (component == 0) { | ||
| 204 | return 8; | ||
| 205 | } | ||
| 206 | if (component == 1) { | ||
| 207 | return 24; | ||
| 208 | } | ||
| 209 | return 0; | ||
| 210 | case TextureFormat::G8R8: | ||
| 211 | return (component == 0 || component == 1) ? 8 : 0; | ||
| 212 | case TextureFormat::G4R4: | ||
| 213 | return (component == 0 || component == 1) ? 4 : 0; | ||
| 214 | default: | ||
| 215 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | ||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | std::size_t GetImageComponentMask(TextureFormat format) { | ||
| 221 | constexpr u8 R = 0b0001; | ||
| 222 | constexpr u8 G = 0b0010; | ||
| 223 | constexpr u8 B = 0b0100; | ||
| 224 | constexpr u8 A = 0b1000; | ||
| 225 | switch (format) { | ||
| 226 | case TextureFormat::R32_G32_B32_A32: | ||
| 227 | case TextureFormat::R16_G16_B16_A16: | ||
| 228 | case TextureFormat::A8R8G8B8: | ||
| 229 | case TextureFormat::A2B10G10R10: | ||
| 230 | case TextureFormat::A4B4G4R4: | ||
| 231 | case TextureFormat::A5B5G5R1: | ||
| 232 | case TextureFormat::A1B5G5R5: | ||
| 233 | return std::size_t{R | G | B | A}; | ||
| 234 | case TextureFormat::R32_G32_B32: | ||
| 235 | case TextureFormat::R32_B24G8: | ||
| 236 | case TextureFormat::B5G6R5: | ||
| 237 | case TextureFormat::B6G5R5: | ||
| 238 | return std::size_t{R | G | B}; | ||
| 239 | case TextureFormat::R32_G32: | ||
| 240 | case TextureFormat::R16_G16: | ||
| 241 | case TextureFormat::G8R24: | ||
| 242 | case TextureFormat::G24R8: | ||
| 243 | case TextureFormat::G8R8: | ||
| 244 | case TextureFormat::G4R4: | ||
| 245 | return std::size_t{R | G}; | ||
| 246 | case TextureFormat::R32: | ||
| 247 | case TextureFormat::R16: | ||
| 248 | case TextureFormat::R8: | ||
| 249 | case TextureFormat::R1: | ||
| 250 | return std::size_t{R}; | ||
| 251 | default: | ||
| 252 | UNIMPLEMENTED_MSG("texture format not implement={}", format); | ||
| 253 | return std::size_t{R | G | B | A}; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 23 | std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { | 257 | std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { |
| 24 | switch (image_type) { | 258 | switch (image_type) { |
| 25 | case Tegra::Shader::ImageType::Texture1D: | 259 | case Tegra::Shader::ImageType::Texture1D: |
| @@ -37,6 +271,39 @@ std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { | |||
| 37 | } | 271 | } |
| 38 | } // Anonymous namespace | 272 | } // Anonymous namespace |
| 39 | 273 | ||
| 274 | std::pair<Node, bool> ShaderIR::GetComponentValue(ComponentType component_type, u32 component_size, | ||
| 275 | Node original_value) { | ||
| 276 | switch (component_type) { | ||
| 277 | case ComponentType::SNORM: { | ||
| 278 | // range [-1.0, 1.0] | ||
| 279 | auto cnv_value = Operation(OperationCode::FMul, original_value, | ||
| 280 | Immediate(static_cast<float>(1 << component_size) / 2.f - 1.f)); | ||
| 281 | cnv_value = Operation(OperationCode::ICastFloat, std::move(cnv_value)); | ||
| 282 | return {BitfieldExtract(std::move(cnv_value), 0, component_size), true}; | ||
| 283 | } | ||
| 284 | case ComponentType::SINT: | ||
| 285 | case ComponentType::UNORM: { | ||
| 286 | bool is_signed = component_type == ComponentType::SINT; | ||
| 287 | // range [0.0, 1.0] | ||
| 288 | auto cnv_value = Operation(OperationCode::FMul, original_value, | ||
| 289 | Immediate(static_cast<float>(1 << component_size) - 1.f)); | ||
| 290 | return {SignedOperation(OperationCode::ICastFloat, is_signed, std::move(cnv_value)), | ||
| 291 | is_signed}; | ||
| 292 | } | ||
| 293 | case ComponentType::UINT: // range [0, (1 << component_size) - 1] | ||
| 294 | return {std::move(original_value), false}; | ||
| 295 | case ComponentType::FLOAT: | ||
| 296 | if (component_size == 16) { | ||
| 297 | return {Operation(OperationCode::HCastFloat, original_value), true}; | ||
| 298 | } else { | ||
| 299 | return {std::move(original_value), true}; | ||
| 300 | } | ||
| 301 | default: | ||
| 302 | UNIMPLEMENTED_MSG("Unimplement component type={}", component_type); | ||
| 303 | return {std::move(original_value), true}; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 40 | u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | 307 | u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { |
| 41 | const Instruction instr = {program_code[pc]}; | 308 | const Instruction instr = {program_code[pc]}; |
| 42 | const auto opcode = OpCode::Decode(instr); | 309 | const auto opcode = OpCode::Decode(instr); |
| @@ -53,7 +320,6 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | |||
| 53 | 320 | ||
| 54 | switch (opcode->get().GetId()) { | 321 | switch (opcode->get().GetId()) { |
| 55 | case OpCode::Id::SULD: { | 322 | case OpCode::Id::SULD: { |
| 56 | UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P); | ||
| 57 | UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store != | 323 | UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store != |
| 58 | Tegra::Shader::OutOfBoundsStore::Ignore); | 324 | Tegra::Shader::OutOfBoundsStore::Ignore); |
| 59 | 325 | ||
| @@ -62,17 +328,89 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) { | |||
| 62 | : GetBindlessImage(instr.gpr39, type)}; | 328 | : GetBindlessImage(instr.gpr39, type)}; |
| 63 | image.MarkRead(); | 329 | image.MarkRead(); |
| 64 | 330 | ||
| 65 | u32 indexer = 0; | 331 | if (instr.suldst.mode == Tegra::Shader::SurfaceDataMode::P) { |
| 66 | for (u32 element = 0; element < 4; ++element) { | 332 | u32 indexer = 0; |
| 67 | if (!instr.suldst.IsComponentEnabled(element)) { | 333 | for (u32 element = 0; element < 4; ++element) { |
| 68 | continue; | 334 | if (!instr.suldst.IsComponentEnabled(element)) { |
| 335 | continue; | ||
| 336 | } | ||
| 337 | MetaImage meta{image, {}, element}; | ||
| 338 | Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type)); | ||
| 339 | SetTemporary(bb, indexer++, std::move(value)); | ||
| 340 | } | ||
| 341 | for (u32 i = 0; i < indexer; ++i) { | ||
| 342 | SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i)); | ||
| 343 | } | ||
| 344 | } else if (instr.suldst.mode == Tegra::Shader::SurfaceDataMode::D_BA) { | ||
| 345 | UNIMPLEMENTED_IF(instr.suldst.GetStoreDataLayout() != StoreType::Bits32 && | ||
| 346 | instr.suldst.GetStoreDataLayout() != StoreType::Bits64); | ||
| 347 | |||
| 348 | auto descriptor = [this, instr] { | ||
| 349 | std::optional<Tegra::Engines::SamplerDescriptor> descriptor; | ||
| 350 | if (instr.suldst.is_immediate) { | ||
| 351 | descriptor = | ||
| 352 | registry.ObtainBoundSampler(static_cast<u32>(instr.image.index.Value())); | ||
| 353 | } else { | ||
| 354 | const Node image_register = GetRegister(instr.gpr39); | ||
| 355 | const auto [base_image, buffer, offset] = TrackCbuf( | ||
| 356 | image_register, global_code, static_cast<s64>(global_code.size())); | ||
| 357 | descriptor = registry.ObtainBindlessSampler(buffer, offset); | ||
| 358 | } | ||
| 359 | if (!descriptor) { | ||
| 360 | UNREACHABLE_MSG("Failed to obtain image descriptor"); | ||
| 361 | } | ||
| 362 | return *descriptor; | ||
| 363 | }(); | ||
| 364 | |||
| 365 | const auto comp_mask = GetImageComponentMask(descriptor.format); | ||
| 366 | |||
| 367 | switch (instr.suldst.GetStoreDataLayout()) { | ||
| 368 | case StoreType::Bits32: | ||
| 369 | case StoreType::Bits64: { | ||
| 370 | u32 indexer = 0; | ||
| 371 | u32 shifted_counter = 0; | ||
| 372 | Node value = Immediate(0); | ||
| 373 | for (u32 element = 0; element < 4; ++element) { | ||
| 374 | if (!IsComponentEnabled(comp_mask, element)) { | ||
| 375 | continue; | ||
| 376 | } | ||
| 377 | const auto component_type = GetComponentType(descriptor, element); | ||
| 378 | const auto component_size = GetComponentSize(descriptor.format, element); | ||
| 379 | MetaImage meta{image, {}, element}; | ||
| 380 | |||
| 381 | auto [converted_value, is_signed] = GetComponentValue( | ||
| 382 | component_type, component_size, | ||
| 383 | Operation(OperationCode::ImageLoad, meta, GetCoordinates(type))); | ||
| 384 | |||
| 385 | // shift element to correct position | ||
| 386 | const auto shifted = shifted_counter; | ||
| 387 | if (shifted > 0) { | ||
| 388 | converted_value = | ||
| 389 | SignedOperation(OperationCode::ILogicalShiftLeft, is_signed, | ||
| 390 | std::move(converted_value), Immediate(shifted)); | ||
| 391 | } | ||
| 392 | shifted_counter += component_size; | ||
| 393 | |||
| 394 | // add value into result | ||
| 395 | value = Operation(OperationCode::UBitwiseOr, value, std::move(converted_value)); | ||
| 396 | |||
| 397 | // if we shifted enough for 1 byte -> we save it into temp | ||
| 398 | if (shifted_counter >= 32) { | ||
| 399 | SetTemporary(bb, indexer++, std::move(value)); | ||
| 400 | // reset counter and value to prepare pack next byte | ||
| 401 | value = Immediate(0); | ||
| 402 | shifted_counter = 0; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | for (u32 i = 0; i < indexer; ++i) { | ||
| 406 | SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i)); | ||
| 407 | } | ||
| 408 | break; | ||
| 409 | } | ||
| 410 | default: | ||
| 411 | UNREACHABLE(); | ||
| 412 | break; | ||
| 69 | } | 413 | } |
| 70 | MetaImage meta{image, {}, element}; | ||
| 71 | Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type)); | ||
| 72 | SetTemporary(bb, indexer++, std::move(value)); | ||
| 73 | } | ||
| 74 | for (u32 i = 0; i < indexer; ++i) { | ||
| 75 | SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i)); | ||
| 76 | } | 414 | } |
| 77 | break; | 415 | break; |
| 78 | } | 416 | } |
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index b5fbc4d58..b8f63922f 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp | |||
| @@ -19,7 +19,6 @@ namespace VideoCommon::Shader { | |||
| 19 | using Tegra::Shader::AtomicOp; | 19 | using Tegra::Shader::AtomicOp; |
| 20 | using Tegra::Shader::AtomicType; | 20 | using Tegra::Shader::AtomicType; |
| 21 | using Tegra::Shader::Attribute; | 21 | using Tegra::Shader::Attribute; |
| 22 | using Tegra::Shader::GlobalAtomicOp; | ||
| 23 | using Tegra::Shader::GlobalAtomicType; | 22 | using Tegra::Shader::GlobalAtomicType; |
| 24 | using Tegra::Shader::Instruction; | 23 | using Tegra::Shader::Instruction; |
| 25 | using Tegra::Shader::OpCode; | 24 | using Tegra::Shader::OpCode; |
| @@ -28,6 +27,31 @@ using Tegra::Shader::StoreType; | |||
| 28 | 27 | ||
| 29 | namespace { | 28 | namespace { |
| 30 | 29 | ||
| 30 | Node GetAtomOperation(AtomicOp op, bool is_signed, Node memory, Node data) { | ||
| 31 | const OperationCode operation_code = [op] { | ||
| 32 | switch (op) { | ||
| 33 | case AtomicOp::Add: | ||
| 34 | return OperationCode::AtomicIAdd; | ||
| 35 | case AtomicOp::Min: | ||
| 36 | return OperationCode::AtomicIMin; | ||
| 37 | case AtomicOp::Max: | ||
| 38 | return OperationCode::AtomicIMax; | ||
| 39 | case AtomicOp::And: | ||
| 40 | return OperationCode::AtomicIAnd; | ||
| 41 | case AtomicOp::Or: | ||
| 42 | return OperationCode::AtomicIOr; | ||
| 43 | case AtomicOp::Xor: | ||
| 44 | return OperationCode::AtomicIXor; | ||
| 45 | case AtomicOp::Exch: | ||
| 46 | return OperationCode::AtomicIExchange; | ||
| 47 | default: | ||
| 48 | UNIMPLEMENTED_MSG("op={}", static_cast<int>(op)); | ||
| 49 | return OperationCode::AtomicIAdd; | ||
| 50 | } | ||
| 51 | }(); | ||
| 52 | return SignedOperation(operation_code, is_signed, std::move(memory), std::move(data)); | ||
| 53 | } | ||
| 54 | |||
| 31 | bool IsUnaligned(Tegra::Shader::UniformType uniform_type) { | 55 | bool IsUnaligned(Tegra::Shader::UniformType uniform_type) { |
| 32 | return uniform_type == Tegra::Shader::UniformType::UnsignedByte || | 56 | return uniform_type == Tegra::Shader::UniformType::UnsignedByte || |
| 33 | uniform_type == Tegra::Shader::UniformType::UnsignedShort; | 57 | uniform_type == Tegra::Shader::UniformType::UnsignedShort; |
| @@ -363,10 +387,13 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 363 | break; | 387 | break; |
| 364 | } | 388 | } |
| 365 | case OpCode::Id::ATOM: { | 389 | case OpCode::Id::ATOM: { |
| 366 | UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}", | 390 | UNIMPLEMENTED_IF_MSG(instr.atom.operation == AtomicOp::Inc || |
| 367 | static_cast<int>(instr.atom.operation.Value())); | 391 | instr.atom.operation == AtomicOp::Dec || |
| 368 | UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}", | 392 | instr.atom.operation == AtomicOp::SafeAdd, |
| 369 | static_cast<int>(instr.atom.type.Value())); | 393 | "operation={}", static_cast<int>(instr.atom.operation.Value())); |
| 394 | UNIMPLEMENTED_IF_MSG(instr.atom.type == GlobalAtomicType::S64 || | ||
| 395 | instr.atom.type == GlobalAtomicType::U64, | ||
| 396 | "type={}", static_cast<int>(instr.atom.type.Value())); | ||
| 370 | 397 | ||
| 371 | const auto [real_address, base_address, descriptor] = | 398 | const auto [real_address, base_address, descriptor] = |
| 372 | TrackGlobalMemory(bb, instr, true, true); | 399 | TrackGlobalMemory(bb, instr, true, true); |
| @@ -375,25 +402,29 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 375 | break; | 402 | break; |
| 376 | } | 403 | } |
| 377 | 404 | ||
| 405 | const bool is_signed = | ||
| 406 | instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64; | ||
| 378 | Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor); | 407 | Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor); |
| 379 | Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20)); | 408 | Node value = GetAtomOperation(static_cast<AtomicOp>(instr.atom.operation), is_signed, gmem, |
| 409 | GetRegister(instr.gpr20)); | ||
| 380 | SetRegister(bb, instr.gpr0, std::move(value)); | 410 | SetRegister(bb, instr.gpr0, std::move(value)); |
| 381 | break; | 411 | break; |
| 382 | } | 412 | } |
| 383 | case OpCode::Id::ATOMS: { | 413 | case OpCode::Id::ATOMS: { |
| 384 | UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", | 414 | UNIMPLEMENTED_IF_MSG(instr.atoms.operation == AtomicOp::Inc || |
| 385 | static_cast<int>(instr.atoms.operation.Value())); | 415 | instr.atoms.operation == AtomicOp::Dec, |
| 386 | UNIMPLEMENTED_IF_MSG(instr.atoms.type != AtomicType::U32, "type={}", | 416 | "operation={}", static_cast<int>(instr.atoms.operation.Value())); |
| 387 | static_cast<int>(instr.atoms.type.Value())); | 417 | UNIMPLEMENTED_IF_MSG(instr.atoms.type == AtomicType::S64 || |
| 388 | 418 | instr.atoms.type == AtomicType::U64, | |
| 419 | "type={}", static_cast<int>(instr.atoms.type.Value())); | ||
| 420 | const bool is_signed = | ||
| 421 | instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64; | ||
| 389 | const s32 offset = instr.atoms.GetImmediateOffset(); | 422 | const s32 offset = instr.atoms.GetImmediateOffset(); |
| 390 | Node address = GetRegister(instr.gpr8); | 423 | Node address = GetRegister(instr.gpr8); |
| 391 | address = Operation(OperationCode::IAdd, std::move(address), Immediate(offset)); | 424 | address = Operation(OperationCode::IAdd, std::move(address), Immediate(offset)); |
| 392 | 425 | Node value = | |
| 393 | Node memory = GetSharedMemory(std::move(address)); | 426 | GetAtomOperation(static_cast<AtomicOp>(instr.atoms.operation), is_signed, |
| 394 | Node data = GetRegister(instr.gpr20); | 427 | GetSharedMemory(std::move(address)), GetRegister(instr.gpr20)); |
| 395 | |||
| 396 | Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data)); | ||
| 397 | SetRegister(bb, instr.gpr0, std::move(value)); | 428 | SetRegister(bb, instr.gpr0, std::move(value)); |
| 398 | break; | 429 | break; |
| 399 | } | 430 | } |
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 4944e9d69..d4f95b18c 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp | |||
| @@ -11,12 +11,17 @@ | |||
| 11 | 11 | ||
| 12 | namespace VideoCommon::Shader { | 12 | namespace VideoCommon::Shader { |
| 13 | 13 | ||
| 14 | using std::move; | ||
| 14 | using Tegra::Shader::ConditionCode; | 15 | using Tegra::Shader::ConditionCode; |
| 15 | using Tegra::Shader::Instruction; | 16 | using Tegra::Shader::Instruction; |
| 17 | using Tegra::Shader::IpaInterpMode; | ||
| 16 | using Tegra::Shader::OpCode; | 18 | using Tegra::Shader::OpCode; |
| 19 | using Tegra::Shader::PixelImap; | ||
| 17 | using Tegra::Shader::Register; | 20 | using Tegra::Shader::Register; |
| 18 | using Tegra::Shader::SystemVariable; | 21 | using Tegra::Shader::SystemVariable; |
| 19 | 22 | ||
| 23 | using Index = Tegra::Shader::Attribute::Index; | ||
| 24 | |||
| 20 | u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | 25 | u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { |
| 21 | const Instruction instr = {program_code[pc]}; | 26 | const Instruction instr = {program_code[pc]}; |
| 22 | const auto opcode = OpCode::Decode(instr); | 27 | const auto opcode = OpCode::Decode(instr); |
| @@ -66,18 +71,24 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 66 | bb.push_back(Operation(OperationCode::Discard)); | 71 | bb.push_back(Operation(OperationCode::Discard)); |
| 67 | break; | 72 | break; |
| 68 | } | 73 | } |
| 69 | case OpCode::Id::MOV_SYS: { | 74 | case OpCode::Id::S2R: { |
| 70 | const Node value = [this, instr] { | 75 | const Node value = [this, instr] { |
| 71 | switch (instr.sys20) { | 76 | switch (instr.sys20) { |
| 72 | case SystemVariable::LaneId: | 77 | case SystemVariable::LaneId: |
| 73 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete"); | 78 | LOG_WARNING(HW_GPU, "S2R instruction with LaneId is incomplete"); |
| 74 | return Immediate(0U); | 79 | return Immediate(0U); |
| 75 | case SystemVariable::InvocationId: | 80 | case SystemVariable::InvocationId: |
| 76 | return Operation(OperationCode::InvocationId); | 81 | return Operation(OperationCode::InvocationId); |
| 77 | case SystemVariable::Ydirection: | 82 | case SystemVariable::Ydirection: |
| 78 | return Operation(OperationCode::YNegate); | 83 | return Operation(OperationCode::YNegate); |
| 79 | case SystemVariable::InvocationInfo: | 84 | case SystemVariable::InvocationInfo: |
| 80 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); | 85 | LOG_WARNING(HW_GPU, "S2R instruction with InvocationInfo is incomplete"); |
| 86 | return Immediate(0U); | ||
| 87 | case SystemVariable::WscaleFactorXY: | ||
| 88 | UNIMPLEMENTED_MSG("S2R WscaleFactorXY is not implemented"); | ||
| 89 | return Immediate(0U); | ||
| 90 | case SystemVariable::WscaleFactorZ: | ||
| 91 | UNIMPLEMENTED_MSG("S2R WscaleFactorZ is not implemented"); | ||
| 81 | return Immediate(0U); | 92 | return Immediate(0U); |
| 82 | case SystemVariable::Tid: { | 93 | case SystemVariable::Tid: { |
| 83 | Node value = Immediate(0); | 94 | Node value = Immediate(0); |
| @@ -213,27 +224,28 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 213 | } | 224 | } |
| 214 | case OpCode::Id::IPA: { | 225 | case OpCode::Id::IPA: { |
| 215 | const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff; | 226 | const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff; |
| 216 | |||
| 217 | const auto attribute = instr.attribute.fmt28; | 227 | const auto attribute = instr.attribute.fmt28; |
| 218 | const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), | 228 | const Index index = attribute.index; |
| 219 | instr.ipa.sample_mode.Value()}; | ||
| 220 | 229 | ||
| 221 | Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8) | 230 | Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8) |
| 222 | : GetInputAttribute(attribute.index, attribute.element); | 231 | : GetInputAttribute(index, attribute.element); |
| 223 | const Tegra::Shader::Attribute::Index index = attribute.index.Value(); | 232 | |
| 224 | const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 && | 233 | // Code taken from Ryujinx. |
| 225 | index <= Tegra::Shader::Attribute::Index::Attribute_31; | 234 | if (index >= Index::Attribute_0 && index <= Index::Attribute_31) { |
| 226 | if (is_generic || is_physical) { | 235 | const u32 location = static_cast<u32>(index) - static_cast<u32>(Index::Attribute_0); |
| 227 | // TODO(Blinkhawk): There are cases where a perspective attribute use PASS. | 236 | if (header.ps.GetPixelImap(location) == PixelImap::Perspective) { |
| 228 | // In theory by setting them as perspective, OpenGL does the perspective correction. | 237 | Node position_w = GetInputAttribute(Index::Position, 3); |
| 229 | // A way must figured to reverse the last step of it. | 238 | value = Operation(OperationCode::FMul, move(value), move(position_w)); |
| 230 | if (input_mode.interpolation_mode == Tegra::Shader::IpaInterpMode::Multiply) { | ||
| 231 | value = Operation(OperationCode::FMul, PRECISE, value, GetRegister(instr.gpr20)); | ||
| 232 | } | 239 | } |
| 233 | } | 240 | } |
| 234 | value = GetSaturatedFloat(value, instr.ipa.saturate); | ||
| 235 | 241 | ||
| 236 | SetRegister(bb, instr.gpr0, value); | 242 | if (instr.ipa.interp_mode == IpaInterpMode::Multiply) { |
| 243 | value = Operation(OperationCode::FMul, move(value), GetRegister(instr.gpr20)); | ||
| 244 | } | ||
| 245 | |||
| 246 | value = GetSaturatedFloat(move(value), instr.ipa.saturate); | ||
| 247 | |||
| 248 | SetRegister(bb, instr.gpr0, move(value)); | ||
| 237 | break; | 249 | break; |
| 238 | } | 250 | } |
| 239 | case OpCode::Id::OUT_R: { | 251 | case OpCode::Id::OUT_R: { |
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index a1828546e..5fcc9da60 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h | |||
| @@ -162,7 +162,21 @@ enum class OperationCode { | |||
| 162 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void | 162 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void |
| 163 | AtomicImageExchange, /// (MetaImage, int[N] coords) -> void | 163 | AtomicImageExchange, /// (MetaImage, int[N] coords) -> void |
| 164 | 164 | ||
| 165 | AtomicAdd, /// (memory, {u}int) -> {u}int | 165 | AtomicUExchange, /// (memory, uint) -> uint |
| 166 | AtomicUAdd, /// (memory, uint) -> uint | ||
| 167 | AtomicUMin, /// (memory, uint) -> uint | ||
| 168 | AtomicUMax, /// (memory, uint) -> uint | ||
| 169 | AtomicUAnd, /// (memory, uint) -> uint | ||
| 170 | AtomicUOr, /// (memory, uint) -> uint | ||
| 171 | AtomicUXor, /// (memory, uint) -> uint | ||
| 172 | |||
| 173 | AtomicIExchange, /// (memory, int) -> int | ||
| 174 | AtomicIAdd, /// (memory, int) -> int | ||
| 175 | AtomicIMin, /// (memory, int) -> int | ||
| 176 | AtomicIMax, /// (memory, int) -> int | ||
| 177 | AtomicIAnd, /// (memory, int) -> int | ||
| 178 | AtomicIOr, /// (memory, int) -> int | ||
| 179 | AtomicIXor, /// (memory, int) -> int | ||
| 166 | 180 | ||
| 167 | Branch, /// (uint branch_target) -> void | 181 | Branch, /// (uint branch_target) -> void |
| 168 | BranchIndirect, /// (uint branch_target) -> void | 182 | BranchIndirect, /// (uint branch_target) -> void |
diff --git a/src/video_core/shader/node_helper.cpp b/src/video_core/shader/node_helper.cpp index 76c56abb5..7bf4ff387 100644 --- a/src/video_core/shader/node_helper.cpp +++ b/src/video_core/shader/node_helper.cpp | |||
| @@ -86,6 +86,20 @@ OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed) | |||
| 86 | return OperationCode::LogicalUNotEqual; | 86 | return OperationCode::LogicalUNotEqual; |
| 87 | case OperationCode::LogicalIGreaterEqual: | 87 | case OperationCode::LogicalIGreaterEqual: |
| 88 | return OperationCode::LogicalUGreaterEqual; | 88 | return OperationCode::LogicalUGreaterEqual; |
| 89 | case OperationCode::AtomicIExchange: | ||
| 90 | return OperationCode::AtomicUExchange; | ||
| 91 | case OperationCode::AtomicIAdd: | ||
| 92 | return OperationCode::AtomicUAdd; | ||
| 93 | case OperationCode::AtomicIMin: | ||
| 94 | return OperationCode::AtomicUMin; | ||
| 95 | case OperationCode::AtomicIMax: | ||
| 96 | return OperationCode::AtomicUMax; | ||
| 97 | case OperationCode::AtomicIAnd: | ||
| 98 | return OperationCode::AtomicUAnd; | ||
| 99 | case OperationCode::AtomicIOr: | ||
| 100 | return OperationCode::AtomicUOr; | ||
| 101 | case OperationCode::AtomicIXor: | ||
| 102 | return OperationCode::AtomicUXor; | ||
| 89 | case OperationCode::INegate: | 103 | case OperationCode::INegate: |
| 90 | UNREACHABLE_MSG("Can't negate an unsigned integer"); | 104 | UNREACHABLE_MSG("Can't negate an unsigned integer"); |
| 91 | return {}; | 105 | return {}; |
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index baf7188d2..8852c8a1b 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -359,6 +359,9 @@ Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) const { | |||
| 359 | switch (cc) { | 359 | switch (cc) { |
| 360 | case Tegra::Shader::ConditionCode::NEU: | 360 | case Tegra::Shader::ConditionCode::NEU: |
| 361 | return GetInternalFlag(InternalFlag::Zero, true); | 361 | return GetInternalFlag(InternalFlag::Zero, true); |
| 362 | case Tegra::Shader::ConditionCode::FCSM_TR: | ||
| 363 | UNIMPLEMENTED_MSG("EXIT.FCSM_TR is not implemented"); | ||
| 364 | return MakeNode<PredicateNode>(Pred::NeverExecute, false); | ||
| 362 | default: | 365 | default: |
| 363 | UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); | 366 | UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc)); |
| 364 | return MakeNode<PredicateNode>(Pred::NeverExecute, false); | 367 | return MakeNode<PredicateNode>(Pred::NeverExecute, false); |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 0f1ebef1b..c6e7bdf50 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -312,6 +312,10 @@ private: | |||
| 312 | /// Conditionally saturates a half float pair | 312 | /// Conditionally saturates a half float pair |
| 313 | Node GetSaturatedHalfFloat(Node value, bool saturate = true); | 313 | Node GetSaturatedHalfFloat(Node value, bool saturate = true); |
| 314 | 314 | ||
| 315 | /// Get image component value by type and size | ||
| 316 | std::pair<Node, bool> GetComponentValue(Tegra::Texture::ComponentType component_type, | ||
| 317 | u32 component_size, Node original_value); | ||
| 318 | |||
| 315 | /// Returns a predicate comparing two floats | 319 | /// Returns a predicate comparing two floats |
| 316 | Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b); | 320 | Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b); |
| 317 | /// Returns a predicate comparing two integers | 321 | /// Returns a predicate comparing two integers |
diff --git a/src/video_core/surface.h b/src/video_core/surface.h index ae8817465..e0acd44d3 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h | |||
| @@ -504,103 +504,6 @@ static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) { | |||
| 504 | return GetFormatBpp(pixel_format) / CHAR_BIT; | 504 | return GetFormatBpp(pixel_format) / CHAR_BIT; |
| 505 | } | 505 | } |
| 506 | 506 | ||
| 507 | enum class SurfaceCompression { | ||
| 508 | None, // Not compressed | ||
| 509 | Compressed, // Texture is compressed | ||
| 510 | Converted, // Texture is converted before upload or after download | ||
| 511 | Rearranged, // Texture is swizzled before upload or after download | ||
| 512 | }; | ||
| 513 | |||
| 514 | constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table = {{ | ||
| 515 | SurfaceCompression::None, // ABGR8U | ||
| 516 | SurfaceCompression::None, // ABGR8S | ||
| 517 | SurfaceCompression::None, // ABGR8UI | ||
| 518 | SurfaceCompression::None, // B5G6R5U | ||
| 519 | SurfaceCompression::None, // A2B10G10R10U | ||
| 520 | SurfaceCompression::None, // A1B5G5R5U | ||
| 521 | SurfaceCompression::None, // R8U | ||
| 522 | SurfaceCompression::None, // R8UI | ||
| 523 | SurfaceCompression::None, // RGBA16F | ||
| 524 | SurfaceCompression::None, // RGBA16U | ||
| 525 | SurfaceCompression::None, // RGBA16S | ||
| 526 | SurfaceCompression::None, // RGBA16UI | ||
| 527 | SurfaceCompression::None, // R11FG11FB10F | ||
| 528 | SurfaceCompression::None, // RGBA32UI | ||
| 529 | SurfaceCompression::Compressed, // DXT1 | ||
| 530 | SurfaceCompression::Compressed, // DXT23 | ||
| 531 | SurfaceCompression::Compressed, // DXT45 | ||
| 532 | SurfaceCompression::Compressed, // DXN1 | ||
| 533 | SurfaceCompression::Compressed, // DXN2UNORM | ||
| 534 | SurfaceCompression::Compressed, // DXN2SNORM | ||
| 535 | SurfaceCompression::Compressed, // BC7U | ||
| 536 | SurfaceCompression::Compressed, // BC6H_UF16 | ||
| 537 | SurfaceCompression::Compressed, // BC6H_SF16 | ||
| 538 | SurfaceCompression::Converted, // ASTC_2D_4X4 | ||
| 539 | SurfaceCompression::None, // BGRA8 | ||
| 540 | SurfaceCompression::None, // RGBA32F | ||
| 541 | SurfaceCompression::None, // RG32F | ||
| 542 | SurfaceCompression::None, // R32F | ||
| 543 | SurfaceCompression::None, // R16F | ||
| 544 | SurfaceCompression::None, // R16U | ||
| 545 | SurfaceCompression::None, // R16S | ||
| 546 | SurfaceCompression::None, // R16UI | ||
| 547 | SurfaceCompression::None, // R16I | ||
| 548 | SurfaceCompression::None, // RG16 | ||
| 549 | SurfaceCompression::None, // RG16F | ||
| 550 | SurfaceCompression::None, // RG16UI | ||
| 551 | SurfaceCompression::None, // RG16I | ||
| 552 | SurfaceCompression::None, // RG16S | ||
| 553 | SurfaceCompression::None, // RGB32F | ||
| 554 | SurfaceCompression::None, // RGBA8_SRGB | ||
| 555 | SurfaceCompression::None, // RG8U | ||
| 556 | SurfaceCompression::None, // RG8S | ||
| 557 | SurfaceCompression::None, // RG32UI | ||
| 558 | SurfaceCompression::None, // RGBX16F | ||
| 559 | SurfaceCompression::None, // R32UI | ||
| 560 | SurfaceCompression::None, // R32I | ||
| 561 | SurfaceCompression::Converted, // ASTC_2D_8X8 | ||
| 562 | SurfaceCompression::Converted, // ASTC_2D_8X5 | ||
| 563 | SurfaceCompression::Converted, // ASTC_2D_5X4 | ||
| 564 | SurfaceCompression::None, // BGRA8_SRGB | ||
| 565 | SurfaceCompression::Compressed, // DXT1_SRGB | ||
| 566 | SurfaceCompression::Compressed, // DXT23_SRGB | ||
| 567 | SurfaceCompression::Compressed, // DXT45_SRGB | ||
| 568 | SurfaceCompression::Compressed, // BC7U_SRGB | ||
| 569 | SurfaceCompression::None, // R4G4B4A4U | ||
| 570 | SurfaceCompression::Converted, // ASTC_2D_4X4_SRGB | ||
| 571 | SurfaceCompression::Converted, // ASTC_2D_8X8_SRGB | ||
| 572 | SurfaceCompression::Converted, // ASTC_2D_8X5_SRGB | ||
| 573 | SurfaceCompression::Converted, // ASTC_2D_5X4_SRGB | ||
| 574 | SurfaceCompression::Converted, // ASTC_2D_5X5 | ||
| 575 | SurfaceCompression::Converted, // ASTC_2D_5X5_SRGB | ||
| 576 | SurfaceCompression::Converted, // ASTC_2D_10X8 | ||
| 577 | SurfaceCompression::Converted, // ASTC_2D_10X8_SRGB | ||
| 578 | SurfaceCompression::Converted, // ASTC_2D_6X6 | ||
| 579 | SurfaceCompression::Converted, // ASTC_2D_6X6_SRGB | ||
| 580 | SurfaceCompression::Converted, // ASTC_2D_10X10 | ||
| 581 | SurfaceCompression::Converted, // ASTC_2D_10X10_SRGB | ||
| 582 | SurfaceCompression::Converted, // ASTC_2D_12X12 | ||
| 583 | SurfaceCompression::Converted, // ASTC_2D_12X12_SRGB | ||
| 584 | SurfaceCompression::Converted, // ASTC_2D_8X6 | ||
| 585 | SurfaceCompression::Converted, // ASTC_2D_8X6_SRGB | ||
| 586 | SurfaceCompression::Converted, // ASTC_2D_6X5 | ||
| 587 | SurfaceCompression::Converted, // ASTC_2D_6X5_SRGB | ||
| 588 | SurfaceCompression::None, // E5B9G9R9F | ||
| 589 | SurfaceCompression::None, // Z32F | ||
| 590 | SurfaceCompression::None, // Z16 | ||
| 591 | SurfaceCompression::None, // Z24S8 | ||
| 592 | SurfaceCompression::Rearranged, // S8Z24 | ||
| 593 | SurfaceCompression::None, // Z32FS8 | ||
| 594 | }}; | ||
| 595 | |||
| 596 | constexpr SurfaceCompression GetFormatCompressionType(PixelFormat format) { | ||
| 597 | if (format == PixelFormat::Invalid) { | ||
| 598 | return SurfaceCompression::None; | ||
| 599 | } | ||
| 600 | DEBUG_ASSERT(static_cast<std::size_t>(format) < compression_type_table.size()); | ||
| 601 | return compression_type_table[static_cast<std::size_t>(format)]; | ||
| 602 | } | ||
| 603 | |||
| 604 | SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type); | 507 | SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type); |
| 605 | 508 | ||
| 606 | bool SurfaceTargetIsLayered(SurfaceTarget target); | 509 | bool SurfaceTargetIsLayered(SurfaceTarget target); |
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 002df414f..7af0e792c 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp | |||
| @@ -18,15 +18,20 @@ MICROPROFILE_DEFINE(GPU_Flush_Texture, "GPU", "Texture Flush", MP_RGB(128, 192, | |||
| 18 | 18 | ||
| 19 | using Tegra::Texture::ConvertFromGuestToHost; | 19 | using Tegra::Texture::ConvertFromGuestToHost; |
| 20 | using VideoCore::MortonSwizzleMode; | 20 | using VideoCore::MortonSwizzleMode; |
| 21 | using VideoCore::Surface::SurfaceCompression; | 21 | using VideoCore::Surface::IsPixelFormatASTC; |
| 22 | using VideoCore::Surface::PixelFormat; | ||
| 22 | 23 | ||
| 23 | StagingCache::StagingCache() = default; | 24 | StagingCache::StagingCache() = default; |
| 24 | 25 | ||
| 25 | StagingCache::~StagingCache() = default; | 26 | StagingCache::~StagingCache() = default; |
| 26 | 27 | ||
| 27 | SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params) | 28 | SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params, |
| 28 | : params{params}, host_memory_size{params.GetHostSizeInBytes()}, gpu_addr{gpu_addr}, | 29 | bool is_astc_supported) |
| 29 | mipmap_sizes(params.num_levels), mipmap_offsets(params.num_levels) { | 30 | : params{params}, gpu_addr{gpu_addr}, mipmap_sizes(params.num_levels), |
| 31 | mipmap_offsets(params.num_levels) { | ||
| 32 | is_converted = IsPixelFormatASTC(params.pixel_format) && !is_astc_supported; | ||
| 33 | host_memory_size = params.GetHostSizeInBytes(is_converted); | ||
| 34 | |||
| 30 | std::size_t offset = 0; | 35 | std::size_t offset = 0; |
| 31 | for (u32 level = 0; level < params.num_levels; ++level) { | 36 | for (u32 level = 0; level < params.num_levels; ++level) { |
| 32 | const std::size_t mipmap_size{params.GetGuestMipmapSize(level)}; | 37 | const std::size_t mipmap_size{params.GetGuestMipmapSize(level)}; |
| @@ -164,7 +169,7 @@ void SurfaceBaseImpl::SwizzleFunc(MortonSwizzleMode mode, u8* memory, const Surf | |||
| 164 | 169 | ||
| 165 | std::size_t guest_offset{mipmap_offsets[level]}; | 170 | std::size_t guest_offset{mipmap_offsets[level]}; |
| 166 | if (params.is_layered) { | 171 | if (params.is_layered) { |
| 167 | std::size_t host_offset{0}; | 172 | std::size_t host_offset = 0; |
| 168 | const std::size_t guest_stride = layer_size; | 173 | const std::size_t guest_stride = layer_size; |
| 169 | const std::size_t host_stride = params.GetHostLayerSize(level); | 174 | const std::size_t host_stride = params.GetHostLayerSize(level); |
| 170 | for (u32 layer = 0; layer < params.depth; ++layer) { | 175 | for (u32 layer = 0; layer < params.depth; ++layer) { |
| @@ -185,28 +190,17 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager, | |||
| 185 | MICROPROFILE_SCOPE(GPU_Load_Texture); | 190 | MICROPROFILE_SCOPE(GPU_Load_Texture); |
| 186 | auto& staging_buffer = staging_cache.GetBuffer(0); | 191 | auto& staging_buffer = staging_cache.GetBuffer(0); |
| 187 | u8* host_ptr; | 192 | u8* host_ptr; |
| 188 | is_continuous = memory_manager.IsBlockContinuous(gpu_addr, guest_memory_size); | 193 | // Use an extra temporal buffer |
| 189 | 194 | auto& tmp_buffer = staging_cache.GetBuffer(1); | |
| 190 | // Handle continuouty | 195 | tmp_buffer.resize(guest_memory_size); |
| 191 | if (is_continuous) { | 196 | host_ptr = tmp_buffer.data(); |
| 192 | // Use physical memory directly | 197 | memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); |
| 193 | host_ptr = memory_manager.GetPointer(gpu_addr); | ||
| 194 | if (!host_ptr) { | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | } else { | ||
| 198 | // Use an extra temporal buffer | ||
| 199 | auto& tmp_buffer = staging_cache.GetBuffer(1); | ||
| 200 | tmp_buffer.resize(guest_memory_size); | ||
| 201 | host_ptr = tmp_buffer.data(); | ||
| 202 | memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); | ||
| 203 | } | ||
| 204 | 198 | ||
| 205 | if (params.is_tiled) { | 199 | if (params.is_tiled) { |
| 206 | ASSERT_MSG(params.block_width == 0, "Block width is defined as {} on texture target {}", | 200 | ASSERT_MSG(params.block_width == 0, "Block width is defined as {} on texture target {}", |
| 207 | params.block_width, static_cast<u32>(params.target)); | 201 | params.block_width, static_cast<u32>(params.target)); |
| 208 | for (u32 level = 0; level < params.num_levels; ++level) { | 202 | for (u32 level = 0; level < params.num_levels; ++level) { |
| 209 | const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)}; | 203 | const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)}; |
| 210 | SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params, | 204 | SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params, |
| 211 | staging_buffer.data() + host_offset, level); | 205 | staging_buffer.data() + host_offset, level); |
| 212 | } | 206 | } |
| @@ -219,7 +213,7 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager, | |||
| 219 | const u32 height{(params.height + block_height - 1) / block_height}; | 213 | const u32 height{(params.height + block_height - 1) / block_height}; |
| 220 | const u32 copy_size{width * bpp}; | 214 | const u32 copy_size{width * bpp}; |
| 221 | if (params.pitch == copy_size) { | 215 | if (params.pitch == copy_size) { |
| 222 | std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes()); | 216 | std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes(false)); |
| 223 | } else { | 217 | } else { |
| 224 | const u8* start{host_ptr}; | 218 | const u8* start{host_ptr}; |
| 225 | u8* write_to{staging_buffer.data()}; | 219 | u8* write_to{staging_buffer.data()}; |
| @@ -231,19 +225,15 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager, | |||
| 231 | } | 225 | } |
| 232 | } | 226 | } |
| 233 | 227 | ||
| 234 | auto compression_type = params.GetCompressionType(); | 228 | if (!is_converted && params.pixel_format != PixelFormat::S8Z24) { |
| 235 | if (compression_type == SurfaceCompression::None || | ||
| 236 | compression_type == SurfaceCompression::Compressed) | ||
| 237 | return; | 229 | return; |
| 230 | } | ||
| 238 | 231 | ||
| 239 | for (u32 level_up = params.num_levels; level_up > 0; --level_up) { | 232 | for (u32 level = params.num_levels; level--;) { |
| 240 | const u32 level = level_up - 1; | 233 | const std::size_t in_host_offset{params.GetHostMipmapLevelOffset(level, false)}; |
| 241 | const std::size_t in_host_offset{params.GetHostMipmapLevelOffset(level)}; | 234 | const std::size_t out_host_offset{params.GetHostMipmapLevelOffset(level, is_converted)}; |
| 242 | const std::size_t out_host_offset = compression_type == SurfaceCompression::Rearranged | 235 | u8* const in_buffer = staging_buffer.data() + in_host_offset; |
| 243 | ? in_host_offset | 236 | u8* const out_buffer = staging_buffer.data() + out_host_offset; |
| 244 | : params.GetConvertedMipmapOffset(level); | ||
| 245 | u8* in_buffer = staging_buffer.data() + in_host_offset; | ||
| 246 | u8* out_buffer = staging_buffer.data() + out_host_offset; | ||
| 247 | ConvertFromGuestToHost(in_buffer, out_buffer, params.pixel_format, | 237 | ConvertFromGuestToHost(in_buffer, out_buffer, params.pixel_format, |
| 248 | params.GetMipWidth(level), params.GetMipHeight(level), | 238 | params.GetMipWidth(level), params.GetMipHeight(level), |
| 249 | params.GetMipDepth(level), true, true); | 239 | params.GetMipDepth(level), true, true); |
| @@ -256,24 +246,15 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager, | |||
| 256 | auto& staging_buffer = staging_cache.GetBuffer(0); | 246 | auto& staging_buffer = staging_cache.GetBuffer(0); |
| 257 | u8* host_ptr; | 247 | u8* host_ptr; |
| 258 | 248 | ||
| 259 | // Handle continuouty | 249 | // Use an extra temporal buffer |
| 260 | if (is_continuous) { | 250 | auto& tmp_buffer = staging_cache.GetBuffer(1); |
| 261 | // Use physical memory directly | 251 | tmp_buffer.resize(guest_memory_size); |
| 262 | host_ptr = memory_manager.GetPointer(gpu_addr); | 252 | host_ptr = tmp_buffer.data(); |
| 263 | if (!host_ptr) { | ||
| 264 | return; | ||
| 265 | } | ||
| 266 | } else { | ||
| 267 | // Use an extra temporal buffer | ||
| 268 | auto& tmp_buffer = staging_cache.GetBuffer(1); | ||
| 269 | tmp_buffer.resize(guest_memory_size); | ||
| 270 | host_ptr = tmp_buffer.data(); | ||
| 271 | } | ||
| 272 | 253 | ||
| 273 | if (params.is_tiled) { | 254 | if (params.is_tiled) { |
| 274 | ASSERT_MSG(params.block_width == 0, "Block width is defined as {}", params.block_width); | 255 | ASSERT_MSG(params.block_width == 0, "Block width is defined as {}", params.block_width); |
| 275 | for (u32 level = 0; level < params.num_levels; ++level) { | 256 | for (u32 level = 0; level < params.num_levels; ++level) { |
| 276 | const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)}; | 257 | const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)}; |
| 277 | SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params, | 258 | SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params, |
| 278 | staging_buffer.data() + host_offset, level); | 259 | staging_buffer.data() + host_offset, level); |
| 279 | } | 260 | } |
| @@ -299,9 +280,7 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager, | |||
| 299 | } | 280 | } |
| 300 | } | 281 | } |
| 301 | } | 282 | } |
| 302 | if (!is_continuous) { | 283 | memory_manager.WriteBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); |
| 303 | memory_manager.WriteBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); | ||
| 304 | } | ||
| 305 | } | 284 | } |
| 306 | 285 | ||
| 307 | } // namespace VideoCommon | 286 | } // namespace VideoCommon |
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h index 5f79bb0aa..a39a8661b 100644 --- a/src/video_core/texture_cache/surface_base.h +++ b/src/video_core/texture_cache/surface_base.h | |||
| @@ -68,8 +68,8 @@ public: | |||
| 68 | return gpu_addr; | 68 | return gpu_addr; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | bool Overlaps(const CacheAddr start, const CacheAddr end) const { | 71 | bool Overlaps(const VAddr start, const VAddr end) const { |
| 72 | return (cache_addr < end) && (cache_addr_end > start); | 72 | return (cpu_addr < end) && (cpu_addr_end > start); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | bool IsInside(const GPUVAddr other_start, const GPUVAddr other_end) { | 75 | bool IsInside(const GPUVAddr other_start, const GPUVAddr other_end) { |
| @@ -86,21 +86,13 @@ public: | |||
| 86 | return cpu_addr; | 86 | return cpu_addr; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | void SetCpuAddr(const VAddr new_addr) { | 89 | VAddr GetCpuAddrEnd() const { |
| 90 | cpu_addr = new_addr; | 90 | return cpu_addr_end; |
| 91 | } | ||
| 92 | |||
| 93 | CacheAddr GetCacheAddr() const { | ||
| 94 | return cache_addr; | ||
| 95 | } | 91 | } |
| 96 | 92 | ||
| 97 | CacheAddr GetCacheAddrEnd() const { | 93 | void SetCpuAddr(const VAddr new_addr) { |
| 98 | return cache_addr_end; | 94 | cpu_addr = new_addr; |
| 99 | } | 95 | cpu_addr_end = new_addr + guest_memory_size; |
| 100 | |||
| 101 | void SetCacheAddr(const CacheAddr new_addr) { | ||
| 102 | cache_addr = new_addr; | ||
| 103 | cache_addr_end = new_addr + guest_memory_size; | ||
| 104 | } | 96 | } |
| 105 | 97 | ||
| 106 | const SurfaceParams& GetSurfaceParams() const { | 98 | const SurfaceParams& GetSurfaceParams() const { |
| @@ -119,18 +111,14 @@ public: | |||
| 119 | return mipmap_sizes[level]; | 111 | return mipmap_sizes[level]; |
| 120 | } | 112 | } |
| 121 | 113 | ||
| 122 | void MarkAsContinuous(const bool is_continuous) { | ||
| 123 | this->is_continuous = is_continuous; | ||
| 124 | } | ||
| 125 | |||
| 126 | bool IsContinuous() const { | ||
| 127 | return is_continuous; | ||
| 128 | } | ||
| 129 | |||
| 130 | bool IsLinear() const { | 114 | bool IsLinear() const { |
| 131 | return !params.is_tiled; | 115 | return !params.is_tiled; |
| 132 | } | 116 | } |
| 133 | 117 | ||
| 118 | bool IsConverted() const { | ||
| 119 | return is_converted; | ||
| 120 | } | ||
| 121 | |||
| 134 | bool MatchFormat(VideoCore::Surface::PixelFormat pixel_format) const { | 122 | bool MatchFormat(VideoCore::Surface::PixelFormat pixel_format) const { |
| 135 | return params.pixel_format == pixel_format; | 123 | return params.pixel_format == pixel_format; |
| 136 | } | 124 | } |
| @@ -160,7 +148,8 @@ public: | |||
| 160 | } | 148 | } |
| 161 | 149 | ||
| 162 | protected: | 150 | protected: |
| 163 | explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params); | 151 | explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params, |
| 152 | bool is_astc_supported); | ||
| 164 | ~SurfaceBaseImpl() = default; | 153 | ~SurfaceBaseImpl() = default; |
| 165 | 154 | ||
| 166 | virtual void DecorateSurfaceName() = 0; | 155 | virtual void DecorateSurfaceName() = 0; |
| @@ -168,12 +157,11 @@ protected: | |||
| 168 | const SurfaceParams params; | 157 | const SurfaceParams params; |
| 169 | std::size_t layer_size; | 158 | std::size_t layer_size; |
| 170 | std::size_t guest_memory_size; | 159 | std::size_t guest_memory_size; |
| 171 | const std::size_t host_memory_size; | 160 | std::size_t host_memory_size; |
| 172 | GPUVAddr gpu_addr{}; | 161 | GPUVAddr gpu_addr{}; |
| 173 | CacheAddr cache_addr{}; | ||
| 174 | CacheAddr cache_addr_end{}; | ||
| 175 | VAddr cpu_addr{}; | 162 | VAddr cpu_addr{}; |
| 176 | bool is_continuous{}; | 163 | VAddr cpu_addr_end{}; |
| 164 | bool is_converted{}; | ||
| 177 | 165 | ||
| 178 | std::vector<std::size_t> mipmap_sizes; | 166 | std::vector<std::size_t> mipmap_sizes; |
| 179 | std::vector<std::size_t> mipmap_offsets; | 167 | std::vector<std::size_t> mipmap_offsets; |
| @@ -288,8 +276,9 @@ public: | |||
| 288 | } | 276 | } |
| 289 | 277 | ||
| 290 | protected: | 278 | protected: |
| 291 | explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params) | 279 | explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params, |
| 292 | : SurfaceBaseImpl(gpu_addr, params) {} | 280 | bool is_astc_supported) |
| 281 | : SurfaceBaseImpl(gpu_addr, params, is_astc_supported) {} | ||
| 293 | 282 | ||
| 294 | ~SurfaceBase() = default; | 283 | ~SurfaceBase() = default; |
| 295 | 284 | ||
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index 9931c5ef7..6f3ef45be 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp | |||
| @@ -113,10 +113,8 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta | |||
| 113 | params.height = tic.Height(); | 113 | params.height = tic.Height(); |
| 114 | params.depth = tic.Depth(); | 114 | params.depth = tic.Depth(); |
| 115 | params.pitch = params.is_tiled ? 0 : tic.Pitch(); | 115 | params.pitch = params.is_tiled ? 0 : tic.Pitch(); |
| 116 | if (params.target == SurfaceTarget::Texture2D && params.depth > 1) { | 116 | if (params.target == SurfaceTarget::TextureCubemap || |
| 117 | params.depth = 1; | 117 | params.target == SurfaceTarget::TextureCubeArray) { |
| 118 | } else if (params.target == SurfaceTarget::TextureCubemap || | ||
| 119 | params.target == SurfaceTarget::TextureCubeArray) { | ||
| 120 | params.depth *= 6; | 118 | params.depth *= 6; |
| 121 | } | 119 | } |
| 122 | params.num_levels = tic.max_mip_level + 1; | 120 | params.num_levels = tic.max_mip_level + 1; |
| @@ -309,28 +307,26 @@ std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const { | |||
| 309 | return offset; | 307 | return offset; |
| 310 | } | 308 | } |
| 311 | 309 | ||
| 312 | std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const { | 310 | std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level, bool is_converted) const { |
| 313 | std::size_t offset = 0; | ||
| 314 | for (u32 i = 0; i < level; i++) { | ||
| 315 | offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers(); | ||
| 316 | } | ||
| 317 | return offset; | ||
| 318 | } | ||
| 319 | |||
| 320 | std::size_t SurfaceParams::GetConvertedMipmapOffset(u32 level) const { | ||
| 321 | std::size_t offset = 0; | 311 | std::size_t offset = 0; |
| 322 | for (u32 i = 0; i < level; i++) { | 312 | if (is_converted) { |
| 323 | offset += GetConvertedMipmapSize(i); | 313 | for (u32 i = 0; i < level; ++i) { |
| 314 | offset += GetConvertedMipmapSize(i) * GetNumLayers(); | ||
| 315 | } | ||
| 316 | } else { | ||
| 317 | for (u32 i = 0; i < level; ++i) { | ||
| 318 | offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers(); | ||
| 319 | } | ||
| 324 | } | 320 | } |
| 325 | return offset; | 321 | return offset; |
| 326 | } | 322 | } |
| 327 | 323 | ||
| 328 | std::size_t SurfaceParams::GetConvertedMipmapSize(u32 level) const { | 324 | std::size_t SurfaceParams::GetConvertedMipmapSize(u32 level) const { |
| 329 | constexpr std::size_t rgba8_bpp = 4ULL; | 325 | constexpr std::size_t rgba8_bpp = 4ULL; |
| 330 | const std::size_t width_t = GetMipWidth(level); | 326 | const std::size_t mip_width = GetMipWidth(level); |
| 331 | const std::size_t height_t = GetMipHeight(level); | 327 | const std::size_t mip_height = GetMipHeight(level); |
| 332 | const std::size_t depth_t = is_layered ? depth : GetMipDepth(level); | 328 | const std::size_t mip_depth = is_layered ? 1 : GetMipDepth(level); |
| 333 | return width_t * height_t * depth_t * rgba8_bpp; | 329 | return mip_width * mip_height * mip_depth * rgba8_bpp; |
| 334 | } | 330 | } |
| 335 | 331 | ||
| 336 | std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) const { | 332 | std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) const { |
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h index 995cc3818..24957df8d 100644 --- a/src/video_core/texture_cache/surface_params.h +++ b/src/video_core/texture_cache/surface_params.h | |||
| @@ -20,8 +20,6 @@ namespace VideoCommon { | |||
| 20 | 20 | ||
| 21 | class FormatLookupTable; | 21 | class FormatLookupTable; |
| 22 | 22 | ||
| 23 | using VideoCore::Surface::SurfaceCompression; | ||
| 24 | |||
| 25 | class SurfaceParams { | 23 | class SurfaceParams { |
| 26 | public: | 24 | public: |
| 27 | /// Creates SurfaceCachedParams from a texture configuration. | 25 | /// Creates SurfaceCachedParams from a texture configuration. |
| @@ -67,16 +65,14 @@ public: | |||
| 67 | return GetInnerMemorySize(false, false, false); | 65 | return GetInnerMemorySize(false, false, false); |
| 68 | } | 66 | } |
| 69 | 67 | ||
| 70 | std::size_t GetHostSizeInBytes() const { | 68 | std::size_t GetHostSizeInBytes(bool is_converted) const { |
| 71 | std::size_t host_size_in_bytes; | 69 | if (!is_converted) { |
| 72 | if (GetCompressionType() == SurfaceCompression::Converted) { | 70 | return GetInnerMemorySize(true, false, false); |
| 73 | // ASTC is uncompressed in software, in emulated as RGBA8 | 71 | } |
| 74 | host_size_in_bytes = 0; | 72 | // ASTC is uncompressed in software, in emulated as RGBA8 |
| 75 | for (u32 level = 0; level < num_levels; ++level) { | 73 | std::size_t host_size_in_bytes = 0; |
| 76 | host_size_in_bytes += GetConvertedMipmapSize(level); | 74 | for (u32 level = 0; level < num_levels; ++level) { |
| 77 | } | 75 | host_size_in_bytes += GetConvertedMipmapSize(level) * GetNumLayers(); |
| 78 | } else { | ||
| 79 | host_size_in_bytes = GetInnerMemorySize(true, false, false); | ||
| 80 | } | 76 | } |
| 81 | return host_size_in_bytes; | 77 | return host_size_in_bytes; |
| 82 | } | 78 | } |
| @@ -107,9 +103,8 @@ public: | |||
| 107 | u32 GetMipBlockDepth(u32 level) const; | 103 | u32 GetMipBlockDepth(u32 level) const; |
| 108 | 104 | ||
| 109 | /// Returns the best possible row/pitch alignment for the surface. | 105 | /// Returns the best possible row/pitch alignment for the surface. |
| 110 | u32 GetRowAlignment(u32 level) const { | 106 | u32 GetRowAlignment(u32 level, bool is_converted) const { |
| 111 | const u32 bpp = | 107 | const u32 bpp = is_converted ? 4 : GetBytesPerPixel(); |
| 112 | GetCompressionType() == SurfaceCompression::Converted ? 4 : GetBytesPerPixel(); | ||
| 113 | return 1U << Common::CountTrailingZeroes32(GetMipWidth(level) * bpp); | 108 | return 1U << Common::CountTrailingZeroes32(GetMipWidth(level) * bpp); |
| 114 | } | 109 | } |
| 115 | 110 | ||
| @@ -117,11 +112,7 @@ public: | |||
| 117 | std::size_t GetGuestMipmapLevelOffset(u32 level) const; | 112 | std::size_t GetGuestMipmapLevelOffset(u32 level) const; |
| 118 | 113 | ||
| 119 | /// Returns the offset in bytes in host memory (linear) of a given mipmap level. | 114 | /// Returns the offset in bytes in host memory (linear) of a given mipmap level. |
| 120 | std::size_t GetHostMipmapLevelOffset(u32 level) const; | 115 | std::size_t GetHostMipmapLevelOffset(u32 level, bool is_converted) const; |
| 121 | |||
| 122 | /// Returns the offset in bytes in host memory (linear) of a given mipmap level | ||
| 123 | /// for a texture that is converted in host gpu. | ||
| 124 | std::size_t GetConvertedMipmapOffset(u32 level) const; | ||
| 125 | 116 | ||
| 126 | /// Returns the size in bytes in guest memory of a given mipmap level. | 117 | /// Returns the size in bytes in guest memory of a given mipmap level. |
| 127 | std::size_t GetGuestMipmapSize(u32 level) const { | 118 | std::size_t GetGuestMipmapSize(u32 level) const { |
| @@ -196,11 +187,6 @@ public: | |||
| 196 | pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat; | 187 | pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat; |
| 197 | } | 188 | } |
| 198 | 189 | ||
| 199 | /// Returns how the compression should be handled for this texture. | ||
| 200 | SurfaceCompression GetCompressionType() const { | ||
| 201 | return VideoCore::Surface::GetFormatCompressionType(pixel_format); | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Returns is the surface is a TextureBuffer type of surface. | 190 | /// Returns is the surface is a TextureBuffer type of surface. |
| 205 | bool IsBuffer() const { | 191 | bool IsBuffer() const { |
| 206 | return target == VideoCore::Surface::SurfaceTarget::TextureBuffer; | 192 | return target == VideoCore::Surface::SurfaceTarget::TextureBuffer; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 6cdbe63d0..88fe3e25f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -52,11 +52,9 @@ using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; | |||
| 52 | 52 | ||
| 53 | template <typename TSurface, typename TView> | 53 | template <typename TSurface, typename TView> |
| 54 | class TextureCache { | 54 | class TextureCache { |
| 55 | using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface>>; | ||
| 56 | using IntervalType = typename IntervalMap::interval_type; | ||
| 57 | 55 | ||
| 58 | public: | 56 | public: |
| 59 | void InvalidateRegion(CacheAddr addr, std::size_t size) { | 57 | void InvalidateRegion(VAddr addr, std::size_t size) { |
| 60 | std::lock_guard lock{mutex}; | 58 | std::lock_guard lock{mutex}; |
| 61 | 59 | ||
| 62 | for (const auto& surface : GetSurfacesInRegion(addr, size)) { | 60 | for (const auto& surface : GetSurfacesInRegion(addr, size)) { |
| @@ -76,7 +74,7 @@ public: | |||
| 76 | guard_samplers = new_guard; | 74 | guard_samplers = new_guard; |
| 77 | } | 75 | } |
| 78 | 76 | ||
| 79 | void FlushRegion(CacheAddr addr, std::size_t size) { | 77 | void FlushRegion(VAddr addr, std::size_t size) { |
| 80 | std::lock_guard lock{mutex}; | 78 | std::lock_guard lock{mutex}; |
| 81 | 79 | ||
| 82 | auto surfaces = GetSurfacesInRegion(addr, size); | 80 | auto surfaces = GetSurfacesInRegion(addr, size); |
| @@ -99,9 +97,9 @@ public: | |||
| 99 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); | 97 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); |
| 100 | } | 98 | } |
| 101 | 99 | ||
| 102 | const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; | 100 | const std::optional<VAddr> cpu_addr = |
| 103 | const auto cache_addr{ToCacheAddr(host_ptr)}; | 101 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); |
| 104 | if (!cache_addr) { | 102 | if (!cpu_addr) { |
| 105 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); | 103 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); |
| 106 | } | 104 | } |
| 107 | 105 | ||
| @@ -110,7 +108,7 @@ public: | |||
| 110 | } | 108 | } |
| 111 | 109 | ||
| 112 | const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)}; | 110 | const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)}; |
| 113 | const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false); | 111 | const auto [surface, view] = GetSurface(gpu_addr, *cpu_addr, params, true, false); |
| 114 | if (guard_samplers) { | 112 | if (guard_samplers) { |
| 115 | sampled_textures.push_back(surface); | 113 | sampled_textures.push_back(surface); |
| 116 | } | 114 | } |
| @@ -124,13 +122,13 @@ public: | |||
| 124 | if (!gpu_addr) { | 122 | if (!gpu_addr) { |
| 125 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); | 123 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); |
| 126 | } | 124 | } |
| 127 | const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; | 125 | const std::optional<VAddr> cpu_addr = |
| 128 | const auto cache_addr{ToCacheAddr(host_ptr)}; | 126 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); |
| 129 | if (!cache_addr) { | 127 | if (!cpu_addr) { |
| 130 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); | 128 | return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); |
| 131 | } | 129 | } |
| 132 | const auto params{SurfaceParams::CreateForImage(format_lookup_table, tic, entry)}; | 130 | const auto params{SurfaceParams::CreateForImage(format_lookup_table, tic, entry)}; |
| 133 | const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false); | 131 | const auto [surface, view] = GetSurface(gpu_addr, *cpu_addr, params, true, false); |
| 134 | if (guard_samplers) { | 132 | if (guard_samplers) { |
| 135 | sampled_textures.push_back(surface); | 133 | sampled_textures.push_back(surface); |
| 136 | } | 134 | } |
| @@ -159,14 +157,14 @@ public: | |||
| 159 | SetEmptyDepthBuffer(); | 157 | SetEmptyDepthBuffer(); |
| 160 | return {}; | 158 | return {}; |
| 161 | } | 159 | } |
| 162 | const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; | 160 | const std::optional<VAddr> cpu_addr = |
| 163 | const auto cache_addr{ToCacheAddr(host_ptr)}; | 161 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); |
| 164 | if (!cache_addr) { | 162 | if (!cpu_addr) { |
| 165 | SetEmptyDepthBuffer(); | 163 | SetEmptyDepthBuffer(); |
| 166 | return {}; | 164 | return {}; |
| 167 | } | 165 | } |
| 168 | const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)}; | 166 | const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)}; |
| 169 | auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true); | 167 | auto surface_view = GetSurface(gpu_addr, *cpu_addr, depth_params, preserve_contents, true); |
| 170 | if (depth_buffer.target) | 168 | if (depth_buffer.target) |
| 171 | depth_buffer.target->MarkAsRenderTarget(false, NO_RT); | 169 | depth_buffer.target->MarkAsRenderTarget(false, NO_RT); |
| 172 | depth_buffer.target = surface_view.first; | 170 | depth_buffer.target = surface_view.first; |
| @@ -199,15 +197,15 @@ public: | |||
| 199 | return {}; | 197 | return {}; |
| 200 | } | 198 | } |
| 201 | 199 | ||
| 202 | const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; | 200 | const std::optional<VAddr> cpu_addr = |
| 203 | const auto cache_addr{ToCacheAddr(host_ptr)}; | 201 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); |
| 204 | if (!cache_addr) { | 202 | if (!cpu_addr) { |
| 205 | SetEmptyColorBuffer(index); | 203 | SetEmptyColorBuffer(index); |
| 206 | return {}; | 204 | return {}; |
| 207 | } | 205 | } |
| 208 | 206 | ||
| 209 | auto surface_view = | 207 | auto surface_view = |
| 210 | GetSurface(gpu_addr, cache_addr, SurfaceParams::CreateForFramebuffer(system, index), | 208 | GetSurface(gpu_addr, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index), |
| 211 | preserve_contents, true); | 209 | preserve_contents, true); |
| 212 | if (render_targets[index].target) | 210 | if (render_targets[index].target) |
| 213 | render_targets[index].target->MarkAsRenderTarget(false, NO_RT); | 211 | render_targets[index].target->MarkAsRenderTarget(false, NO_RT); |
| @@ -257,27 +255,26 @@ public: | |||
| 257 | const GPUVAddr src_gpu_addr = src_config.Address(); | 255 | const GPUVAddr src_gpu_addr = src_config.Address(); |
| 258 | const GPUVAddr dst_gpu_addr = dst_config.Address(); | 256 | const GPUVAddr dst_gpu_addr = dst_config.Address(); |
| 259 | DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); | 257 | DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); |
| 260 | const auto dst_host_ptr{system.GPU().MemoryManager().GetPointer(dst_gpu_addr)}; | 258 | const std::optional<VAddr> dst_cpu_addr = |
| 261 | const auto dst_cache_addr{ToCacheAddr(dst_host_ptr)}; | 259 | system.GPU().MemoryManager().GpuToCpuAddress(dst_gpu_addr); |
| 262 | const auto src_host_ptr{system.GPU().MemoryManager().GetPointer(src_gpu_addr)}; | 260 | const std::optional<VAddr> src_cpu_addr = |
| 263 | const auto src_cache_addr{ToCacheAddr(src_host_ptr)}; | 261 | system.GPU().MemoryManager().GpuToCpuAddress(src_gpu_addr); |
| 264 | std::pair<TSurface, TView> dst_surface = | 262 | std::pair<TSurface, TView> dst_surface = |
| 265 | GetSurface(dst_gpu_addr, dst_cache_addr, dst_params, true, false); | 263 | GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false); |
| 266 | std::pair<TSurface, TView> src_surface = | 264 | std::pair<TSurface, TView> src_surface = |
| 267 | GetSurface(src_gpu_addr, src_cache_addr, src_params, true, false); | 265 | GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false); |
| 268 | ImageBlit(src_surface.second, dst_surface.second, copy_config); | 266 | ImageBlit(src_surface.second, dst_surface.second, copy_config); |
| 269 | dst_surface.first->MarkAsModified(true, Tick()); | 267 | dst_surface.first->MarkAsModified(true, Tick()); |
| 270 | } | 268 | } |
| 271 | 269 | ||
| 272 | TSurface TryFindFramebufferSurface(const u8* host_ptr) { | 270 | TSurface TryFindFramebufferSurface(VAddr addr) { |
| 273 | const CacheAddr cache_addr = ToCacheAddr(host_ptr); | 271 | if (!addr) { |
| 274 | if (!cache_addr) { | ||
| 275 | return nullptr; | 272 | return nullptr; |
| 276 | } | 273 | } |
| 277 | const CacheAddr page = cache_addr >> registry_page_bits; | 274 | const VAddr page = addr >> registry_page_bits; |
| 278 | std::vector<TSurface>& list = registry[page]; | 275 | std::vector<TSurface>& list = registry[page]; |
| 279 | for (auto& surface : list) { | 276 | for (auto& surface : list) { |
| 280 | if (surface->GetCacheAddr() == cache_addr) { | 277 | if (surface->GetCpuAddr() == addr) { |
| 281 | return surface; | 278 | return surface; |
| 282 | } | 279 | } |
| 283 | } | 280 | } |
| @@ -289,8 +286,9 @@ public: | |||
| 289 | } | 286 | } |
| 290 | 287 | ||
| 291 | protected: | 288 | protected: |
| 292 | TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | 289 | explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 293 | : system{system}, rasterizer{rasterizer} { | 290 | bool is_astc_supported) |
| 291 | : system{system}, is_astc_supported{is_astc_supported}, rasterizer{rasterizer} { | ||
| 294 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 292 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { |
| 295 | SetEmptyColorBuffer(i); | 293 | SetEmptyColorBuffer(i); |
| 296 | } | 294 | } |
| @@ -337,18 +335,14 @@ protected: | |||
| 337 | 335 | ||
| 338 | void Register(TSurface surface) { | 336 | void Register(TSurface surface) { |
| 339 | const GPUVAddr gpu_addr = surface->GetGpuAddr(); | 337 | const GPUVAddr gpu_addr = surface->GetGpuAddr(); |
| 340 | const CacheAddr cache_ptr = ToCacheAddr(system.GPU().MemoryManager().GetPointer(gpu_addr)); | ||
| 341 | const std::size_t size = surface->GetSizeInBytes(); | 338 | const std::size_t size = surface->GetSizeInBytes(); |
| 342 | const std::optional<VAddr> cpu_addr = | 339 | const std::optional<VAddr> cpu_addr = |
| 343 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); | 340 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); |
| 344 | if (!cache_ptr || !cpu_addr) { | 341 | if (!cpu_addr) { |
| 345 | LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}", | 342 | LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}", |
| 346 | gpu_addr); | 343 | gpu_addr); |
| 347 | return; | 344 | return; |
| 348 | } | 345 | } |
| 349 | const bool continuous = system.GPU().MemoryManager().IsBlockContinuous(gpu_addr, size); | ||
| 350 | surface->MarkAsContinuous(continuous); | ||
| 351 | surface->SetCacheAddr(cache_ptr); | ||
| 352 | surface->SetCpuAddr(*cpu_addr); | 346 | surface->SetCpuAddr(*cpu_addr); |
| 353 | RegisterInnerCache(surface); | 347 | RegisterInnerCache(surface); |
| 354 | surface->MarkAsRegistered(true); | 348 | surface->MarkAsRegistered(true); |
| @@ -381,6 +375,7 @@ protected: | |||
| 381 | } | 375 | } |
| 382 | 376 | ||
| 383 | Core::System& system; | 377 | Core::System& system; |
| 378 | const bool is_astc_supported; | ||
| 384 | 379 | ||
| 385 | private: | 380 | private: |
| 386 | enum class RecycleStrategy : u32 { | 381 | enum class RecycleStrategy : u32 { |
| @@ -632,7 +627,7 @@ private: | |||
| 632 | std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, | 627 | std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, |
| 633 | const SurfaceParams& params, | 628 | const SurfaceParams& params, |
| 634 | const GPUVAddr gpu_addr, | 629 | const GPUVAddr gpu_addr, |
| 635 | const CacheAddr cache_addr, | 630 | const VAddr cpu_addr, |
| 636 | bool preserve_contents) { | 631 | bool preserve_contents) { |
| 637 | if (params.target == SurfaceTarget::Texture3D) { | 632 | if (params.target == SurfaceTarget::Texture3D) { |
| 638 | bool failed = false; | 633 | bool failed = false; |
| @@ -657,7 +652,7 @@ private: | |||
| 657 | failed = true; | 652 | failed = true; |
| 658 | break; | 653 | break; |
| 659 | } | 654 | } |
| 660 | const u32 offset = static_cast<u32>(surface->GetCacheAddr() - cache_addr); | 655 | const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr); |
| 661 | const auto [x, y, z] = params.GetBlockOffsetXYZ(offset); | 656 | const auto [x, y, z] = params.GetBlockOffsetXYZ(offset); |
| 662 | modified |= surface->IsModified(); | 657 | modified |= surface->IsModified(); |
| 663 | const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height, | 658 | const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height, |
| @@ -677,7 +672,7 @@ private: | |||
| 677 | } else { | 672 | } else { |
| 678 | for (const auto& surface : overlaps) { | 673 | for (const auto& surface : overlaps) { |
| 679 | if (!surface->MatchTarget(params.target)) { | 674 | if (!surface->MatchTarget(params.target)) { |
| 680 | if (overlaps.size() == 1 && surface->GetCacheAddr() == cache_addr) { | 675 | if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { |
| 681 | if (Settings::values.use_accurate_gpu_emulation) { | 676 | if (Settings::values.use_accurate_gpu_emulation) { |
| 682 | return std::nullopt; | 677 | return std::nullopt; |
| 683 | } | 678 | } |
| @@ -686,7 +681,7 @@ private: | |||
| 686 | } | 681 | } |
| 687 | return std::nullopt; | 682 | return std::nullopt; |
| 688 | } | 683 | } |
| 689 | if (surface->GetCacheAddr() != cache_addr) { | 684 | if (surface->GetCpuAddr() != cpu_addr) { |
| 690 | continue; | 685 | continue; |
| 691 | } | 686 | } |
| 692 | if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { | 687 | if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { |
| @@ -720,13 +715,13 @@ private: | |||
| 720 | * left blank. | 715 | * left blank. |
| 721 | * @param is_render Whether or not the surface is a render target. | 716 | * @param is_render Whether or not the surface is a render target. |
| 722 | **/ | 717 | **/ |
| 723 | std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const CacheAddr cache_addr, | 718 | std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const VAddr cpu_addr, |
| 724 | const SurfaceParams& params, bool preserve_contents, | 719 | const SurfaceParams& params, bool preserve_contents, |
| 725 | bool is_render) { | 720 | bool is_render) { |
| 726 | // Step 1 | 721 | // Step 1 |
| 727 | // Check Level 1 Cache for a fast structural match. If candidate surface | 722 | // Check Level 1 Cache for a fast structural match. If candidate surface |
| 728 | // matches at certain level we are pretty much done. | 723 | // matches at certain level we are pretty much done. |
| 729 | if (const auto iter = l1_cache.find(cache_addr); iter != l1_cache.end()) { | 724 | if (const auto iter = l1_cache.find(cpu_addr); iter != l1_cache.end()) { |
| 730 | TSurface& current_surface = iter->second; | 725 | TSurface& current_surface = iter->second; |
| 731 | const auto topological_result = current_surface->MatchesTopology(params); | 726 | const auto topological_result = current_surface->MatchesTopology(params); |
| 732 | if (topological_result != MatchTopologyResult::FullMatch) { | 727 | if (topological_result != MatchTopologyResult::FullMatch) { |
| @@ -753,7 +748,7 @@ private: | |||
| 753 | // Step 2 | 748 | // Step 2 |
| 754 | // Obtain all possible overlaps in the memory region | 749 | // Obtain all possible overlaps in the memory region |
| 755 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); | 750 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); |
| 756 | auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)}; | 751 | auto overlaps{GetSurfacesInRegion(cpu_addr, candidate_size)}; |
| 757 | 752 | ||
| 758 | // If none are found, we are done. we just load the surface and create it. | 753 | // If none are found, we are done. we just load the surface and create it. |
| 759 | if (overlaps.empty()) { | 754 | if (overlaps.empty()) { |
| @@ -775,7 +770,7 @@ private: | |||
| 775 | // Check if it's a 3D texture | 770 | // Check if it's a 3D texture |
| 776 | if (params.block_depth > 0) { | 771 | if (params.block_depth > 0) { |
| 777 | auto surface = | 772 | auto surface = |
| 778 | Manage3DSurfaces(overlaps, params, gpu_addr, cache_addr, preserve_contents); | 773 | Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents); |
| 779 | if (surface) { | 774 | if (surface) { |
| 780 | return *surface; | 775 | return *surface; |
| 781 | } | 776 | } |
| @@ -850,16 +845,16 @@ private: | |||
| 850 | * @param params The parameters on the candidate surface. | 845 | * @param params The parameters on the candidate surface. |
| 851 | **/ | 846 | **/ |
| 852 | Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) { | 847 | Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) { |
| 853 | const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; | 848 | const std::optional<VAddr> cpu_addr = |
| 854 | const auto cache_addr{ToCacheAddr(host_ptr)}; | 849 | system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); |
| 855 | 850 | ||
| 856 | if (!cache_addr) { | 851 | if (!cpu_addr) { |
| 857 | Deduction result{}; | 852 | Deduction result{}; |
| 858 | result.type = DeductionType::DeductionFailed; | 853 | result.type = DeductionType::DeductionFailed; |
| 859 | return result; | 854 | return result; |
| 860 | } | 855 | } |
| 861 | 856 | ||
| 862 | if (const auto iter = l1_cache.find(cache_addr); iter != l1_cache.end()) { | 857 | if (const auto iter = l1_cache.find(*cpu_addr); iter != l1_cache.end()) { |
| 863 | TSurface& current_surface = iter->second; | 858 | TSurface& current_surface = iter->second; |
| 864 | const auto topological_result = current_surface->MatchesTopology(params); | 859 | const auto topological_result = current_surface->MatchesTopology(params); |
| 865 | if (topological_result != MatchTopologyResult::FullMatch) { | 860 | if (topological_result != MatchTopologyResult::FullMatch) { |
| @@ -878,7 +873,7 @@ private: | |||
| 878 | } | 873 | } |
| 879 | 874 | ||
| 880 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); | 875 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); |
| 881 | auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)}; | 876 | auto overlaps{GetSurfacesInRegion(*cpu_addr, candidate_size)}; |
| 882 | 877 | ||
| 883 | if (overlaps.empty()) { | 878 | if (overlaps.empty()) { |
| 884 | Deduction result{}; | 879 | Deduction result{}; |
| @@ -1022,10 +1017,10 @@ private: | |||
| 1022 | } | 1017 | } |
| 1023 | 1018 | ||
| 1024 | void RegisterInnerCache(TSurface& surface) { | 1019 | void RegisterInnerCache(TSurface& surface) { |
| 1025 | const CacheAddr cache_addr = surface->GetCacheAddr(); | 1020 | const VAddr cpu_addr = surface->GetCpuAddr(); |
| 1026 | CacheAddr start = cache_addr >> registry_page_bits; | 1021 | VAddr start = cpu_addr >> registry_page_bits; |
| 1027 | const CacheAddr end = (surface->GetCacheAddrEnd() - 1) >> registry_page_bits; | 1022 | const VAddr end = (surface->GetCpuAddrEnd() - 1) >> registry_page_bits; |
| 1028 | l1_cache[cache_addr] = surface; | 1023 | l1_cache[cpu_addr] = surface; |
| 1029 | while (start <= end) { | 1024 | while (start <= end) { |
| 1030 | registry[start].push_back(surface); | 1025 | registry[start].push_back(surface); |
| 1031 | start++; | 1026 | start++; |
| @@ -1033,10 +1028,10 @@ private: | |||
| 1033 | } | 1028 | } |
| 1034 | 1029 | ||
| 1035 | void UnregisterInnerCache(TSurface& surface) { | 1030 | void UnregisterInnerCache(TSurface& surface) { |
| 1036 | const CacheAddr cache_addr = surface->GetCacheAddr(); | 1031 | const VAddr cpu_addr = surface->GetCpuAddr(); |
| 1037 | CacheAddr start = cache_addr >> registry_page_bits; | 1032 | VAddr start = cpu_addr >> registry_page_bits; |
| 1038 | const CacheAddr end = (surface->GetCacheAddrEnd() - 1) >> registry_page_bits; | 1033 | const VAddr end = (surface->GetCpuAddrEnd() - 1) >> registry_page_bits; |
| 1039 | l1_cache.erase(cache_addr); | 1034 | l1_cache.erase(cpu_addr); |
| 1040 | while (start <= end) { | 1035 | while (start <= end) { |
| 1041 | auto& reg{registry[start]}; | 1036 | auto& reg{registry[start]}; |
| 1042 | reg.erase(std::find(reg.begin(), reg.end(), surface)); | 1037 | reg.erase(std::find(reg.begin(), reg.end(), surface)); |
| @@ -1044,18 +1039,18 @@ private: | |||
| 1044 | } | 1039 | } |
| 1045 | } | 1040 | } |
| 1046 | 1041 | ||
| 1047 | std::vector<TSurface> GetSurfacesInRegion(const CacheAddr cache_addr, const std::size_t size) { | 1042 | std::vector<TSurface> GetSurfacesInRegion(const VAddr cpu_addr, const std::size_t size) { |
| 1048 | if (size == 0) { | 1043 | if (size == 0) { |
| 1049 | return {}; | 1044 | return {}; |
| 1050 | } | 1045 | } |
| 1051 | const CacheAddr cache_addr_end = cache_addr + size; | 1046 | const VAddr cpu_addr_end = cpu_addr + size; |
| 1052 | CacheAddr start = cache_addr >> registry_page_bits; | 1047 | VAddr start = cpu_addr >> registry_page_bits; |
| 1053 | const CacheAddr end = (cache_addr_end - 1) >> registry_page_bits; | 1048 | const VAddr end = (cpu_addr_end - 1) >> registry_page_bits; |
| 1054 | std::vector<TSurface> surfaces; | 1049 | std::vector<TSurface> surfaces; |
| 1055 | while (start <= end) { | 1050 | while (start <= end) { |
| 1056 | std::vector<TSurface>& list = registry[start]; | 1051 | std::vector<TSurface>& list = registry[start]; |
| 1057 | for (auto& surface : list) { | 1052 | for (auto& surface : list) { |
| 1058 | if (!surface->IsPicked() && surface->Overlaps(cache_addr, cache_addr_end)) { | 1053 | if (!surface->IsPicked() && surface->Overlaps(cpu_addr, cpu_addr_end)) { |
| 1059 | surface->MarkAsPicked(true); | 1054 | surface->MarkAsPicked(true); |
| 1060 | surfaces.push_back(surface); | 1055 | surfaces.push_back(surface); |
| 1061 | } | 1056 | } |
| @@ -1144,14 +1139,14 @@ private: | |||
| 1144 | // large in size. | 1139 | // large in size. |
| 1145 | static constexpr u64 registry_page_bits{20}; | 1140 | static constexpr u64 registry_page_bits{20}; |
| 1146 | static constexpr u64 registry_page_size{1 << registry_page_bits}; | 1141 | static constexpr u64 registry_page_size{1 << registry_page_bits}; |
| 1147 | std::unordered_map<CacheAddr, std::vector<TSurface>> registry; | 1142 | std::unordered_map<VAddr, std::vector<TSurface>> registry; |
| 1148 | 1143 | ||
| 1149 | static constexpr u32 DEPTH_RT = 8; | 1144 | static constexpr u32 DEPTH_RT = 8; |
| 1150 | static constexpr u32 NO_RT = 0xFFFFFFFF; | 1145 | static constexpr u32 NO_RT = 0xFFFFFFFF; |
| 1151 | 1146 | ||
| 1152 | // The L1 Cache is used for fast texture lookup before checking the overlaps | 1147 | // The L1 Cache is used for fast texture lookup before checking the overlaps |
| 1153 | // This avoids calculating size and other stuffs. | 1148 | // This avoids calculating size and other stuffs. |
| 1154 | std::unordered_map<CacheAddr, TSurface> l1_cache; | 1149 | std::unordered_map<VAddr, TSurface> l1_cache; |
| 1155 | 1150 | ||
| 1156 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have | 1151 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have |
| 1157 | /// previously been used. This is to prevent surfaces from being constantly created and | 1152 | /// previously been used. This is to prevent surfaces from being constantly created and |
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp new file mode 100644 index 000000000..d1939d744 --- /dev/null +++ b/src/video_core/textures/texture.cpp | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "core/settings.h" | ||
| 9 | #include "video_core/textures/texture.h" | ||
| 10 | |||
| 11 | namespace Tegra::Texture { | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | |||
| 15 | constexpr std::array<float, 256> SRGB_CONVERSION_LUT = { | ||
| 16 | 0.000000f, 0.000000f, 0.000000f, 0.000012f, 0.000021f, 0.000033f, 0.000046f, 0.000062f, | ||
| 17 | 0.000081f, 0.000102f, 0.000125f, 0.000151f, 0.000181f, 0.000214f, 0.000251f, 0.000293f, | ||
| 18 | 0.000338f, 0.000388f, 0.000443f, 0.000503f, 0.000568f, 0.000639f, 0.000715f, 0.000798f, | ||
| 19 | 0.000887f, 0.000983f, 0.001085f, 0.001195f, 0.001312f, 0.001437f, 0.001569f, 0.001710f, | ||
| 20 | 0.001860f, 0.002019f, 0.002186f, 0.002364f, 0.002551f, 0.002748f, 0.002955f, 0.003174f, | ||
| 21 | 0.003403f, 0.003643f, 0.003896f, 0.004160f, 0.004436f, 0.004725f, 0.005028f, 0.005343f, | ||
| 22 | 0.005672f, 0.006015f, 0.006372f, 0.006744f, 0.007130f, 0.007533f, 0.007950f, 0.008384f, | ||
| 23 | 0.008834f, 0.009301f, 0.009785f, 0.010286f, 0.010805f, 0.011342f, 0.011898f, 0.012472f, | ||
| 24 | 0.013066f, 0.013680f, 0.014313f, 0.014967f, 0.015641f, 0.016337f, 0.017054f, 0.017793f, | ||
| 25 | 0.018554f, 0.019337f, 0.020144f, 0.020974f, 0.021828f, 0.022706f, 0.023609f, 0.024536f, | ||
| 26 | 0.025489f, 0.026468f, 0.027473f, 0.028504f, 0.029563f, 0.030649f, 0.031762f, 0.032904f, | ||
| 27 | 0.034074f, 0.035274f, 0.036503f, 0.037762f, 0.039050f, 0.040370f, 0.041721f, 0.043103f, | ||
| 28 | 0.044518f, 0.045964f, 0.047444f, 0.048956f, 0.050503f, 0.052083f, 0.053699f, 0.055349f, | ||
| 29 | 0.057034f, 0.058755f, 0.060513f, 0.062307f, 0.064139f, 0.066008f, 0.067915f, 0.069861f, | ||
| 30 | 0.071845f, 0.073869f, 0.075933f, 0.078037f, 0.080182f, 0.082369f, 0.084597f, 0.086867f, | ||
| 31 | 0.089180f, 0.091535f, 0.093935f, 0.096378f, 0.098866f, 0.101398f, 0.103977f, 0.106601f, | ||
| 32 | 0.109271f, 0.111988f, 0.114753f, 0.117565f, 0.120426f, 0.123335f, 0.126293f, 0.129301f, | ||
| 33 | 0.132360f, 0.135469f, 0.138629f, 0.141841f, 0.145105f, 0.148421f, 0.151791f, 0.155214f, | ||
| 34 | 0.158691f, 0.162224f, 0.165810f, 0.169453f, 0.173152f, 0.176907f, 0.180720f, 0.184589f, | ||
| 35 | 0.188517f, 0.192504f, 0.196549f, 0.200655f, 0.204820f, 0.209046f, 0.213334f, 0.217682f, | ||
| 36 | 0.222093f, 0.226567f, 0.231104f, 0.235704f, 0.240369f, 0.245099f, 0.249894f, 0.254754f, | ||
| 37 | 0.259681f, 0.264674f, 0.269736f, 0.274864f, 0.280062f, 0.285328f, 0.290664f, 0.296070f, | ||
| 38 | 0.301546f, 0.307094f, 0.312713f, 0.318404f, 0.324168f, 0.330006f, 0.335916f, 0.341902f, | ||
| 39 | 0.347962f, 0.354097f, 0.360309f, 0.366597f, 0.372961f, 0.379403f, 0.385924f, 0.392524f, | ||
| 40 | 0.399202f, 0.405960f, 0.412798f, 0.419718f, 0.426719f, 0.433802f, 0.440967f, 0.448216f, | ||
| 41 | 0.455548f, 0.462965f, 0.470465f, 0.478052f, 0.485725f, 0.493484f, 0.501329f, 0.509263f, | ||
| 42 | 0.517285f, 0.525396f, 0.533595f, 0.541885f, 0.550265f, 0.558736f, 0.567299f, 0.575954f, | ||
| 43 | 0.584702f, 0.593542f, 0.602477f, 0.611507f, 0.620632f, 0.629852f, 0.639168f, 0.648581f, | ||
| 44 | 0.658092f, 0.667700f, 0.677408f, 0.687214f, 0.697120f, 0.707127f, 0.717234f, 0.727443f, | ||
| 45 | 0.737753f, 0.748167f, 0.758685f, 0.769305f, 0.780031f, 0.790861f, 0.801798f, 0.812839f, | ||
| 46 | 0.823989f, 0.835246f, 0.846611f, 0.858085f, 0.869668f, 0.881360f, 0.893164f, 0.905078f, | ||
| 47 | 0.917104f, 0.929242f, 0.941493f, 0.953859f, 0.966338f, 1.000000f, 1.000000f, 1.000000f, | ||
| 48 | }; | ||
| 49 | |||
| 50 | unsigned SettingsMinimumAnisotropy() noexcept { | ||
| 51 | switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) { | ||
| 52 | default: | ||
| 53 | case Anisotropy::Default: | ||
| 54 | return 1U; | ||
| 55 | case Anisotropy::Filter2x: | ||
| 56 | return 2U; | ||
| 57 | case Anisotropy::Filter4x: | ||
| 58 | return 4U; | ||
| 59 | case Anisotropy::Filter8x: | ||
| 60 | return 8U; | ||
| 61 | case Anisotropy::Filter16x: | ||
| 62 | return 16U; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | } // Anonymous namespace | ||
| 67 | |||
| 68 | std::array<float, 4> TSCEntry::GetBorderColor() const noexcept { | ||
| 69 | if (!srgb_conversion) { | ||
| 70 | return border_color; | ||
| 71 | } | ||
| 72 | return {SRGB_CONVERSION_LUT[srgb_border_color_r], SRGB_CONVERSION_LUT[srgb_border_color_g], | ||
| 73 | SRGB_CONVERSION_LUT[srgb_border_color_b], border_color[3]}; | ||
| 74 | } | ||
| 75 | |||
| 76 | float TSCEntry::GetMaxAnisotropy() const noexcept { | ||
| 77 | return static_cast<float>(std::max(1U << max_anisotropy, SettingsMinimumAnisotropy())); | ||
| 78 | } | ||
| 79 | |||
| 80 | } // namespace Tegra::Texture | ||
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 7edc4abe1..eba05aced 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/settings.h" | ||
| 12 | 11 | ||
| 13 | namespace Tegra::Texture { | 12 | namespace Tegra::Texture { |
| 14 | 13 | ||
| @@ -132,6 +131,20 @@ enum class SwizzleSource : u32 { | |||
| 132 | OneFloat = 7, | 131 | OneFloat = 7, |
| 133 | }; | 132 | }; |
| 134 | 133 | ||
| 134 | enum class MsaaMode : u32 { | ||
| 135 | Msaa1x1 = 0, | ||
| 136 | Msaa2x1 = 1, | ||
| 137 | Msaa2x2 = 2, | ||
| 138 | Msaa4x2 = 3, | ||
| 139 | Msaa4x2_D3D = 4, | ||
| 140 | Msaa2x1_D3D = 5, | ||
| 141 | Msaa4x4 = 6, | ||
| 142 | Msaa2x2_VC4 = 8, | ||
| 143 | Msaa2x2_VC12 = 9, | ||
| 144 | Msaa4x2_VC8 = 10, | ||
| 145 | Msaa4x2_VC24 = 11, | ||
| 146 | }; | ||
| 147 | |||
| 135 | union TextureHandle { | 148 | union TextureHandle { |
| 136 | TextureHandle(u32 raw) : raw{raw} {} | 149 | TextureHandle(u32 raw) : raw{raw} {} |
| 137 | 150 | ||
| @@ -198,6 +211,7 @@ struct TICEntry { | |||
| 198 | union { | 211 | union { |
| 199 | BitField<0, 4, u32> res_min_mip_level; | 212 | BitField<0, 4, u32> res_min_mip_level; |
| 200 | BitField<4, 4, u32> res_max_mip_level; | 213 | BitField<4, 4, u32> res_max_mip_level; |
| 214 | BitField<8, 4, MsaaMode> msaa_mode; | ||
| 201 | BitField<12, 12, u32> min_lod_clamp; | 215 | BitField<12, 12, u32> min_lod_clamp; |
| 202 | }; | 216 | }; |
| 203 | 217 | ||
| @@ -336,24 +350,9 @@ struct TSCEntry { | |||
| 336 | std::array<u8, 0x20> raw; | 350 | std::array<u8, 0x20> raw; |
| 337 | }; | 351 | }; |
| 338 | 352 | ||
| 339 | float GetMaxAnisotropy() const { | 353 | std::array<float, 4> GetBorderColor() const noexcept; |
| 340 | const u32 min_value = [] { | 354 | |
| 341 | switch (static_cast<Anisotropy>(Settings::values.max_anisotropy)) { | 355 | float GetMaxAnisotropy() const noexcept; |
| 342 | default: | ||
| 343 | case Anisotropy::Default: | ||
| 344 | return 1U; | ||
| 345 | case Anisotropy::Filter2x: | ||
| 346 | return 2U; | ||
| 347 | case Anisotropy::Filter4x: | ||
| 348 | return 4U; | ||
| 349 | case Anisotropy::Filter8x: | ||
| 350 | return 8U; | ||
| 351 | case Anisotropy::Filter16x: | ||
| 352 | return 16U; | ||
| 353 | } | ||
| 354 | }(); | ||
| 355 | return static_cast<float>(std::max(1U << max_anisotropy, min_value)); | ||
| 356 | } | ||
| 357 | 356 | ||
| 358 | float GetMinLod() const { | 357 | float GetMinLod() const { |
| 359 | return static_cast<float>(min_lod_clamp) / 256.0f; | 358 | return static_cast<float>(min_lod_clamp) / 256.0f; |
| @@ -368,15 +367,6 @@ struct TSCEntry { | |||
| 368 | constexpr u32 mask = 1U << (13 - 1); | 367 | constexpr u32 mask = 1U << (13 - 1); |
| 369 | return static_cast<float>(static_cast<s32>((mip_lod_bias ^ mask) - mask)) / 256.0f; | 368 | return static_cast<float>(static_cast<s32>((mip_lod_bias ^ mask) - mask)) / 256.0f; |
| 370 | } | 369 | } |
| 371 | |||
| 372 | std::array<float, 4> GetBorderColor() const { | ||
| 373 | if (srgb_conversion) { | ||
| 374 | return {static_cast<float>(srgb_border_color_r) / 255.0f, | ||
| 375 | static_cast<float>(srgb_border_color_g) / 255.0f, | ||
| 376 | static_cast<float>(srgb_border_color_b) / 255.0f, border_color[3]}; | ||
| 377 | } | ||
| 378 | return border_color; | ||
| 379 | } | ||
| 380 | }; | 370 | }; |
| 381 | static_assert(sizeof(TSCEntry) == 0x20, "TSCEntry has wrong size"); | 371 | static_assert(sizeof(TSCEntry) == 0x20, "TSCEntry has wrong size"); |
| 382 | 372 | ||
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index a5f81a8a0..f60bdc60a 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -15,13 +15,13 @@ | |||
| 15 | #endif | 15 | #endif |
| 16 | #include "video_core/video_core.h" | 16 | #include "video_core/video_core.h" |
| 17 | 17 | ||
| 18 | namespace VideoCore { | 18 | namespace { |
| 19 | 19 | std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | |
| 20 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | 20 | Core::System& system, |
| 21 | Core::System& system) { | 21 | Core::Frontend::GraphicsContext& context) { |
| 22 | switch (Settings::values.renderer_backend) { | 22 | switch (Settings::values.renderer_backend) { |
| 23 | case Settings::RendererBackend::OpenGL: | 23 | case Settings::RendererBackend::OpenGL: |
| 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); | 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context); |
| 25 | #ifdef HAS_VULKAN | 25 | #ifdef HAS_VULKAN |
| 26 | case Settings::RendererBackend::Vulkan: | 26 | case Settings::RendererBackend::Vulkan: |
| 27 | return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); | 27 | return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); |
| @@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind | |||
| 30 | return nullptr; | 30 | return nullptr; |
| 31 | } | 31 | } |
| 32 | } | 32 | } |
| 33 | } // Anonymous namespace | ||
| 33 | 34 | ||
| 34 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { | 35 | namespace VideoCore { |
| 35 | if (Settings::values.use_asynchronous_gpu_emulation) { | 36 | |
| 36 | return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer()); | 37 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { |
| 38 | auto context = emu_window.CreateSharedContext(); | ||
| 39 | const auto scope = context->Acquire(); | ||
| 40 | auto renderer = CreateRenderer(emu_window, system, *context); | ||
| 41 | if (!renderer->Init()) { | ||
| 42 | return nullptr; | ||
| 37 | } | 43 | } |
| 38 | 44 | ||
| 39 | return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer()); | 45 | if (Settings::values.use_asynchronous_gpu_emulation) { |
| 46 | return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), | ||
| 47 | std::move(context)); | ||
| 48 | } | ||
| 49 | return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context)); | ||
| 40 | } | 50 | } |
| 41 | 51 | ||
| 42 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { | 52 | u16 GetResolutionScaleFactor(const RendererBase& renderer) { |
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index b8e0ac372..f5c27125d 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h | |||
| @@ -22,17 +22,8 @@ namespace VideoCore { | |||
| 22 | 22 | ||
| 23 | class RendererBase; | 23 | class RendererBase; |
| 24 | 24 | ||
| 25 | /** | ||
| 26 | * Creates a renderer instance. | ||
| 27 | * | ||
| 28 | * @note The returned renderer instance is simply allocated. Its Init() | ||
| 29 | * function still needs to be called to fully complete its setup. | ||
| 30 | */ | ||
| 31 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | ||
| 32 | Core::System& system); | ||
| 33 | |||
| 34 | /// Creates an emulated GPU instance using the given system context. | 25 | /// Creates an emulated GPU instance using the given system context. |
| 35 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system); | 26 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system); |
| 36 | 27 | ||
| 37 | u16 GetResolutionScaleFactor(const RendererBase& renderer); | 28 | u16 GetResolutionScaleFactor(const RendererBase& renderer); |
| 38 | 29 | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index d34b47b3f..8b9404718 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -150,6 +150,10 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core) | |||
| 150 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) | 150 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) |
| 151 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | 151 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) |
| 152 | 152 | ||
| 153 | if (ENABLE_VULKAN AND NOT WIN32) | ||
| 154 | target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) | ||
| 155 | endif() | ||
| 156 | |||
| 153 | target_compile_definitions(yuzu PRIVATE | 157 | target_compile_definitions(yuzu PRIVATE |
| 154 | # Use QStringBuilder for string concatenation to reduce | 158 | # Use QStringBuilder for string concatenation to reduce |
| 155 | # the overall number of temporary strings created. | 159 | # the overall number of temporary strings created. |
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index d39b3f07a..695b2ef5f 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp | |||
| @@ -3,15 +3,22 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QIcon> | 5 | #include <QIcon> |
| 6 | #include <fmt/format.h> | ||
| 6 | #include "common/scm_rev.h" | 7 | #include "common/scm_rev.h" |
| 7 | #include "ui_aboutdialog.h" | 8 | #include "ui_aboutdialog.h" |
| 8 | #include "yuzu/about_dialog.h" | 9 | #include "yuzu/about_dialog.h" |
| 9 | 10 | ||
| 10 | AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { | 11 | AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { |
| 12 | const auto build_id = std::string(Common::g_build_id); | ||
| 13 | const auto fmt = std::string(Common::g_title_bar_format_idle); | ||
| 14 | const auto yuzu_build_version = | ||
| 15 | fmt::format(fmt.empty() ? "yuzu Development Build" : fmt, std::string{}, std::string{}, | ||
| 16 | std::string{}, std::string{}, std::string{}, build_id); | ||
| 17 | |||
| 11 | ui->setupUi(this); | 18 | ui->setupUi(this); |
| 12 | ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200)); | 19 | ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200)); |
| 13 | ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( | 20 | ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( |
| 14 | QString::fromUtf8(Common::g_build_fullname), QString::fromUtf8(Common::g_scm_branch), | 21 | QString::fromStdString(yuzu_build_version), QString::fromUtf8(Common::g_scm_branch), |
| 15 | QString::fromUtf8(Common::g_scm_desc), QString::fromUtf8(Common::g_build_date).left(10))); | 22 | QString::fromUtf8(Common::g_scm_desc), QString::fromUtf8(Common::g_build_date).left(10))); |
| 16 | } | 23 | } |
| 17 | 24 | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c3dbb1a88..1cac2f942 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -10,15 +10,13 @@ | |||
| 10 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 11 | #include <QOffscreenSurface> | 11 | #include <QOffscreenSurface> |
| 12 | #include <QOpenGLContext> | 12 | #include <QOpenGLContext> |
| 13 | #include <QOpenGLFunctions> | ||
| 14 | #include <QOpenGLFunctions_4_3_Core> | ||
| 15 | #include <QOpenGLWindow> | ||
| 16 | #include <QPainter> | 13 | #include <QPainter> |
| 17 | #include <QScreen> | 14 | #include <QScreen> |
| 18 | #include <QStringList> | 15 | #include <QStringList> |
| 19 | #include <QWindow> | 16 | #include <QWindow> |
| 20 | #ifdef HAS_VULKAN | 17 | |
| 21 | #include <QVulkanWindow> | 18 | #if !defined(WIN32) && HAS_VULKAN |
| 19 | #include <qpa/qplatformnativeinterface.h> | ||
| 22 | #endif | 20 | #endif |
| 23 | 21 | ||
| 24 | #include <fmt/format.h> | 22 | #include <fmt/format.h> |
| @@ -29,7 +27,6 @@ | |||
| 29 | #include "common/scope_exit.h" | 27 | #include "common/scope_exit.h" |
| 30 | #include "core/core.h" | 28 | #include "core/core.h" |
| 31 | #include "core/frontend/framebuffer_layout.h" | 29 | #include "core/frontend/framebuffer_layout.h" |
| 32 | #include "core/frontend/scope_acquire_context.h" | ||
| 33 | #include "core/settings.h" | 30 | #include "core/settings.h" |
| 34 | #include "input_common/keyboard.h" | 31 | #include "input_common/keyboard.h" |
| 35 | #include "input_common/main.h" | 32 | #include "input_common/main.h" |
| @@ -39,26 +36,16 @@ | |||
| 39 | #include "yuzu/bootmanager.h" | 36 | #include "yuzu/bootmanager.h" |
| 40 | #include "yuzu/main.h" | 37 | #include "yuzu/main.h" |
| 41 | 38 | ||
| 42 | EmuThread::EmuThread(GRenderWindow& window) | 39 | EmuThread::EmuThread() = default; |
| 43 | : shared_context{window.CreateSharedContext()}, | ||
| 44 | context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context | ||
| 45 | : window} {} | ||
| 46 | 40 | ||
| 47 | EmuThread::~EmuThread() = default; | 41 | EmuThread::~EmuThread() = default; |
| 48 | 42 | ||
| 49 | static GMainWindow* GetMainWindow() { | ||
| 50 | for (QWidget* w : qApp->topLevelWidgets()) { | ||
| 51 | if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) { | ||
| 52 | return main; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | return nullptr; | ||
| 56 | } | ||
| 57 | |||
| 58 | void EmuThread::run() { | 43 | void EmuThread::run() { |
| 59 | MicroProfileOnThreadCreate("EmuThread"); | 44 | MicroProfileOnThreadCreate("EmuThread"); |
| 60 | 45 | ||
| 61 | Core::Frontend::ScopeAcquireContext acquire_context{context}; | 46 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 47 | // execution. | ||
| 48 | Core::System::GetInstance().GPU().Start(); | ||
| 62 | 49 | ||
| 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 50 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 64 | 51 | ||
| @@ -111,163 +98,190 @@ void EmuThread::run() { | |||
| 111 | #endif | 98 | #endif |
| 112 | } | 99 | } |
| 113 | 100 | ||
| 114 | class GGLContext : public Core::Frontend::GraphicsContext { | 101 | class OpenGLSharedContext : public Core::Frontend::GraphicsContext { |
| 115 | public: | 102 | public: |
| 116 | explicit GGLContext(QOpenGLContext* shared_context) | 103 | /// Create the original context that should be shared from |
| 117 | : context(new QOpenGLContext(shared_context->parent())), | 104 | explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { |
| 118 | surface(new QOffscreenSurface(nullptr)) { | 105 | QSurfaceFormat format; |
| 106 | format.setVersion(4, 3); | ||
| 107 | format.setProfile(QSurfaceFormat::CompatibilityProfile); | ||
| 108 | format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | ||
| 109 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 110 | format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 111 | format.setSwapInterval(0); | ||
| 112 | |||
| 113 | context = std::make_unique<QOpenGLContext>(); | ||
| 114 | context->setFormat(format); | ||
| 115 | if (!context->create()) { | ||
| 116 | LOG_ERROR(Frontend, "Unable to create main openGL context"); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Create the shared contexts for rendering and presentation | ||
| 121 | explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) { | ||
| 119 | 122 | ||
| 120 | // disable vsync for any shared contexts | 123 | // disable vsync for any shared contexts |
| 121 | auto format = shared_context->format(); | 124 | auto format = share_context->format(); |
| 122 | format.setSwapInterval(0); | 125 | format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); |
| 123 | 126 | ||
| 124 | context->setShareContext(shared_context); | 127 | context = std::make_unique<QOpenGLContext>(); |
| 128 | context->setShareContext(share_context); | ||
| 125 | context->setFormat(format); | 129 | context->setFormat(format); |
| 126 | context->create(); | 130 | if (!context->create()) { |
| 127 | surface->setParent(shared_context->parent()); | 131 | LOG_ERROR(Frontend, "Unable to create shared openGL context"); |
| 128 | surface->setFormat(format); | 132 | } |
| 129 | surface->create(); | 133 | |
| 134 | if (!main_surface) { | ||
| 135 | offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr); | ||
| 136 | offscreen_surface->setFormat(format); | ||
| 137 | offscreen_surface->create(); | ||
| 138 | surface = offscreen_surface.get(); | ||
| 139 | } else { | ||
| 140 | surface = main_surface; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | ~OpenGLSharedContext() { | ||
| 145 | DoneCurrent(); | ||
| 146 | } | ||
| 147 | |||
| 148 | void SwapBuffers() override { | ||
| 149 | context->swapBuffers(surface); | ||
| 130 | } | 150 | } |
| 131 | 151 | ||
| 132 | void MakeCurrent() override { | 152 | void MakeCurrent() override { |
| 133 | context->makeCurrent(surface); | 153 | if (is_current) { |
| 154 | return; | ||
| 155 | } | ||
| 156 | is_current = context->makeCurrent(surface); | ||
| 134 | } | 157 | } |
| 135 | 158 | ||
| 136 | void DoneCurrent() override { | 159 | void DoneCurrent() override { |
| 160 | if (!is_current) { | ||
| 161 | return; | ||
| 162 | } | ||
| 137 | context->doneCurrent(); | 163 | context->doneCurrent(); |
| 164 | is_current = false; | ||
| 138 | } | 165 | } |
| 139 | 166 | ||
| 140 | private: | 167 | QOpenGLContext* GetShareContext() { |
| 141 | QOpenGLContext* context; | 168 | return context.get(); |
| 142 | QOffscreenSurface* surface; | ||
| 143 | }; | ||
| 144 | |||
| 145 | class ChildRenderWindow : public QWindow { | ||
| 146 | public: | ||
| 147 | ChildRenderWindow(QWindow* parent, QWidget* event_handler) | ||
| 148 | : QWindow{parent}, event_handler{event_handler} {} | ||
| 149 | |||
| 150 | virtual ~ChildRenderWindow() = default; | ||
| 151 | |||
| 152 | virtual void Present() = 0; | ||
| 153 | |||
| 154 | protected: | ||
| 155 | bool event(QEvent* event) override { | ||
| 156 | switch (event->type()) { | ||
| 157 | case QEvent::UpdateRequest: | ||
| 158 | Present(); | ||
| 159 | return true; | ||
| 160 | case QEvent::MouseButtonPress: | ||
| 161 | case QEvent::MouseButtonRelease: | ||
| 162 | case QEvent::MouseButtonDblClick: | ||
| 163 | case QEvent::MouseMove: | ||
| 164 | case QEvent::KeyPress: | ||
| 165 | case QEvent::KeyRelease: | ||
| 166 | case QEvent::FocusIn: | ||
| 167 | case QEvent::FocusOut: | ||
| 168 | case QEvent::FocusAboutToChange: | ||
| 169 | case QEvent::Enter: | ||
| 170 | case QEvent::Leave: | ||
| 171 | case QEvent::Wheel: | ||
| 172 | case QEvent::TabletMove: | ||
| 173 | case QEvent::TabletPress: | ||
| 174 | case QEvent::TabletRelease: | ||
| 175 | case QEvent::TabletEnterProximity: | ||
| 176 | case QEvent::TabletLeaveProximity: | ||
| 177 | case QEvent::TouchBegin: | ||
| 178 | case QEvent::TouchUpdate: | ||
| 179 | case QEvent::TouchEnd: | ||
| 180 | case QEvent::InputMethodQuery: | ||
| 181 | case QEvent::TouchCancel: | ||
| 182 | return QCoreApplication::sendEvent(event_handler, event); | ||
| 183 | case QEvent::Drop: | ||
| 184 | GetMainWindow()->DropAction(static_cast<QDropEvent*>(event)); | ||
| 185 | return true; | ||
| 186 | case QEvent::DragResponse: | ||
| 187 | case QEvent::DragEnter: | ||
| 188 | case QEvent::DragLeave: | ||
| 189 | case QEvent::DragMove: | ||
| 190 | GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event)); | ||
| 191 | return true; | ||
| 192 | default: | ||
| 193 | return QWindow::event(event); | ||
| 194 | } | ||
| 195 | } | 169 | } |
| 196 | 170 | ||
| 197 | void exposeEvent(QExposeEvent* event) override { | 171 | const QOpenGLContext* GetShareContext() const { |
| 198 | QWindow::requestUpdate(); | 172 | return context.get(); |
| 199 | QWindow::exposeEvent(event); | ||
| 200 | } | 173 | } |
| 201 | 174 | ||
| 202 | private: | 175 | private: |
| 203 | QWidget* event_handler{}; | 176 | // Avoid using Qt parent system here since we might move the QObjects to new threads |
| 177 | // As a note, this means we should avoid using slots/signals with the objects too | ||
| 178 | std::unique_ptr<QOpenGLContext> context; | ||
| 179 | std::unique_ptr<QOffscreenSurface> offscreen_surface{}; | ||
| 180 | QSurface* surface; | ||
| 181 | bool is_current = false; | ||
| 204 | }; | 182 | }; |
| 205 | 183 | ||
| 206 | class OpenGLWindow final : public ChildRenderWindow { | 184 | class DummyContext : public Core::Frontend::GraphicsContext {}; |
| 185 | |||
| 186 | class RenderWidget : public QWidget { | ||
| 207 | public: | 187 | public: |
| 208 | OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) | 188 | explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { |
| 209 | : ChildRenderWindow{parent, event_handler}, | 189 | setAttribute(Qt::WA_NativeWindow); |
| 210 | context(new QOpenGLContext(shared_context->parent())) { | 190 | setAttribute(Qt::WA_PaintOnScreen); |
| 191 | } | ||
| 211 | 192 | ||
| 212 | // disable vsync for any shared contexts | 193 | virtual ~RenderWidget() = default; |
| 213 | auto format = shared_context->format(); | ||
| 214 | format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); | ||
| 215 | this->setFormat(format); | ||
| 216 | 194 | ||
| 217 | context->setShareContext(shared_context); | 195 | /// Called on the UI thread when this Widget is ready to draw |
| 218 | context->setScreen(this->screen()); | 196 | /// Dervied classes can override this to draw the latest frame. |
| 219 | context->setFormat(format); | 197 | virtual void Present() {} |
| 220 | context->create(); | ||
| 221 | 198 | ||
| 222 | setSurfaceType(QWindow::OpenGLSurface); | 199 | void paintEvent(QPaintEvent* event) override { |
| 200 | Present(); | ||
| 201 | update(); | ||
| 202 | } | ||
| 223 | 203 | ||
| 224 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 204 | QPaintEngine* paintEngine() const override { |
| 225 | // WA_DontShowOnScreen, WA_DeleteOnClose | 205 | return nullptr; |
| 226 | } | 206 | } |
| 227 | 207 | ||
| 228 | ~OpenGLWindow() override { | 208 | private: |
| 229 | context->doneCurrent(); | 209 | GRenderWindow* render_window; |
| 210 | }; | ||
| 211 | |||
| 212 | class OpenGLRenderWidget : public RenderWidget { | ||
| 213 | public: | ||
| 214 | explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { | ||
| 215 | windowHandle()->setSurfaceType(QWindow::OpenGLSurface); | ||
| 216 | } | ||
| 217 | |||
| 218 | void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { | ||
| 219 | context = std::move(context_); | ||
| 230 | } | 220 | } |
| 231 | 221 | ||
| 232 | void Present() override { | 222 | void Present() override { |
| 233 | if (!isExposed()) { | 223 | if (!isVisible()) { |
| 234 | return; | 224 | return; |
| 235 | } | 225 | } |
| 236 | 226 | ||
| 237 | context->makeCurrent(this); | 227 | context->MakeCurrent(); |
| 238 | Core::System::GetInstance().Renderer().TryPresent(100); | 228 | if (Core::System::GetInstance().Renderer().TryPresent(100)) { |
| 239 | context->swapBuffers(this); | 229 | context->SwapBuffers(); |
| 240 | auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); | 230 | glFinish(); |
| 241 | f->glFinish(); | 231 | } |
| 242 | QWindow::requestUpdate(); | ||
| 243 | } | 232 | } |
| 244 | 233 | ||
| 245 | private: | 234 | private: |
| 246 | QOpenGLContext* context{}; | 235 | std::unique_ptr<Core::Frontend::GraphicsContext> context{}; |
| 247 | }; | 236 | }; |
| 248 | 237 | ||
| 249 | #ifdef HAS_VULKAN | 238 | #ifdef HAS_VULKAN |
| 250 | class VulkanWindow final : public ChildRenderWindow { | 239 | class VulkanRenderWidget : public RenderWidget { |
| 251 | public: | 240 | public: |
| 252 | VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) | 241 | explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { |
| 253 | : ChildRenderWindow{parent, event_handler} { | 242 | windowHandle()->setSurfaceType(QWindow::VulkanSurface); |
| 254 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | ||
| 255 | setVulkanInstance(instance); | ||
| 256 | } | 243 | } |
| 244 | }; | ||
| 245 | #endif | ||
| 257 | 246 | ||
| 258 | ~VulkanWindow() override = default; | 247 | static Core::Frontend::WindowSystemType GetWindowSystemType() { |
| 248 | // Determine WSI type based on Qt platform. | ||
| 249 | QString platform_name = QGuiApplication::platformName(); | ||
| 250 | if (platform_name == QStringLiteral("windows")) | ||
| 251 | return Core::Frontend::WindowSystemType::Windows; | ||
| 252 | else if (platform_name == QStringLiteral("xcb")) | ||
| 253 | return Core::Frontend::WindowSystemType::X11; | ||
| 254 | else if (platform_name == QStringLiteral("wayland")) | ||
| 255 | return Core::Frontend::WindowSystemType::Wayland; | ||
| 259 | 256 | ||
| 260 | void Present() override { | 257 | LOG_CRITICAL(Frontend, "Unknown Qt platform!"); |
| 261 | // TODO(bunnei): ImplementMe | 258 | return Core::Frontend::WindowSystemType::Windows; |
| 262 | } | 259 | } |
| 263 | 260 | ||
| 264 | private: | 261 | static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { |
| 265 | QWidget* event_handler{}; | 262 | Core::Frontend::EmuWindow::WindowSystemInfo wsi; |
| 266 | }; | 263 | wsi.type = GetWindowSystemType(); |
| 264 | |||
| 265 | #ifdef HAS_VULKAN | ||
| 266 | // Our Win32 Qt external doesn't have the private API. | ||
| 267 | #if defined(WIN32) || defined(__APPLE__) | ||
| 268 | wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; | ||
| 269 | #else | ||
| 270 | QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); | ||
| 271 | wsi.display_connection = pni->nativeResourceForWindow("display", window); | ||
| 272 | if (wsi.type == Core::Frontend::WindowSystemType::Wayland) | ||
| 273 | wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr; | ||
| 274 | else | ||
| 275 | wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr; | ||
| 276 | #endif | ||
| 277 | wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f; | ||
| 267 | #endif | 278 | #endif |
| 268 | 279 | ||
| 269 | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | 280 | return wsi; |
| 270 | : QWidget(parent_), emu_thread(emu_thread) { | 281 | } |
| 282 | |||
| 283 | GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_) | ||
| 284 | : QWidget(parent_), emu_thread(emu_thread_) { | ||
| 271 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 285 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 272 | .arg(QString::fromUtf8(Common::g_build_name), | 286 | .arg(QString::fromUtf8(Common::g_build_name), |
| 273 | QString::fromUtf8(Common::g_scm_branch), | 287 | QString::fromUtf8(Common::g_scm_branch), |
| @@ -278,26 +292,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | |||
| 278 | setLayout(layout); | 292 | setLayout(layout); |
| 279 | InputCommon::Init(); | 293 | InputCommon::Init(); |
| 280 | 294 | ||
| 281 | GMainWindow* parent = GetMainWindow(); | 295 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); |
| 282 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | ||
| 283 | } | 296 | } |
| 284 | 297 | ||
| 285 | GRenderWindow::~GRenderWindow() { | 298 | GRenderWindow::~GRenderWindow() { |
| 286 | InputCommon::Shutdown(); | 299 | InputCommon::Shutdown(); |
| 287 | } | 300 | } |
| 288 | 301 | ||
| 289 | void GRenderWindow::MakeCurrent() { | ||
| 290 | if (core_context) { | ||
| 291 | core_context->MakeCurrent(); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | void GRenderWindow::DoneCurrent() { | ||
| 296 | if (core_context) { | ||
| 297 | core_context->DoneCurrent(); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | void GRenderWindow::PollEvents() { | 302 | void GRenderWindow::PollEvents() { |
| 302 | if (!first_frame) { | 303 | if (!first_frame) { |
| 303 | first_frame = true; | 304 | first_frame = true; |
| @@ -309,21 +310,6 @@ bool GRenderWindow::IsShown() const { | |||
| 309 | return !isMinimized(); | 310 | return !isMinimized(); |
| 310 | } | 311 | } |
| 311 | 312 | ||
| 312 | void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 313 | void* surface) const { | ||
| 314 | #ifdef HAS_VULKAN | ||
| 315 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | ||
| 316 | const VkInstance instance_copy = vk_instance->vkInstance(); | ||
| 317 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window); | ||
| 318 | |||
| 319 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 320 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | ||
| 321 | std::memcpy(surface, &surface_copy, sizeof(surface_copy)); | ||
| 322 | #else | ||
| 323 | UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); | ||
| 324 | #endif | ||
| 325 | } | ||
| 326 | |||
| 327 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). | 313 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 328 | // | 314 | // |
| 329 | // Older versions get the window size (density independent pixels), | 315 | // Older versions get the window size (density independent pixels), |
| @@ -367,7 +353,7 @@ qreal GRenderWindow::windowPixelRatio() const { | |||
| 367 | return devicePixelRatio(); | 353 | return devicePixelRatio(); |
| 368 | } | 354 | } |
| 369 | 355 | ||
| 370 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 356 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { |
| 371 | const qreal pixel_ratio = windowPixelRatio(); | 357 | const qreal pixel_ratio = windowPixelRatio(); |
| 372 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 358 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 373 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 359 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| @@ -387,8 +373,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| 387 | } | 373 | } |
| 388 | 374 | ||
| 389 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 375 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| 390 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 376 | // touch input is handled in TouchBeginEvent |
| 391 | return; // touch input is handled in TouchBeginEvent | 377 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 378 | return; | ||
| 379 | } | ||
| 392 | 380 | ||
| 393 | auto pos = event->pos(); | 381 | auto pos = event->pos(); |
| 394 | if (event->button() == Qt::LeftButton) { | 382 | if (event->button() == Qt::LeftButton) { |
| @@ -400,8 +388,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { | |||
| 400 | } | 388 | } |
| 401 | 389 | ||
| 402 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | 390 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { |
| 403 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 391 | // touch input is handled in TouchUpdateEvent |
| 404 | return; // touch input is handled in TouchUpdateEvent | 392 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 393 | return; | ||
| 394 | } | ||
| 405 | 395 | ||
| 406 | auto pos = event->pos(); | 396 | auto pos = event->pos(); |
| 407 | const auto [x, y] = ScaleTouch(pos); | 397 | const auto [x, y] = ScaleTouch(pos); |
| @@ -410,13 +400,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 410 | } | 400 | } |
| 411 | 401 | ||
| 412 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 402 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| 413 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 403 | // touch input is handled in TouchEndEvent |
| 414 | return; // touch input is handled in TouchEndEvent | 404 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 405 | return; | ||
| 406 | } | ||
| 415 | 407 | ||
| 416 | if (event->button() == Qt::LeftButton) | 408 | if (event->button() == Qt::LeftButton) { |
| 417 | this->TouchReleased(); | 409 | this->TouchReleased(); |
| 418 | else if (event->button() == Qt::RightButton) | 410 | } else if (event->button() == Qt::RightButton) { |
| 419 | InputCommon::GetMotionEmu()->EndTilt(); | 411 | InputCommon::GetMotionEmu()->EndTilt(); |
| 412 | } | ||
| 420 | } | 413 | } |
| 421 | 414 | ||
| 422 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 415 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| @@ -474,9 +467,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | |||
| 474 | 467 | ||
| 475 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 468 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 476 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 469 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 477 | return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); | 470 | auto c = static_cast<OpenGLSharedContext*>(main_context.get()); |
| 471 | // Bind the shared contexts to the main surface in case the backend wants to take over | ||
| 472 | // presentation | ||
| 473 | return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), | ||
| 474 | child_widget->windowHandle()); | ||
| 478 | } | 475 | } |
| 479 | return {}; | 476 | return std::make_unique<DummyContext>(); |
| 480 | } | 477 | } |
| 481 | 478 | ||
| 482 | bool GRenderWindow::InitRenderTarget() { | 479 | bool GRenderWindow::InitRenderTarget() { |
| @@ -497,14 +494,14 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 497 | break; | 494 | break; |
| 498 | } | 495 | } |
| 499 | 496 | ||
| 497 | // Update the Window System information with the new render target | ||
| 498 | window_info = GetWindowSystemInfo(child_widget->windowHandle()); | ||
| 499 | |||
| 500 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 501 | layout()->addWidget(child_widget); | ||
| 500 | // Reset minimum required size to avoid resizing issues on the main window after restarting. | 502 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 501 | setMinimumSize(1, 1); | 503 | setMinimumSize(1, 1); |
| 502 | 504 | ||
| 503 | // Show causes the window to actually be created and the gl context as well, but we don't want | ||
| 504 | // the widget to be shown yet, so immediately hide it. | ||
| 505 | show(); | ||
| 506 | hide(); | ||
| 507 | |||
| 508 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | 505 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); |
| 509 | 506 | ||
| 510 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 507 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| @@ -523,9 +520,10 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 523 | void GRenderWindow::ReleaseRenderTarget() { | 520 | void GRenderWindow::ReleaseRenderTarget() { |
| 524 | if (child_widget) { | 521 | if (child_widget) { |
| 525 | layout()->removeWidget(child_widget); | 522 | layout()->removeWidget(child_widget); |
| 526 | delete child_widget; | 523 | child_widget->deleteLater(); |
| 527 | child_widget = nullptr; | 524 | child_widget = nullptr; |
| 528 | } | 525 | } |
| 526 | main_context.reset(); | ||
| 529 | } | 527 | } |
| 530 | 528 | ||
| 531 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 529 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| @@ -557,60 +555,23 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 557 | bool GRenderWindow::InitializeOpenGL() { | 555 | bool GRenderWindow::InitializeOpenGL() { |
| 558 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 556 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 559 | // WA_DontShowOnScreen, WA_DeleteOnClose | 557 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 560 | QSurfaceFormat fmt; | 558 | auto child = new OpenGLRenderWidget(this); |
| 561 | fmt.setVersion(4, 3); | 559 | child_widget = child; |
| 562 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | 560 | child_widget->windowHandle()->create(); |
| 563 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 561 | auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle()); |
| 564 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 562 | main_context = context; |
| 565 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 563 | child->SetContext( |
| 566 | fmt.setSwapInterval(0); | 564 | std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle())); |
| 567 | QSurfaceFormat::setDefaultFormat(fmt); | ||
| 568 | |||
| 569 | GMainWindow* parent = GetMainWindow(); | ||
| 570 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | ||
| 571 | child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); | ||
| 572 | child_window->create(); | ||
| 573 | child_widget = createWindowContainer(child_window, this); | ||
| 574 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 575 | layout()->addWidget(child_widget); | ||
| 576 | |||
| 577 | core_context = CreateSharedContext(); | ||
| 578 | 565 | ||
| 579 | return true; | 566 | return true; |
| 580 | } | 567 | } |
| 581 | 568 | ||
| 582 | bool GRenderWindow::InitializeVulkan() { | 569 | bool GRenderWindow::InitializeVulkan() { |
| 583 | #ifdef HAS_VULKAN | 570 | #ifdef HAS_VULKAN |
| 584 | vk_instance = std::make_unique<QVulkanInstance>(); | 571 | auto child = new VulkanRenderWidget(this); |
| 585 | vk_instance->setApiVersion(QVersionNumber(1, 1, 0)); | 572 | child_widget = child; |
| 586 | vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); | 573 | child_widget->windowHandle()->create(); |
| 587 | if (Settings::values.renderer_debug) { | 574 | main_context = std::make_unique<DummyContext>(); |
| 588 | const auto supported_layers{vk_instance->supportedLayers()}; | ||
| 589 | const bool found = | ||
| 590 | std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) { | ||
| 591 | constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation"; | ||
| 592 | return layer.name == searched_layer; | ||
| 593 | }); | ||
| 594 | if (found) { | ||
| 595 | vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); | ||
| 596 | vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 597 | } | ||
| 598 | } | ||
| 599 | if (!vk_instance->create()) { | ||
| 600 | QMessageBox::critical( | ||
| 601 | this, tr("Error while initializing Vulkan 1.1!"), | ||
| 602 | tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the " | ||
| 603 | "latest graphics drivers.")); | ||
| 604 | return false; | ||
| 605 | } | ||
| 606 | |||
| 607 | GMainWindow* parent = GetMainWindow(); | ||
| 608 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | ||
| 609 | child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); | ||
| 610 | child_window->create(); | ||
| 611 | child_widget = createWindowContainer(child_window, this); | ||
| 612 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 613 | layout()->addWidget(child_widget); | ||
| 614 | 575 | ||
| 615 | return true; | 576 | return true; |
| 616 | #else | 577 | #else |
| @@ -621,7 +582,8 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 621 | } | 582 | } |
| 622 | 583 | ||
| 623 | bool GRenderWindow::LoadOpenGL() { | 584 | bool GRenderWindow::LoadOpenGL() { |
| 624 | Core::Frontend::ScopeAcquireContext acquire_context{*this}; | 585 | auto context = CreateSharedContext(); |
| 586 | auto scope = context->Acquire(); | ||
| 625 | if (!gladLoadGL()) { | 587 | if (!gladLoadGL()) { |
| 626 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), | 588 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), |
| 627 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " | 589 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 79b030304..3626604ca 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -18,15 +18,10 @@ | |||
| 18 | #include "core/frontend/emu_window.h" | 18 | #include "core/frontend/emu_window.h" |
| 19 | 19 | ||
| 20 | class GRenderWindow; | 20 | class GRenderWindow; |
| 21 | class GMainWindow; | ||
| 21 | class QKeyEvent; | 22 | class QKeyEvent; |
| 22 | class QScreen; | ||
| 23 | class QTouchEvent; | 23 | class QTouchEvent; |
| 24 | class QStringList; | 24 | class QStringList; |
| 25 | class QSurface; | ||
| 26 | class QOpenGLContext; | ||
| 27 | #ifdef HAS_VULKAN | ||
| 28 | class QVulkanInstance; | ||
| 29 | #endif | ||
| 30 | 25 | ||
| 31 | namespace VideoCore { | 26 | namespace VideoCore { |
| 32 | enum class LoadCallbackStage; | 27 | enum class LoadCallbackStage; |
| @@ -36,7 +31,7 @@ class EmuThread final : public QThread { | |||
| 36 | Q_OBJECT | 31 | Q_OBJECT |
| 37 | 32 | ||
| 38 | public: | 33 | public: |
| 39 | explicit EmuThread(GRenderWindow& window); | 34 | explicit EmuThread(); |
| 40 | ~EmuThread() override; | 35 | ~EmuThread() override; |
| 41 | 36 | ||
| 42 | /** | 37 | /** |
| @@ -90,12 +85,6 @@ private: | |||
| 90 | std::mutex running_mutex; | 85 | std::mutex running_mutex; |
| 91 | std::condition_variable running_cv; | 86 | std::condition_variable running_cv; |
| 92 | 87 | ||
| 93 | /// Only used in asynchronous GPU mode | ||
| 94 | std::unique_ptr<Core::Frontend::GraphicsContext> shared_context; | ||
| 95 | |||
| 96 | /// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode | ||
| 97 | Core::Frontend::GraphicsContext& context; | ||
| 98 | |||
| 99 | signals: | 88 | signals: |
| 100 | /** | 89 | /** |
| 101 | * Emitted when the CPU has halted execution | 90 | * Emitted when the CPU has halted execution |
| @@ -124,16 +113,12 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | |||
| 124 | Q_OBJECT | 113 | Q_OBJECT |
| 125 | 114 | ||
| 126 | public: | 115 | public: |
| 127 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); | 116 | GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); |
| 128 | ~GRenderWindow() override; | 117 | ~GRenderWindow() override; |
| 129 | 118 | ||
| 130 | // EmuWindow implementation. | 119 | // EmuWindow implementation. |
| 131 | void MakeCurrent() override; | ||
| 132 | void DoneCurrent() override; | ||
| 133 | void PollEvents() override; | 120 | void PollEvents() override; |
| 134 | bool IsShown() const override; | 121 | bool IsShown() const override; |
| 135 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 136 | void* surface) const override; | ||
| 137 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 122 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 138 | 123 | ||
| 139 | void BackupGeometry(); | 124 | void BackupGeometry(); |
| @@ -165,6 +150,8 @@ public: | |||
| 165 | 150 | ||
| 166 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 151 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 167 | 152 | ||
| 153 | std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; | ||
| 154 | |||
| 168 | public slots: | 155 | public slots: |
| 169 | void OnEmulationStarting(EmuThread* emu_thread); | 156 | void OnEmulationStarting(EmuThread* emu_thread); |
| 170 | void OnEmulationStopping(); | 157 | void OnEmulationStopping(); |
| @@ -176,7 +163,6 @@ signals: | |||
| 176 | void FirstFrameDisplayed(); | 163 | void FirstFrameDisplayed(); |
| 177 | 164 | ||
| 178 | private: | 165 | private: |
| 179 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 180 | void TouchBeginEvent(const QTouchEvent* event); | 166 | void TouchBeginEvent(const QTouchEvent* event); |
| 181 | void TouchUpdateEvent(const QTouchEvent* event); | 167 | void TouchUpdateEvent(const QTouchEvent* event); |
| 182 | void TouchEndEvent(); | 168 | void TouchEndEvent(); |
| @@ -190,23 +176,16 @@ private: | |||
| 190 | 176 | ||
| 191 | EmuThread* emu_thread; | 177 | EmuThread* emu_thread; |
| 192 | 178 | ||
| 193 | std::unique_ptr<GraphicsContext> core_context; | 179 | // Main context that will be shared with all other contexts that are requested. |
| 194 | 180 | // If this is used in a shared context setting, then this should not be used directly, but | |
| 195 | #ifdef HAS_VULKAN | 181 | // should instead be shared from |
| 196 | std::unique_ptr<QVulkanInstance> vk_instance; | 182 | std::shared_ptr<Core::Frontend::GraphicsContext> main_context; |
| 197 | #endif | ||
| 198 | 183 | ||
| 199 | /// Temporary storage of the screenshot taken | 184 | /// Temporary storage of the screenshot taken |
| 200 | QImage screenshot_image; | 185 | QImage screenshot_image; |
| 201 | 186 | ||
| 202 | QByteArray geometry; | 187 | QByteArray geometry; |
| 203 | 188 | ||
| 204 | /// Native window handle that backs this presentation widget | ||
| 205 | QWindow* child_window = nullptr; | ||
| 206 | |||
| 207 | /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to | ||
| 208 | /// put the child_window into a widget then add it to the layout. This child_widget can be | ||
| 209 | /// parented to GRenderWindow and use Qt's lifetime system | ||
| 210 | QWidget* child_widget = nullptr; | 189 | QWidget* child_widget = nullptr; |
| 211 | 190 | ||
| 212 | bool first_frame = false; | 191 | bool first_frame = false; |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index a821c7b3c..ea667caef 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -15,6 +15,10 @@ | |||
| 15 | #include "ui_configure_graphics.h" | 15 | #include "ui_configure_graphics.h" |
| 16 | #include "yuzu/configuration/configure_graphics.h" | 16 | #include "yuzu/configuration/configure_graphics.h" |
| 17 | 17 | ||
| 18 | #ifdef HAS_VULKAN | ||
| 19 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||
| 20 | #endif | ||
| 21 | |||
| 18 | namespace { | 22 | namespace { |
| 19 | enum class Resolution : int { | 23 | enum class Resolution : int { |
| 20 | Auto, | 24 | Auto, |
| @@ -165,41 +169,9 @@ void ConfigureGraphics::UpdateDeviceComboBox() { | |||
| 165 | 169 | ||
| 166 | void ConfigureGraphics::RetrieveVulkanDevices() { | 170 | void ConfigureGraphics::RetrieveVulkanDevices() { |
| 167 | #ifdef HAS_VULKAN | 171 | #ifdef HAS_VULKAN |
| 168 | QVulkanInstance instance; | 172 | vulkan_devices.clear(); |
| 169 | instance.setApiVersion(QVersionNumber(1, 1, 0)); | 173 | for (auto& name : Vulkan::RendererVulkan::EnumerateDevices()) { |
| 170 | if (!instance.create()) { | 174 | vulkan_devices.push_back(QString::fromStdString(name)); |
| 171 | LOG_INFO(Frontend, "Vulkan 1.1 not available"); | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>( | ||
| 175 | instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))}; | ||
| 176 | if (vkEnumeratePhysicalDevices == nullptr) { | ||
| 177 | LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices"); | ||
| 178 | return; | ||
| 179 | } | ||
| 180 | u32 physical_device_count; | ||
| 181 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) != | ||
| 182 | VK_SUCCESS) { | ||
| 183 | LOG_INFO(Frontend, "Failed to get physical devices count"); | ||
| 184 | return; | ||
| 185 | } | ||
| 186 | std::vector<VkPhysicalDevice> physical_devices(physical_device_count); | ||
| 187 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, | ||
| 188 | physical_devices.data()) != VK_SUCCESS) { | ||
| 189 | LOG_INFO(Frontend, "Failed to get physical devices"); | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | |||
| 193 | const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>( | ||
| 194 | instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))}; | ||
| 195 | if (vkGetPhysicalDeviceProperties == nullptr) { | ||
| 196 | LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties"); | ||
| 197 | return; | ||
| 198 | } | ||
| 199 | for (const auto physical_device : physical_devices) { | ||
| 200 | VkPhysicalDeviceProperties properties; | ||
| 201 | vkGetPhysicalDeviceProperties(physical_device, &properties); | ||
| 202 | vulkan_devices.push_back(QString::fromUtf8(properties.deviceName)); | ||
| 203 | } | 175 | } |
| 204 | #endif | 176 | #endif |
| 205 | } | 177 | } |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 96dec50e2..15ac30f12 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -541,18 +541,19 @@ void ConfigureInputPlayer::HandleClick( | |||
| 541 | button->setText(tr("[press key]")); | 541 | button->setText(tr("[press key]")); |
| 542 | button->setFocus(); | 542 | button->setFocus(); |
| 543 | 543 | ||
| 544 | const auto iter = std::find(button_map.begin(), button_map.end(), button); | 544 | // Keyboard keys can only be used as button devices |
| 545 | ASSERT(iter != button_map.end()); | 545 | want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; |
| 546 | const auto index = std::distance(button_map.begin(), iter); | 546 | if (want_keyboard_keys) { |
| 547 | ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); | 547 | const auto iter = std::find(button_map.begin(), button_map.end(), button); |
| 548 | ASSERT(iter != button_map.end()); | ||
| 549 | const auto index = std::distance(button_map.begin(), iter); | ||
| 550 | ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); | ||
| 551 | } | ||
| 548 | 552 | ||
| 549 | input_setter = new_input_setter; | 553 | input_setter = new_input_setter; |
| 550 | 554 | ||
| 551 | device_pollers = InputCommon::Polling::GetPollers(type); | 555 | device_pollers = InputCommon::Polling::GetPollers(type); |
| 552 | 556 | ||
| 553 | // Keyboard keys can only be used as button devices | ||
| 554 | want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; | ||
| 555 | |||
| 556 | for (auto& poller : device_pollers) { | 557 | for (auto& poller : device_pollers) { |
| 557 | poller->Start(); | 558 | poller->Start(); |
| 558 | } | 559 | } |
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 1556481d0..4b37746a1 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -839,21 +839,21 @@ | |||
| 839 | <bool>false</bool> | 839 | <bool>false</bool> |
| 840 | </property> | 840 | </property> |
| 841 | <layout class="QGridLayout" name="gridLayout_3"> | 841 | <layout class="QGridLayout" name="gridLayout_3"> |
| 842 | <item row="3" column="0"> | 842 | <item row="0" column="0"> |
| 843 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> | 843 | <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> |
| 844 | <item> | 844 | <item> |
| 845 | <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout"> | 845 | <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout"> |
| 846 | <item> | 846 | <item> |
| 847 | <widget class="QLabel" name="labelSL"> | 847 | <widget class="QLabel" name="labelL"> |
| 848 | <property name="text"> | 848 | <property name="text"> |
| 849 | <string>SL:</string> | 849 | <string>L:</string> |
| 850 | </property> | 850 | </property> |
| 851 | </widget> | 851 | </widget> |
| 852 | </item> | 852 | </item> |
| 853 | </layout> | 853 | </layout> |
| 854 | </item> | 854 | </item> |
| 855 | <item> | 855 | <item> |
| 856 | <widget class="QPushButton" name="buttonSL"> | 856 | <widget class="QPushButton" name="buttonL"> |
| 857 | <property name="text"> | 857 | <property name="text"> |
| 858 | <string/> | 858 | <string/> |
| 859 | </property> | 859 | </property> |
| @@ -861,21 +861,21 @@ | |||
| 861 | </item> | 861 | </item> |
| 862 | </layout> | 862 | </layout> |
| 863 | </item> | 863 | </item> |
| 864 | <item row="2" column="1"> | 864 | <item row="0" column="1"> |
| 865 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> | 865 | <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> |
| 866 | <item> | 866 | <item> |
| 867 | <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout"> | 867 | <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> |
| 868 | <item> | 868 | <item> |
| 869 | <widget class="QLabel" name="labelZR"> | 869 | <widget class="QLabel" name="labelR"> |
| 870 | <property name="text"> | 870 | <property name="text"> |
| 871 | <string>ZR:</string> | 871 | <string>R:</string> |
| 872 | </property> | 872 | </property> |
| 873 | </widget> | 873 | </widget> |
| 874 | </item> | 874 | </item> |
| 875 | </layout> | 875 | </layout> |
| 876 | </item> | 876 | </item> |
| 877 | <item> | 877 | <item> |
| 878 | <widget class="QPushButton" name="buttonZR"> | 878 | <widget class="QPushButton" name="buttonR"> |
| 879 | <property name="text"> | 879 | <property name="text"> |
| 880 | <string/> | 880 | <string/> |
| 881 | </property> | 881 | </property> |
| @@ -883,21 +883,21 @@ | |||
| 883 | </item> | 883 | </item> |
| 884 | </layout> | 884 | </layout> |
| 885 | </item> | 885 | </item> |
| 886 | <item row="3" column="1"> | 886 | <item row="1" column="0"> |
| 887 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> | 887 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> |
| 888 | <item> | 888 | <item> |
| 889 | <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout"> | 889 | <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout"> |
| 890 | <item> | 890 | <item> |
| 891 | <widget class="QLabel" name="labelSR"> | 891 | <widget class="QLabel" name="labelZL"> |
| 892 | <property name="text"> | 892 | <property name="text"> |
| 893 | <string>SR:</string> | 893 | <string>ZL:</string> |
| 894 | </property> | 894 | </property> |
| 895 | </widget> | 895 | </widget> |
| 896 | </item> | 896 | </item> |
| 897 | </layout> | 897 | </layout> |
| 898 | </item> | 898 | </item> |
| 899 | <item> | 899 | <item> |
| 900 | <widget class="QPushButton" name="buttonSR"> | 900 | <widget class="QPushButton" name="buttonZL"> |
| 901 | <property name="text"> | 901 | <property name="text"> |
| 902 | <string/> | 902 | <string/> |
| 903 | </property> | 903 | </property> |
| @@ -905,21 +905,21 @@ | |||
| 905 | </item> | 905 | </item> |
| 906 | </layout> | 906 | </layout> |
| 907 | </item> | 907 | </item> |
| 908 | <item row="0" column="1"> | 908 | <item row="1" column="1"> |
| 909 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> | 909 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> |
| 910 | <item> | 910 | <item> |
| 911 | <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout"> | 911 | <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout"> |
| 912 | <item> | 912 | <item> |
| 913 | <widget class="QLabel" name="labelZL"> | 913 | <widget class="QLabel" name="labelZR"> |
| 914 | <property name="text"> | 914 | <property name="text"> |
| 915 | <string>ZL:</string> | 915 | <string>ZR:</string> |
| 916 | </property> | 916 | </property> |
| 917 | </widget> | 917 | </widget> |
| 918 | </item> | 918 | </item> |
| 919 | </layout> | 919 | </layout> |
| 920 | </item> | 920 | </item> |
| 921 | <item> | 921 | <item> |
| 922 | <widget class="QPushButton" name="buttonZL"> | 922 | <widget class="QPushButton" name="buttonZR"> |
| 923 | <property name="text"> | 923 | <property name="text"> |
| 924 | <string/> | 924 | <string/> |
| 925 | </property> | 925 | </property> |
| @@ -927,21 +927,21 @@ | |||
| 927 | </item> | 927 | </item> |
| 928 | </layout> | 928 | </layout> |
| 929 | </item> | 929 | </item> |
| 930 | <item row="0" column="0"> | 930 | <item row="0" column="2"> |
| 931 | <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> | 931 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> |
| 932 | <item> | 932 | <item> |
| 933 | <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout"> | 933 | <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout"> |
| 934 | <item> | 934 | <item> |
| 935 | <widget class="QLabel" name="labelL"> | 935 | <widget class="QLabel" name="labelSL"> |
| 936 | <property name="text"> | 936 | <property name="text"> |
| 937 | <string>L:</string> | 937 | <string>SL:</string> |
| 938 | </property> | 938 | </property> |
| 939 | </widget> | 939 | </widget> |
| 940 | </item> | 940 | </item> |
| 941 | </layout> | 941 | </layout> |
| 942 | </item> | 942 | </item> |
| 943 | <item> | 943 | <item> |
| 944 | <widget class="QPushButton" name="buttonL"> | 944 | <widget class="QPushButton" name="buttonSL"> |
| 945 | <property name="text"> | 945 | <property name="text"> |
| 946 | <string/> | 946 | <string/> |
| 947 | </property> | 947 | </property> |
| @@ -949,21 +949,21 @@ | |||
| 949 | </item> | 949 | </item> |
| 950 | </layout> | 950 | </layout> |
| 951 | </item> | 951 | </item> |
| 952 | <item row="2" column="0"> | 952 | <item row="1" column="2"> |
| 953 | <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> | 953 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> |
| 954 | <item> | 954 | <item> |
| 955 | <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> | 955 | <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout"> |
| 956 | <item> | 956 | <item> |
| 957 | <widget class="QLabel" name="labelR"> | 957 | <widget class="QLabel" name="labelSR"> |
| 958 | <property name="text"> | 958 | <property name="text"> |
| 959 | <string>R:</string> | 959 | <string>SR:</string> |
| 960 | </property> | 960 | </property> |
| 961 | </widget> | 961 | </widget> |
| 962 | </item> | 962 | </item> |
| 963 | </layout> | 963 | </layout> |
| 964 | </item> | 964 | </item> |
| 965 | <item> | 965 | <item> |
| 966 | <widget class="QPushButton" name="buttonR"> | 966 | <widget class="QPushButton" name="buttonSR"> |
| 967 | <property name="text"> | 967 | <property name="text"> |
| 968 | <string/> | 968 | <string/> |
| 969 | </property> | 969 | </property> |
diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp index ab3a11d30..0e0e8f113 100644 --- a/src/yuzu/configuration/configure_input_simple.cpp +++ b/src/yuzu/configuration/configure_input_simple.cpp | |||
| @@ -35,6 +35,7 @@ void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) { | |||
| 35 | // - Open any dialogs | 35 | // - Open any dialogs |
| 36 | // - Block in any way | 36 | // - Block in any way |
| 37 | 37 | ||
| 38 | constexpr std::size_t PLAYER_0_INDEX = 0; | ||
| 38 | constexpr std::size_t HANDHELD_INDEX = 8; | 39 | constexpr std::size_t HANDHELD_INDEX = 8; |
| 39 | 40 | ||
| 40 | void HandheldOnProfileSelect() { | 41 | void HandheldOnProfileSelect() { |
| @@ -53,8 +54,8 @@ void HandheldOnProfileSelect() { | |||
| 53 | } | 54 | } |
| 54 | 55 | ||
| 55 | void DualJoyconsDockedOnProfileSelect() { | 56 | void DualJoyconsDockedOnProfileSelect() { |
| 56 | Settings::values.players[0].connected = true; | 57 | Settings::values.players[PLAYER_0_INDEX].connected = true; |
| 57 | Settings::values.players[0].type = Settings::ControllerType::DualJoycon; | 58 | Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon; |
| 58 | 59 | ||
| 59 | for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) { | 60 | for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) { |
| 60 | Settings::values.players[player].connected = false; | 61 | Settings::values.players[player].connected = false; |
| @@ -64,7 +65,7 @@ void DualJoyconsDockedOnProfileSelect() { | |||
| 64 | Settings::values.keyboard_enabled = false; | 65 | Settings::values.keyboard_enabled = false; |
| 65 | Settings::values.mouse_enabled = false; | 66 | Settings::values.mouse_enabled = false; |
| 66 | Settings::values.debug_pad_enabled = false; | 67 | Settings::values.debug_pad_enabled = false; |
| 67 | Settings::values.touchscreen.enabled = false; | 68 | Settings::values.touchscreen.enabled = true; |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | // Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure | 71 | // Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure |
| @@ -78,7 +79,7 @@ constexpr std::array<InputProfile, 3> INPUT_PROFILES{{ | |||
| 78 | }}, | 79 | }}, |
| 79 | {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect, | 80 | {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect, |
| 80 | [](ConfigureInputSimple* caller) { | 81 | [](ConfigureInputSimple* caller) { |
| 81 | CallConfigureDialog<ConfigureInputPlayer>(caller, 1, false); | 82 | CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false); |
| 82 | }}, | 83 | }}, |
| 83 | {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>}, | 84 | {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>}, |
| 84 | }}; | 85 | }}; |
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index 0a4abe34f..e0647ea5b 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp | |||
| @@ -184,18 +184,19 @@ void ConfigureMouseAdvanced::HandleClick( | |||
| 184 | button->setText(tr("[press key]")); | 184 | button->setText(tr("[press key]")); |
| 185 | button->setFocus(); | 185 | button->setFocus(); |
| 186 | 186 | ||
| 187 | const auto iter = std::find(button_map.begin(), button_map.end(), button); | 187 | // Keyboard keys can only be used as button devices |
| 188 | ASSERT(iter != button_map.end()); | 188 | want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; |
| 189 | const auto index = std::distance(button_map.begin(), iter); | 189 | if (want_keyboard_keys) { |
| 190 | ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); | 190 | const auto iter = std::find(button_map.begin(), button_map.end(), button); |
| 191 | ASSERT(iter != button_map.end()); | ||
| 192 | const auto index = std::distance(button_map.begin(), iter); | ||
| 193 | ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); | ||
| 194 | } | ||
| 191 | 195 | ||
| 192 | input_setter = new_input_setter; | 196 | input_setter = new_input_setter; |
| 193 | 197 | ||
| 194 | device_pollers = InputCommon::Polling::GetPollers(type); | 198 | device_pollers = InputCommon::Polling::GetPollers(type); |
| 195 | 199 | ||
| 196 | // Keyboard keys can only be used as button devices | ||
| 197 | want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; | ||
| 198 | |||
| 199 | for (auto& poller : device_pollers) { | 200 | for (auto& poller : device_pollers) { |
| 200 | poller->Start(); | 201 | poller->Start(); |
| 201 | } | 202 | } |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index a2b88c787..dccbabcbf 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -315,7 +315,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide | |||
| 315 | item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type")); | 315 | item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type")); |
| 316 | item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size")); | 316 | item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size")); |
| 317 | } | 317 | } |
| 318 | item_model->setSortRole(GameListItemPath::TitleRole); | 318 | item_model->setSortRole(GameListItemPath::SortRole); |
| 319 | 319 | ||
| 320 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons); | 320 | connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons); |
| 321 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); | 321 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); |
| @@ -441,6 +441,8 @@ void GameList::DonePopulating(QStringList watch_list) { | |||
| 441 | if (children_total > 0) { | 441 | if (children_total > 0) { |
| 442 | search_field->setFocus(); | 442 | search_field->setFocus(); |
| 443 | } | 443 | } |
| 444 | item_model->sort(tree_view->header()->sortIndicatorSection(), | ||
| 445 | tree_view->header()->sortIndicatorOrder()); | ||
| 444 | } | 446 | } |
| 445 | 447 | ||
| 446 | void GameList::PopupContextMenu(const QPoint& menu_location) { | 448 | void GameList::PopupContextMenu(const QPoint& menu_location) { |
| @@ -666,8 +668,6 @@ void GameList::LoadInterfaceLayout() { | |||
| 666 | // so make it as large as possible as default. | 668 | // so make it as large as possible as default. |
| 667 | header->resizeSection(COLUMN_NAME, header->width()); | 669 | header->resizeSection(COLUMN_NAME, header->width()); |
| 668 | } | 670 | } |
| 669 | |||
| 670 | item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); | ||
| 671 | } | 671 | } |
| 672 | 672 | ||
| 673 | const QStringList GameList::supported_file_extensions = { | 673 | const QStringList GameList::supported_file_extensions = { |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 7cde72d1b..3e6d5a7cd 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -65,10 +65,10 @@ public: | |||
| 65 | */ | 65 | */ |
| 66 | class GameListItemPath : public GameListItem { | 66 | class GameListItemPath : public GameListItem { |
| 67 | public: | 67 | public: |
| 68 | static const int TitleRole = SortRole; | 68 | static const int TitleRole = SortRole + 1; |
| 69 | static const int FullPathRole = SortRole + 1; | 69 | static const int FullPathRole = SortRole + 2; |
| 70 | static const int ProgramIdRole = SortRole + 2; | 70 | static const int ProgramIdRole = SortRole + 3; |
| 71 | static const int FileTypeRole = SortRole + 3; | 71 | static const int FileTypeRole = SortRole + 4; |
| 72 | 72 | ||
| 73 | GameListItemPath() = default; | 73 | GameListItemPath() = default; |
| 74 | GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data, | 74 | GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data, |
| @@ -95,7 +95,7 @@ public: | |||
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | QVariant data(int role) const override { | 97 | QVariant data(int role) const override { |
| 98 | if (role == Qt::DisplayRole) { | 98 | if (role == Qt::DisplayRole || role == SortRole) { |
| 99 | std::string filename; | 99 | std::string filename; |
| 100 | Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, | 100 | Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, |
| 101 | nullptr); | 101 | nullptr); |
| @@ -110,6 +110,9 @@ public: | |||
| 110 | const auto& row1 = row_data.at(UISettings::values.row_1_text_id); | 110 | const auto& row1 = row_data.at(UISettings::values.row_1_text_id); |
| 111 | const int row2_id = UISettings::values.row_2_text_id; | 111 | const int row2_id = UISettings::values.row_2_text_id; |
| 112 | 112 | ||
| 113 | if (role == SortRole) | ||
| 114 | return row1.toLower(); | ||
| 115 | |||
| 113 | if (row2_id == 4) // None | 116 | if (row2_id == 4) // None |
| 114 | return row1; | 117 | return row1; |
| 115 | 118 | ||
| @@ -123,6 +126,13 @@ public: | |||
| 123 | 126 | ||
| 124 | return GameListItem::data(role); | 127 | return GameListItem::data(role); |
| 125 | } | 128 | } |
| 129 | |||
| 130 | /** | ||
| 131 | * Override to prevent automatic sorting. | ||
| 132 | */ | ||
| 133 | bool operator<(const QStandardItem& other) const override { | ||
| 134 | return false; | ||
| 135 | } | ||
| 126 | }; | 136 | }; |
| 127 | 137 | ||
| 128 | class GameListItemCompat : public GameListItem { | 138 | class GameListItemCompat : public GameListItem { |
| @@ -289,6 +299,10 @@ public: | |||
| 289 | int type() const override { | 299 | int type() const override { |
| 290 | return static_cast<int>(GameListItemType::AddDir); | 300 | return static_cast<int>(GameListItemType::AddDir); |
| 291 | } | 301 | } |
| 302 | |||
| 303 | bool operator<(const QStandardItem& other) const override { | ||
| 304 | return false; | ||
| 305 | } | ||
| 292 | }; | 306 | }; |
| 293 | 307 | ||
| 294 | class GameList; | 308 | class GameList; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d7e59d0cd..1717e06f9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -205,7 +205,13 @@ GMainWindow::GMainWindow() | |||
| 205 | ConnectMenuEvents(); | 205 | ConnectMenuEvents(); |
| 206 | ConnectWidgetEvents(); | 206 | ConnectWidgetEvents(); |
| 207 | 207 | ||
| 208 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | 208 | const auto build_id = std::string(Common::g_build_id); |
| 209 | const auto fmt = std::string(Common::g_title_bar_format_idle); | ||
| 210 | const auto yuzu_build_version = | ||
| 211 | fmt::format(fmt.empty() ? "yuzu Development Build" : fmt, std::string{}, std::string{}, | ||
| 212 | std::string{}, std::string{}, std::string{}, build_id); | ||
| 213 | |||
| 214 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch, | ||
| 209 | Common::g_scm_desc); | 215 | Common::g_scm_desc); |
| 210 | #ifdef ARCHITECTURE_x86_64 | 216 | #ifdef ARCHITECTURE_x86_64 |
| 211 | LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); | 217 | LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); |
| @@ -984,7 +990,7 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 984 | return; | 990 | return; |
| 985 | 991 | ||
| 986 | // Create and start the emulation thread | 992 | // Create and start the emulation thread |
| 987 | emu_thread = std::make_unique<EmuThread>(*render_window); | 993 | emu_thread = std::make_unique<EmuThread>(); |
| 988 | emit EmulationStarting(emu_thread.get()); | 994 | emit EmulationStarting(emu_thread.get()); |
| 989 | emu_thread->start(); | 995 | emu_thread->start(); |
| 990 | 996 | ||
| @@ -2378,7 +2384,6 @@ int main(int argc, char* argv[]) { | |||
| 2378 | 2384 | ||
| 2379 | // Enables the core to make the qt created contexts current on std::threads | 2385 | // Enables the core to make the qt created contexts current on std::threads |
| 2380 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2386 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 2381 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); | ||
| 2382 | QApplication app(argc, argv); | 2387 | QApplication app(argc, argv); |
| 2383 | 2388 | ||
| 2384 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2389 | // Qt changes the locale and causes issues in float conversion using std::to_string() when |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index c0d373477..411e7e647 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -37,16 +37,24 @@ public: | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void MakeCurrent() override { | 39 | void MakeCurrent() override { |
| 40 | SDL_GL_MakeCurrent(window, context); | 40 | if (is_current) { |
| 41 | return; | ||
| 42 | } | ||
| 43 | is_current = SDL_GL_MakeCurrent(window, context) == 0; | ||
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | void DoneCurrent() override { | 46 | void DoneCurrent() override { |
| 47 | if (!is_current) { | ||
| 48 | return; | ||
| 49 | } | ||
| 44 | SDL_GL_MakeCurrent(window, nullptr); | 50 | SDL_GL_MakeCurrent(window, nullptr); |
| 51 | is_current = false; | ||
| 45 | } | 52 | } |
| 46 | 53 | ||
| 47 | private: | 54 | private: |
| 48 | SDL_Window* window; | 55 | SDL_Window* window; |
| 49 | SDL_GLContext context; | 56 | SDL_GLContext context; |
| 57 | bool is_current = false; | ||
| 50 | }; | 58 | }; |
| 51 | 59 | ||
| 52 | bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { | 60 | bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { |
| @@ -148,20 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | |||
| 148 | SDL_GL_DeleteContext(window_context); | 156 | SDL_GL_DeleteContext(window_context); |
| 149 | } | 157 | } |
| 150 | 158 | ||
| 151 | void EmuWindow_SDL2_GL::MakeCurrent() { | ||
| 152 | core_context->MakeCurrent(); | ||
| 153 | } | ||
| 154 | |||
| 155 | void EmuWindow_SDL2_GL::DoneCurrent() { | ||
| 156 | core_context->DoneCurrent(); | ||
| 157 | } | ||
| 158 | |||
| 159 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 160 | void* surface) const { | ||
| 161 | // Should not have been called from OpenGL | ||
| 162 | UNREACHABLE(); | ||
| 163 | } | ||
| 164 | |||
| 165 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | 159 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { |
| 166 | return std::make_unique<SDLGLContext>(); | 160 | return std::make_unique<SDLGLContext>(); |
| 167 | } | 161 | } |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h index b80669ff0..48bb41683 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -13,14 +13,8 @@ public: | |||
| 13 | explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); | 13 | explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_GL(); | 14 | ~EmuWindow_SDL2_GL(); |
| 15 | 15 | ||
| 16 | void MakeCurrent() override; | ||
| 17 | void DoneCurrent() override; | ||
| 18 | void Present() override; | 16 | void Present() override; |
| 19 | 17 | ||
| 20 | /// Ignored in OpenGL | ||
| 21 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 22 | void* surface) const override; | ||
| 23 | |||
| 24 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 18 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 25 | 19 | ||
| 26 | private: | 20 | private: |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index abcc58165..f2990910e 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -2,102 +2,62 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <cstdlib> |
| 6 | #include <memory> | ||
| 6 | #include <string> | 7 | #include <string> |
| 7 | #include <vector> | 8 | |
| 8 | #include <SDL.h> | ||
| 9 | #include <SDL_vulkan.h> | ||
| 10 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| 11 | #include <vulkan/vulkan.h> | 10 | |
| 12 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 13 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 14 | #include "common/scm_rev.h" | 13 | #include "common/scm_rev.h" |
| 15 | #include "core/settings.h" | 14 | #include "core/settings.h" |
| 15 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||
| 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" | 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" |
| 17 | 17 | ||
| 18 | // Include these late to avoid polluting everything with Xlib macros | ||
| 19 | #include <SDL.h> | ||
| 20 | #include <SDL_syswm.h> | ||
| 21 | |||
| 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) | 22 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) |
| 19 | : EmuWindow_SDL2{system, fullscreen} { | 23 | : EmuWindow_SDL2{system, fullscreen} { |
| 20 | if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { | ||
| 21 | LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); | ||
| 22 | exit(EXIT_FAILURE); | ||
| 23 | } | ||
| 24 | |||
| 25 | vkGetInstanceProcAddr = | ||
| 26 | reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr()); | ||
| 27 | if (vkGetInstanceProcAddr == nullptr) { | ||
| 28 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 29 | exit(EXIT_FAILURE); | ||
| 30 | } | ||
| 31 | |||
| 32 | const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, | 24 | const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, |
| 33 | Common::g_scm_branch, Common::g_scm_desc); | 25 | Common::g_scm_branch, Common::g_scm_desc); |
| 34 | render_window = | 26 | render_window = |
| 35 | SDL_CreateWindow(window_title.c_str(), | 27 | SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, |
| 36 | SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 37 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 38 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | 28 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, |
| 39 | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN); | 29 | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |
| 40 | |||
| 41 | const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr); | ||
| 42 | |||
| 43 | u32 extra_ext_count{}; | ||
| 44 | if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) { | ||
| 45 | LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}", | ||
| 46 | SDL_GetError()); | ||
| 47 | exit(1); | ||
| 48 | } | ||
| 49 | |||
| 50 | auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count); | ||
| 51 | if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) { | ||
| 52 | LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError()); | ||
| 53 | exit(1); | ||
| 54 | } | ||
| 55 | std::vector<const char*> enabled_extensions; | ||
| 56 | enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(), | ||
| 57 | extra_ext_names.get() + extra_ext_count); | ||
| 58 | |||
| 59 | std::vector<const char*> enabled_layers; | ||
| 60 | if (use_standard_layers) { | ||
| 61 | enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 62 | enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation"); | ||
| 63 | } | ||
| 64 | |||
| 65 | VkApplicationInfo app_info{}; | ||
| 66 | app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | ||
| 67 | app_info.apiVersion = VK_API_VERSION_1_1; | ||
| 68 | app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 69 | app_info.pApplicationName = "yuzu-emu"; | ||
| 70 | app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 71 | app_info.pEngineName = "yuzu-emu"; | ||
| 72 | |||
| 73 | VkInstanceCreateInfo instance_ci{}; | ||
| 74 | instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | ||
| 75 | instance_ci.pApplicationInfo = &app_info; | ||
| 76 | instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size()); | ||
| 77 | instance_ci.ppEnabledExtensionNames = enabled_extensions.data(); | ||
| 78 | if (Settings::values.renderer_debug) { | ||
| 79 | instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size()); | ||
| 80 | instance_ci.ppEnabledLayerNames = enabled_layers.data(); | ||
| 81 | } | ||
| 82 | 30 | ||
| 83 | const auto vkCreateInstance = | 31 | SDL_SysWMinfo wm; |
| 84 | reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance")); | 32 | if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { |
| 85 | if (vkCreateInstance == nullptr || | 33 | LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); |
| 86 | vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) { | 34 | std::exit(EXIT_FAILURE); |
| 87 | LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!"); | ||
| 88 | exit(EXIT_FAILURE); | ||
| 89 | } | 35 | } |
| 90 | 36 | ||
| 91 | vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>( | 37 | switch (wm.subsystem) { |
| 92 | vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance")); | 38 | #ifdef SDL_VIDEO_DRIVER_WINDOWS |
| 93 | if (vkDestroyInstance == nullptr) { | 39 | case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS: |
| 94 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | 40 | window_info.type = Core::Frontend::WindowSystemType::Windows; |
| 95 | exit(EXIT_FAILURE); | 41 | window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window); |
| 96 | } | 42 | break; |
| 97 | 43 | #endif | |
| 98 | if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) { | 44 | #ifdef SDL_VIDEO_DRIVER_X11 |
| 99 | LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError()); | 45 | case SDL_SYSWM_TYPE::SDL_SYSWM_X11: |
| 100 | exit(EXIT_FAILURE); | 46 | window_info.type = Core::Frontend::WindowSystemType::X11; |
| 47 | window_info.display_connection = wm.info.x11.display; | ||
| 48 | window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window); | ||
| 49 | break; | ||
| 50 | #endif | ||
| 51 | #ifdef SDL_VIDEO_DRIVER_WAYLAND | ||
| 52 | case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: | ||
| 53 | window_info.type = Core::Frontend::WindowSystemType::Wayland; | ||
| 54 | window_info.display_connection = wm.info.wl.display; | ||
| 55 | window_info.render_surface = wm.info.wl.surface; | ||
| 56 | break; | ||
| 57 | #endif | ||
| 58 | default: | ||
| 59 | LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); | ||
| 60 | std::exit(EXIT_FAILURE); | ||
| 101 | } | 61 | } |
| 102 | 62 | ||
| 103 | OnResize(); | 63 | OnResize(); |
| @@ -107,59 +67,12 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) | |||
| 107 | Common::g_scm_branch, Common::g_scm_desc); | 67 | Common::g_scm_branch, Common::g_scm_desc); |
| 108 | } | 68 | } |
| 109 | 69 | ||
| 110 | EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { | 70 | EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; |
| 111 | vkDestroyInstance(vk_instance, nullptr); | ||
| 112 | } | ||
| 113 | |||
| 114 | void EmuWindow_SDL2_VK::MakeCurrent() { | ||
| 115 | // Unused on Vulkan | ||
| 116 | } | ||
| 117 | |||
| 118 | void EmuWindow_SDL2_VK::DoneCurrent() { | ||
| 119 | // Unused on Vulkan | ||
| 120 | } | ||
| 121 | |||
| 122 | void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 123 | void* surface) const { | ||
| 124 | const auto instance_proc_addr = vkGetInstanceProcAddr; | ||
| 125 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 126 | std::memcpy(instance, &vk_instance, sizeof(vk_instance)); | ||
| 127 | std::memcpy(surface, &vk_surface, sizeof(vk_surface)); | ||
| 128 | } | ||
| 129 | 71 | ||
| 130 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { | 72 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { |
| 131 | return nullptr; | 73 | return nullptr; |
| 132 | } | 74 | } |
| 133 | 75 | ||
| 134 | bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const { | ||
| 135 | if (!Settings::values.renderer_debug) { | ||
| 136 | return false; | ||
| 137 | } | ||
| 138 | |||
| 139 | const auto vkEnumerateInstanceLayerProperties = | ||
| 140 | reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>( | ||
| 141 | vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties")); | ||
| 142 | if (vkEnumerateInstanceLayerProperties == nullptr) { | ||
| 143 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 144 | return false; | ||
| 145 | } | ||
| 146 | |||
| 147 | u32 available_layers_count{}; | ||
| 148 | if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) { | ||
| 149 | LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); | ||
| 150 | return false; | ||
| 151 | } | ||
| 152 | std::vector<VkLayerProperties> layers(available_layers_count); | ||
| 153 | if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) { | ||
| 154 | LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | |||
| 158 | return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { | ||
| 159 | return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); | ||
| 160 | }) != layers.end(); | ||
| 161 | } | ||
| 162 | |||
| 163 | void EmuWindow_SDL2_VK::Present() { | 76 | void EmuWindow_SDL2_VK::Present() { |
| 164 | // TODO (bunnei): ImplementMe | 77 | // TODO (bunnei): ImplementMe |
| 165 | } | 78 | } |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index 1eb8c0868..b8021ebea 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -4,29 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vulkan/vulkan.h> | 7 | #include <memory> |
| 8 | |||
| 8 | #include "core/frontend/emu_window.h" | 9 | #include "core/frontend/emu_window.h" |
| 9 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 10 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 10 | 11 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | 16 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { |
| 12 | public: | 17 | public: |
| 13 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); | 18 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_VK(); | 19 | ~EmuWindow_SDL2_VK(); |
| 15 | 20 | ||
| 16 | void MakeCurrent() override; | ||
| 17 | void DoneCurrent() override; | ||
| 18 | void Present() override; | 21 | void Present() override; |
| 19 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 20 | void* surface) const override; | ||
| 21 | 22 | ||
| 22 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 23 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 23 | |||
| 24 | private: | ||
| 25 | bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const; | ||
| 26 | |||
| 27 | VkInstance vk_instance{}; | ||
| 28 | VkSurfaceKHR vk_surface{}; | ||
| 29 | |||
| 30 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; | ||
| 31 | PFN_vkDestroyInstance vkDestroyInstance{}; | ||
| 32 | }; | 24 | }; |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index babf4c3a4..4d2ea7e9e 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -230,17 +230,10 @@ int main(int argc, char** argv) { | |||
| 230 | 230 | ||
| 231 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); | 231 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); |
| 232 | 232 | ||
| 233 | system.Renderer().Rasterizer().LoadDiskResources(); | 233 | // Core is loaded, start the GPU (makes the GPU contexts current to this thread) |
| 234 | system.GPU().Start(); | ||
| 234 | 235 | ||
| 235 | // Acquire render context for duration of the thread if this is the rendering thread | 236 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 236 | if (!Settings::values.use_asynchronous_gpu_emulation) { | ||
| 237 | emu_window->MakeCurrent(); | ||
| 238 | } | ||
| 239 | SCOPE_EXIT({ | ||
| 240 | if (!Settings::values.use_asynchronous_gpu_emulation) { | ||
| 241 | emu_window->DoneCurrent(); | ||
| 242 | } | ||
| 243 | }); | ||
| 244 | 237 | ||
| 245 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | 238 | std::thread render_thread([&emu_window] { emu_window->Present(); }); |
| 246 | while (emu_window->IsOpen()) { | 239 | while (emu_window->IsOpen()) { |
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp index a1bdb1a12..8584f6671 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | |||
| @@ -102,8 +102,6 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() { | |||
| 102 | LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname, | 102 | LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname, |
| 103 | Common::g_scm_branch, Common::g_scm_desc); | 103 | Common::g_scm_branch, Common::g_scm_desc); |
| 104 | Settings::LogSettings(); | 104 | Settings::LogSettings(); |
| 105 | |||
| 106 | DoneCurrent(); | ||
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | 107 | EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { |
| @@ -114,18 +112,38 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | |||
| 114 | 112 | ||
| 115 | void EmuWindow_SDL2_Hide::PollEvents() {} | 113 | void EmuWindow_SDL2_Hide::PollEvents() {} |
| 116 | 114 | ||
| 117 | void EmuWindow_SDL2_Hide::MakeCurrent() { | ||
| 118 | SDL_GL_MakeCurrent(render_window, gl_context); | ||
| 119 | } | ||
| 120 | |||
| 121 | void EmuWindow_SDL2_Hide::DoneCurrent() { | ||
| 122 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 123 | } | ||
| 124 | |||
| 125 | bool EmuWindow_SDL2_Hide::IsShown() const { | 115 | bool EmuWindow_SDL2_Hide::IsShown() const { |
| 126 | return false; | 116 | return false; |
| 127 | } | 117 | } |
| 128 | 118 | ||
| 129 | void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { | 119 | class SDLGLContext : public Core::Frontend::GraphicsContext { |
| 130 | UNREACHABLE(); | 120 | public: |
| 121 | explicit SDLGLContext() { | ||
| 122 | // create a hidden window to make the shared context against | ||
| 123 | window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||
| 124 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||
| 125 | context = SDL_GL_CreateContext(window); | ||
| 126 | } | ||
| 127 | |||
| 128 | ~SDLGLContext() { | ||
| 129 | DoneCurrent(); | ||
| 130 | SDL_GL_DeleteContext(context); | ||
| 131 | SDL_DestroyWindow(window); | ||
| 132 | } | ||
| 133 | |||
| 134 | void MakeCurrent() override { | ||
| 135 | SDL_GL_MakeCurrent(window, context); | ||
| 136 | } | ||
| 137 | |||
| 138 | void DoneCurrent() override { | ||
| 139 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 140 | } | ||
| 141 | |||
| 142 | private: | ||
| 143 | SDL_Window* window; | ||
| 144 | SDL_GLContext context; | ||
| 145 | }; | ||
| 146 | |||
| 147 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const { | ||
| 148 | return std::make_unique<SDLGLContext>(); | ||
| 131 | } | 149 | } |
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h index b13e15309..c13a82df2 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -16,21 +16,10 @@ public: | |||
| 16 | /// Polls window events | 16 | /// Polls window events |
| 17 | void PollEvents() override; | 17 | void PollEvents() override; |
| 18 | 18 | ||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | ||
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | ||
| 24 | |||
| 25 | /// Whether the screen is being shown or not. | 19 | /// Whether the screen is being shown or not. |
| 26 | bool IsShown() const override; | 20 | bool IsShown() const override; |
| 27 | 21 | ||
| 28 | /// Retrieves Vulkan specific handlers from the window | 22 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 29 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 30 | void* surface) const override; | ||
| 31 | |||
| 32 | /// Whether the window is still open, and a close request hasn't yet been sent | ||
| 33 | bool IsOpen() const; | ||
| 34 | 23 | ||
| 35 | private: | 24 | private: |
| 36 | /// Whether the GPU and driver supports the OpenGL extension required | 25 | /// Whether the GPU and driver supports the OpenGL extension required |
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 94ad50cb3..676e70ebd 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp | |||
| @@ -164,11 +164,6 @@ int main(int argc, char** argv) { | |||
| 164 | 164 | ||
| 165 | std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; | 165 | std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; |
| 166 | 166 | ||
| 167 | if (!Settings::values.use_multi_core) { | ||
| 168 | // Single core mode must acquire OpenGL context for entire emulation session | ||
| 169 | emu_window->MakeCurrent(); | ||
| 170 | } | ||
| 171 | |||
| 172 | bool finished = false; | 167 | bool finished = false; |
| 173 | int return_value = 0; | 168 | int return_value = 0; |
| 174 | const auto callback = [&finished, | 169 | const auto callback = [&finished, |
| @@ -257,6 +252,7 @@ int main(int argc, char** argv) { | |||
| 257 | 252 | ||
| 258 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); | 253 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); |
| 259 | 254 | ||
| 255 | system.GPU().Start(); | ||
| 260 | system.Renderer().Rasterizer().LoadDiskResources(); | 256 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 261 | 257 | ||
| 262 | while (!finished) { | 258 | while (!finished) { |