diff options
Diffstat (limited to 'src')
59 files changed, 2809 insertions, 659 deletions
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/frontend/emu_window.h b/src/core/frontend/emu_window.h index 5eb87fb63..72294d4d8 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -13,19 +13,39 @@ | |||
| 13 | namespace Core::Frontend { | 13 | namespace Core::Frontend { |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Represents a graphics context that can be used for background computation or drawing. If the | 16 | * 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 | */ | 17 | */ |
| 20 | class GraphicsContext { | 18 | class GraphicsContext { |
| 21 | public: | 19 | public: |
| 22 | virtual ~GraphicsContext(); | 20 | virtual ~GraphicsContext(); |
| 23 | 21 | ||
| 22 | /// Inform the driver to swap the front/back buffers and present the current image | ||
| 23 | virtual void SwapBuffers() {} | ||
| 24 | |||
| 24 | /// Makes the graphics context current for the caller thread | 25 | /// Makes the graphics context current for the caller thread |
| 25 | virtual void MakeCurrent() = 0; | 26 | virtual void MakeCurrent() {} |
| 26 | 27 | ||
| 27 | /// Releases (dunno if this is the "right" word) the context from the caller thread | 28 | /// Releases (dunno if this is the "right" word) the context from the caller thread |
| 28 | virtual void DoneCurrent() = 0; | 29 | virtual void DoneCurrent() {} |
| 30 | |||
| 31 | class Scoped { | ||
| 32 | public: | ||
| 33 | explicit Scoped(GraphicsContext& context_) : context(context_) { | ||
| 34 | context.MakeCurrent(); | ||
| 35 | } | ||
| 36 | ~Scoped() { | ||
| 37 | context.DoneCurrent(); | ||
| 38 | } | ||
| 39 | |||
| 40 | private: | ||
| 41 | GraphicsContext& context; | ||
| 42 | }; | ||
| 43 | |||
| 44 | /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value | ||
| 45 | /// ends | ||
| 46 | Scoped Acquire() { | ||
| 47 | return Scoped{*this}; | ||
| 48 | } | ||
| 29 | }; | 49 | }; |
| 30 | 50 | ||
| 31 | /** | 51 | /** |
| @@ -46,7 +66,7 @@ public: | |||
| 46 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | 66 | * - 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. | 67 | * re-read the upper points again and think about it if you don't see this. |
| 48 | */ | 68 | */ |
| 49 | class EmuWindow : public GraphicsContext { | 69 | class EmuWindow { |
| 50 | public: | 70 | public: |
| 51 | /// Data structure to store emuwindow configuration | 71 | /// Data structure to store emuwindow configuration |
| 52 | struct WindowConfig { | 72 | struct WindowConfig { |
| @@ -60,17 +80,9 @@ public: | |||
| 60 | virtual void PollEvents() = 0; | 80 | virtual void PollEvents() = 0; |
| 61 | 81 | ||
| 62 | /** | 82 | /** |
| 63 | * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This | 83 | * 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 | */ | 84 | */ |
| 71 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { | 85 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0; |
| 72 | return nullptr; | ||
| 73 | } | ||
| 74 | 86 | ||
| 75 | /// Returns if window is shown (not minimized) | 87 | /// Returns if window is shown (not minimized) |
| 76 | virtual bool IsShown() const = 0; | 88 | virtual bool IsShown() const = 0; |
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/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 91df062d7..effe76a63 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -210,6 +210,8 @@ if (ENABLE_VULKAN) | |||
| 210 | renderer_vulkan/vk_texture_cache.h | 210 | renderer_vulkan/vk_texture_cache.h |
| 211 | renderer_vulkan/vk_update_descriptor.cpp | 211 | renderer_vulkan/vk_update_descriptor.cpp |
| 212 | renderer_vulkan/vk_update_descriptor.h | 212 | renderer_vulkan/vk_update_descriptor.h |
| 213 | renderer_vulkan/wrapper.cpp | ||
| 214 | renderer_vulkan/wrapper.h | ||
| 213 | ) | 215 | ) |
| 214 | 216 | ||
| 215 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) | 217 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 49dc5abe0..930b605af 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 | ||
| @@ -1001,7 +989,7 @@ union Instruction { | |||
| 1001 | } stg; | 989 | } stg; |
| 1002 | 990 | ||
| 1003 | union { | 991 | union { |
| 1004 | BitField<52, 4, GlobalAtomicOp> operation; | 992 | BitField<52, 4, AtomicOp> operation; |
| 1005 | BitField<49, 3, GlobalAtomicType> type; | 993 | BitField<49, 3, GlobalAtomicType> type; |
| 1006 | BitField<28, 20, s64> offset; | 994 | BitField<28, 20, s64> offset; |
| 1007 | } atom; | 995 | } atom; |
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..ced9d7e28 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 | ||
| @@ -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..925be8d7b 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) { |
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 1241ade1d..265c62758 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,7 +20,8 @@ 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; |
| @@ -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..bd5278a5c 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(CacheAddr 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(CacheAddr 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(CacheAddr 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..866a94c8c 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,7 +19,8 @@ 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; |
| @@ -29,6 +34,9 @@ public: | |||
| 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..270c7ae0d 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) { |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 882e2d9c7..be36c580e 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 | ||
| @@ -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); |
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_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 826eee7df..31add708f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -444,6 +444,7 @@ void RasterizerOpenGL::Clear() { | |||
| 444 | } | 444 | } |
| 445 | 445 | ||
| 446 | SyncRasterizeEnable(); | 446 | SyncRasterizeEnable(); |
| 447 | SyncStencilTestState(); | ||
| 447 | 448 | ||
| 448 | if (regs.clear_flags.scissor) { | 449 | if (regs.clear_flags.scissor) { |
| 449 | SyncScissorTest(); | 450 | SyncScissorTest(); |
| @@ -1052,12 +1053,8 @@ void RasterizerOpenGL::SyncStencilTestState() { | |||
| 1052 | flags[Dirty::StencilTest] = false; | 1053 | flags[Dirty::StencilTest] = false; |
| 1053 | 1054 | ||
| 1054 | const auto& regs = gpu.regs; | 1055 | const auto& regs = gpu.regs; |
| 1055 | if (!regs.stencil_enable) { | 1056 | oglEnable(GL_STENCIL_TEST, regs.stencil_enable); |
| 1056 | glDisable(GL_STENCIL_TEST); | ||
| 1057 | return; | ||
| 1058 | } | ||
| 1059 | 1057 | ||
| 1060 | glEnable(GL_STENCIL_TEST); | ||
| 1061 | glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), | 1058 | glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), |
| 1062 | regs.stencil_front_func_ref, regs.stencil_front_func_mask); | 1059 | regs.stencil_front_func_ref, regs.stencil_front_func_mask); |
| 1063 | glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), | 1060 | glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index e3d31c3eb..046ee55a5 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -327,8 +327,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 327 | 327 | ||
| 328 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, | 328 | const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin, |
| 329 | std::size_t end) { | 329 | std::size_t end) { |
| 330 | context->MakeCurrent(); | 330 | const auto scope = context->Acquire(); |
| 331 | SCOPE_EXIT({ return context->DoneCurrent(); }); | ||
| 332 | 331 | ||
| 333 | for (std::size_t i = begin; i < end; ++i) { | 332 | for (std::size_t i = begin; i < end; ++i) { |
| 334 | if (stop_loading) { | 333 | if (stop_loading) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8aa4a7ac9..c7d24cf14 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -2114,6 +2114,10 @@ private: | |||
| 2114 | 2114 | ||
| 2115 | template <const std::string_view& opname, Type type> | 2115 | template <const std::string_view& opname, Type type> |
| 2116 | Expression Atomic(Operation operation) { | 2116 | Expression Atomic(Operation operation) { |
| 2117 | if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) { | ||
| 2118 | UNIMPLEMENTED_MSG("Unimplemented Min & Max for atomic operations"); | ||
| 2119 | return {}; | ||
| 2120 | } | ||
| 2117 | return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), | 2121 | return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), |
| 2118 | Visit(operation[1]).As(type)), | 2122 | Visit(operation[1]).As(type)), |
| 2119 | type}; | 2123 | type}; |
| @@ -2307,6 +2311,8 @@ private: | |||
| 2307 | ~Func() = delete; | 2311 | ~Func() = delete; |
| 2308 | 2312 | ||
| 2309 | static constexpr std::string_view Add = "Add"; | 2313 | static constexpr std::string_view Add = "Add"; |
| 2314 | static constexpr std::string_view Min = "Min"; | ||
| 2315 | static constexpr std::string_view Max = "Max"; | ||
| 2310 | static constexpr std::string_view And = "And"; | 2316 | static constexpr std::string_view And = "And"; |
| 2311 | static constexpr std::string_view Or = "Or"; | 2317 | static constexpr std::string_view Or = "Or"; |
| 2312 | static constexpr std::string_view Xor = "Xor"; | 2318 | static constexpr std::string_view Xor = "Xor"; |
| @@ -2457,7 +2463,21 @@ private: | |||
| 2457 | &GLSLDecompiler::AtomicImage<Func::Xor>, | 2463 | &GLSLDecompiler::AtomicImage<Func::Xor>, |
| 2458 | &GLSLDecompiler::AtomicImage<Func::Exchange>, | 2464 | &GLSLDecompiler::AtomicImage<Func::Exchange>, |
| 2459 | 2465 | ||
| 2466 | &GLSLDecompiler::Atomic<Func::Exchange, Type::Uint>, | ||
| 2460 | &GLSLDecompiler::Atomic<Func::Add, Type::Uint>, | 2467 | &GLSLDecompiler::Atomic<Func::Add, Type::Uint>, |
| 2468 | &GLSLDecompiler::Atomic<Func::Min, Type::Uint>, | ||
| 2469 | &GLSLDecompiler::Atomic<Func::Max, Type::Uint>, | ||
| 2470 | &GLSLDecompiler::Atomic<Func::And, Type::Uint>, | ||
| 2471 | &GLSLDecompiler::Atomic<Func::Or, Type::Uint>, | ||
| 2472 | &GLSLDecompiler::Atomic<Func::Xor, Type::Uint>, | ||
| 2473 | |||
| 2474 | &GLSLDecompiler::Atomic<Func::Exchange, Type::Int>, | ||
| 2475 | &GLSLDecompiler::Atomic<Func::Add, Type::Int>, | ||
| 2476 | &GLSLDecompiler::Atomic<Func::Min, Type::Int>, | ||
| 2477 | &GLSLDecompiler::Atomic<Func::Max, Type::Int>, | ||
| 2478 | &GLSLDecompiler::Atomic<Func::And, Type::Int>, | ||
| 2479 | &GLSLDecompiler::Atomic<Func::Or, Type::Int>, | ||
| 2480 | &GLSLDecompiler::Atomic<Func::Xor, Type::Int>, | ||
| 2461 | 2481 | ||
| 2462 | &GLSLDecompiler::Branch, | 2482 | &GLSLDecompiler::Branch, |
| 2463 | &GLSLDecompiler::BranchIndirect, | 2483 | &GLSLDecompiler::BranchIndirect, |
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/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 42bb01418..6953aaafe 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -141,8 +141,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 141 | render_window.PollEvents(); | 141 | render_window.PollEvents(); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | void RendererVulkan::TryPresent(int /*timeout_ms*/) { | 144 | bool RendererVulkan::TryPresent(int /*timeout_ms*/) { |
| 145 | // TODO (bunnei): ImplementMe | 145 | // TODO (bunnei): ImplementMe |
| 146 | return true; | ||
| 146 | } | 147 | } |
| 147 | 148 | ||
| 148 | bool RendererVulkan::Init() { | 149 | bool RendererVulkan::Init() { |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 3da08d2e4..d14384e79 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -42,7 +42,7 @@ public: | |||
| 42 | bool Init() override; | 42 | bool Init() override; |
| 43 | void ShutDown() override; | 43 | void ShutDown() override; |
| 44 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 44 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 45 | void TryPresent(int timeout_ms) override; | 45 | bool TryPresent(int timeout_ms) override; |
| 46 | 46 | ||
| 47 | private: | 47 | private: |
| 48 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( | 48 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 51ecb5567..d67f08cf9 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -1941,7 +1941,11 @@ private: | |||
| 1941 | return {}; | 1941 | return {}; |
| 1942 | } | 1942 | } |
| 1943 | 1943 | ||
| 1944 | Expression AtomicAdd(Operation operation) { | 1944 | template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, |
| 1945 | Type value_type = result_type> | ||
| 1946 | Expression Atomic(Operation operation) { | ||
| 1947 | const Id type_def = GetTypeDefinition(result_type); | ||
| 1948 | |||
| 1945 | Id pointer; | 1949 | Id pointer; |
| 1946 | if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { | 1950 | if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { |
| 1947 | pointer = GetSharedMemoryPointer(*smem); | 1951 | pointer = GetSharedMemoryPointer(*smem); |
| @@ -1949,14 +1953,15 @@ private: | |||
| 1949 | pointer = GetGlobalMemoryPointer(*gmem); | 1953 | pointer = GetGlobalMemoryPointer(*gmem); |
| 1950 | } else { | 1954 | } else { |
| 1951 | UNREACHABLE(); | 1955 | UNREACHABLE(); |
| 1952 | return {Constant(t_uint, 0), Type::Uint}; | 1956 | return {Constant(type_def, 0), result_type}; |
| 1953 | } | 1957 | } |
| 1954 | 1958 | ||
| 1959 | const Id value = As(Visit(operation[1]), value_type); | ||
| 1960 | |||
| 1955 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); | 1961 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); |
| 1956 | const Id semantics = Constant(t_uint, 0U); | 1962 | const Id semantics = Constant(type_def, 0); |
| 1957 | 1963 | ||
| 1958 | const Id value = AsUint(Visit(operation[1])); | 1964 | return {(this->*func)(type_def, pointer, scope, semantics, value), result_type}; |
| 1959 | return {OpAtomicIAdd(t_uint, pointer, scope, semantics, value), Type::Uint}; | ||
| 1960 | } | 1965 | } |
| 1961 | 1966 | ||
| 1962 | Expression Branch(Operation operation) { | 1967 | Expression Branch(Operation operation) { |
| @@ -2545,7 +2550,21 @@ private: | |||
| 2545 | &SPIRVDecompiler::AtomicImageXor, | 2550 | &SPIRVDecompiler::AtomicImageXor, |
| 2546 | &SPIRVDecompiler::AtomicImageExchange, | 2551 | &SPIRVDecompiler::AtomicImageExchange, |
| 2547 | 2552 | ||
| 2548 | &SPIRVDecompiler::AtomicAdd, | 2553 | &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Uint>, |
| 2554 | &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Uint>, | ||
| 2555 | &SPIRVDecompiler::Atomic<&Module::OpAtomicUMin, Type::Uint>, | ||
| 2556 | &SPIRVDecompiler::Atomic<&Module::OpAtomicUMax, Type::Uint>, | ||
| 2557 | &SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Uint>, | ||
| 2558 | &SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Uint>, | ||
| 2559 | &SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Uint>, | ||
| 2560 | |||
| 2561 | &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Int>, | ||
| 2562 | &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Int>, | ||
| 2563 | &SPIRVDecompiler::Atomic<&Module::OpAtomicSMin, Type::Int>, | ||
| 2564 | &SPIRVDecompiler::Atomic<&Module::OpAtomicSMax, Type::Int>, | ||
| 2565 | &SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Int>, | ||
| 2566 | &SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Int>, | ||
| 2567 | &SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Int>, | ||
| 2549 | 2568 | ||
| 2550 | &SPIRVDecompiler::Branch, | 2569 | &SPIRVDecompiler::Branch, |
| 2551 | &SPIRVDecompiler::BranchIndirect, | 2570 | &SPIRVDecompiler::BranchIndirect, |
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/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/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/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/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c3dbb1a88..eaded2640 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -10,9 +10,6 @@ | |||
| 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> |
| @@ -29,7 +26,6 @@ | |||
| 29 | #include "common/scope_exit.h" | 26 | #include "common/scope_exit.h" |
| 30 | #include "core/core.h" | 27 | #include "core/core.h" |
| 31 | #include "core/frontend/framebuffer_layout.h" | 28 | #include "core/frontend/framebuffer_layout.h" |
| 32 | #include "core/frontend/scope_acquire_context.h" | ||
| 33 | #include "core/settings.h" | 29 | #include "core/settings.h" |
| 34 | #include "input_common/keyboard.h" | 30 | #include "input_common/keyboard.h" |
| 35 | #include "input_common/main.h" | 31 | #include "input_common/main.h" |
| @@ -39,26 +35,16 @@ | |||
| 39 | #include "yuzu/bootmanager.h" | 35 | #include "yuzu/bootmanager.h" |
| 40 | #include "yuzu/main.h" | 36 | #include "yuzu/main.h" |
| 41 | 37 | ||
| 42 | EmuThread::EmuThread(GRenderWindow& window) | 38 | EmuThread::EmuThread() = default; |
| 43 | : shared_context{window.CreateSharedContext()}, | ||
| 44 | context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context | ||
| 45 | : window} {} | ||
| 46 | 39 | ||
| 47 | EmuThread::~EmuThread() = default; | 40 | EmuThread::~EmuThread() = default; |
| 48 | 41 | ||
| 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() { | 42 | void EmuThread::run() { |
| 59 | MicroProfileOnThreadCreate("EmuThread"); | 43 | MicroProfileOnThreadCreate("EmuThread"); |
| 60 | 44 | ||
| 61 | Core::Frontend::ScopeAcquireContext acquire_context{context}; | 45 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 46 | // execution. | ||
| 47 | Core::System::GetInstance().GPU().Start(); | ||
| 62 | 48 | ||
| 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 49 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 64 | 50 | ||
| @@ -111,162 +97,156 @@ void EmuThread::run() { | |||
| 111 | #endif | 97 | #endif |
| 112 | } | 98 | } |
| 113 | 99 | ||
| 114 | class GGLContext : public Core::Frontend::GraphicsContext { | 100 | class OpenGLSharedContext : public Core::Frontend::GraphicsContext { |
| 115 | public: | 101 | public: |
| 116 | explicit GGLContext(QOpenGLContext* shared_context) | 102 | /// Create the original context that should be shared from |
| 117 | : context(new QOpenGLContext(shared_context->parent())), | 103 | explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { |
| 118 | surface(new QOffscreenSurface(nullptr)) { | 104 | QSurfaceFormat format; |
| 105 | format.setVersion(4, 3); | ||
| 106 | format.setProfile(QSurfaceFormat::CompatibilityProfile); | ||
| 107 | format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | ||
| 108 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 109 | format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 110 | format.setSwapInterval(0); | ||
| 111 | |||
| 112 | context = std::make_unique<QOpenGLContext>(); | ||
| 113 | context->setFormat(format); | ||
| 114 | if (!context->create()) { | ||
| 115 | LOG_ERROR(Frontend, "Unable to create main openGL context"); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Create the shared contexts for rendering and presentation | ||
| 120 | explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) { | ||
| 119 | 121 | ||
| 120 | // disable vsync for any shared contexts | 122 | // disable vsync for any shared contexts |
| 121 | auto format = shared_context->format(); | 123 | auto format = share_context->format(); |
| 122 | format.setSwapInterval(0); | 124 | format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); |
| 123 | 125 | ||
| 124 | context->setShareContext(shared_context); | 126 | context = std::make_unique<QOpenGLContext>(); |
| 127 | context->setShareContext(share_context); | ||
| 125 | context->setFormat(format); | 128 | context->setFormat(format); |
| 126 | context->create(); | 129 | if (!context->create()) { |
| 127 | surface->setParent(shared_context->parent()); | 130 | LOG_ERROR(Frontend, "Unable to create shared openGL context"); |
| 128 | surface->setFormat(format); | 131 | } |
| 129 | surface->create(); | 132 | |
| 133 | if (!main_surface) { | ||
| 134 | offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr); | ||
| 135 | offscreen_surface->setFormat(format); | ||
| 136 | offscreen_surface->create(); | ||
| 137 | surface = offscreen_surface.get(); | ||
| 138 | } else { | ||
| 139 | surface = main_surface; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | ~OpenGLSharedContext() { | ||
| 144 | DoneCurrent(); | ||
| 145 | } | ||
| 146 | |||
| 147 | void SwapBuffers() override { | ||
| 148 | context->swapBuffers(surface); | ||
| 130 | } | 149 | } |
| 131 | 150 | ||
| 132 | void MakeCurrent() override { | 151 | void MakeCurrent() override { |
| 133 | context->makeCurrent(surface); | 152 | if (is_current) { |
| 153 | return; | ||
| 154 | } | ||
| 155 | is_current = context->makeCurrent(surface); | ||
| 134 | } | 156 | } |
| 135 | 157 | ||
| 136 | void DoneCurrent() override { | 158 | void DoneCurrent() override { |
| 159 | if (!is_current) { | ||
| 160 | return; | ||
| 161 | } | ||
| 137 | context->doneCurrent(); | 162 | context->doneCurrent(); |
| 163 | is_current = false; | ||
| 138 | } | 164 | } |
| 139 | 165 | ||
| 140 | private: | 166 | QOpenGLContext* GetShareContext() { |
| 141 | QOpenGLContext* context; | 167 | 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 | } | 168 | } |
| 196 | 169 | ||
| 197 | void exposeEvent(QExposeEvent* event) override { | 170 | const QOpenGLContext* GetShareContext() const { |
| 198 | QWindow::requestUpdate(); | 171 | return context.get(); |
| 199 | QWindow::exposeEvent(event); | ||
| 200 | } | 172 | } |
| 201 | 173 | ||
| 202 | private: | 174 | private: |
| 203 | QWidget* event_handler{}; | 175 | // Avoid using Qt parent system here since we might move the QObjects to new threads |
| 176 | // As a note, this means we should avoid using slots/signals with the objects too | ||
| 177 | std::unique_ptr<QOpenGLContext> context; | ||
| 178 | std::unique_ptr<QOffscreenSurface> offscreen_surface{}; | ||
| 179 | QSurface* surface; | ||
| 180 | bool is_current = false; | ||
| 204 | }; | 181 | }; |
| 205 | 182 | ||
| 206 | class OpenGLWindow final : public ChildRenderWindow { | 183 | class DummyContext : public Core::Frontend::GraphicsContext {}; |
| 184 | |||
| 185 | class RenderWidget : public QWidget { | ||
| 207 | public: | 186 | public: |
| 208 | OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) | 187 | explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { |
| 209 | : ChildRenderWindow{parent, event_handler}, | 188 | setAttribute(Qt::WA_NativeWindow); |
| 210 | context(new QOpenGLContext(shared_context->parent())) { | 189 | setAttribute(Qt::WA_PaintOnScreen); |
| 190 | } | ||
| 211 | 191 | ||
| 212 | // disable vsync for any shared contexts | 192 | virtual ~RenderWidget() = default; |
| 213 | auto format = shared_context->format(); | ||
| 214 | format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); | ||
| 215 | this->setFormat(format); | ||
| 216 | 193 | ||
| 217 | context->setShareContext(shared_context); | 194 | /// Called on the UI thread when this Widget is ready to draw |
| 218 | context->setScreen(this->screen()); | 195 | /// Dervied classes can override this to draw the latest frame. |
| 219 | context->setFormat(format); | 196 | virtual void Present() {} |
| 220 | context->create(); | ||
| 221 | 197 | ||
| 222 | setSurfaceType(QWindow::OpenGLSurface); | 198 | void paintEvent(QPaintEvent* event) override { |
| 199 | Present(); | ||
| 200 | update(); | ||
| 201 | } | ||
| 223 | 202 | ||
| 224 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 203 | QPaintEngine* paintEngine() const override { |
| 225 | // WA_DontShowOnScreen, WA_DeleteOnClose | 204 | return nullptr; |
| 226 | } | 205 | } |
| 227 | 206 | ||
| 228 | ~OpenGLWindow() override { | 207 | private: |
| 229 | context->doneCurrent(); | 208 | GRenderWindow* render_window; |
| 209 | }; | ||
| 210 | |||
| 211 | class OpenGLRenderWidget : public RenderWidget { | ||
| 212 | public: | ||
| 213 | explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { | ||
| 214 | windowHandle()->setSurfaceType(QWindow::OpenGLSurface); | ||
| 215 | } | ||
| 216 | |||
| 217 | void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { | ||
| 218 | context = std::move(context_); | ||
| 230 | } | 219 | } |
| 231 | 220 | ||
| 232 | void Present() override { | 221 | void Present() override { |
| 233 | if (!isExposed()) { | 222 | if (!isVisible()) { |
| 234 | return; | 223 | return; |
| 235 | } | 224 | } |
| 236 | 225 | ||
| 237 | context->makeCurrent(this); | 226 | context->MakeCurrent(); |
| 238 | Core::System::GetInstance().Renderer().TryPresent(100); | 227 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| 239 | context->swapBuffers(this); | 228 | if (Core::System::GetInstance().Renderer().TryPresent(100)) { |
| 240 | auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); | 229 | context->SwapBuffers(); |
| 241 | f->glFinish(); | 230 | glFinish(); |
| 242 | QWindow::requestUpdate(); | 231 | } |
| 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, QVulkanInstance* instance) |
| 253 | : ChildRenderWindow{parent, event_handler} { | 242 | : RenderWidget(parent) { |
| 254 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | 243 | windowHandle()->setSurfaceType(QWindow::VulkanSurface); |
| 255 | setVulkanInstance(instance); | 244 | windowHandle()->setVulkanInstance(instance); |
| 256 | } | ||
| 257 | |||
| 258 | ~VulkanWindow() override = default; | ||
| 259 | |||
| 260 | void Present() override { | ||
| 261 | // TODO(bunnei): ImplementMe | ||
| 262 | } | 245 | } |
| 263 | |||
| 264 | private: | ||
| 265 | QWidget* event_handler{}; | ||
| 266 | }; | 246 | }; |
| 267 | #endif | 247 | #endif |
| 268 | 248 | ||
| 269 | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | 249 | GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread) |
| 270 | : QWidget(parent_), emu_thread(emu_thread) { | 250 | : QWidget(parent_), emu_thread(emu_thread) { |
| 271 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 251 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 272 | .arg(QString::fromUtf8(Common::g_build_name), | 252 | .arg(QString::fromUtf8(Common::g_build_name), |
| @@ -278,26 +258,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) | |||
| 278 | setLayout(layout); | 258 | setLayout(layout); |
| 279 | InputCommon::Init(); | 259 | InputCommon::Init(); |
| 280 | 260 | ||
| 281 | GMainWindow* parent = GetMainWindow(); | 261 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete); |
| 282 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | ||
| 283 | } | 262 | } |
| 284 | 263 | ||
| 285 | GRenderWindow::~GRenderWindow() { | 264 | GRenderWindow::~GRenderWindow() { |
| 286 | InputCommon::Shutdown(); | 265 | InputCommon::Shutdown(); |
| 287 | } | 266 | } |
| 288 | 267 | ||
| 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() { | 268 | void GRenderWindow::PollEvents() { |
| 302 | if (!first_frame) { | 269 | if (!first_frame) { |
| 303 | first_frame = true; | 270 | first_frame = true; |
| @@ -309,21 +276,6 @@ bool GRenderWindow::IsShown() const { | |||
| 309 | return !isMinimized(); | 276 | return !isMinimized(); |
| 310 | } | 277 | } |
| 311 | 278 | ||
| 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). | 279 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 328 | // | 280 | // |
| 329 | // Older versions get the window size (density independent pixels), | 281 | // Older versions get the window size (density independent pixels), |
| @@ -367,7 +319,7 @@ qreal GRenderWindow::windowPixelRatio() const { | |||
| 367 | return devicePixelRatio(); | 319 | return devicePixelRatio(); |
| 368 | } | 320 | } |
| 369 | 321 | ||
| 370 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 322 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const { |
| 371 | const qreal pixel_ratio = windowPixelRatio(); | 323 | const qreal pixel_ratio = windowPixelRatio(); |
| 372 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 324 | 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}))}; | 325 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| @@ -387,8 +339,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| 387 | } | 339 | } |
| 388 | 340 | ||
| 389 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 341 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| 390 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 342 | // touch input is handled in TouchBeginEvent |
| 391 | return; // touch input is handled in TouchBeginEvent | 343 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 344 | return; | ||
| 345 | } | ||
| 392 | 346 | ||
| 393 | auto pos = event->pos(); | 347 | auto pos = event->pos(); |
| 394 | if (event->button() == Qt::LeftButton) { | 348 | if (event->button() == Qt::LeftButton) { |
| @@ -400,8 +354,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { | |||
| 400 | } | 354 | } |
| 401 | 355 | ||
| 402 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | 356 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { |
| 403 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 357 | // touch input is handled in TouchUpdateEvent |
| 404 | return; // touch input is handled in TouchUpdateEvent | 358 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 359 | return; | ||
| 360 | } | ||
| 405 | 361 | ||
| 406 | auto pos = event->pos(); | 362 | auto pos = event->pos(); |
| 407 | const auto [x, y] = ScaleTouch(pos); | 363 | const auto [x, y] = ScaleTouch(pos); |
| @@ -410,13 +366,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 410 | } | 366 | } |
| 411 | 367 | ||
| 412 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 368 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| 413 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 369 | // touch input is handled in TouchEndEvent |
| 414 | return; // touch input is handled in TouchEndEvent | 370 | if (event->source() == Qt::MouseEventSynthesizedBySystem) { |
| 371 | return; | ||
| 372 | } | ||
| 415 | 373 | ||
| 416 | if (event->button() == Qt::LeftButton) | 374 | if (event->button() == Qt::LeftButton) { |
| 417 | this->TouchReleased(); | 375 | this->TouchReleased(); |
| 418 | else if (event->button() == Qt::RightButton) | 376 | } else if (event->button() == Qt::RightButton) { |
| 419 | InputCommon::GetMotionEmu()->EndTilt(); | 377 | InputCommon::GetMotionEmu()->EndTilt(); |
| 378 | } | ||
| 420 | } | 379 | } |
| 421 | 380 | ||
| 422 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 381 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| @@ -474,9 +433,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) { | |||
| 474 | 433 | ||
| 475 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 434 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 476 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 435 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 477 | return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); | 436 | auto c = static_cast<OpenGLSharedContext*>(main_context.get()); |
| 437 | // Bind the shared contexts to the main surface in case the backend wants to take over | ||
| 438 | // presentation | ||
| 439 | return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), | ||
| 440 | child_widget->windowHandle()); | ||
| 478 | } | 441 | } |
| 479 | return {}; | 442 | return std::make_unique<DummyContext>(); |
| 480 | } | 443 | } |
| 481 | 444 | ||
| 482 | bool GRenderWindow::InitRenderTarget() { | 445 | bool GRenderWindow::InitRenderTarget() { |
| @@ -497,14 +460,11 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 497 | break; | 460 | break; |
| 498 | } | 461 | } |
| 499 | 462 | ||
| 463 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 464 | layout()->addWidget(child_widget); | ||
| 500 | // Reset minimum required size to avoid resizing issues on the main window after restarting. | 465 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 501 | setMinimumSize(1, 1); | 466 | setMinimumSize(1, 1); |
| 502 | 467 | ||
| 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); | 468 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); |
| 509 | 469 | ||
| 510 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 470 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| @@ -523,9 +483,10 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 523 | void GRenderWindow::ReleaseRenderTarget() { | 483 | void GRenderWindow::ReleaseRenderTarget() { |
| 524 | if (child_widget) { | 484 | if (child_widget) { |
| 525 | layout()->removeWidget(child_widget); | 485 | layout()->removeWidget(child_widget); |
| 526 | delete child_widget; | 486 | child_widget->deleteLater(); |
| 527 | child_widget = nullptr; | 487 | child_widget = nullptr; |
| 528 | } | 488 | } |
| 489 | main_context.reset(); | ||
| 529 | } | 490 | } |
| 530 | 491 | ||
| 531 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 492 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| @@ -557,24 +518,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 557 | bool GRenderWindow::InitializeOpenGL() { | 518 | bool GRenderWindow::InitializeOpenGL() { |
| 558 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 519 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 559 | // WA_DontShowOnScreen, WA_DeleteOnClose | 520 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 560 | QSurfaceFormat fmt; | 521 | auto child = new OpenGLRenderWidget(this); |
| 561 | fmt.setVersion(4, 3); | 522 | child_widget = child; |
| 562 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | 523 | child_widget->windowHandle()->create(); |
| 563 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 524 | auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle()); |
| 564 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 525 | main_context = context; |
| 565 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 526 | child->SetContext( |
| 566 | fmt.setSwapInterval(0); | 527 | 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 | 528 | ||
| 579 | return true; | 529 | return true; |
| 580 | } | 530 | } |
| @@ -604,13 +554,10 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 604 | return false; | 554 | return false; |
| 605 | } | 555 | } |
| 606 | 556 | ||
| 607 | GMainWindow* parent = GetMainWindow(); | 557 | auto child = new VulkanRenderWidget(this, vk_instance.get()); |
| 608 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | 558 | child_widget = child; |
| 609 | child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); | 559 | child_widget->windowHandle()->create(); |
| 610 | child_window->create(); | 560 | main_context = std::make_unique<DummyContext>(); |
| 611 | child_widget = createWindowContainer(child_window, this); | ||
| 612 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 613 | layout()->addWidget(child_widget); | ||
| 614 | 561 | ||
| 615 | return true; | 562 | return true; |
| 616 | #else | 563 | #else |
| @@ -620,8 +567,24 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 620 | #endif | 567 | #endif |
| 621 | } | 568 | } |
| 622 | 569 | ||
| 570 | void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 571 | void* surface) const { | ||
| 572 | #ifdef HAS_VULKAN | ||
| 573 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | ||
| 574 | const VkInstance instance_copy = vk_instance->vkInstance(); | ||
| 575 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle()); | ||
| 576 | |||
| 577 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 578 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | ||
| 579 | std::memcpy(surface, &surface_copy, sizeof(surface_copy)); | ||
| 580 | #else | ||
| 581 | UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); | ||
| 582 | #endif | ||
| 583 | } | ||
| 584 | |||
| 623 | bool GRenderWindow::LoadOpenGL() { | 585 | bool GRenderWindow::LoadOpenGL() { |
| 624 | Core::Frontend::ScopeAcquireContext acquire_context{*this}; | 586 | auto context = CreateSharedContext(); |
| 587 | auto scope = context->Acquire(); | ||
| 625 | if (!gladLoadGL()) { | 588 | if (!gladLoadGL()) { |
| 626 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), | 589 | 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 " | 590 | 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..d69078df1 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -18,12 +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 | 25 | #ifdef HAS_VULKAN |
| 28 | class QVulkanInstance; | 26 | class QVulkanInstance; |
| 29 | #endif | 27 | #endif |
| @@ -36,7 +34,7 @@ class EmuThread final : public QThread { | |||
| 36 | Q_OBJECT | 34 | Q_OBJECT |
| 37 | 35 | ||
| 38 | public: | 36 | public: |
| 39 | explicit EmuThread(GRenderWindow& window); | 37 | explicit EmuThread(); |
| 40 | ~EmuThread() override; | 38 | ~EmuThread() override; |
| 41 | 39 | ||
| 42 | /** | 40 | /** |
| @@ -90,12 +88,6 @@ private: | |||
| 90 | std::mutex running_mutex; | 88 | std::mutex running_mutex; |
| 91 | std::condition_variable running_cv; | 89 | std::condition_variable running_cv; |
| 92 | 90 | ||
| 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: | 91 | signals: |
| 100 | /** | 92 | /** |
| 101 | * Emitted when the CPU has halted execution | 93 | * Emitted when the CPU has halted execution |
| @@ -124,12 +116,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | |||
| 124 | Q_OBJECT | 116 | Q_OBJECT |
| 125 | 117 | ||
| 126 | public: | 118 | public: |
| 127 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); | 119 | GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); |
| 128 | ~GRenderWindow() override; | 120 | ~GRenderWindow() override; |
| 129 | 121 | ||
| 130 | // EmuWindow implementation. | 122 | // EmuWindow implementation. |
| 131 | void MakeCurrent() override; | ||
| 132 | void DoneCurrent() override; | ||
| 133 | void PollEvents() override; | 123 | void PollEvents() override; |
| 134 | bool IsShown() const override; | 124 | bool IsShown() const override; |
| 135 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 125 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| @@ -165,6 +155,8 @@ public: | |||
| 165 | 155 | ||
| 166 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 156 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 167 | 157 | ||
| 158 | std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; | ||
| 159 | |||
| 168 | public slots: | 160 | public slots: |
| 169 | void OnEmulationStarting(EmuThread* emu_thread); | 161 | void OnEmulationStarting(EmuThread* emu_thread); |
| 170 | void OnEmulationStopping(); | 162 | void OnEmulationStopping(); |
| @@ -176,7 +168,6 @@ signals: | |||
| 176 | void FirstFrameDisplayed(); | 168 | void FirstFrameDisplayed(); |
| 177 | 169 | ||
| 178 | private: | 170 | private: |
| 179 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 180 | void TouchBeginEvent(const QTouchEvent* event); | 171 | void TouchBeginEvent(const QTouchEvent* event); |
| 181 | void TouchUpdateEvent(const QTouchEvent* event); | 172 | void TouchUpdateEvent(const QTouchEvent* event); |
| 182 | void TouchEndEvent(); | 173 | void TouchEndEvent(); |
| @@ -190,7 +181,10 @@ private: | |||
| 190 | 181 | ||
| 191 | EmuThread* emu_thread; | 182 | EmuThread* emu_thread; |
| 192 | 183 | ||
| 193 | std::unique_ptr<GraphicsContext> core_context; | 184 | // Main context that will be shared with all other contexts that are requested. |
| 185 | // If this is used in a shared context setting, then this should not be used directly, but | ||
| 186 | // should instead be shared from | ||
| 187 | std::shared_ptr<Core::Frontend::GraphicsContext> main_context; | ||
| 194 | 188 | ||
| 195 | #ifdef HAS_VULKAN | 189 | #ifdef HAS_VULKAN |
| 196 | std::unique_ptr<QVulkanInstance> vk_instance; | 190 | std::unique_ptr<QVulkanInstance> vk_instance; |
| @@ -201,12 +195,6 @@ private: | |||
| 201 | 195 | ||
| 202 | QByteArray geometry; | 196 | QByteArray geometry; |
| 203 | 197 | ||
| 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; | 198 | QWidget* child_widget = nullptr; |
| 211 | 199 | ||
| 212 | bool first_frame = false; | 200 | bool first_frame = false; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d7e59d0cd..940f24dc8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -984,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 984 | return; | 984 | return; |
| 985 | 985 | ||
| 986 | // Create and start the emulation thread | 986 | // Create and start the emulation thread |
| 987 | emu_thread = std::make_unique<EmuThread>(*render_window); | 987 | emu_thread = std::make_unique<EmuThread>(); |
| 988 | emit EmulationStarting(emu_thread.get()); | 988 | emit EmulationStarting(emu_thread.get()); |
| 989 | emu_thread->start(); | 989 | emu_thread->start(); |
| 990 | 990 | ||
| @@ -2378,7 +2378,6 @@ int main(int argc, char* argv[]) { | |||
| 2378 | 2378 | ||
| 2379 | // Enables the core to make the qt created contexts current on std::threads | 2379 | // Enables the core to make the qt created contexts current on std::threads |
| 2380 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2380 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 2381 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); | ||
| 2382 | QApplication app(argc, argv); | 2381 | QApplication app(argc, argv); |
| 2383 | 2382 | ||
| 2384 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2383 | // 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..3522dcf6d 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,14 +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, | 159 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 160 | void* surface) const { | 160 | void* surface) const { |
| 161 | // Should not have been called from OpenGL | 161 | // Should not have been called from OpenGL |
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..e092021d7 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -13,8 +13,6 @@ 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 | 18 | /// Ignored in OpenGL |
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..46d053f04 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { | |||
| 111 | vkDestroyInstance(vk_instance, nullptr); | 111 | vkDestroyInstance(vk_instance, nullptr); |
| 112 | } | 112 | } |
| 113 | 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, | 114 | void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 123 | void* surface) const { | 115 | void* surface) const { |
| 124 | const auto instance_proc_addr = vkGetInstanceProcAddr; | 116 | const auto instance_proc_addr = vkGetInstanceProcAddr; |
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..3dd1f3f61 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -13,8 +13,6 @@ public: | |||
| 13 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); | 13 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_VK(); | 14 | ~EmuWindow_SDL2_VK(); |
| 15 | 15 | ||
| 16 | void MakeCurrent() override; | ||
| 17 | void DoneCurrent() override; | ||
| 18 | void Present() override; | 16 | void Present() override; |
| 19 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 17 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 20 | void* surface) const override; | 18 | void* surface) const override; |
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..a837430cc 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,14 +112,6 @@ 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 | } |
| @@ -129,3 +119,35 @@ bool EmuWindow_SDL2_Hide::IsShown() const { | |||
| 129 | void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { | 119 | void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { |
| 130 | UNREACHABLE(); | 120 | UNREACHABLE(); |
| 131 | } | 121 | } |
| 122 | |||
| 123 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 124 | public: | ||
| 125 | explicit SDLGLContext() { | ||
| 126 | // create a hidden window to make the shared context against | ||
| 127 | window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||
| 128 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||
| 129 | context = SDL_GL_CreateContext(window); | ||
| 130 | } | ||
| 131 | |||
| 132 | ~SDLGLContext() { | ||
| 133 | DoneCurrent(); | ||
| 134 | SDL_GL_DeleteContext(context); | ||
| 135 | SDL_DestroyWindow(window); | ||
| 136 | } | ||
| 137 | |||
| 138 | void MakeCurrent() override { | ||
| 139 | SDL_GL_MakeCurrent(window, context); | ||
| 140 | } | ||
| 141 | |||
| 142 | void DoneCurrent() override { | ||
| 143 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 144 | } | ||
| 145 | |||
| 146 | private: | ||
| 147 | SDL_Window* window; | ||
| 148 | SDL_GLContext context; | ||
| 149 | }; | ||
| 150 | |||
| 151 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const { | ||
| 152 | return std::make_unique<SDLGLContext>(); | ||
| 153 | } | ||
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..9f5d04fca 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -16,12 +16,6 @@ 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 | ||
| @@ -29,8 +23,7 @@ public: | |||
| 29 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 23 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 30 | void* surface) const override; | 24 | void* surface) const override; |
| 31 | 25 | ||
| 32 | /// Whether the window is still open, and a close request hasn't yet been sent | 26 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 33 | bool IsOpen() const; | ||
| 34 | 27 | ||
| 35 | private: | 28 | private: |
| 36 | /// Whether the GPU and driver supports the OpenGL extension required | 29 | /// 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) { |