diff options
Diffstat (limited to 'src')
72 files changed, 1402 insertions, 636 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 26612e692..54be7dc0c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -131,8 +131,8 @@ 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_window_context.cpp | 134 | frontend/scope_acquire_context.cpp |
| 135 | frontend/scope_acquire_window_context.h | 135 | frontend/scope_acquire_context.h |
| 136 | gdbstub/gdbstub.cpp | 136 | gdbstub/gdbstub.cpp |
| 137 | gdbstub/gdbstub.h | 137 | gdbstub/gdbstub.h |
| 138 | hardware_interrupt_manager.cpp | 138 | hardware_interrupt_manager.cpp |
| @@ -187,6 +187,8 @@ add_library(core STATIC | |||
| 187 | hle/kernel/synchronization.h | 187 | hle/kernel/synchronization.h |
| 188 | hle/kernel/thread.cpp | 188 | hle/kernel/thread.cpp |
| 189 | hle/kernel/thread.h | 189 | hle/kernel/thread.h |
| 190 | hle/kernel/time_manager.cpp | ||
| 191 | hle/kernel/time_manager.h | ||
| 190 | hle/kernel/transfer_memory.cpp | 192 | hle/kernel/transfer_memory.cpp |
| 191 | hle/kernel/transfer_memory.h | 193 | hle/kernel/transfer_memory.h |
| 192 | hle/kernel/vm_manager.cpp | 194 | hle/kernel/vm_manager.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 0eb0c0dca..a82faf127 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -24,6 +24,7 @@ | |||
| 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" | ||
| 27 | #include "core/gdbstub/gdbstub.h" | 28 | #include "core/gdbstub/gdbstub.h" |
| 28 | #include "core/hardware_interrupt_manager.h" | 29 | #include "core/hardware_interrupt_manager.h" |
| 29 | #include "core/hle/kernel/client_port.h" | 30 | #include "core/hle/kernel/client_port.h" |
| @@ -184,6 +185,8 @@ struct System::Impl { | |||
| 184 | 185 | ||
| 185 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, | 186 | ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, |
| 186 | const std::string& filepath) { | 187 | const std::string& filepath) { |
| 188 | Core::Frontend::ScopeAcquireContext acquire_context{emu_window}; | ||
| 189 | |||
| 187 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); | 190 | app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); |
| 188 | if (!app_loader) { | 191 | if (!app_loader) { |
| 189 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | 192 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
| @@ -707,4 +710,12 @@ const Service::SM::ServiceManager& System::ServiceManager() const { | |||
| 707 | return *impl->service_manager; | 710 | return *impl->service_manager; |
| 708 | } | 711 | } |
| 709 | 712 | ||
| 713 | void System::RegisterCoreThread(std::size_t id) { | ||
| 714 | impl->kernel.RegisterCoreThread(id); | ||
| 715 | } | ||
| 716 | |||
| 717 | void System::RegisterHostThread() { | ||
| 718 | impl->kernel.RegisterHostThread(); | ||
| 719 | } | ||
| 720 | |||
| 710 | } // namespace Core | 721 | } // namespace Core |
diff --git a/src/core/core.h b/src/core/core.h index e69d68fcf..8d862a8e6 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -360,6 +360,12 @@ public: | |||
| 360 | 360 | ||
| 361 | const CurrentBuildProcessID& GetCurrentProcessBuildID() const; | 361 | const CurrentBuildProcessID& GetCurrentProcessBuildID() const; |
| 362 | 362 | ||
| 363 | /// Register a host thread as an emulated CPU Core. | ||
| 364 | void RegisterCoreThread(std::size_t id); | ||
| 365 | |||
| 366 | /// Register a host thread as an auxiliary thread. | ||
| 367 | void RegisterHostThread(); | ||
| 368 | |||
| 363 | private: | 369 | private: |
| 364 | System(); | 370 | System(); |
| 365 | 371 | ||
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 3376eedc5..5eb87fb63 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -26,9 +26,6 @@ public: | |||
| 26 | 26 | ||
| 27 | /// Releases (dunno if this is the "right" word) the context from the caller thread | 27 | /// Releases (dunno if this is the "right" word) the context from the caller thread |
| 28 | virtual void DoneCurrent() = 0; | 28 | virtual void DoneCurrent() = 0; |
| 29 | |||
| 30 | /// Swap buffers to display the next frame | ||
| 31 | virtual void SwapBuffers() = 0; | ||
| 32 | }; | 29 | }; |
| 33 | 30 | ||
| 34 | /** | 31 | /** |
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 1d39c1faf..e9d0a40d3 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h | |||
| @@ -29,6 +29,7 @@ enum class AspectRatio { | |||
| 29 | struct FramebufferLayout { | 29 | struct FramebufferLayout { |
| 30 | u32 width{ScreenUndocked::Width}; | 30 | u32 width{ScreenUndocked::Width}; |
| 31 | u32 height{ScreenUndocked::Height}; | 31 | u32 height{ScreenUndocked::Height}; |
| 32 | bool is_srgb{}; | ||
| 32 | 33 | ||
| 33 | Common::Rectangle<u32> screen; | 34 | Common::Rectangle<u32> screen; |
| 34 | 35 | ||
diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp new file mode 100644 index 000000000..878c3157c --- /dev/null +++ b/src/core/frontend/scope_acquire_context.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 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_window_context.h b/src/core/frontend/scope_acquire_context.h index 2d9f6e825..7a65c0623 100644 --- a/src/core/frontend/scope_acquire_window_context.h +++ b/src/core/frontend/scope_acquire_context.h | |||
| @@ -8,16 +8,16 @@ | |||
| 8 | 8 | ||
| 9 | namespace Core::Frontend { | 9 | namespace Core::Frontend { |
| 10 | 10 | ||
| 11 | class EmuWindow; | 11 | class GraphicsContext; |
| 12 | 12 | ||
| 13 | /// Helper class to acquire/release window context within a given scope | 13 | /// Helper class to acquire/release window context within a given scope |
| 14 | class ScopeAcquireWindowContext : NonCopyable { | 14 | class ScopeAcquireContext : NonCopyable { |
| 15 | public: | 15 | public: |
| 16 | explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window); | 16 | explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context); |
| 17 | ~ScopeAcquireWindowContext(); | 17 | ~ScopeAcquireContext(); |
| 18 | 18 | ||
| 19 | private: | 19 | private: |
| 20 | Core::Frontend::EmuWindow& emu_window; | 20 | Core::Frontend::GraphicsContext& context; |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | } // namespace Core::Frontend | 23 | } // namespace Core::Frontend |
diff --git a/src/core/frontend/scope_acquire_window_context.cpp b/src/core/frontend/scope_acquire_window_context.cpp deleted file mode 100644 index 3663dad17..000000000 --- a/src/core/frontend/scope_acquire_window_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_window_context.h" | ||
| 7 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_) | ||
| 11 | : emu_window{emu_window_} { | ||
| 12 | emu_window.MakeCurrent(); | ||
| 13 | } | ||
| 14 | ScopeAcquireWindowContext::~ScopeAcquireWindowContext() { | ||
| 15 | emu_window.DoneCurrent(); | ||
| 16 | } | ||
| 17 | |||
| 18 | } // namespace Core::Frontend | ||
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index 213461b6a..b04e046ed 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h | |||
| @@ -20,6 +20,8 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores | |||
| 20 | 20 | ||
| 21 | } // namespace Hardware | 21 | } // namespace Hardware |
| 22 | 22 | ||
| 23 | constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF; | ||
| 24 | |||
| 23 | struct EmuThreadHandle { | 25 | struct EmuThreadHandle { |
| 24 | u32 host_handle; | 26 | u32 host_handle; |
| 25 | u32 guest_handle; | 27 | u32 guest_handle; |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 4eb1d8703..9232f4d7e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -3,9 +3,12 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <atomic> | 5 | #include <atomic> |
| 6 | #include <bitset> | ||
| 6 | #include <functional> | 7 | #include <functional> |
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <mutex> | 9 | #include <mutex> |
| 10 | #include <thread> | ||
| 11 | #include <unordered_map> | ||
| 9 | #include <utility> | 12 | #include <utility> |
| 10 | 13 | ||
| 11 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| @@ -15,6 +18,7 @@ | |||
| 15 | #include "core/core.h" | 18 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 17 | #include "core/core_timing_util.h" | 20 | #include "core/core_timing_util.h" |
| 21 | #include "core/hardware_properties.h" | ||
| 18 | #include "core/hle/kernel/client_port.h" | 22 | #include "core/hle/kernel/client_port.h" |
| 19 | #include "core/hle/kernel/errors.h" | 23 | #include "core/hle/kernel/errors.h" |
| 20 | #include "core/hle/kernel/handle_table.h" | 24 | #include "core/hle/kernel/handle_table.h" |
| @@ -25,6 +29,7 @@ | |||
| 25 | #include "core/hle/kernel/scheduler.h" | 29 | #include "core/hle/kernel/scheduler.h" |
| 26 | #include "core/hle/kernel/synchronization.h" | 30 | #include "core/hle/kernel/synchronization.h" |
| 27 | #include "core/hle/kernel/thread.h" | 31 | #include "core/hle/kernel/thread.h" |
| 32 | #include "core/hle/kernel/time_manager.h" | ||
| 28 | #include "core/hle/lock.h" | 33 | #include "core/hle/lock.h" |
| 29 | #include "core/hle/result.h" | 34 | #include "core/hle/result.h" |
| 30 | #include "core/memory.h" | 35 | #include "core/memory.h" |
| @@ -44,7 +49,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||
| 44 | std::lock_guard lock{HLE::g_hle_lock}; | 49 | std::lock_guard lock{HLE::g_hle_lock}; |
| 45 | 50 | ||
| 46 | std::shared_ptr<Thread> thread = | 51 | std::shared_ptr<Thread> thread = |
| 47 | system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle); | 52 | system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); |
| 48 | if (thread == nullptr) { | 53 | if (thread == nullptr) { |
| 49 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); | 54 | LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); |
| 50 | return; | 55 | return; |
| @@ -97,8 +102,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||
| 97 | } | 102 | } |
| 98 | 103 | ||
| 99 | struct KernelCore::Impl { | 104 | struct KernelCore::Impl { |
| 100 | explicit Impl(Core::System& system) | 105 | explicit Impl(Core::System& system, KernelCore& kernel) |
| 101 | : system{system}, global_scheduler{system}, synchronization{system} {} | 106 | : system{system}, global_scheduler{kernel}, synchronization{system}, time_manager{system} {} |
| 102 | 107 | ||
| 103 | void Initialize(KernelCore& kernel) { | 108 | void Initialize(KernelCore& kernel) { |
| 104 | Shutdown(); | 109 | Shutdown(); |
| @@ -120,7 +125,7 @@ struct KernelCore::Impl { | |||
| 120 | 125 | ||
| 121 | system_resource_limit = nullptr; | 126 | system_resource_limit = nullptr; |
| 122 | 127 | ||
| 123 | thread_wakeup_callback_handle_table.Clear(); | 128 | global_handle_table.Clear(); |
| 124 | thread_wakeup_event_type = nullptr; | 129 | thread_wakeup_event_type = nullptr; |
| 125 | preemption_event = nullptr; | 130 | preemption_event = nullptr; |
| 126 | 131 | ||
| @@ -138,8 +143,8 @@ struct KernelCore::Impl { | |||
| 138 | 143 | ||
| 139 | void InitializePhysicalCores() { | 144 | void InitializePhysicalCores() { |
| 140 | exclusive_monitor = | 145 | exclusive_monitor = |
| 141 | Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount()); | 146 | Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); |
| 142 | for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) { | 147 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { |
| 143 | cores.emplace_back(system, i, *exclusive_monitor); | 148 | cores.emplace_back(system, i, *exclusive_monitor); |
| 144 | } | 149 | } |
| 145 | } | 150 | } |
| @@ -184,6 +189,50 @@ struct KernelCore::Impl { | |||
| 184 | system.Memory().SetCurrentPageTable(*process); | 189 | system.Memory().SetCurrentPageTable(*process); |
| 185 | } | 190 | } |
| 186 | 191 | ||
| 192 | void RegisterCoreThread(std::size_t core_id) { | ||
| 193 | std::unique_lock lock{register_thread_mutex}; | ||
| 194 | const std::thread::id this_id = std::this_thread::get_id(); | ||
| 195 | const auto it = host_thread_ids.find(this_id); | ||
| 196 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 197 | ASSERT(it == host_thread_ids.end()); | ||
| 198 | ASSERT(!registered_core_threads[core_id]); | ||
| 199 | host_thread_ids[this_id] = static_cast<u32>(core_id); | ||
| 200 | registered_core_threads.set(core_id); | ||
| 201 | } | ||
| 202 | |||
| 203 | void RegisterHostThread() { | ||
| 204 | std::unique_lock lock{register_thread_mutex}; | ||
| 205 | const std::thread::id this_id = std::this_thread::get_id(); | ||
| 206 | const auto it = host_thread_ids.find(this_id); | ||
| 207 | ASSERT(it == host_thread_ids.end()); | ||
| 208 | host_thread_ids[this_id] = registered_thread_ids++; | ||
| 209 | } | ||
| 210 | |||
| 211 | u32 GetCurrentHostThreadID() const { | ||
| 212 | const std::thread::id this_id = std::this_thread::get_id(); | ||
| 213 | const auto it = host_thread_ids.find(this_id); | ||
| 214 | if (it == host_thread_ids.end()) { | ||
| 215 | return Core::INVALID_HOST_THREAD_ID; | ||
| 216 | } | ||
| 217 | return it->second; | ||
| 218 | } | ||
| 219 | |||
| 220 | Core::EmuThreadHandle GetCurrentEmuThreadID() const { | ||
| 221 | Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); | ||
| 222 | result.host_handle = GetCurrentHostThreadID(); | ||
| 223 | if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { | ||
| 224 | return result; | ||
| 225 | } | ||
| 226 | const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); | ||
| 227 | const Kernel::Thread* current = sched.GetCurrentThread(); | ||
| 228 | if (current != nullptr) { | ||
| 229 | result.guest_handle = current->GetGlobalHandle(); | ||
| 230 | } else { | ||
| 231 | result.guest_handle = InvalidHandle; | ||
| 232 | } | ||
| 233 | return result; | ||
| 234 | } | ||
| 235 | |||
| 187 | std::atomic<u32> next_object_id{0}; | 236 | std::atomic<u32> next_object_id{0}; |
| 188 | std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; | 237 | std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; |
| 189 | std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; | 238 | std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; |
| @@ -194,15 +243,16 @@ struct KernelCore::Impl { | |||
| 194 | Process* current_process = nullptr; | 243 | Process* current_process = nullptr; |
| 195 | Kernel::GlobalScheduler global_scheduler; | 244 | Kernel::GlobalScheduler global_scheduler; |
| 196 | Kernel::Synchronization synchronization; | 245 | Kernel::Synchronization synchronization; |
| 246 | Kernel::TimeManager time_manager; | ||
| 197 | 247 | ||
| 198 | std::shared_ptr<ResourceLimit> system_resource_limit; | 248 | std::shared_ptr<ResourceLimit> system_resource_limit; |
| 199 | 249 | ||
| 200 | std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; | 250 | std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; |
| 201 | std::shared_ptr<Core::Timing::EventType> preemption_event; | 251 | std::shared_ptr<Core::Timing::EventType> preemption_event; |
| 202 | 252 | ||
| 203 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, | 253 | // This is the kernel's handle table or supervisor handle table which |
| 204 | // allowing us to simply use a pool index or similar. | 254 | // stores all the objects in place. |
| 205 | Kernel::HandleTable thread_wakeup_callback_handle_table; | 255 | Kernel::HandleTable global_handle_table; |
| 206 | 256 | ||
| 207 | /// Map of named ports managed by the kernel, which can be retrieved using | 257 | /// Map of named ports managed by the kernel, which can be retrieved using |
| 208 | /// the ConnectToPort SVC. | 258 | /// the ConnectToPort SVC. |
| @@ -211,11 +261,17 @@ struct KernelCore::Impl { | |||
| 211 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; | 261 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; |
| 212 | std::vector<Kernel::PhysicalCore> cores; | 262 | std::vector<Kernel::PhysicalCore> cores; |
| 213 | 263 | ||
| 264 | // 0-3 IDs represent core threads, >3 represent others | ||
| 265 | std::unordered_map<std::thread::id, u32> host_thread_ids; | ||
| 266 | u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; | ||
| 267 | std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; | ||
| 268 | std::mutex register_thread_mutex; | ||
| 269 | |||
| 214 | // System context | 270 | // System context |
| 215 | Core::System& system; | 271 | Core::System& system; |
| 216 | }; | 272 | }; |
| 217 | 273 | ||
| 218 | KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {} | 274 | KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system, *this)} {} |
| 219 | KernelCore::~KernelCore() { | 275 | KernelCore::~KernelCore() { |
| 220 | Shutdown(); | 276 | Shutdown(); |
| 221 | } | 277 | } |
| @@ -232,9 +288,8 @@ std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const { | |||
| 232 | return impl->system_resource_limit; | 288 | return impl->system_resource_limit; |
| 233 | } | 289 | } |
| 234 | 290 | ||
| 235 | std::shared_ptr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable( | 291 | std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { |
| 236 | Handle handle) const { | 292 | return impl->global_handle_table.Get<Thread>(handle); |
| 237 | return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle); | ||
| 238 | } | 293 | } |
| 239 | 294 | ||
| 240 | void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) { | 295 | void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) { |
| @@ -265,6 +320,14 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { | |||
| 265 | return impl->global_scheduler; | 320 | return impl->global_scheduler; |
| 266 | } | 321 | } |
| 267 | 322 | ||
| 323 | Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { | ||
| 324 | return impl->cores[id].Scheduler(); | ||
| 325 | } | ||
| 326 | |||
| 327 | const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { | ||
| 328 | return impl->cores[id].Scheduler(); | ||
| 329 | } | ||
| 330 | |||
| 268 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | 331 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { |
| 269 | return impl->cores[id]; | 332 | return impl->cores[id]; |
| 270 | } | 333 | } |
| @@ -281,6 +344,14 @@ const Kernel::Synchronization& KernelCore::Synchronization() const { | |||
| 281 | return impl->synchronization; | 344 | return impl->synchronization; |
| 282 | } | 345 | } |
| 283 | 346 | ||
| 347 | Kernel::TimeManager& KernelCore::TimeManager() { | ||
| 348 | return impl->time_manager; | ||
| 349 | } | ||
| 350 | |||
| 351 | const Kernel::TimeManager& KernelCore::TimeManager() const { | ||
| 352 | return impl->time_manager; | ||
| 353 | } | ||
| 354 | |||
| 284 | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | 355 | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { |
| 285 | return *impl->exclusive_monitor; | 356 | return *impl->exclusive_monitor; |
| 286 | } | 357 | } |
| @@ -338,12 +409,28 @@ const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallback | |||
| 338 | return impl->thread_wakeup_event_type; | 409 | return impl->thread_wakeup_event_type; |
| 339 | } | 410 | } |
| 340 | 411 | ||
| 341 | Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() { | 412 | Kernel::HandleTable& KernelCore::GlobalHandleTable() { |
| 342 | return impl->thread_wakeup_callback_handle_table; | 413 | return impl->global_handle_table; |
| 414 | } | ||
| 415 | |||
| 416 | const Kernel::HandleTable& KernelCore::GlobalHandleTable() const { | ||
| 417 | return impl->global_handle_table; | ||
| 418 | } | ||
| 419 | |||
| 420 | void KernelCore::RegisterCoreThread(std::size_t core_id) { | ||
| 421 | impl->RegisterCoreThread(core_id); | ||
| 422 | } | ||
| 423 | |||
| 424 | void KernelCore::RegisterHostThread() { | ||
| 425 | impl->RegisterHostThread(); | ||
| 426 | } | ||
| 427 | |||
| 428 | u32 KernelCore::GetCurrentHostThreadID() const { | ||
| 429 | return impl->GetCurrentHostThreadID(); | ||
| 343 | } | 430 | } |
| 344 | 431 | ||
| 345 | const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const { | 432 | Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const { |
| 346 | return impl->thread_wakeup_callback_handle_table; | 433 | return impl->GetCurrentEmuThreadID(); |
| 347 | } | 434 | } |
| 348 | 435 | ||
| 349 | } // namespace Kernel | 436 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 1eede3063..c4f78ab71 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/hle/kernel/object.h" | 11 | #include "core/hle/kernel/object.h" |
| 12 | 12 | ||
| 13 | namespace Core { | 13 | namespace Core { |
| 14 | struct EmuThreadHandle; | ||
| 14 | class ExclusiveMonitor; | 15 | class ExclusiveMonitor; |
| 15 | class System; | 16 | class System; |
| 16 | } // namespace Core | 17 | } // namespace Core |
| @@ -29,8 +30,10 @@ class HandleTable; | |||
| 29 | class PhysicalCore; | 30 | class PhysicalCore; |
| 30 | class Process; | 31 | class Process; |
| 31 | class ResourceLimit; | 32 | class ResourceLimit; |
| 33 | class Scheduler; | ||
| 32 | class Synchronization; | 34 | class Synchronization; |
| 33 | class Thread; | 35 | class Thread; |
| 36 | class TimeManager; | ||
| 34 | 37 | ||
| 35 | /// Represents a single instance of the kernel. | 38 | /// Represents a single instance of the kernel. |
| 36 | class KernelCore { | 39 | class KernelCore { |
| @@ -64,7 +67,7 @@ public: | |||
| 64 | std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; | 67 | std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; |
| 65 | 68 | ||
| 66 | /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. | 69 | /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. |
| 67 | std::shared_ptr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const; | 70 | std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const; |
| 68 | 71 | ||
| 69 | /// Adds the given shared pointer to an internal list of active processes. | 72 | /// Adds the given shared pointer to an internal list of active processes. |
| 70 | void AppendNewProcess(std::shared_ptr<Process> process); | 73 | void AppendNewProcess(std::shared_ptr<Process> process); |
| @@ -87,6 +90,12 @@ public: | |||
| 87 | /// Gets the sole instance of the global scheduler | 90 | /// Gets the sole instance of the global scheduler |
| 88 | const Kernel::GlobalScheduler& GlobalScheduler() const; | 91 | const Kernel::GlobalScheduler& GlobalScheduler() const; |
| 89 | 92 | ||
| 93 | /// Gets the sole instance of the Scheduler assoviated with cpu core 'id' | ||
| 94 | Kernel::Scheduler& Scheduler(std::size_t id); | ||
| 95 | |||
| 96 | /// Gets the sole instance of the Scheduler assoviated with cpu core 'id' | ||
| 97 | const Kernel::Scheduler& Scheduler(std::size_t id) const; | ||
| 98 | |||
| 90 | /// Gets the an instance of the respective physical CPU core. | 99 | /// Gets the an instance of the respective physical CPU core. |
| 91 | Kernel::PhysicalCore& PhysicalCore(std::size_t id); | 100 | Kernel::PhysicalCore& PhysicalCore(std::size_t id); |
| 92 | 101 | ||
| @@ -99,6 +108,12 @@ public: | |||
| 99 | /// Gets the an instance of the Synchronization Interface. | 108 | /// Gets the an instance of the Synchronization Interface. |
| 100 | const Kernel::Synchronization& Synchronization() const; | 109 | const Kernel::Synchronization& Synchronization() const; |
| 101 | 110 | ||
| 111 | /// Gets the an instance of the TimeManager Interface. | ||
| 112 | Kernel::TimeManager& TimeManager(); | ||
| 113 | |||
| 114 | /// Gets the an instance of the TimeManager Interface. | ||
| 115 | const Kernel::TimeManager& TimeManager() const; | ||
| 116 | |||
| 102 | /// Stops execution of 'id' core, in order to reschedule a new thread. | 117 | /// Stops execution of 'id' core, in order to reschedule a new thread. |
| 103 | void PrepareReschedule(std::size_t id); | 118 | void PrepareReschedule(std::size_t id); |
| 104 | 119 | ||
| @@ -120,6 +135,18 @@ public: | |||
| 120 | /// Determines whether or not the given port is a valid named port. | 135 | /// Determines whether or not the given port is a valid named port. |
| 121 | bool IsValidNamedPort(NamedPortTable::const_iterator port) const; | 136 | bool IsValidNamedPort(NamedPortTable::const_iterator port) const; |
| 122 | 137 | ||
| 138 | /// Gets the current host_thread/guest_thread handle. | ||
| 139 | Core::EmuThreadHandle GetCurrentEmuThreadID() const; | ||
| 140 | |||
| 141 | /// Gets the current host_thread handle. | ||
| 142 | u32 GetCurrentHostThreadID() const; | ||
| 143 | |||
| 144 | /// Register the current thread as a CPU Core Thread. | ||
| 145 | void RegisterCoreThread(std::size_t core_id); | ||
| 146 | |||
| 147 | /// Register the current thread as a non CPU core thread. | ||
| 148 | void RegisterHostThread(); | ||
| 149 | |||
| 123 | private: | 150 | private: |
| 124 | friend class Object; | 151 | friend class Object; |
| 125 | friend class Process; | 152 | friend class Process; |
| @@ -140,11 +167,11 @@ private: | |||
| 140 | /// Retrieves the event type used for thread wakeup callbacks. | 167 | /// Retrieves the event type used for thread wakeup callbacks. |
| 141 | const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; | 168 | const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; |
| 142 | 169 | ||
| 143 | /// Provides a reference to the thread wakeup callback handle table. | 170 | /// Provides a reference to the global handle table. |
| 144 | Kernel::HandleTable& ThreadWakeupCallbackHandleTable(); | 171 | Kernel::HandleTable& GlobalHandleTable(); |
| 145 | 172 | ||
| 146 | /// Provides a const reference to the thread wakeup callback handle table. | 173 | /// Provides a const reference to the global handle table. |
| 147 | const Kernel::HandleTable& ThreadWakeupCallbackHandleTable() const; | 174 | const Kernel::HandleTable& GlobalHandleTable() const; |
| 148 | 175 | ||
| 149 | struct Impl; | 176 | struct Impl; |
| 150 | std::unique_ptr<Impl> impl; | 177 | std::unique_ptr<Impl> impl; |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 86f1421bf..c65f82fb7 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -18,10 +18,11 @@ | |||
| 18 | #include "core/hle/kernel/kernel.h" | 18 | #include "core/hle/kernel/kernel.h" |
| 19 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| 20 | #include "core/hle/kernel/scheduler.h" | 20 | #include "core/hle/kernel/scheduler.h" |
| 21 | #include "core/hle/kernel/time_manager.h" | ||
| 21 | 22 | ||
| 22 | namespace Kernel { | 23 | namespace Kernel { |
| 23 | 24 | ||
| 24 | GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {} | 25 | GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {} |
| 25 | 26 | ||
| 26 | GlobalScheduler::~GlobalScheduler() = default; | 27 | GlobalScheduler::~GlobalScheduler() = default; |
| 27 | 28 | ||
| @@ -35,7 +36,7 @@ void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { | |||
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | void GlobalScheduler::UnloadThread(std::size_t core) { | 38 | void GlobalScheduler::UnloadThread(std::size_t core) { |
| 38 | Scheduler& sched = system.Scheduler(core); | 39 | Scheduler& sched = kernel.Scheduler(core); |
| 39 | sched.UnloadThread(); | 40 | sched.UnloadThread(); |
| 40 | } | 41 | } |
| 41 | 42 | ||
| @@ -50,7 +51,7 @@ void GlobalScheduler::SelectThread(std::size_t core) { | |||
| 50 | sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; | 51 | sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; |
| 51 | std::atomic_thread_fence(std::memory_order_seq_cst); | 52 | std::atomic_thread_fence(std::memory_order_seq_cst); |
| 52 | }; | 53 | }; |
| 53 | Scheduler& sched = system.Scheduler(core); | 54 | Scheduler& sched = kernel.Scheduler(core); |
| 54 | Thread* current_thread = nullptr; | 55 | Thread* current_thread = nullptr; |
| 55 | // Step 1: Get top thread in schedule queue. | 56 | // Step 1: Get top thread in schedule queue. |
| 56 | current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); | 57 | current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); |
| @@ -356,6 +357,32 @@ void GlobalScheduler::Shutdown() { | |||
| 356 | thread_list.clear(); | 357 | thread_list.clear(); |
| 357 | } | 358 | } |
| 358 | 359 | ||
| 360 | void GlobalScheduler::Lock() { | ||
| 361 | Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); | ||
| 362 | if (current_thread == current_owner) { | ||
| 363 | ++scope_lock; | ||
| 364 | } else { | ||
| 365 | inner_lock.lock(); | ||
| 366 | current_owner = current_thread; | ||
| 367 | ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle()); | ||
| 368 | scope_lock = 1; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | void GlobalScheduler::Unlock() { | ||
| 373 | if (--scope_lock != 0) { | ||
| 374 | ASSERT(scope_lock > 0); | ||
| 375 | return; | ||
| 376 | } | ||
| 377 | for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||
| 378 | SelectThread(i); | ||
| 379 | } | ||
| 380 | current_owner = Core::EmuThreadHandle::InvalidHandle(); | ||
| 381 | scope_lock = 1; | ||
| 382 | inner_lock.unlock(); | ||
| 383 | // TODO(Blinkhawk): Setup the interrupts and change context on current core. | ||
| 384 | } | ||
| 385 | |||
| 359 | Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id) | 386 | Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id) |
| 360 | : system(system), cpu_core(cpu_core), core_id(core_id) {} | 387 | : system(system), cpu_core(cpu_core), core_id(core_id) {} |
| 361 | 388 | ||
| @@ -485,4 +512,27 @@ void Scheduler::Shutdown() { | |||
| 485 | selected_thread = nullptr; | 512 | selected_thread = nullptr; |
| 486 | } | 513 | } |
| 487 | 514 | ||
| 515 | SchedulerLock::SchedulerLock(KernelCore& kernel) : kernel{kernel} { | ||
| 516 | kernel.GlobalScheduler().Lock(); | ||
| 517 | } | ||
| 518 | |||
| 519 | SchedulerLock::~SchedulerLock() { | ||
| 520 | kernel.GlobalScheduler().Unlock(); | ||
| 521 | } | ||
| 522 | |||
| 523 | SchedulerLockAndSleep::SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, | ||
| 524 | Thread* time_task, s64 nanoseconds) | ||
| 525 | : SchedulerLock{kernel}, event_handle{event_handle}, time_task{time_task}, nanoseconds{ | ||
| 526 | nanoseconds} { | ||
| 527 | event_handle = InvalidHandle; | ||
| 528 | } | ||
| 529 | |||
| 530 | SchedulerLockAndSleep::~SchedulerLockAndSleep() { | ||
| 531 | if (sleep_cancelled) { | ||
| 532 | return; | ||
| 533 | } | ||
| 534 | auto& time_manager = kernel.TimeManager(); | ||
| 535 | time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds); | ||
| 536 | } | ||
| 537 | |||
| 488 | } // namespace Kernel | 538 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 96db049cb..1c93a838c 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <mutex> | ||
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | 11 | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| @@ -20,11 +21,13 @@ class System; | |||
| 20 | 21 | ||
| 21 | namespace Kernel { | 22 | namespace Kernel { |
| 22 | 23 | ||
| 24 | class KernelCore; | ||
| 23 | class Process; | 25 | class Process; |
| 26 | class SchedulerLock; | ||
| 24 | 27 | ||
| 25 | class GlobalScheduler final { | 28 | class GlobalScheduler final { |
| 26 | public: | 29 | public: |
| 27 | explicit GlobalScheduler(Core::System& system); | 30 | explicit GlobalScheduler(KernelCore& kernel); |
| 28 | ~GlobalScheduler(); | 31 | ~GlobalScheduler(); |
| 29 | 32 | ||
| 30 | /// Adds a new thread to the scheduler | 33 | /// Adds a new thread to the scheduler |
| @@ -138,6 +141,14 @@ public: | |||
| 138 | void Shutdown(); | 141 | void Shutdown(); |
| 139 | 142 | ||
| 140 | private: | 143 | private: |
| 144 | friend class SchedulerLock; | ||
| 145 | |||
| 146 | /// Lock the scheduler to the current thread. | ||
| 147 | void Lock(); | ||
| 148 | |||
| 149 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling | ||
| 150 | /// and reschedules current core if needed. | ||
| 151 | void Unlock(); | ||
| 141 | /** | 152 | /** |
| 142 | * Transfers a thread into an specific core. If the destination_core is -1 | 153 | * Transfers a thread into an specific core. If the destination_core is -1 |
| 143 | * it will be unscheduled from its source code and added into its suggested | 154 | * it will be unscheduled from its source code and added into its suggested |
| @@ -158,9 +169,14 @@ private: | |||
| 158 | // ordered from Core 0 to Core 3. | 169 | // ordered from Core 0 to Core 3. |
| 159 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; | 170 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; |
| 160 | 171 | ||
| 172 | /// Scheduler lock mechanisms. | ||
| 173 | std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock | ||
| 174 | std::atomic<s64> scope_lock{}; | ||
| 175 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; | ||
| 176 | |||
| 161 | /// Lists all thread ids that aren't deleted/etc. | 177 | /// Lists all thread ids that aren't deleted/etc. |
| 162 | std::vector<std::shared_ptr<Thread>> thread_list; | 178 | std::vector<std::shared_ptr<Thread>> thread_list; |
| 163 | Core::System& system; | 179 | KernelCore& kernel; |
| 164 | }; | 180 | }; |
| 165 | 181 | ||
| 166 | class Scheduler final { | 182 | class Scheduler final { |
| @@ -227,4 +243,30 @@ private: | |||
| 227 | bool is_context_switch_pending = false; | 243 | bool is_context_switch_pending = false; |
| 228 | }; | 244 | }; |
| 229 | 245 | ||
| 246 | class SchedulerLock { | ||
| 247 | public: | ||
| 248 | explicit SchedulerLock(KernelCore& kernel); | ||
| 249 | ~SchedulerLock(); | ||
| 250 | |||
| 251 | protected: | ||
| 252 | KernelCore& kernel; | ||
| 253 | }; | ||
| 254 | |||
| 255 | class SchedulerLockAndSleep : public SchedulerLock { | ||
| 256 | public: | ||
| 257 | explicit SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* time_task, | ||
| 258 | s64 nanoseconds); | ||
| 259 | ~SchedulerLockAndSleep(); | ||
| 260 | |||
| 261 | void CancelSleep() { | ||
| 262 | sleep_cancelled = true; | ||
| 263 | } | ||
| 264 | |||
| 265 | private: | ||
| 266 | Handle& event_handle; | ||
| 267 | Thread* time_task; | ||
| 268 | s64 nanoseconds; | ||
| 269 | bool sleep_cancelled{}; | ||
| 270 | }; | ||
| 271 | |||
| 230 | } // namespace Kernel | 272 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index ae5f2c8bd..bf850e0b2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -46,9 +46,9 @@ Thread::~Thread() = default; | |||
| 46 | void Thread::Stop() { | 46 | void Thread::Stop() { |
| 47 | // Cancel any outstanding wakeup events for this thread | 47 | // Cancel any outstanding wakeup events for this thread |
| 48 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | 48 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), |
| 49 | callback_handle); | 49 | global_handle); |
| 50 | kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); | 50 | kernel.GlobalHandleTable().Close(global_handle); |
| 51 | callback_handle = 0; | 51 | global_handle = 0; |
| 52 | SetStatus(ThreadStatus::Dead); | 52 | SetStatus(ThreadStatus::Dead); |
| 53 | Signal(); | 53 | Signal(); |
| 54 | 54 | ||
| @@ -73,12 +73,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 73 | // thread-safe version of ScheduleEvent. | 73 | // thread-safe version of ScheduleEvent. |
| 74 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | 74 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); |
| 75 | Core::System::GetInstance().CoreTiming().ScheduleEvent( | 75 | Core::System::GetInstance().CoreTiming().ScheduleEvent( |
| 76 | cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle); | 76 | cycles, kernel.ThreadWakeupCallbackEventType(), global_handle); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | void Thread::CancelWakeupTimer() { | 79 | void Thread::CancelWakeupTimer() { |
| 80 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | 80 | Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), |
| 81 | callback_handle); | 81 | global_handle); |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | void Thread::ResumeFromWait() { | 84 | void Thread::ResumeFromWait() { |
| @@ -190,7 +190,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||
| 190 | thread->condvar_wait_address = 0; | 190 | thread->condvar_wait_address = 0; |
| 191 | thread->wait_handle = 0; | 191 | thread->wait_handle = 0; |
| 192 | thread->name = std::move(name); | 192 | thread->name = std::move(name); |
| 193 | thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); | 193 | thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); |
| 194 | thread->owner_process = &owner_process; | 194 | thread->owner_process = &owner_process; |
| 195 | auto& scheduler = kernel.GlobalScheduler(); | 195 | auto& scheduler = kernel.GlobalScheduler(); |
| 196 | scheduler.AddThread(thread); | 196 | scheduler.AddThread(thread); |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 7a4916318..129e7858a 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -453,6 +453,10 @@ public: | |||
| 453 | is_sync_cancelled = value; | 453 | is_sync_cancelled = value; |
| 454 | } | 454 | } |
| 455 | 455 | ||
| 456 | Handle GetGlobalHandle() const { | ||
| 457 | return global_handle; | ||
| 458 | } | ||
| 459 | |||
| 456 | private: | 460 | private: |
| 457 | void SetSchedulingStatus(ThreadSchedStatus new_status); | 461 | void SetSchedulingStatus(ThreadSchedStatus new_status); |
| 458 | void SetCurrentPriority(u32 new_priority); | 462 | void SetCurrentPriority(u32 new_priority); |
| @@ -514,7 +518,7 @@ private: | |||
| 514 | VAddr arb_wait_address{0}; | 518 | VAddr arb_wait_address{0}; |
| 515 | 519 | ||
| 516 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | 520 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. |
| 517 | Handle callback_handle = 0; | 521 | Handle global_handle = 0; |
| 518 | 522 | ||
| 519 | /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 523 | /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread |
| 520 | /// was waiting via WaitSynchronization then the object will be the last object that became | 524 | /// was waiting via WaitSynchronization then the object will be the last object that became |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp new file mode 100644 index 000000000..21b290468 --- /dev/null +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -0,0 +1,44 @@ | |||
| 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 "common/assert.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 8 | #include "core/core_timing_util.h" | ||
| 9 | #include "core/hle/kernel/handle_table.h" | ||
| 10 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | ||
| 12 | #include "core/hle/kernel/time_manager.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | TimeManager::TimeManager(Core::System& system) : system{system} { | ||
| 17 | time_manager_event_type = Core::Timing::CreateEvent( | ||
| 18 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | ||
| 19 | Handle proper_handle = static_cast<Handle>(thread_handle); | ||
| 20 | std::shared_ptr<Thread> thread = | ||
| 21 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | ||
| 22 | thread->ResumeFromWait(); | ||
| 23 | }); | ||
| 24 | } | ||
| 25 | |||
| 26 | void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { | ||
| 27 | if (nanoseconds > 0) { | ||
| 28 | ASSERT(timetask); | ||
| 29 | event_handle = timetask->GetGlobalHandle(); | ||
| 30 | const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | ||
| 31 | system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); | ||
| 32 | } else { | ||
| 33 | event_handle = InvalidHandle; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { | ||
| 38 | if (event_handle == InvalidHandle) { | ||
| 39 | return; | ||
| 40 | } | ||
| 41 | system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); | ||
| 42 | } | ||
| 43 | |||
| 44 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h new file mode 100644 index 000000000..eaec486d1 --- /dev/null +++ b/src/core/hle/kernel/time_manager.h | |||
| @@ -0,0 +1,43 @@ | |||
| 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 <memory> | ||
| 8 | |||
| 9 | #include "core/hle/kernel/object.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } // namespace Core | ||
| 14 | |||
| 15 | namespace Core::Timing { | ||
| 16 | struct EventType; | ||
| 17 | } // namespace Core::Timing | ||
| 18 | |||
| 19 | namespace Kernel { | ||
| 20 | |||
| 21 | class Thread; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp | ||
| 25 | * method when the event is triggered. | ||
| 26 | */ | ||
| 27 | class TimeManager { | ||
| 28 | public: | ||
| 29 | explicit TimeManager(Core::System& system); | ||
| 30 | |||
| 31 | /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds' | ||
| 32 | /// returns a non-invalid handle in `event_handle` if correctly scheduled | ||
| 33 | void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds); | ||
| 34 | |||
| 35 | /// Unschedule an existing time event | ||
| 36 | void UnscheduleTimeEvent(Handle event_handle); | ||
| 37 | |||
| 38 | private: | ||
| 39 | Core::System& system; | ||
| 40 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; | ||
| 41 | }; | ||
| 42 | |||
| 43 | } // namespace Kernel | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index cc978713b..d1bf13c89 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -607,7 +607,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system, | |||
| 607 | {40, nullptr, "GetCradleFwVersion"}, | 607 | {40, nullptr, "GetCradleFwVersion"}, |
| 608 | {50, nullptr, "IsVrModeEnabled"}, | 608 | {50, nullptr, "IsVrModeEnabled"}, |
| 609 | {51, nullptr, "SetVrModeEnabled"}, | 609 | {51, nullptr, "SetVrModeEnabled"}, |
| 610 | {52, nullptr, "SwitchLcdBacklight"}, | 610 | {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"}, |
| 611 | {53, nullptr, "BeginVrModeEx"}, | 611 | {53, nullptr, "BeginVrModeEx"}, |
| 612 | {54, nullptr, "EndVrModeEx"}, | 612 | {54, nullptr, "EndVrModeEx"}, |
| 613 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, | 613 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, |
| @@ -636,7 +636,6 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) { | |||
| 636 | 636 | ||
| 637 | IPC::ResponseBuilder rb{ctx, 3}; | 637 | IPC::ResponseBuilder rb{ctx, 3}; |
| 638 | rb.Push(RESULT_SUCCESS); | 638 | rb.Push(RESULT_SUCCESS); |
| 639 | |||
| 640 | rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode | 639 | rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode |
| 641 | } | 640 | } |
| 642 | 641 | ||
| @@ -660,6 +659,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { | |||
| 660 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); | 659 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); |
| 661 | return; | 660 | return; |
| 662 | } | 661 | } |
| 662 | |||
| 663 | rb.Push(RESULT_SUCCESS); | 663 | rb.Push(RESULT_SUCCESS); |
| 664 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); | 664 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); |
| 665 | } | 665 | } |
| @@ -672,6 +672,17 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { | |||
| 672 | rb.Push(static_cast<u8>(FocusState::InFocus)); | 672 | rb.Push(static_cast<u8>(FocusState::InFocus)); |
| 673 | } | 673 | } |
| 674 | 674 | ||
| 675 | void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) { | ||
| 676 | IPC::RequestParser rp{ctx}; | ||
| 677 | const auto is_lcd_backlight_off_enabled = rp.Pop<bool>(); | ||
| 678 | |||
| 679 | LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}", | ||
| 680 | is_lcd_backlight_off_enabled); | ||
| 681 | |||
| 682 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 683 | rb.Push(RESULT_SUCCESS); | ||
| 684 | } | ||
| 685 | |||
| 675 | void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { | 686 | void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { |
| 676 | LOG_DEBUG(Service_AM, "called"); | 687 | LOG_DEBUG(Service_AM, "called"); |
| 677 | 688 | ||
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 0b9a4332d..0843de781 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -182,6 +182,7 @@ private: | |||
| 182 | void GetOperationMode(Kernel::HLERequestContext& ctx); | 182 | void GetOperationMode(Kernel::HLERequestContext& ctx); |
| 183 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); | 183 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); |
| 184 | void GetBootMode(Kernel::HLERequestContext& ctx); | 184 | void GetBootMode(Kernel::HLERequestContext& ctx); |
| 185 | void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx); | ||
| 185 | void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); | 186 | void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); |
| 186 | void SetCpuBoostMode(Kernel::HLERequestContext& ctx); | 187 | void SetCpuBoostMode(Kernel::HLERequestContext& ctx); |
| 187 | 188 | ||
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 15c09f04c..c1e32b28c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -287,13 +287,13 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | |||
| 287 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | 287 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( |
| 288 | Input::AnalogDirection::DOWN)); | 288 | Input::AnalogDirection::DOWN)); |
| 289 | 289 | ||
| 290 | pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 291 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); | ||
| 292 | pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 293 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); | ||
| 294 | pad_state.r_stick_right.Assign( | 290 | pad_state.r_stick_right.Assign( |
| 295 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | 291 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] |
| 296 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); | 292 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); |
| 293 | pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 294 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); | ||
| 295 | pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 296 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); | ||
| 297 | pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | 297 | pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] |
| 298 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); | 298 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); |
| 299 | 299 | ||
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d1fc94060..7c0303684 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -94,6 +94,7 @@ void LogSettings() { | |||
| 94 | LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); | 94 | LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); |
| 95 | LogSetting("Renderer_UseAsynchronousGpuEmulation", | 95 | LogSetting("Renderer_UseAsynchronousGpuEmulation", |
| 96 | Settings::values.use_asynchronous_gpu_emulation); | 96 | Settings::values.use_asynchronous_gpu_emulation); |
| 97 | LogSetting("Renderer_UseVsync", Settings::values.use_vsync); | ||
| 97 | LogSetting("Audio_OutputEngine", Settings::values.sink_id); | 98 | LogSetting("Audio_OutputEngine", Settings::values.sink_id); |
| 98 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | 99 | LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); |
| 99 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); | 100 | LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); |
diff --git a/src/core/settings.h b/src/core/settings.h index f837d3fbc..15b691342 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -435,6 +435,7 @@ struct Values { | |||
| 435 | bool use_disk_shader_cache; | 435 | bool use_disk_shader_cache; |
| 436 | bool use_accurate_gpu_emulation; | 436 | bool use_accurate_gpu_emulation; |
| 437 | bool use_asynchronous_gpu_emulation; | 437 | bool use_asynchronous_gpu_emulation; |
| 438 | bool use_vsync; | ||
| 438 | bool force_30fps_mode; | 439 | bool force_30fps_mode; |
| 439 | 440 | ||
| 440 | float bg_red; | 441 | float bg_red; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 0e72d31cd..0f3685d1c 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -188,6 +188,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | |||
| 188 | Settings::values.use_accurate_gpu_emulation); | 188 | Settings::values.use_accurate_gpu_emulation); |
| 189 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", | 189 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", |
| 190 | Settings::values.use_asynchronous_gpu_emulation); | 190 | Settings::values.use_asynchronous_gpu_emulation); |
| 191 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); | ||
| 191 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); | 192 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); |
| 192 | } | 193 | } |
| 193 | 194 | ||
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index e1a260762..6cabdaa3c 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp | |||
| @@ -34,6 +34,20 @@ public: | |||
| 34 | y * coef * (x == 0 ? 1.0f : SQRT_HALF)); | 34 | y * coef * (x == 0 ? 1.0f : SQRT_HALF)); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 38 | switch (direction) { | ||
| 39 | case Input::AnalogDirection::RIGHT: | ||
| 40 | return right->GetStatus(); | ||
| 41 | case Input::AnalogDirection::LEFT: | ||
| 42 | return left->GetStatus(); | ||
| 43 | case Input::AnalogDirection::UP: | ||
| 44 | return up->GetStatus(); | ||
| 45 | case Input::AnalogDirection::DOWN: | ||
| 46 | return down->GetStatus(); | ||
| 47 | } | ||
| 48 | return false; | ||
| 49 | } | ||
| 50 | |||
| 37 | private: | 51 | private: |
| 38 | Button up; | 52 | Button up; |
| 39 | Button down; | 53 | Button down; |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 26939be3f..6ea7cc6a5 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -542,7 +542,7 @@ public: | |||
| 542 | BitField<12, 1, InvMemoryLayout> type; | 542 | BitField<12, 1, InvMemoryLayout> type; |
| 543 | } memory_layout; | 543 | } memory_layout; |
| 544 | union { | 544 | union { |
| 545 | BitField<0, 16, u32> array_mode; | 545 | BitField<0, 16, u32> layers; |
| 546 | BitField<16, 1, u32> volume; | 546 | BitField<16, 1, u32> volume; |
| 547 | }; | 547 | }; |
| 548 | u32 layer_stride; | 548 | u32 layer_stride; |
| @@ -800,8 +800,12 @@ public: | |||
| 800 | 800 | ||
| 801 | u32 zeta_width; | 801 | u32 zeta_width; |
| 802 | u32 zeta_height; | 802 | u32 zeta_height; |
| 803 | union { | ||
| 804 | BitField<0, 16, u32> zeta_layers; | ||
| 805 | BitField<16, 1, u32> zeta_volume; | ||
| 806 | }; | ||
| 803 | 807 | ||
| 804 | INSERT_UNION_PADDING_WORDS(0x27); | 808 | INSERT_UNION_PADDING_WORDS(0x26); |
| 805 | 809 | ||
| 806 | u32 depth_test_enable; | 810 | u32 depth_test_enable; |
| 807 | 811 | ||
| @@ -1507,6 +1511,7 @@ ASSERT_REG_POSITION(vertex_attrib_format, 0x458); | |||
| 1507 | ASSERT_REG_POSITION(rt_control, 0x487); | 1511 | ASSERT_REG_POSITION(rt_control, 0x487); |
| 1508 | ASSERT_REG_POSITION(zeta_width, 0x48a); | 1512 | ASSERT_REG_POSITION(zeta_width, 0x48a); |
| 1509 | ASSERT_REG_POSITION(zeta_height, 0x48b); | 1513 | ASSERT_REG_POSITION(zeta_height, 0x48b); |
| 1514 | ASSERT_REG_POSITION(zeta_layers, 0x48c); | ||
| 1510 | ASSERT_REG_POSITION(depth_test_enable, 0x4B3); | 1515 | ASSERT_REG_POSITION(depth_test_enable, 0x4B3); |
| 1511 | ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); | 1516 | ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); |
| 1512 | ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); | 1517 | ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 7d7137109..e8f763ce9 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -140,71 +140,6 @@ void GPU::FlushCommands() { | |||
| 140 | renderer.Rasterizer().FlushCommands(); | 140 | renderer.Rasterizer().FlushCommands(); |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | u32 RenderTargetBytesPerPixel(RenderTargetFormat format) { | ||
| 144 | ASSERT(format != RenderTargetFormat::NONE); | ||
| 145 | |||
| 146 | switch (format) { | ||
| 147 | case RenderTargetFormat::RGBA32_FLOAT: | ||
| 148 | case RenderTargetFormat::RGBA32_UINT: | ||
| 149 | return 16; | ||
| 150 | case RenderTargetFormat::RGBA16_UINT: | ||
| 151 | case RenderTargetFormat::RGBA16_UNORM: | ||
| 152 | case RenderTargetFormat::RGBA16_FLOAT: | ||
| 153 | case RenderTargetFormat::RGBX16_FLOAT: | ||
| 154 | case RenderTargetFormat::RG32_FLOAT: | ||
| 155 | case RenderTargetFormat::RG32_UINT: | ||
| 156 | return 8; | ||
| 157 | case RenderTargetFormat::RGBA8_UNORM: | ||
| 158 | case RenderTargetFormat::RGBA8_SNORM: | ||
| 159 | case RenderTargetFormat::RGBA8_SRGB: | ||
| 160 | case RenderTargetFormat::RGBA8_UINT: | ||
| 161 | case RenderTargetFormat::RGB10_A2_UNORM: | ||
| 162 | case RenderTargetFormat::BGRA8_UNORM: | ||
| 163 | case RenderTargetFormat::BGRA8_SRGB: | ||
| 164 | case RenderTargetFormat::RG16_UNORM: | ||
| 165 | case RenderTargetFormat::RG16_SNORM: | ||
| 166 | case RenderTargetFormat::RG16_UINT: | ||
| 167 | case RenderTargetFormat::RG16_SINT: | ||
| 168 | case RenderTargetFormat::RG16_FLOAT: | ||
| 169 | case RenderTargetFormat::R32_FLOAT: | ||
| 170 | case RenderTargetFormat::R11G11B10_FLOAT: | ||
| 171 | case RenderTargetFormat::R32_UINT: | ||
| 172 | return 4; | ||
| 173 | case RenderTargetFormat::R16_UNORM: | ||
| 174 | case RenderTargetFormat::R16_SNORM: | ||
| 175 | case RenderTargetFormat::R16_UINT: | ||
| 176 | case RenderTargetFormat::R16_SINT: | ||
| 177 | case RenderTargetFormat::R16_FLOAT: | ||
| 178 | case RenderTargetFormat::RG8_UNORM: | ||
| 179 | case RenderTargetFormat::RG8_SNORM: | ||
| 180 | return 2; | ||
| 181 | case RenderTargetFormat::R8_UNORM: | ||
| 182 | case RenderTargetFormat::R8_UINT: | ||
| 183 | return 1; | ||
| 184 | default: | ||
| 185 | UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format)); | ||
| 186 | return 1; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | u32 DepthFormatBytesPerPixel(DepthFormat format) { | ||
| 191 | switch (format) { | ||
| 192 | case DepthFormat::Z32_S8_X24_FLOAT: | ||
| 193 | return 8; | ||
| 194 | case DepthFormat::Z32_FLOAT: | ||
| 195 | case DepthFormat::S8_Z24_UNORM: | ||
| 196 | case DepthFormat::Z24_X8_UNORM: | ||
| 197 | case DepthFormat::Z24_S8_UNORM: | ||
| 198 | case DepthFormat::Z24_C8_UNORM: | ||
| 199 | return 4; | ||
| 200 | case DepthFormat::Z16_UNORM: | ||
| 201 | return 2; | ||
| 202 | default: | ||
| 203 | UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format)); | ||
| 204 | return 1; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence | 143 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence |
| 209 | // their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. | 144 | // their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. |
| 210 | // So the values you see in docs might be multiplied by 4. | 145 | // So the values you see in docs might be multiplied by 4. |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 07727210c..ba8c9d665 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -57,6 +57,7 @@ enum class RenderTargetFormat : u32 { | |||
| 57 | RG16_UINT = 0xDD, | 57 | RG16_UINT = 0xDD, |
| 58 | RG16_FLOAT = 0xDE, | 58 | RG16_FLOAT = 0xDE, |
| 59 | R11G11B10_FLOAT = 0xE0, | 59 | R11G11B10_FLOAT = 0xE0, |
| 60 | R32_SINT = 0xE3, | ||
| 60 | R32_UINT = 0xE4, | 61 | R32_UINT = 0xE4, |
| 61 | R32_FLOAT = 0xE5, | 62 | R32_FLOAT = 0xE5, |
| 62 | B5G6R5_UNORM = 0xE8, | 63 | B5G6R5_UNORM = 0xE8, |
| @@ -82,12 +83,6 @@ enum class DepthFormat : u32 { | |||
| 82 | Z32_S8_X24_FLOAT = 0x19, | 83 | Z32_S8_X24_FLOAT = 0x19, |
| 83 | }; | 84 | }; |
| 84 | 85 | ||
| 85 | /// Returns the number of bytes per pixel of each rendertarget format. | ||
| 86 | u32 RenderTargetBytesPerPixel(RenderTargetFormat format); | ||
| 87 | |||
| 88 | /// Returns the number of bytes per pixel of each depth format. | ||
| 89 | u32 DepthFormatBytesPerPixel(DepthFormat format); | ||
| 90 | |||
| 91 | struct CommandListHeader; | 86 | struct CommandListHeader; |
| 92 | class DebugContext; | 87 | class DebugContext; |
| 93 | 88 | ||
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 2cdf1aa7f..b1088af3d 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_window_context.h" | 8 | #include "core/frontend/scope_acquire_context.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" |
| @@ -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::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; | 30 | Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()}; |
| 31 | 31 | ||
| 32 | CommandDataContainer next; | 32 | CommandDataContainer next; |
| 33 | while (state.is_running) { | 33 | while (state.is_running) { |
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp index 2f2fe6859..f2c83266e 100644 --- a/src/video_core/morton.cpp +++ b/src/video_core/morton.cpp | |||
| @@ -85,6 +85,7 @@ static constexpr ConversionArray morton_to_linear_fns = { | |||
| 85 | MortonCopy<true, PixelFormat::RG32UI>, | 85 | MortonCopy<true, PixelFormat::RG32UI>, |
| 86 | MortonCopy<true, PixelFormat::RGBX16F>, | 86 | MortonCopy<true, PixelFormat::RGBX16F>, |
| 87 | MortonCopy<true, PixelFormat::R32UI>, | 87 | MortonCopy<true, PixelFormat::R32UI>, |
| 88 | MortonCopy<true, PixelFormat::R32I>, | ||
| 88 | MortonCopy<true, PixelFormat::ASTC_2D_8X8>, | 89 | MortonCopy<true, PixelFormat::ASTC_2D_8X8>, |
| 89 | MortonCopy<true, PixelFormat::ASTC_2D_8X5>, | 90 | MortonCopy<true, PixelFormat::ASTC_2D_8X5>, |
| 90 | MortonCopy<true, PixelFormat::ASTC_2D_5X4>, | 91 | MortonCopy<true, PixelFormat::ASTC_2D_5X4>, |
| @@ -166,6 +167,7 @@ static constexpr ConversionArray linear_to_morton_fns = { | |||
| 166 | MortonCopy<false, PixelFormat::RG32UI>, | 167 | MortonCopy<false, PixelFormat::RG32UI>, |
| 167 | MortonCopy<false, PixelFormat::RGBX16F>, | 168 | MortonCopy<false, PixelFormat::RGBX16F>, |
| 168 | MortonCopy<false, PixelFormat::R32UI>, | 169 | MortonCopy<false, PixelFormat::R32UI>, |
| 170 | MortonCopy<false, PixelFormat::R32I>, | ||
| 169 | nullptr, | 171 | nullptr, |
| 170 | nullptr, | 172 | nullptr, |
| 171 | nullptr, | 173 | nullptr, |
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index af1bebc4f..5ec99a126 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -35,15 +35,19 @@ public: | |||
| 35 | explicit RendererBase(Core::Frontend::EmuWindow& window); | 35 | explicit RendererBase(Core::Frontend::EmuWindow& window); |
| 36 | virtual ~RendererBase(); | 36 | virtual ~RendererBase(); |
| 37 | 37 | ||
| 38 | /// Swap buffers (render frame) | ||
| 39 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; | ||
| 40 | |||
| 41 | /// Initialize the renderer | 38 | /// Initialize the renderer |
| 42 | virtual bool Init() = 0; | 39 | virtual bool Init() = 0; |
| 43 | 40 | ||
| 44 | /// Shutdown the renderer | 41 | /// Shutdown the renderer |
| 45 | virtual void ShutDown() = 0; | 42 | virtual void ShutDown() = 0; |
| 46 | 43 | ||
| 44 | /// Finalize rendering the guest frame and draw into the presentation texture | ||
| 45 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; | ||
| 46 | |||
| 47 | /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer | ||
| 48 | /// specific implementation) | ||
| 49 | virtual void TryPresent(int timeout_ms) = 0; | ||
| 50 | |||
| 47 | // Getter/setter functions: | 51 | // Getter/setter functions: |
| 48 | // ------------------------ | 52 | // ------------------------ |
| 49 | 53 | ||
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index f0ddfb276..c0aee770f 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp | |||
| @@ -15,6 +15,24 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R | |||
| 15 | 15 | ||
| 16 | namespace OpenGL { | 16 | namespace OpenGL { |
| 17 | 17 | ||
| 18 | void OGLRenderbuffer::Create() { | ||
| 19 | if (handle != 0) | ||
| 20 | return; | ||
| 21 | |||
| 22 | MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | ||
| 23 | glGenRenderbuffers(1, &handle); | ||
| 24 | } | ||
| 25 | |||
| 26 | void OGLRenderbuffer::Release() { | ||
| 27 | if (handle == 0) | ||
| 28 | return; | ||
| 29 | |||
| 30 | MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); | ||
| 31 | glDeleteRenderbuffers(1, &handle); | ||
| 32 | OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply(); | ||
| 33 | handle = 0; | ||
| 34 | } | ||
| 35 | |||
| 18 | void OGLTexture::Create(GLenum target) { | 36 | void OGLTexture::Create(GLenum target) { |
| 19 | if (handle != 0) | 37 | if (handle != 0) |
| 20 | return; | 38 | return; |
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 514d1d165..995a4e45e 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h | |||
| @@ -11,6 +11,31 @@ | |||
| 11 | 11 | ||
| 12 | namespace OpenGL { | 12 | namespace OpenGL { |
| 13 | 13 | ||
| 14 | class OGLRenderbuffer : private NonCopyable { | ||
| 15 | public: | ||
| 16 | OGLRenderbuffer() = default; | ||
| 17 | |||
| 18 | OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | ||
| 19 | |||
| 20 | ~OGLRenderbuffer() { | ||
| 21 | Release(); | ||
| 22 | } | ||
| 23 | |||
| 24 | OGLRenderbuffer& operator=(OGLRenderbuffer&& o) noexcept { | ||
| 25 | Release(); | ||
| 26 | handle = std::exchange(o.handle, 0); | ||
| 27 | return *this; | ||
| 28 | } | ||
| 29 | |||
| 30 | /// Creates a new internal OpenGL resource and stores the handle | ||
| 31 | void Create(); | ||
| 32 | |||
| 33 | /// Deletes the internal OpenGL resource | ||
| 34 | void Release(); | ||
| 35 | |||
| 36 | GLuint handle = 0; | ||
| 37 | }; | ||
| 38 | |||
| 14 | class OGLTexture : private NonCopyable { | 39 | class OGLTexture : private NonCopyable { |
| 15 | public: | 40 | public: |
| 16 | OGLTexture() = default; | 41 | OGLTexture() = default; |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index ab1f7983c..7d3bc1a1f 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -423,6 +423,13 @@ void OpenGLState::ApplyClipControl() { | |||
| 423 | } | 423 | } |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | void OpenGLState::ApplyRenderBuffer() { | ||
| 427 | if (cur_state.renderbuffer != renderbuffer) { | ||
| 428 | cur_state.renderbuffer = renderbuffer; | ||
| 429 | glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 426 | void OpenGLState::ApplyTextures() { | 433 | void OpenGLState::ApplyTextures() { |
| 427 | const std::size_t size = std::size(textures); | 434 | const std::size_t size = std::size(textures); |
| 428 | for (std::size_t i = 0; i < size; ++i) { | 435 | for (std::size_t i = 0; i < size; ++i) { |
| @@ -478,6 +485,7 @@ void OpenGLState::Apply() { | |||
| 478 | ApplyPolygonOffset(); | 485 | ApplyPolygonOffset(); |
| 479 | ApplyAlphaTest(); | 486 | ApplyAlphaTest(); |
| 480 | ApplyClipControl(); | 487 | ApplyClipControl(); |
| 488 | ApplyRenderBuffer(); | ||
| 481 | } | 489 | } |
| 482 | 490 | ||
| 483 | void OpenGLState::EmulateViewportWithScissor() { | 491 | void OpenGLState::EmulateViewportWithScissor() { |
| @@ -551,4 +559,11 @@ OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) { | |||
| 551 | return *this; | 559 | return *this; |
| 552 | } | 560 | } |
| 553 | 561 | ||
| 562 | OpenGLState& OpenGLState::ResetRenderbuffer(GLuint handle) { | ||
| 563 | if (renderbuffer == handle) { | ||
| 564 | renderbuffer = 0; | ||
| 565 | } | ||
| 566 | return *this; | ||
| 567 | } | ||
| 568 | |||
| 554 | } // namespace OpenGL | 569 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 4953eeda2..bce662f2c 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -158,6 +158,8 @@ public: | |||
| 158 | GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE; | 158 | GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE; |
| 159 | } clip_control; | 159 | } clip_control; |
| 160 | 160 | ||
| 161 | GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING | ||
| 162 | |||
| 161 | OpenGLState(); | 163 | OpenGLState(); |
| 162 | 164 | ||
| 163 | /// Get the currently active OpenGL state | 165 | /// Get the currently active OpenGL state |
| @@ -196,6 +198,7 @@ public: | |||
| 196 | void ApplyPolygonOffset(); | 198 | void ApplyPolygonOffset(); |
| 197 | void ApplyAlphaTest(); | 199 | void ApplyAlphaTest(); |
| 198 | void ApplyClipControl(); | 200 | void ApplyClipControl(); |
| 201 | void ApplyRenderBuffer(); | ||
| 199 | 202 | ||
| 200 | /// Resets any references to the given resource | 203 | /// Resets any references to the given resource |
| 201 | OpenGLState& UnbindTexture(GLuint handle); | 204 | OpenGLState& UnbindTexture(GLuint handle); |
| @@ -204,6 +207,7 @@ public: | |||
| 204 | OpenGLState& ResetPipeline(GLuint handle); | 207 | OpenGLState& ResetPipeline(GLuint handle); |
| 205 | OpenGLState& ResetVertexArray(GLuint handle); | 208 | OpenGLState& ResetVertexArray(GLuint handle); |
| 206 | OpenGLState& ResetFramebuffer(GLuint handle); | 209 | OpenGLState& ResetFramebuffer(GLuint handle); |
| 210 | OpenGLState& ResetRenderbuffer(GLuint handle); | ||
| 207 | 211 | ||
| 208 | /// Viewport does not affects glClearBuffer so emulate viewport using scissor test | 212 | /// Viewport does not affects glClearBuffer so emulate viewport using scissor test |
| 209 | void EmulateViewportWithScissor(); | 213 | void EmulateViewportWithScissor(); |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index d4b81cd87..cf934b0d8 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -87,6 +87,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format | |||
| 87 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false}, // RG32UI | 87 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false}, // RG32UI |
| 88 | {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBX16F | 88 | {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBX16F |
| 89 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false}, // R32UI | 89 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false}, // R32UI |
| 90 | {GL_R32I, GL_RED_INTEGER, GL_INT, false}, // R32I | ||
| 90 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8 | 91 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8 |
| 91 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5 | 92 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5 |
| 92 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4 | 93 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4 |
| @@ -260,6 +261,13 @@ CachedSurface::~CachedSurface() = default; | |||
| 260 | void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { | 261 | void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { |
| 261 | MICROPROFILE_SCOPE(OpenGL_Texture_Download); | 262 | MICROPROFILE_SCOPE(OpenGL_Texture_Download); |
| 262 | 263 | ||
| 264 | if (params.IsBuffer()) { | ||
| 265 | glGetNamedBufferSubData(texture_buffer.handle, 0, | ||
| 266 | static_cast<GLsizeiptr>(params.GetHostSizeInBytes()), | ||
| 267 | staging_buffer.data()); | ||
| 268 | return; | ||
| 269 | } | ||
| 270 | |||
| 263 | SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); | 271 | SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); |
| 264 | 272 | ||
| 265 | for (u32 level = 0; level < params.emulated_levels; ++level) { | 273 | for (u32 level = 0; level < params.emulated_levels; ++level) { |
| @@ -398,24 +406,36 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p | |||
| 398 | CachedSurfaceView::~CachedSurfaceView() = default; | 406 | CachedSurfaceView::~CachedSurfaceView() = default; |
| 399 | 407 | ||
| 400 | void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const { | 408 | void CachedSurfaceView::Attach(GLenum attachment, GLenum target) const { |
| 401 | ASSERT(params.num_layers == 1 && params.num_levels == 1); | 409 | ASSERT(params.num_levels == 1); |
| 402 | 410 | ||
| 403 | const auto& owner_params = surface.GetSurfaceParams(); | 411 | const GLuint texture = surface.GetTexture(); |
| 412 | if (params.num_layers > 1) { | ||
| 413 | // Layered framebuffer attachments | ||
| 414 | UNIMPLEMENTED_IF(params.base_layer != 0); | ||
| 415 | |||
| 416 | switch (params.target) { | ||
| 417 | case SurfaceTarget::Texture2DArray: | ||
| 418 | glFramebufferTexture(target, attachment, texture, params.base_level); | ||
| 419 | break; | ||
| 420 | default: | ||
| 421 | UNIMPLEMENTED(); | ||
| 422 | } | ||
| 423 | return; | ||
| 424 | } | ||
| 404 | 425 | ||
| 405 | switch (owner_params.target) { | 426 | const GLenum view_target = surface.GetTarget(); |
| 427 | switch (surface.GetSurfaceParams().target) { | ||
| 406 | case SurfaceTarget::Texture1D: | 428 | case SurfaceTarget::Texture1D: |
| 407 | glFramebufferTexture1D(target, attachment, surface.GetTarget(), surface.GetTexture(), | 429 | glFramebufferTexture1D(target, attachment, view_target, texture, params.base_level); |
| 408 | params.base_level); | ||
| 409 | break; | 430 | break; |
| 410 | case SurfaceTarget::Texture2D: | 431 | case SurfaceTarget::Texture2D: |
| 411 | glFramebufferTexture2D(target, attachment, surface.GetTarget(), surface.GetTexture(), | 432 | glFramebufferTexture2D(target, attachment, view_target, texture, params.base_level); |
| 412 | params.base_level); | ||
| 413 | break; | 433 | break; |
| 414 | case SurfaceTarget::Texture1DArray: | 434 | case SurfaceTarget::Texture1DArray: |
| 415 | case SurfaceTarget::Texture2DArray: | 435 | case SurfaceTarget::Texture2DArray: |
| 416 | case SurfaceTarget::TextureCubemap: | 436 | case SurfaceTarget::TextureCubemap: |
| 417 | case SurfaceTarget::TextureCubeArray: | 437 | case SurfaceTarget::TextureCubeArray: |
| 418 | glFramebufferTextureLayer(target, attachment, surface.GetTexture(), params.base_level, | 438 | glFramebufferTextureLayer(target, attachment, texture, params.base_level, |
| 419 | params.base_layer); | 439 | params.base_layer); |
| 420 | break; | 440 | break; |
| 421 | default: | 441 | default: |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index bba16afaf..a4340b502 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -9,11 +9,11 @@ | |||
| 9 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/microprofile.h" | ||
| 12 | #include "common/telemetry.h" | 13 | #include "common/telemetry.h" |
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 15 | #include "core/frontend/emu_window.h" | 16 | #include "core/frontend/emu_window.h" |
| 16 | #include "core/frontend/scope_acquire_window_context.h" | ||
| 17 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| 18 | #include "core/perf_stats.h" | 18 | #include "core/perf_stats.h" |
| 19 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| @@ -24,6 +24,144 @@ | |||
| 24 | 24 | ||
| 25 | namespace OpenGL { | 25 | namespace OpenGL { |
| 26 | 26 | ||
| 27 | // If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have | ||
| 28 | // to wait on available presentation frames. | ||
| 29 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; | ||
| 30 | |||
| 31 | struct Frame { | ||
| 32 | u32 width{}; /// Width of the frame (to detect resize) | ||
| 33 | u32 height{}; /// Height of the frame | ||
| 34 | bool color_reloaded{}; /// Texture attachment was recreated (ie: resized) | ||
| 35 | OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO | ||
| 36 | OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread | ||
| 37 | OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread | ||
| 38 | GLsync render_fence{}; /// Fence created on the render thread | ||
| 39 | GLsync present_fence{}; /// Fence created on the presentation thread | ||
| 40 | bool is_srgb{}; /// Framebuffer is sRGB or RGB | ||
| 41 | }; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * For smooth Vsync rendering, we want to always present the latest frame that the core generates, | ||
| 45 | * but also make sure that rendering happens at the pace that the frontend dictates. This is a | ||
| 46 | * helper class that the renderer uses to sync frames between the render thread and the presentation | ||
| 47 | * thread | ||
| 48 | */ | ||
| 49 | class FrameMailbox { | ||
| 50 | public: | ||
| 51 | std::mutex swap_chain_lock; | ||
| 52 | std::condition_variable present_cv; | ||
| 53 | std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{}; | ||
| 54 | std::queue<Frame*> free_queue; | ||
| 55 | std::deque<Frame*> present_queue; | ||
| 56 | Frame* previous_frame{}; | ||
| 57 | |||
| 58 | FrameMailbox() { | ||
| 59 | for (auto& frame : swap_chain) { | ||
| 60 | free_queue.push(&frame); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | ~FrameMailbox() { | ||
| 65 | // lock the mutex and clear out the present and free_queues and notify any people who are | ||
| 66 | // blocked to prevent deadlock on shutdown | ||
| 67 | std::scoped_lock lock{swap_chain_lock}; | ||
| 68 | std::queue<Frame*>().swap(free_queue); | ||
| 69 | present_queue.clear(); | ||
| 70 | present_cv.notify_all(); | ||
| 71 | } | ||
| 72 | |||
| 73 | void ReloadPresentFrame(Frame* frame, u32 height, u32 width) { | ||
| 74 | frame->present.Release(); | ||
| 75 | frame->present.Create(); | ||
| 76 | GLint previous_draw_fbo{}; | ||
| 77 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); | ||
| 78 | glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); | ||
| 79 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||
| 80 | frame->color.handle); | ||
| 81 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | ||
| 82 | LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); | ||
| 83 | } | ||
| 84 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo); | ||
| 85 | frame->color_reloaded = false; | ||
| 86 | } | ||
| 87 | |||
| 88 | void ReloadRenderFrame(Frame* frame, u32 width, u32 height) { | ||
| 89 | OpenGLState prev_state = OpenGLState::GetCurState(); | ||
| 90 | OpenGLState state = OpenGLState::GetCurState(); | ||
| 91 | |||
| 92 | // Recreate the color texture attachment | ||
| 93 | frame->color.Release(); | ||
| 94 | frame->color.Create(); | ||
| 95 | state.renderbuffer = frame->color.handle; | ||
| 96 | state.Apply(); | ||
| 97 | glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height); | ||
| 98 | |||
| 99 | // Recreate the FBO for the render target | ||
| 100 | frame->render.Release(); | ||
| 101 | frame->render.Create(); | ||
| 102 | state.draw.read_framebuffer = frame->render.handle; | ||
| 103 | state.draw.draw_framebuffer = frame->render.handle; | ||
| 104 | state.Apply(); | ||
| 105 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||
| 106 | frame->color.handle); | ||
| 107 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | ||
| 108 | LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); | ||
| 109 | } | ||
| 110 | prev_state.Apply(); | ||
| 111 | frame->width = width; | ||
| 112 | frame->height = height; | ||
| 113 | frame->color_reloaded = true; | ||
| 114 | } | ||
| 115 | |||
| 116 | Frame* GetRenderFrame() { | ||
| 117 | std::unique_lock lock{swap_chain_lock}; | ||
| 118 | |||
| 119 | // If theres no free frames, we will reuse the oldest render frame | ||
| 120 | if (free_queue.empty()) { | ||
| 121 | auto frame = present_queue.back(); | ||
| 122 | present_queue.pop_back(); | ||
| 123 | return frame; | ||
| 124 | } | ||
| 125 | |||
| 126 | Frame* frame = free_queue.front(); | ||
| 127 | free_queue.pop(); | ||
| 128 | return frame; | ||
| 129 | } | ||
| 130 | |||
| 131 | void ReleaseRenderFrame(Frame* frame) { | ||
| 132 | std::unique_lock lock{swap_chain_lock}; | ||
| 133 | present_queue.push_front(frame); | ||
| 134 | present_cv.notify_one(); | ||
| 135 | } | ||
| 136 | |||
| 137 | Frame* TryGetPresentFrame(int timeout_ms) { | ||
| 138 | std::unique_lock lock{swap_chain_lock}; | ||
| 139 | // wait for new entries in the present_queue | ||
| 140 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), | ||
| 141 | [&] { return !present_queue.empty(); }); | ||
| 142 | if (present_queue.empty()) { | ||
| 143 | // timed out waiting for a frame to draw so return the previous frame | ||
| 144 | return previous_frame; | ||
| 145 | } | ||
| 146 | |||
| 147 | // free the previous frame and add it back to the free queue | ||
| 148 | if (previous_frame) { | ||
| 149 | free_queue.push(previous_frame); | ||
| 150 | } | ||
| 151 | |||
| 152 | // the newest entries are pushed to the front of the queue | ||
| 153 | Frame* frame = present_queue.front(); | ||
| 154 | present_queue.pop_front(); | ||
| 155 | // remove all old entries from the present queue and move them back to the free_queue | ||
| 156 | for (auto f : present_queue) { | ||
| 157 | free_queue.push(f); | ||
| 158 | } | ||
| 159 | present_queue.clear(); | ||
| 160 | previous_frame = frame; | ||
| 161 | return frame; | ||
| 162 | } | ||
| 163 | }; | ||
| 164 | |||
| 27 | namespace { | 165 | namespace { |
| 28 | 166 | ||
| 29 | constexpr char vertex_shader[] = R"( | 167 | constexpr char vertex_shader[] = R"( |
| @@ -158,21 +296,91 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit | |||
| 158 | } // Anonymous namespace | 296 | } // Anonymous namespace |
| 159 | 297 | ||
| 160 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) | 298 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) |
| 161 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {} | 299 | : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, |
| 300 | frame_mailbox{std::make_unique<FrameMailbox>()} {} | ||
| 162 | 301 | ||
| 163 | RendererOpenGL::~RendererOpenGL() = default; | 302 | RendererOpenGL::~RendererOpenGL() = default; |
| 164 | 303 | ||
| 304 | MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); | ||
| 305 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); | ||
| 306 | |||
| 165 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 307 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 308 | render_window.PollEvents(); | ||
| 309 | |||
| 310 | if (!framebuffer) { | ||
| 311 | return; | ||
| 312 | } | ||
| 313 | |||
| 166 | // Maintain the rasterizer's state as a priority | 314 | // Maintain the rasterizer's state as a priority |
| 167 | OpenGLState prev_state = OpenGLState::GetCurState(); | 315 | OpenGLState prev_state = OpenGLState::GetCurState(); |
| 168 | state.AllDirty(); | 316 | state.AllDirty(); |
| 169 | state.Apply(); | 317 | state.Apply(); |
| 170 | 318 | ||
| 319 | PrepareRendertarget(framebuffer); | ||
| 320 | RenderScreenshot(); | ||
| 321 | |||
| 322 | Frame* frame; | ||
| 323 | { | ||
| 324 | MICROPROFILE_SCOPE(OpenGL_WaitPresent); | ||
| 325 | |||
| 326 | frame = frame_mailbox->GetRenderFrame(); | ||
| 327 | |||
| 328 | // Clean up sync objects before drawing | ||
| 329 | |||
| 330 | // INTEL driver workaround. We can't delete the previous render sync object until we are | ||
| 331 | // sure that the presentation is done | ||
| 332 | if (frame->present_fence) { | ||
| 333 | glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED); | ||
| 334 | } | ||
| 335 | |||
| 336 | // delete the draw fence if the frame wasn't presented | ||
| 337 | if (frame->render_fence) { | ||
| 338 | glDeleteSync(frame->render_fence); | ||
| 339 | frame->render_fence = 0; | ||
| 340 | } | ||
| 341 | |||
| 342 | // wait for the presentation to be done | ||
| 343 | if (frame->present_fence) { | ||
| 344 | glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED); | ||
| 345 | glDeleteSync(frame->present_fence); | ||
| 346 | frame->present_fence = 0; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | { | ||
| 351 | MICROPROFILE_SCOPE(OpenGL_RenderFrame); | ||
| 352 | const auto& layout = render_window.GetFramebufferLayout(); | ||
| 353 | |||
| 354 | // Recreate the frame if the size of the window has changed | ||
| 355 | if (layout.width != frame->width || layout.height != frame->height || | ||
| 356 | screen_info.display_srgb != frame->is_srgb) { | ||
| 357 | LOG_DEBUG(Render_OpenGL, "Reloading render frame"); | ||
| 358 | frame->is_srgb = screen_info.display_srgb; | ||
| 359 | frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height); | ||
| 360 | } | ||
| 361 | state.draw.draw_framebuffer = frame->render.handle; | ||
| 362 | state.Apply(); | ||
| 363 | DrawScreen(layout); | ||
| 364 | // Create a fence for the frontend to wait on and swap this frame to OffTex | ||
| 365 | frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); | ||
| 366 | glFlush(); | ||
| 367 | frame_mailbox->ReleaseRenderFrame(frame); | ||
| 368 | m_current_frame++; | ||
| 369 | rasterizer->TickFrame(); | ||
| 370 | } | ||
| 371 | |||
| 372 | // Restore the rasterizer state | ||
| 373 | prev_state.AllDirty(); | ||
| 374 | prev_state.Apply(); | ||
| 375 | } | ||
| 376 | |||
| 377 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { | ||
| 171 | if (framebuffer) { | 378 | if (framebuffer) { |
| 172 | // If framebuffer is provided, reload it from memory to a texture | 379 | // If framebuffer is provided, reload it from memory to a texture |
| 173 | if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || | 380 | if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || |
| 174 | screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || | 381 | screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || |
| 175 | screen_info.texture.pixel_format != framebuffer->pixel_format) { | 382 | screen_info.texture.pixel_format != framebuffer->pixel_format || |
| 383 | gl_framebuffer_data.empty()) { | ||
| 176 | // Reallocate texture if the framebuffer size has changed. | 384 | // Reallocate texture if the framebuffer size has changed. |
| 177 | // This is expected to not happen very often and hence should not be a | 385 | // This is expected to not happen very often and hence should not be a |
| 178 | // performance problem. | 386 | // performance problem. |
| @@ -181,22 +389,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 181 | 389 | ||
| 182 | // Load the framebuffer from memory, draw it to the screen, and swap buffers | 390 | // Load the framebuffer from memory, draw it to the screen, and swap buffers |
| 183 | LoadFBToScreenInfo(*framebuffer); | 391 | LoadFBToScreenInfo(*framebuffer); |
| 184 | |||
| 185 | if (renderer_settings.screenshot_requested) | ||
| 186 | CaptureScreenshot(); | ||
| 187 | |||
| 188 | DrawScreen(render_window.GetFramebufferLayout()); | ||
| 189 | |||
| 190 | rasterizer->TickFrame(); | ||
| 191 | |||
| 192 | render_window.SwapBuffers(); | ||
| 193 | } | 392 | } |
| 194 | |||
| 195 | render_window.PollEvents(); | ||
| 196 | |||
| 197 | // Restore the rasterizer state | ||
| 198 | prev_state.AllDirty(); | ||
| 199 | prev_state.Apply(); | ||
| 200 | } | 393 | } |
| 201 | 394 | ||
| 202 | void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | 395 | void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { |
| @@ -418,13 +611,48 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 418 | DrawScreenTriangles(screen_info, static_cast<float>(screen.left), | 611 | DrawScreenTriangles(screen_info, static_cast<float>(screen.left), |
| 419 | static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()), | 612 | static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()), |
| 420 | static_cast<float>(screen.GetHeight())); | 613 | static_cast<float>(screen.GetHeight())); |
| 614 | } | ||
| 421 | 615 | ||
| 422 | m_current_frame++; | 616 | void RendererOpenGL::TryPresent(int timeout_ms) { |
| 617 | const auto& layout = render_window.GetFramebufferLayout(); | ||
| 618 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); | ||
| 619 | if (!frame) { | ||
| 620 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); | ||
| 621 | return; | ||
| 622 | } | ||
| 623 | |||
| 624 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a | ||
| 625 | // readback since we won't be doing any blending | ||
| 626 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 627 | |||
| 628 | // Recreate the presentation FBO if the color attachment was changed | ||
| 629 | if (frame->color_reloaded) { | ||
| 630 | LOG_DEBUG(Render_OpenGL, "Reloading present frame"); | ||
| 631 | frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height); | ||
| 632 | } | ||
| 633 | glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED); | ||
| 634 | // INTEL workaround. | ||
| 635 | // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete | ||
| 636 | // it on the emulation thread without too much penalty | ||
| 637 | // glDeleteSync(frame.render_sync); | ||
| 638 | // frame.render_sync = 0; | ||
| 639 | |||
| 640 | glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle); | ||
| 641 | glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height, | ||
| 642 | GL_COLOR_BUFFER_BIT, GL_LINEAR); | ||
| 643 | |||
| 644 | // Insert fence for the main thread to block on | ||
| 645 | frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); | ||
| 646 | glFlush(); | ||
| 647 | |||
| 648 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | ||
| 423 | } | 649 | } |
| 424 | 650 | ||
| 425 | void RendererOpenGL::UpdateFramerate() {} | 651 | void RendererOpenGL::RenderScreenshot() { |
| 652 | if (!renderer_settings.screenshot_requested) { | ||
| 653 | return; | ||
| 654 | } | ||
| 426 | 655 | ||
| 427 | void RendererOpenGL::CaptureScreenshot() { | ||
| 428 | // Draw the current frame to the screenshot framebuffer | 656 | // Draw the current frame to the screenshot framebuffer |
| 429 | screenshot_framebuffer.Create(); | 657 | screenshot_framebuffer.Create(); |
| 430 | GLuint old_read_fb = state.draw.read_framebuffer; | 658 | GLuint old_read_fb = state.draw.read_framebuffer; |
| @@ -459,8 +687,6 @@ void RendererOpenGL::CaptureScreenshot() { | |||
| 459 | } | 687 | } |
| 460 | 688 | ||
| 461 | bool RendererOpenGL::Init() { | 689 | bool RendererOpenGL::Init() { |
| 462 | Core::Frontend::ScopeAcquireWindowContext acquire_context{render_window}; | ||
| 463 | |||
| 464 | if (GLAD_GL_KHR_debug) { | 690 | if (GLAD_GL_KHR_debug) { |
| 465 | glEnable(GL_DEBUG_OUTPUT); | 691 | glEnable(GL_DEBUG_OUTPUT); |
| 466 | glDebugMessageCallback(DebugHandler, nullptr); | 692 | glDebugMessageCallback(DebugHandler, nullptr); |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index b56328a7f..d45e69cbc 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -44,19 +44,23 @@ struct ScreenInfo { | |||
| 44 | TextureInfo texture; | 44 | TextureInfo texture; |
| 45 | }; | 45 | }; |
| 46 | 46 | ||
| 47 | struct PresentationTexture { | ||
| 48 | u32 width = 0; | ||
| 49 | u32 height = 0; | ||
| 50 | OGLTexture texture; | ||
| 51 | }; | ||
| 52 | |||
| 53 | class FrameMailbox; | ||
| 54 | |||
| 47 | class RendererOpenGL final : public VideoCore::RendererBase { | 55 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 48 | public: | 56 | public: |
| 49 | explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); | 57 | explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); |
| 50 | ~RendererOpenGL() override; | 58 | ~RendererOpenGL() override; |
| 51 | 59 | ||
| 52 | /// Swap buffers (render frame) | ||
| 53 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | ||
| 54 | |||
| 55 | /// Initialize the renderer | ||
| 56 | bool Init() override; | 60 | bool Init() override; |
| 57 | |||
| 58 | /// Shutdown the renderer | ||
| 59 | void ShutDown() override; | 61 | void ShutDown() override; |
| 62 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | ||
| 63 | void TryPresent(int timeout_ms) override; | ||
| 60 | 64 | ||
| 61 | private: | 65 | private: |
| 62 | /// Initializes the OpenGL state and creates persistent objects. | 66 | /// Initializes the OpenGL state and creates persistent objects. |
| @@ -74,10 +78,7 @@ private: | |||
| 74 | 78 | ||
| 75 | void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); | 79 | void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); |
| 76 | 80 | ||
| 77 | /// Updates the framerate. | 81 | void RenderScreenshot(); |
| 78 | void UpdateFramerate(); | ||
| 79 | |||
| 80 | void CaptureScreenshot(); | ||
| 81 | 82 | ||
| 82 | /// Loads framebuffer from emulated memory into the active OpenGL texture. | 83 | /// Loads framebuffer from emulated memory into the active OpenGL texture. |
| 83 | void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); | 84 | void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); |
| @@ -87,6 +88,8 @@ private: | |||
| 87 | void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | 88 | void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, |
| 88 | const TextureInfo& texture); | 89 | const TextureInfo& texture); |
| 89 | 90 | ||
| 91 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); | ||
| 92 | |||
| 90 | Core::Frontend::EmuWindow& emu_window; | 93 | Core::Frontend::EmuWindow& emu_window; |
| 91 | Core::System& system; | 94 | Core::System& system; |
| 92 | 95 | ||
| @@ -107,6 +110,9 @@ private: | |||
| 107 | /// Used for transforming the framebuffer orientation | 110 | /// Used for transforming the framebuffer orientation |
| 108 | Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; | 111 | Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; |
| 109 | Common::Rectangle<int> framebuffer_crop_rect; | 112 | Common::Rectangle<int> framebuffer_crop_rect; |
| 113 | |||
| 114 | /// Frame presentation mailbox | ||
| 115 | std::unique_ptr<FrameMailbox> frame_mailbox; | ||
| 110 | }; | 116 | }; |
| 111 | 117 | ||
| 112 | } // namespace OpenGL | 118 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 5403c3ab7..ef66dd141 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -159,6 +159,7 @@ struct FormatTuple { | |||
| 159 | {vk::Format::eR32G32Uint, Attachable | Storage}, // RG32UI | 159 | {vk::Format::eR32G32Uint, Attachable | Storage}, // RG32UI |
| 160 | {vk::Format::eUndefined, {}}, // RGBX16F | 160 | {vk::Format::eUndefined, {}}, // RGBX16F |
| 161 | {vk::Format::eR32Uint, Attachable | Storage}, // R32UI | 161 | {vk::Format::eR32Uint, Attachable | Storage}, // R32UI |
| 162 | {vk::Format::eR32Sint, Attachable | Storage}, // R32I | ||
| 162 | {vk::Format::eAstc8x8UnormBlock, {}}, // ASTC_2D_8X8 | 163 | {vk::Format::eAstc8x8UnormBlock, {}}, // ASTC_2D_8X8 |
| 163 | {vk::Format::eUndefined, {}}, // ASTC_2D_8X5 | 164 | {vk::Format::eUndefined, {}}, // ASTC_2D_8X5 |
| 164 | {vk::Format::eUndefined, {}}, // ASTC_2D_5X4 | 165 | {vk::Format::eUndefined, {}}, // ASTC_2D_5X4 |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index d5032b432..ddc62bc97 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -106,8 +106,14 @@ RendererVulkan::~RendererVulkan() { | |||
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 108 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 109 | render_window.PollEvents(); | ||
| 110 | |||
| 111 | if (!framebuffer) { | ||
| 112 | return; | ||
| 113 | } | ||
| 114 | |||
| 109 | const auto& layout = render_window.GetFramebufferLayout(); | 115 | const auto& layout = render_window.GetFramebufferLayout(); |
| 110 | if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) { | 116 | if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) { |
| 111 | const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; | 117 | const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; |
| 112 | const bool use_accelerated = | 118 | const bool use_accelerated = |
| 113 | rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); | 119 | rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); |
| @@ -128,13 +134,16 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 128 | blit_screen->Recreate(); | 134 | blit_screen->Recreate(); |
| 129 | } | 135 | } |
| 130 | 136 | ||
| 131 | render_window.SwapBuffers(); | ||
| 132 | rasterizer->TickFrame(); | 137 | rasterizer->TickFrame(); |
| 133 | } | 138 | } |
| 134 | 139 | ||
| 135 | render_window.PollEvents(); | 140 | render_window.PollEvents(); |
| 136 | } | 141 | } |
| 137 | 142 | ||
| 143 | void RendererVulkan::TryPresent(int /*timeout_ms*/) { | ||
| 144 | // TODO (bunnei): ImplementMe | ||
| 145 | } | ||
| 146 | |||
| 138 | bool RendererVulkan::Init() { | 147 | bool RendererVulkan::Init() { |
| 139 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; | 148 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; |
| 140 | render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); | 149 | render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); |
| @@ -262,4 +271,4 @@ void RendererVulkan::Report() const { | |||
| 262 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | 271 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |
| 263 | } | 272 | } |
| 264 | 273 | ||
| 265 | } // namespace Vulkan \ No newline at end of file | 274 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index a472c5dc9..f513397f0 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -36,14 +36,10 @@ public: | |||
| 36 | explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); | 36 | explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); |
| 37 | ~RendererVulkan() override; | 37 | ~RendererVulkan() override; |
| 38 | 38 | ||
| 39 | /// Swap buffers (render frame) | ||
| 40 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | ||
| 41 | |||
| 42 | /// Initialize the renderer | ||
| 43 | bool Init() override; | 39 | bool Init() override; |
| 44 | |||
| 45 | /// Shutdown the renderer | ||
| 46 | void ShutDown() override; | 40 | void ShutDown() override; |
| 41 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | ||
| 42 | void TryPresent(int timeout_ms) override; | ||
| 47 | 43 | ||
| 48 | private: | 44 | private: |
| 49 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( | 45 | std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( |
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index d1da4f9d3..886bde3b9 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -523,6 +523,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti | |||
| 523 | vk::Format::eB10G11R11UfloatPack32, | 523 | vk::Format::eB10G11R11UfloatPack32, |
| 524 | vk::Format::eR32Sfloat, | 524 | vk::Format::eR32Sfloat, |
| 525 | vk::Format::eR32Uint, | 525 | vk::Format::eR32Uint, |
| 526 | vk::Format::eR32Sint, | ||
| 526 | vk::Format::eR16Sfloat, | 527 | vk::Format::eR16Sfloat, |
| 527 | vk::Format::eR16G16B16A16Sfloat, | 528 | vk::Format::eR16G16B16A16Sfloat, |
| 528 | vk::Format::eB8G8R8A8Unorm, | 529 | vk::Format::eB8G8R8A8Unorm, |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 31c078f6a..3bf86da87 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -611,33 +611,34 @@ bool RasterizerVulkan::WalkAttachmentOverlaps(const CachedSurfaceView& attachmen | |||
| 611 | std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers( | 611 | std::tuple<vk::Framebuffer, vk::Extent2D> RasterizerVulkan::ConfigureFramebuffers( |
| 612 | vk::RenderPass renderpass) { | 612 | vk::RenderPass renderpass) { |
| 613 | FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(), | 613 | FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(), |
| 614 | std::numeric_limits<u32>::max()}; | 614 | std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()}; |
| 615 | 615 | ||
| 616 | const auto MarkAsModifiedAndPush = [&](const View& view) { | 616 | const auto try_push = [&](const View& view) { |
| 617 | if (view == nullptr) { | 617 | if (!view) { |
| 618 | return false; | 618 | return false; |
| 619 | } | 619 | } |
| 620 | key.views.push_back(view->GetHandle()); | 620 | key.views.push_back(view->GetHandle()); |
| 621 | key.width = std::min(key.width, view->GetWidth()); | 621 | key.width = std::min(key.width, view->GetWidth()); |
| 622 | key.height = std::min(key.height, view->GetHeight()); | 622 | key.height = std::min(key.height, view->GetHeight()); |
| 623 | key.layers = std::min(key.layers, view->GetNumLayers()); | ||
| 623 | return true; | 624 | return true; |
| 624 | }; | 625 | }; |
| 625 | 626 | ||
| 626 | for (std::size_t index = 0; index < std::size(color_attachments); ++index) { | 627 | for (std::size_t index = 0; index < std::size(color_attachments); ++index) { |
| 627 | if (MarkAsModifiedAndPush(color_attachments[index])) { | 628 | if (try_push(color_attachments[index])) { |
| 628 | texture_cache.MarkColorBufferInUse(index); | 629 | texture_cache.MarkColorBufferInUse(index); |
| 629 | } | 630 | } |
| 630 | } | 631 | } |
| 631 | if (MarkAsModifiedAndPush(zeta_attachment)) { | 632 | if (try_push(zeta_attachment)) { |
| 632 | texture_cache.MarkDepthBufferInUse(); | 633 | texture_cache.MarkDepthBufferInUse(); |
| 633 | } | 634 | } |
| 634 | 635 | ||
| 635 | const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key); | 636 | const auto [fbentry, is_cache_miss] = framebuffer_cache.try_emplace(key); |
| 636 | auto& framebuffer = fbentry->second; | 637 | auto& framebuffer = fbentry->second; |
| 637 | if (is_cache_miss) { | 638 | if (is_cache_miss) { |
| 638 | const vk::FramebufferCreateInfo framebuffer_ci({}, key.renderpass, | 639 | const vk::FramebufferCreateInfo framebuffer_ci( |
| 639 | static_cast<u32>(key.views.size()), | 640 | {}, key.renderpass, static_cast<u32>(key.views.size()), key.views.data(), key.width, |
| 640 | key.views.data(), key.width, key.height, 1); | 641 | key.height, key.layers); |
| 641 | const auto dev = device.GetLogical(); | 642 | const auto dev = device.GetLogical(); |
| 642 | const auto& dld = device.GetDispatchLoader(); | 643 | const auto& dld = device.GetDispatchLoader(); |
| 643 | framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld); | 644 | framebuffer = dev.createFramebufferUnique(framebuffer_ci, nullptr, dld); |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 138903d60..4dc8af6e8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -56,6 +56,7 @@ struct FramebufferCacheKey { | |||
| 56 | vk::RenderPass renderpass{}; | 56 | vk::RenderPass renderpass{}; |
| 57 | u32 width = 0; | 57 | u32 width = 0; |
| 58 | u32 height = 0; | 58 | u32 height = 0; |
| 59 | u32 layers = 0; | ||
| 59 | ImageViewsPack views; | 60 | ImageViewsPack views; |
| 60 | 61 | ||
| 61 | std::size_t Hash() const noexcept { | 62 | std::size_t Hash() const noexcept { |
| @@ -66,12 +67,17 @@ struct FramebufferCacheKey { | |||
| 66 | } | 67 | } |
| 67 | boost::hash_combine(hash, width); | 68 | boost::hash_combine(hash, width); |
| 68 | boost::hash_combine(hash, height); | 69 | boost::hash_combine(hash, height); |
| 70 | boost::hash_combine(hash, layers); | ||
| 69 | return hash; | 71 | return hash; |
| 70 | } | 72 | } |
| 71 | 73 | ||
| 72 | bool operator==(const FramebufferCacheKey& rhs) const noexcept { | 74 | bool operator==(const FramebufferCacheKey& rhs) const noexcept { |
| 73 | return std::tie(renderpass, views, width, height) == | 75 | return std::tie(renderpass, views, width, height, layers) == |
| 74 | std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height); | 76 | std::tie(rhs.renderpass, rhs.views, rhs.width, rhs.height, rhs.layers); |
| 77 | } | ||
| 78 | |||
| 79 | bool operator!=(const FramebufferCacheKey& rhs) const noexcept { | ||
| 80 | return !operator==(rhs); | ||
| 75 | } | 81 | } |
| 76 | }; | 82 | }; |
| 77 | 83 | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 6d0bf6aa1..2da622d15 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -86,6 +86,7 @@ struct AttributeType { | |||
| 86 | 86 | ||
| 87 | struct VertexIndices { | 87 | struct VertexIndices { |
| 88 | std::optional<u32> position; | 88 | std::optional<u32> position; |
| 89 | std::optional<u32> layer; | ||
| 89 | std::optional<u32> viewport; | 90 | std::optional<u32> viewport; |
| 90 | std::optional<u32> point_size; | 91 | std::optional<u32> point_size; |
| 91 | std::optional<u32> clip_distances; | 92 | std::optional<u32> clip_distances; |
| @@ -284,9 +285,11 @@ public: | |||
| 284 | AddExtension("SPV_KHR_variable_pointers"); | 285 | AddExtension("SPV_KHR_variable_pointers"); |
| 285 | AddExtension("SPV_KHR_shader_draw_parameters"); | 286 | AddExtension("SPV_KHR_shader_draw_parameters"); |
| 286 | 287 | ||
| 287 | if (ir.UsesViewportIndex()) { | 288 | if (ir.UsesLayer() || ir.UsesViewportIndex()) { |
| 288 | AddCapability(spv::Capability::MultiViewport); | 289 | if (ir.UsesViewportIndex()) { |
| 289 | if (device.IsExtShaderViewportIndexLayerSupported()) { | 290 | AddCapability(spv::Capability::MultiViewport); |
| 291 | } | ||
| 292 | if (stage != ShaderType::Geometry && device.IsExtShaderViewportIndexLayerSupported()) { | ||
| 290 | AddExtension("SPV_EXT_shader_viewport_index_layer"); | 293 | AddExtension("SPV_EXT_shader_viewport_index_layer"); |
| 291 | AddCapability(spv::Capability::ShaderViewportIndexLayerEXT); | 294 | AddCapability(spv::Capability::ShaderViewportIndexLayerEXT); |
| 292 | } | 295 | } |
| @@ -928,13 +931,22 @@ private: | |||
| 928 | VertexIndices indices; | 931 | VertexIndices indices; |
| 929 | indices.position = AddBuiltIn(t_float4, spv::BuiltIn::Position, "position"); | 932 | indices.position = AddBuiltIn(t_float4, spv::BuiltIn::Position, "position"); |
| 930 | 933 | ||
| 934 | if (ir.UsesLayer()) { | ||
| 935 | if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) { | ||
| 936 | indices.layer = AddBuiltIn(t_int, spv::BuiltIn::Layer, "layer"); | ||
| 937 | } else { | ||
| 938 | LOG_ERROR( | ||
| 939 | Render_Vulkan, | ||
| 940 | "Shader requires Layer but it's not supported on this stage with this device."); | ||
| 941 | } | ||
| 942 | } | ||
| 943 | |||
| 931 | if (ir.UsesViewportIndex()) { | 944 | if (ir.UsesViewportIndex()) { |
| 932 | if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) { | 945 | if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) { |
| 933 | indices.viewport = AddBuiltIn(t_int, spv::BuiltIn::ViewportIndex, "viewport_index"); | 946 | indices.viewport = AddBuiltIn(t_int, spv::BuiltIn::ViewportIndex, "viewport_index"); |
| 934 | } else { | 947 | } else { |
| 935 | LOG_ERROR(Render_Vulkan, | 948 | LOG_ERROR(Render_Vulkan, "Shader requires ViewportIndex but it's not supported on " |
| 936 | "Shader requires ViewportIndex but it's not supported on this " | 949 | "this stage with this device."); |
| 937 | "stage with this device."); | ||
| 938 | } | 950 | } |
| 939 | } | 951 | } |
| 940 | 952 | ||
| @@ -1296,6 +1308,13 @@ private: | |||
| 1296 | } | 1308 | } |
| 1297 | case Attribute::Index::LayerViewportPointSize: | 1309 | case Attribute::Index::LayerViewportPointSize: |
| 1298 | switch (element) { | 1310 | switch (element) { |
| 1311 | case 1: { | ||
| 1312 | if (!out_indices.layer) { | ||
| 1313 | return {}; | ||
| 1314 | } | ||
| 1315 | const u32 index = out_indices.layer.value(); | ||
| 1316 | return {AccessElement(t_out_int, out_vertex, index), Type::Int}; | ||
| 1317 | } | ||
| 1299 | case 2: { | 1318 | case 2: { |
| 1300 | if (!out_indices.viewport) { | 1319 | if (!out_indices.viewport) { |
| 1301 | return {}; | 1320 | return {}; |
| @@ -1366,6 +1385,11 @@ private: | |||
| 1366 | UNIMPLEMENTED(); | 1385 | UNIMPLEMENTED(); |
| 1367 | } | 1386 | } |
| 1368 | 1387 | ||
| 1388 | if (!target.id) { | ||
| 1389 | // On failure we return a nullptr target.id, skip these stores. | ||
| 1390 | return {}; | ||
| 1391 | } | ||
| 1392 | |||
| 1369 | OpStore(target.id, As(Visit(src), target.type)); | 1393 | OpStore(target.id, As(Visit(src), target.type)); |
| 1370 | return {}; | 1394 | return {}; |
| 1371 | } | 1395 | } |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index d3edbe80c..22e3d34de 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h | |||
| @@ -151,6 +151,10 @@ public: | |||
| 151 | return params.GetMipHeight(base_level); | 151 | return params.GetMipHeight(base_level); |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | u32 GetNumLayers() const { | ||
| 155 | return num_layers; | ||
| 156 | } | ||
| 157 | |||
| 154 | bool IsBufferView() const { | 158 | bool IsBufferView() const { |
| 155 | return buffer_view; | 159 | return buffer_view; |
| 156 | } | 160 | } |
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp index 90240c765..478394682 100644 --- a/src/video_core/shader/decode/arithmetic.cpp +++ b/src/video_core/shader/decode/arithmetic.cpp | |||
| @@ -53,29 +53,24 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { | |||
| 53 | 53 | ||
| 54 | op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b); | 54 | op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b); |
| 55 | 55 | ||
| 56 | // TODO(Rodrigo): Should precise be used when there's a postfactor? | 56 | static constexpr std::array FmulPostFactor = { |
| 57 | Node value = Operation(OperationCode::FMul, PRECISE, op_a, op_b); | 57 | 1.000f, // None |
| 58 | 0.500f, // Divide 2 | ||
| 59 | 0.250f, // Divide 4 | ||
| 60 | 0.125f, // Divide 8 | ||
| 61 | 8.000f, // Mul 8 | ||
| 62 | 4.000f, // Mul 4 | ||
| 63 | 2.000f, // Mul 2 | ||
| 64 | }; | ||
| 58 | 65 | ||
| 59 | if (instr.fmul.postfactor != 0) { | 66 | if (instr.fmul.postfactor != 0) { |
| 60 | auto postfactor = static_cast<s32>(instr.fmul.postfactor); | 67 | op_a = Operation(OperationCode::FMul, NO_PRECISE, op_a, |
| 61 | 68 | Immediate(FmulPostFactor[instr.fmul.postfactor])); | |
| 62 | // Postfactor encoded as 3-bit 1's complement in instruction, interpreted with below | ||
| 63 | // logic. | ||
| 64 | if (postfactor >= 4) { | ||
| 65 | postfactor = 7 - postfactor; | ||
| 66 | } else { | ||
| 67 | postfactor = 0 - postfactor; | ||
| 68 | } | ||
| 69 | |||
| 70 | if (postfactor > 0) { | ||
| 71 | value = Operation(OperationCode::FMul, NO_PRECISE, value, | ||
| 72 | Immediate(static_cast<f32>(1 << postfactor))); | ||
| 73 | } else { | ||
| 74 | value = Operation(OperationCode::FDiv, NO_PRECISE, value, | ||
| 75 | Immediate(static_cast<f32>(1 << -postfactor))); | ||
| 76 | } | ||
| 77 | } | 69 | } |
| 78 | 70 | ||
| 71 | // TODO(Rodrigo): Should precise be used when there's a postfactor? | ||
| 72 | Node value = Operation(OperationCode::FMul, PRECISE, op_a, op_b); | ||
| 73 | |||
| 79 | value = GetSaturatedFloat(value, instr.alu.saturate_d); | 74 | value = GetSaturatedFloat(value, instr.alu.saturate_d); |
| 80 | 75 | ||
| 81 | SetInternalFlagsFromFloat(bb, value, instr.generates_cc); | 76 | SetInternalFlagsFromFloat(bb, value, instr.generates_cc); |
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp index 21366869d..2fe787d6f 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp | |||
| @@ -293,44 +293,66 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) { | |||
| 293 | 293 | ||
| 294 | void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c, | 294 | void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c, |
| 295 | Node imm_lut, bool sets_cc) { | 295 | Node imm_lut, bool sets_cc) { |
| 296 | constexpr u32 lop_iterations = 32; | 296 | const Node lop3_fast = [&](const Node na, const Node nb, const Node nc, const Node ttbl) { |
| 297 | const Node one = Immediate(1); | 297 | Node value = Immediate(0); |
| 298 | const Node two = Immediate(2); | 298 | const ImmediateNode imm = std::get<ImmediateNode>(*ttbl); |
| 299 | 299 | if (imm.GetValue() & 0x01) { | |
| 300 | Node value; | 300 | const Node a = Operation(OperationCode::IBitwiseNot, na); |
| 301 | for (u32 i = 0; i < lop_iterations; ++i) { | 301 | const Node b = Operation(OperationCode::IBitwiseNot, nb); |
| 302 | const Node shift_amount = Immediate(i); | 302 | const Node c = Operation(OperationCode::IBitwiseNot, nc); |
| 303 | 303 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b); | |
| 304 | const Node a = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_c, shift_amount); | 304 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c); |
| 305 | const Node pack_0 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, one); | 305 | value = Operation(OperationCode::IBitwiseOr, value, r); |
| 306 | |||
| 307 | const Node b = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_b, shift_amount); | ||
| 308 | const Node c = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, b, one); | ||
| 309 | const Node pack_1 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, c, one); | ||
| 310 | |||
| 311 | const Node d = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_a, shift_amount); | ||
| 312 | const Node e = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, d, one); | ||
| 313 | const Node pack_2 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, e, two); | ||
| 314 | |||
| 315 | const Node pack_01 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_0, pack_1); | ||
| 316 | const Node pack_012 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_01, pack_2); | ||
| 317 | |||
| 318 | const Node shifted_bit = | ||
| 319 | Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, imm_lut, pack_012); | ||
| 320 | const Node bit = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, shifted_bit, one); | ||
| 321 | |||
| 322 | const Node right = | ||
| 323 | Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, bit, shift_amount); | ||
| 324 | |||
| 325 | if (i > 0) { | ||
| 326 | value = Operation(OperationCode::IBitwiseOr, NO_PRECISE, value, right); | ||
| 327 | } else { | ||
| 328 | value = right; | ||
| 329 | } | 306 | } |
| 330 | } | 307 | if (imm.GetValue() & 0x02) { |
| 308 | const Node a = Operation(OperationCode::IBitwiseNot, na); | ||
| 309 | const Node b = Operation(OperationCode::IBitwiseNot, nb); | ||
| 310 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b); | ||
| 311 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc); | ||
| 312 | value = Operation(OperationCode::IBitwiseOr, value, r); | ||
| 313 | } | ||
| 314 | if (imm.GetValue() & 0x04) { | ||
| 315 | const Node a = Operation(OperationCode::IBitwiseNot, na); | ||
| 316 | const Node c = Operation(OperationCode::IBitwiseNot, nc); | ||
| 317 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb); | ||
| 318 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c); | ||
| 319 | value = Operation(OperationCode::IBitwiseOr, value, r); | ||
| 320 | } | ||
| 321 | if (imm.GetValue() & 0x08) { | ||
| 322 | const Node a = Operation(OperationCode::IBitwiseNot, na); | ||
| 323 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb); | ||
| 324 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc); | ||
| 325 | value = Operation(OperationCode::IBitwiseOr, value, r); | ||
| 326 | } | ||
| 327 | if (imm.GetValue() & 0x10) { | ||
| 328 | const Node b = Operation(OperationCode::IBitwiseNot, nb); | ||
| 329 | const Node c = Operation(OperationCode::IBitwiseNot, nc); | ||
| 330 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b); | ||
| 331 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c); | ||
| 332 | value = Operation(OperationCode::IBitwiseOr, value, r); | ||
| 333 | } | ||
| 334 | if (imm.GetValue() & 0x20) { | ||
| 335 | const Node b = Operation(OperationCode::IBitwiseNot, nb); | ||
| 336 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b); | ||
| 337 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc); | ||
| 338 | value = Operation(OperationCode::IBitwiseOr, value, r); | ||
| 339 | } | ||
| 340 | if (imm.GetValue() & 0x40) { | ||
| 341 | const Node c = Operation(OperationCode::IBitwiseNot, nc); | ||
| 342 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb); | ||
| 343 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c); | ||
| 344 | value = Operation(OperationCode::IBitwiseOr, value, r); | ||
| 345 | } | ||
| 346 | if (imm.GetValue() & 0x80) { | ||
| 347 | Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb); | ||
| 348 | r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc); | ||
| 349 | value = Operation(OperationCode::IBitwiseOr, value, r); | ||
| 350 | } | ||
| 351 | return value; | ||
| 352 | }(op_a, op_b, op_c, imm_lut); | ||
| 331 | 353 | ||
| 332 | SetInternalFlagsFromInteger(bb, value, sets_cc); | 354 | SetInternalFlagsFromInteger(bb, lop3_fast, sets_cc); |
| 333 | SetRegister(bb, dest, value); | 355 | SetRegister(bb, dest, lop3_fast); |
| 334 | } | 356 | } |
| 335 | 357 | ||
| 336 | } // namespace VideoCommon::Shader | 358 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 1655ccf16..9707c353d 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -155,6 +155,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) | |||
| 155 | return PixelFormat::R16I; | 155 | return PixelFormat::R16I; |
| 156 | case Tegra::RenderTargetFormat::R32_FLOAT: | 156 | case Tegra::RenderTargetFormat::R32_FLOAT: |
| 157 | return PixelFormat::R32F; | 157 | return PixelFormat::R32F; |
| 158 | case Tegra::RenderTargetFormat::R32_SINT: | ||
| 159 | return PixelFormat::R32I; | ||
| 158 | case Tegra::RenderTargetFormat::R32_UINT: | 160 | case Tegra::RenderTargetFormat::R32_UINT: |
| 159 | return PixelFormat::R32UI; | 161 | return PixelFormat::R32UI; |
| 160 | case Tegra::RenderTargetFormat::RG32_UINT: | 162 | case Tegra::RenderTargetFormat::RG32_UINT: |
diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 0d17a93ed..d88109e5a 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h | |||
| @@ -59,47 +59,48 @@ enum class PixelFormat { | |||
| 59 | RG32UI = 41, | 59 | RG32UI = 41, |
| 60 | RGBX16F = 42, | 60 | RGBX16F = 42, |
| 61 | R32UI = 43, | 61 | R32UI = 43, |
| 62 | ASTC_2D_8X8 = 44, | 62 | R32I = 44, |
| 63 | ASTC_2D_8X5 = 45, | 63 | ASTC_2D_8X8 = 45, |
| 64 | ASTC_2D_5X4 = 46, | 64 | ASTC_2D_8X5 = 46, |
| 65 | BGRA8_SRGB = 47, | 65 | ASTC_2D_5X4 = 47, |
| 66 | DXT1_SRGB = 48, | 66 | BGRA8_SRGB = 48, |
| 67 | DXT23_SRGB = 49, | 67 | DXT1_SRGB = 49, |
| 68 | DXT45_SRGB = 50, | 68 | DXT23_SRGB = 50, |
| 69 | BC7U_SRGB = 51, | 69 | DXT45_SRGB = 51, |
| 70 | R4G4B4A4U = 52, | 70 | BC7U_SRGB = 52, |
| 71 | ASTC_2D_4X4_SRGB = 53, | 71 | R4G4B4A4U = 53, |
| 72 | ASTC_2D_8X8_SRGB = 54, | 72 | ASTC_2D_4X4_SRGB = 54, |
| 73 | ASTC_2D_8X5_SRGB = 55, | 73 | ASTC_2D_8X8_SRGB = 55, |
| 74 | ASTC_2D_5X4_SRGB = 56, | 74 | ASTC_2D_8X5_SRGB = 56, |
| 75 | ASTC_2D_5X5 = 57, | 75 | ASTC_2D_5X4_SRGB = 57, |
| 76 | ASTC_2D_5X5_SRGB = 58, | 76 | ASTC_2D_5X5 = 58, |
| 77 | ASTC_2D_10X8 = 59, | 77 | ASTC_2D_5X5_SRGB = 59, |
| 78 | ASTC_2D_10X8_SRGB = 60, | 78 | ASTC_2D_10X8 = 60, |
| 79 | ASTC_2D_6X6 = 61, | 79 | ASTC_2D_10X8_SRGB = 61, |
| 80 | ASTC_2D_6X6_SRGB = 62, | 80 | ASTC_2D_6X6 = 62, |
| 81 | ASTC_2D_10X10 = 63, | 81 | ASTC_2D_6X6_SRGB = 63, |
| 82 | ASTC_2D_10X10_SRGB = 64, | 82 | ASTC_2D_10X10 = 64, |
| 83 | ASTC_2D_12X12 = 65, | 83 | ASTC_2D_10X10_SRGB = 65, |
| 84 | ASTC_2D_12X12_SRGB = 66, | 84 | ASTC_2D_12X12 = 66, |
| 85 | ASTC_2D_8X6 = 67, | 85 | ASTC_2D_12X12_SRGB = 67, |
| 86 | ASTC_2D_8X6_SRGB = 68, | 86 | ASTC_2D_8X6 = 68, |
| 87 | ASTC_2D_6X5 = 69, | 87 | ASTC_2D_8X6_SRGB = 69, |
| 88 | ASTC_2D_6X5_SRGB = 70, | 88 | ASTC_2D_6X5 = 70, |
| 89 | E5B9G9R9F = 71, | 89 | ASTC_2D_6X5_SRGB = 71, |
| 90 | E5B9G9R9F = 72, | ||
| 90 | 91 | ||
| 91 | MaxColorFormat, | 92 | MaxColorFormat, |
| 92 | 93 | ||
| 93 | // Depth formats | 94 | // Depth formats |
| 94 | Z32F = 72, | 95 | Z32F = 73, |
| 95 | Z16 = 73, | 96 | Z16 = 74, |
| 96 | 97 | ||
| 97 | MaxDepthFormat, | 98 | MaxDepthFormat, |
| 98 | 99 | ||
| 99 | // DepthStencil formats | 100 | // DepthStencil formats |
| 100 | Z24S8 = 74, | 101 | Z24S8 = 75, |
| 101 | S8Z24 = 75, | 102 | S8Z24 = 76, |
| 102 | Z32FS8 = 76, | 103 | Z32FS8 = 77, |
| 103 | 104 | ||
| 104 | MaxDepthStencilFormat, | 105 | MaxDepthStencilFormat, |
| 105 | 106 | ||
| @@ -171,6 +172,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{ | |||
| 171 | 0, // RG32UI | 172 | 0, // RG32UI |
| 172 | 0, // RGBX16F | 173 | 0, // RGBX16F |
| 173 | 0, // R32UI | 174 | 0, // R32UI |
| 175 | 0, // R32I | ||
| 174 | 2, // ASTC_2D_8X8 | 176 | 2, // ASTC_2D_8X8 |
| 175 | 2, // ASTC_2D_8X5 | 177 | 2, // ASTC_2D_8X5 |
| 176 | 2, // ASTC_2D_5X4 | 178 | 2, // ASTC_2D_5X4 |
| @@ -267,6 +269,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{ | |||
| 267 | 1, // RG32UI | 269 | 1, // RG32UI |
| 268 | 1, // RGBX16F | 270 | 1, // RGBX16F |
| 269 | 1, // R32UI | 271 | 1, // R32UI |
| 272 | 1, // R32I | ||
| 270 | 8, // ASTC_2D_8X8 | 273 | 8, // ASTC_2D_8X8 |
| 271 | 8, // ASTC_2D_8X5 | 274 | 8, // ASTC_2D_8X5 |
| 272 | 5, // ASTC_2D_5X4 | 275 | 5, // ASTC_2D_5X4 |
| @@ -355,6 +358,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{ | |||
| 355 | 1, // RG32UI | 358 | 1, // RG32UI |
| 356 | 1, // RGBX16F | 359 | 1, // RGBX16F |
| 357 | 1, // R32UI | 360 | 1, // R32UI |
| 361 | 1, // R32I | ||
| 358 | 8, // ASTC_2D_8X8 | 362 | 8, // ASTC_2D_8X8 |
| 359 | 5, // ASTC_2D_8X5 | 363 | 5, // ASTC_2D_8X5 |
| 360 | 4, // ASTC_2D_5X4 | 364 | 4, // ASTC_2D_5X4 |
| @@ -443,6 +447,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ | |||
| 443 | 64, // RG32UI | 447 | 64, // RG32UI |
| 444 | 64, // RGBX16F | 448 | 64, // RGBX16F |
| 445 | 32, // R32UI | 449 | 32, // R32UI |
| 450 | 32, // R32I | ||
| 446 | 128, // ASTC_2D_8X8 | 451 | 128, // ASTC_2D_8X8 |
| 447 | 128, // ASTC_2D_8X5 | 452 | 128, // ASTC_2D_8X5 |
| 448 | 128, // ASTC_2D_5X4 | 453 | 128, // ASTC_2D_5X4 |
| @@ -546,6 +551,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table | |||
| 546 | SurfaceCompression::None, // RG32UI | 551 | SurfaceCompression::None, // RG32UI |
| 547 | SurfaceCompression::None, // RGBX16F | 552 | SurfaceCompression::None, // RGBX16F |
| 548 | SurfaceCompression::None, // R32UI | 553 | SurfaceCompression::None, // R32UI |
| 554 | SurfaceCompression::None, // R32I | ||
| 549 | SurfaceCompression::Converted, // ASTC_2D_8X8 | 555 | SurfaceCompression::Converted, // ASTC_2D_8X8 |
| 550 | SurfaceCompression::Converted, // ASTC_2D_8X5 | 556 | SurfaceCompression::Converted, // ASTC_2D_8X5 |
| 551 | SurfaceCompression::Converted, // ASTC_2D_5X4 | 557 | SurfaceCompression::Converted, // ASTC_2D_5X4 |
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp index 81fb9f633..cc3ad8417 100644 --- a/src/video_core/texture_cache/format_lookup_table.cpp +++ b/src/video_core/texture_cache/format_lookup_table.cpp | |||
| @@ -41,7 +41,7 @@ struct Table { | |||
| 41 | ComponentType alpha_component; | 41 | ComponentType alpha_component; |
| 42 | bool is_srgb; | 42 | bool is_srgb; |
| 43 | }; | 43 | }; |
| 44 | constexpr std::array<Table, 74> DefinitionTable = {{ | 44 | constexpr std::array<Table, 75> DefinitionTable = {{ |
| 45 | {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U}, | 45 | {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U}, |
| 46 | {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S}, | 46 | {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S}, |
| 47 | {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI}, | 47 | {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI}, |
| @@ -89,6 +89,7 @@ constexpr std::array<Table, 74> DefinitionTable = {{ | |||
| 89 | 89 | ||
| 90 | {TextureFormat::R32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32F}, | 90 | {TextureFormat::R32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32F}, |
| 91 | {TextureFormat::R32, C, UINT, UINT, UINT, UINT, PixelFormat::R32UI}, | 91 | {TextureFormat::R32, C, UINT, UINT, UINT, UINT, PixelFormat::R32UI}, |
| 92 | {TextureFormat::R32, C, SINT, SINT, SINT, SINT, PixelFormat::R32I}, | ||
| 92 | 93 | ||
| 93 | {TextureFormat::E5B9G9R9_SHAREDEXP, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::E5B9G9R9F}, | 94 | {TextureFormat::E5B9G9R9_SHAREDEXP, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::E5B9G9R9F}, |
| 94 | 95 | ||
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 84469b7ba..002df414f 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp | |||
| @@ -277,6 +277,10 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager, | |||
| 277 | SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params, | 277 | SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params, |
| 278 | staging_buffer.data() + host_offset, level); | 278 | staging_buffer.data() + host_offset, level); |
| 279 | } | 279 | } |
| 280 | } else if (params.IsBuffer()) { | ||
| 281 | // Buffers don't have pitch or any fancy layout property. We can just memcpy them to guest | ||
| 282 | // memory. | ||
| 283 | std::memcpy(host_ptr, staging_buffer.data(), guest_memory_size); | ||
| 280 | } else { | 284 | } else { |
| 281 | ASSERT(params.target == SurfaceTarget::Texture2D); | 285 | ASSERT(params.target == SurfaceTarget::Texture2D); |
| 282 | ASSERT(params.num_levels == 1); | 286 | ASSERT(params.num_levels == 1); |
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index 38b3a4ba8..f00839313 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp | |||
| @@ -84,19 +84,16 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta | |||
| 84 | if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) { | 84 | if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) { |
| 85 | switch (params.pixel_format) { | 85 | switch (params.pixel_format) { |
| 86 | case PixelFormat::R16U: | 86 | case PixelFormat::R16U: |
| 87 | case PixelFormat::R16F: { | 87 | case PixelFormat::R16F: |
| 88 | params.pixel_format = PixelFormat::Z16; | 88 | params.pixel_format = PixelFormat::Z16; |
| 89 | break; | 89 | break; |
| 90 | } | 90 | case PixelFormat::R32F: |
| 91 | case PixelFormat::R32F: { | ||
| 92 | params.pixel_format = PixelFormat::Z32F; | 91 | params.pixel_format = PixelFormat::Z32F; |
| 93 | break; | 92 | break; |
| 94 | } | 93 | default: |
| 95 | default: { | ||
| 96 | UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}", | 94 | UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}", |
| 97 | static_cast<u32>(params.pixel_format)); | 95 | static_cast<u32>(params.pixel_format)); |
| 98 | } | 96 | } |
| 99 | } | ||
| 100 | params.type = GetFormatType(params.pixel_format); | 97 | params.type = GetFormatType(params.pixel_format); |
| 101 | } | 98 | } |
| 102 | params.type = GetFormatType(params.pixel_format); | 99 | params.type = GetFormatType(params.pixel_format); |
| @@ -168,27 +165,29 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl | |||
| 168 | return params; | 165 | return params; |
| 169 | } | 166 | } |
| 170 | 167 | ||
| 171 | SurfaceParams SurfaceParams::CreateForDepthBuffer( | 168 | SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) { |
| 172 | Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | 169 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 173 | u32 block_width, u32 block_height, u32 block_depth, | 170 | regs.zeta_width, regs.zeta_height, regs.zeta.format, regs.zeta.memory_layout.type; |
| 174 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { | ||
| 175 | SurfaceParams params; | 171 | SurfaceParams params; |
| 176 | params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; | 172 | params.is_tiled = regs.zeta.memory_layout.type == |
| 173 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; | ||
| 177 | params.srgb_conversion = false; | 174 | params.srgb_conversion = false; |
| 178 | params.block_width = std::min(block_width, 5U); | 175 | params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U); |
| 179 | params.block_height = std::min(block_height, 5U); | 176 | params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U); |
| 180 | params.block_depth = std::min(block_depth, 5U); | 177 | params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U); |
| 181 | params.tile_width_spacing = 1; | 178 | params.tile_width_spacing = 1; |
| 182 | params.pixel_format = PixelFormatFromDepthFormat(format); | 179 | params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format); |
| 183 | params.type = GetFormatType(params.pixel_format); | 180 | params.type = GetFormatType(params.pixel_format); |
| 184 | params.width = zeta_width; | 181 | params.width = regs.zeta_width; |
| 185 | params.height = zeta_height; | 182 | params.height = regs.zeta_height; |
| 186 | params.target = SurfaceTarget::Texture2D; | ||
| 187 | params.depth = 1; | ||
| 188 | params.pitch = 0; | 183 | params.pitch = 0; |
| 189 | params.num_levels = 1; | 184 | params.num_levels = 1; |
| 190 | params.emulated_levels = 1; | 185 | params.emulated_levels = 1; |
| 191 | params.is_layered = false; | 186 | |
| 187 | const bool is_layered = regs.zeta_layers > 1 && params.block_depth == 0; | ||
| 188 | params.is_layered = is_layered; | ||
| 189 | params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D; | ||
| 190 | params.depth = is_layered ? regs.zeta_layers.Value() : 1U; | ||
| 192 | return params; | 191 | return params; |
| 193 | } | 192 | } |
| 194 | 193 | ||
| @@ -214,11 +213,13 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz | |||
| 214 | params.width = params.pitch / bpp; | 213 | params.width = params.pitch / bpp; |
| 215 | } | 214 | } |
| 216 | params.height = config.height; | 215 | params.height = config.height; |
| 217 | params.depth = 1; | ||
| 218 | params.target = SurfaceTarget::Texture2D; | ||
| 219 | params.num_levels = 1; | 216 | params.num_levels = 1; |
| 220 | params.emulated_levels = 1; | 217 | params.emulated_levels = 1; |
| 221 | params.is_layered = false; | 218 | |
| 219 | const bool is_layered = config.layers > 1 && params.block_depth == 0; | ||
| 220 | params.is_layered = is_layered; | ||
| 221 | params.depth = is_layered ? config.layers.Value() : 1; | ||
| 222 | params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D; | ||
| 222 | return params; | 223 | return params; |
| 223 | } | 224 | } |
| 224 | 225 | ||
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h index 9256fd6d9..995cc3818 100644 --- a/src/video_core/texture_cache/surface_params.h +++ b/src/video_core/texture_cache/surface_params.h | |||
| @@ -35,10 +35,7 @@ public: | |||
| 35 | const VideoCommon::Shader::Image& entry); | 35 | const VideoCommon::Shader::Image& entry); |
| 36 | 36 | ||
| 37 | /// Creates SurfaceCachedParams for a depth buffer configuration. | 37 | /// Creates SurfaceCachedParams for a depth buffer configuration. |
| 38 | static SurfaceParams CreateForDepthBuffer( | 38 | static SurfaceParams CreateForDepthBuffer(Core::System& system); |
| 39 | Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | ||
| 40 | u32 block_width, u32 block_height, u32 block_depth, | ||
| 41 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); | ||
| 42 | 39 | ||
| 43 | /// Creates SurfaceCachedParams from a framebuffer configuration. | 40 | /// Creates SurfaceCachedParams from a framebuffer configuration. |
| 44 | static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); | 41 | static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 0d105d386..c70e4aec2 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -160,10 +160,7 @@ public: | |||
| 160 | SetEmptyDepthBuffer(); | 160 | SetEmptyDepthBuffer(); |
| 161 | return {}; | 161 | return {}; |
| 162 | } | 162 | } |
| 163 | const auto depth_params{SurfaceParams::CreateForDepthBuffer( | 163 | const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)}; |
| 164 | system, regs.zeta_width, regs.zeta_height, regs.zeta.format, | ||
| 165 | regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, | ||
| 166 | regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; | ||
| 167 | auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true); | 164 | auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true); |
| 168 | if (depth_buffer.target) | 165 | if (depth_buffer.target) |
| 169 | depth_buffer.target->MarkAsRenderTarget(false, NO_RT); | 166 | depth_buffer.target->MarkAsRenderTarget(false, NO_RT); |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 55a37fffa..c3dbb1a88 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -9,6 +9,9 @@ | |||
| 9 | #include <QKeyEvent> | 9 | #include <QKeyEvent> |
| 10 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 11 | #include <QOffscreenSurface> | 11 | #include <QOffscreenSurface> |
| 12 | #include <QOpenGLContext> | ||
| 13 | #include <QOpenGLFunctions> | ||
| 14 | #include <QOpenGLFunctions_4_3_Core> | ||
| 12 | #include <QOpenGLWindow> | 15 | #include <QOpenGLWindow> |
| 13 | #include <QPainter> | 16 | #include <QPainter> |
| 14 | #include <QScreen> | 17 | #include <QScreen> |
| @@ -23,9 +26,10 @@ | |||
| 23 | #include "common/assert.h" | 26 | #include "common/assert.h" |
| 24 | #include "common/microprofile.h" | 27 | #include "common/microprofile.h" |
| 25 | #include "common/scm_rev.h" | 28 | #include "common/scm_rev.h" |
| 29 | #include "common/scope_exit.h" | ||
| 26 | #include "core/core.h" | 30 | #include "core/core.h" |
| 27 | #include "core/frontend/framebuffer_layout.h" | 31 | #include "core/frontend/framebuffer_layout.h" |
| 28 | #include "core/frontend/scope_acquire_window_context.h" | 32 | #include "core/frontend/scope_acquire_context.h" |
| 29 | #include "core/settings.h" | 33 | #include "core/settings.h" |
| 30 | #include "input_common/keyboard.h" | 34 | #include "input_common/keyboard.h" |
| 31 | #include "input_common/main.h" | 35 | #include "input_common/main.h" |
| @@ -35,15 +39,27 @@ | |||
| 35 | #include "yuzu/bootmanager.h" | 39 | #include "yuzu/bootmanager.h" |
| 36 | #include "yuzu/main.h" | 40 | #include "yuzu/main.h" |
| 37 | 41 | ||
| 38 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | 42 | EmuThread::EmuThread(GRenderWindow& window) |
| 43 | : shared_context{window.CreateSharedContext()}, | ||
| 44 | context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context | ||
| 45 | : window} {} | ||
| 39 | 46 | ||
| 40 | EmuThread::~EmuThread() = default; | 47 | EmuThread::~EmuThread() = default; |
| 41 | 48 | ||
| 42 | void EmuThread::run() { | 49 | static GMainWindow* GetMainWindow() { |
| 43 | render_window->MakeCurrent(); | 50 | for (QWidget* w : qApp->topLevelWidgets()) { |
| 51 | if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) { | ||
| 52 | return main; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | return nullptr; | ||
| 56 | } | ||
| 44 | 57 | ||
| 58 | void EmuThread::run() { | ||
| 45 | MicroProfileOnThreadCreate("EmuThread"); | 59 | MicroProfileOnThreadCreate("EmuThread"); |
| 46 | 60 | ||
| 61 | Core::Frontend::ScopeAcquireContext acquire_context{context}; | ||
| 62 | |||
| 47 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 63 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| 48 | 64 | ||
| 49 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( | 65 | Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( |
| @@ -53,11 +69,6 @@ void EmuThread::run() { | |||
| 53 | 69 | ||
| 54 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 70 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 55 | 71 | ||
| 56 | if (Settings::values.use_asynchronous_gpu_emulation) { | ||
| 57 | // Release OpenGL context for the GPU thread | ||
| 58 | render_window->DoneCurrent(); | ||
| 59 | } | ||
| 60 | |||
| 61 | // Holds whether the cpu was running during the last iteration, | 72 | // Holds whether the cpu was running during the last iteration, |
| 62 | // so that the DebugModeLeft signal can be emitted before the | 73 | // so that the DebugModeLeft signal can be emitted before the |
| 63 | // next execution step | 74 | // next execution step |
| @@ -98,190 +109,202 @@ void EmuThread::run() { | |||
| 98 | #if MICROPROFILE_ENABLED | 109 | #if MICROPROFILE_ENABLED |
| 99 | MicroProfileOnThreadExit(); | 110 | MicroProfileOnThreadExit(); |
| 100 | #endif | 111 | #endif |
| 101 | |||
| 102 | render_window->moveContext(); | ||
| 103 | } | 112 | } |
| 104 | 113 | ||
| 105 | class GGLContext : public Core::Frontend::GraphicsContext { | 114 | class GGLContext : public Core::Frontend::GraphicsContext { |
| 106 | public: | 115 | public: |
| 107 | explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { | 116 | explicit GGLContext(QOpenGLContext* shared_context) |
| 108 | context.setFormat(shared_context->format()); | 117 | : context(new QOpenGLContext(shared_context->parent())), |
| 109 | context.setShareContext(shared_context); | 118 | surface(new QOffscreenSurface(nullptr)) { |
| 110 | context.create(); | 119 | |
| 120 | // disable vsync for any shared contexts | ||
| 121 | auto format = shared_context->format(); | ||
| 122 | format.setSwapInterval(0); | ||
| 123 | |||
| 124 | context->setShareContext(shared_context); | ||
| 125 | context->setFormat(format); | ||
| 126 | context->create(); | ||
| 127 | surface->setParent(shared_context->parent()); | ||
| 128 | surface->setFormat(format); | ||
| 129 | surface->create(); | ||
| 111 | } | 130 | } |
| 112 | 131 | ||
| 113 | void MakeCurrent() override { | 132 | void MakeCurrent() override { |
| 114 | context.makeCurrent(shared_context->surface()); | 133 | context->makeCurrent(surface); |
| 115 | } | 134 | } |
| 116 | 135 | ||
| 117 | void DoneCurrent() override { | 136 | void DoneCurrent() override { |
| 118 | context.doneCurrent(); | 137 | context->doneCurrent(); |
| 119 | } | 138 | } |
| 120 | 139 | ||
| 121 | void SwapBuffers() override {} | ||
| 122 | |||
| 123 | private: | 140 | private: |
| 124 | QOpenGLContext* shared_context; | 141 | QOpenGLContext* context; |
| 125 | QOpenGLContext context; | 142 | QOffscreenSurface* surface; |
| 126 | }; | 143 | }; |
| 127 | 144 | ||
| 128 | class GWidgetInternal : public QWindow { | 145 | class ChildRenderWindow : public QWindow { |
| 129 | public: | 146 | public: |
| 130 | GWidgetInternal(GRenderWindow* parent) : parent(parent) {} | 147 | ChildRenderWindow(QWindow* parent, QWidget* event_handler) |
| 131 | virtual ~GWidgetInternal() = default; | 148 | : QWindow{parent}, event_handler{event_handler} {} |
| 132 | 149 | ||
| 133 | void resizeEvent(QResizeEvent* ev) override { | 150 | virtual ~ChildRenderWindow() = default; |
| 134 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); | ||
| 135 | parent->OnFramebufferSizeChanged(); | ||
| 136 | } | ||
| 137 | 151 | ||
| 138 | void keyPressEvent(QKeyEvent* event) override { | 152 | virtual void Present() = 0; |
| 139 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 140 | } | ||
| 141 | 153 | ||
| 142 | void keyReleaseEvent(QKeyEvent* event) override { | 154 | protected: |
| 143 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | 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 | } | ||
| 144 | } | 195 | } |
| 145 | 196 | ||
| 146 | void mousePressEvent(QMouseEvent* event) override { | 197 | void exposeEvent(QExposeEvent* event) override { |
| 147 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 198 | QWindow::requestUpdate(); |
| 148 | return; // touch input is handled in TouchBeginEvent | 199 | QWindow::exposeEvent(event); |
| 149 | |||
| 150 | const auto pos{event->pos()}; | ||
| 151 | if (event->button() == Qt::LeftButton) { | ||
| 152 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 153 | parent->TouchPressed(x, y); | ||
| 154 | } else if (event->button() == Qt::RightButton) { | ||
| 155 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 156 | } | ||
| 157 | } | 200 | } |
| 158 | 201 | ||
| 159 | void mouseMoveEvent(QMouseEvent* event) override { | 202 | private: |
| 160 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 203 | QWidget* event_handler{}; |
| 161 | return; // touch input is handled in TouchUpdateEvent | 204 | }; |
| 162 | 205 | ||
| 163 | const auto pos{event->pos()}; | 206 | class OpenGLWindow final : public ChildRenderWindow { |
| 164 | const auto [x, y] = parent->ScaleTouch(pos); | 207 | public: |
| 165 | parent->TouchMoved(x, y); | 208 | OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) |
| 166 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | 209 | : ChildRenderWindow{parent, event_handler}, |
| 167 | } | 210 | context(new QOpenGLContext(shared_context->parent())) { |
| 168 | 211 | ||
| 169 | void mouseReleaseEvent(QMouseEvent* event) override { | 212 | // disable vsync for any shared contexts |
| 170 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | 213 | auto format = shared_context->format(); |
| 171 | return; // touch input is handled in TouchEndEvent | 214 | format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); |
| 215 | this->setFormat(format); | ||
| 172 | 216 | ||
| 173 | if (event->button() == Qt::LeftButton) | 217 | context->setShareContext(shared_context); |
| 174 | parent->TouchReleased(); | 218 | context->setScreen(this->screen()); |
| 175 | else if (event->button() == Qt::RightButton) | 219 | context->setFormat(format); |
| 176 | InputCommon::GetMotionEmu()->EndTilt(); | 220 | context->create(); |
| 177 | } | ||
| 178 | 221 | ||
| 179 | void DisablePainting() { | 222 | setSurfaceType(QWindow::OpenGLSurface); |
| 180 | do_painting = false; | ||
| 181 | } | ||
| 182 | 223 | ||
| 183 | void EnablePainting() { | 224 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 184 | do_painting = true; | 225 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 185 | } | 226 | } |
| 186 | 227 | ||
| 187 | std::pair<unsigned, unsigned> GetSize() const { | 228 | ~OpenGLWindow() override { |
| 188 | return std::make_pair(width(), height()); | 229 | context->doneCurrent(); |
| 189 | } | 230 | } |
| 190 | 231 | ||
| 191 | protected: | 232 | void Present() override { |
| 192 | bool IsPaintingEnabled() const { | 233 | if (!isExposed()) { |
| 193 | return do_painting; | 234 | return; |
| 235 | } | ||
| 236 | |||
| 237 | context->makeCurrent(this); | ||
| 238 | Core::System::GetInstance().Renderer().TryPresent(100); | ||
| 239 | context->swapBuffers(this); | ||
| 240 | auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); | ||
| 241 | f->glFinish(); | ||
| 242 | QWindow::requestUpdate(); | ||
| 194 | } | 243 | } |
| 195 | 244 | ||
| 196 | private: | 245 | private: |
| 197 | GRenderWindow* parent; | 246 | QOpenGLContext* context{}; |
| 198 | bool do_painting = false; | ||
| 199 | }; | ||
| 200 | |||
| 201 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | ||
| 202 | // context. | ||
| 203 | // The corresponding functionality is handled in EmuThread instead | ||
| 204 | class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow { | ||
| 205 | public: | ||
| 206 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) | ||
| 207 | : GWidgetInternal(parent), QOpenGLWindow(shared_context) {} | ||
| 208 | ~GGLWidgetInternal() override = default; | ||
| 209 | |||
| 210 | void paintEvent(QPaintEvent* ev) override { | ||
| 211 | if (IsPaintingEnabled()) { | ||
| 212 | QPainter painter(this); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | }; | 247 | }; |
| 216 | 248 | ||
| 217 | #ifdef HAS_VULKAN | 249 | #ifdef HAS_VULKAN |
| 218 | class GVKWidgetInternal final : public GWidgetInternal { | 250 | class VulkanWindow final : public ChildRenderWindow { |
| 219 | public: | 251 | public: |
| 220 | GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { | 252 | VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) |
| 253 | : ChildRenderWindow{parent, event_handler} { | ||
| 221 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | 254 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); |
| 222 | setVulkanInstance(instance); | 255 | setVulkanInstance(instance); |
| 223 | } | 256 | } |
| 224 | ~GVKWidgetInternal() override = default; | 257 | |
| 258 | ~VulkanWindow() override = default; | ||
| 259 | |||
| 260 | void Present() override { | ||
| 261 | // TODO(bunnei): ImplementMe | ||
| 262 | } | ||
| 263 | |||
| 264 | private: | ||
| 265 | QWidget* event_handler{}; | ||
| 225 | }; | 266 | }; |
| 226 | #endif | 267 | #endif |
| 227 | 268 | ||
| 228 | GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) | 269 | GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) |
| 229 | : QWidget(parent), emu_thread(emu_thread) { | 270 | : QWidget(parent_), emu_thread(emu_thread) { |
| 230 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 271 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 231 | .arg(QString::fromUtf8(Common::g_build_name), | 272 | .arg(QString::fromUtf8(Common::g_build_name), |
| 232 | QString::fromUtf8(Common::g_scm_branch), | 273 | QString::fromUtf8(Common::g_scm_branch), |
| 233 | QString::fromUtf8(Common::g_scm_desc))); | 274 | QString::fromUtf8(Common::g_scm_desc))); |
| 234 | setAttribute(Qt::WA_AcceptTouchEvents); | 275 | setAttribute(Qt::WA_AcceptTouchEvents); |
| 235 | 276 | auto layout = new QHBoxLayout(this); | |
| 277 | layout->setMargin(0); | ||
| 278 | setLayout(layout); | ||
| 236 | InputCommon::Init(); | 279 | InputCommon::Init(); |
| 280 | |||
| 281 | GMainWindow* parent = GetMainWindow(); | ||
| 237 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | 282 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); |
| 238 | } | 283 | } |
| 239 | 284 | ||
| 240 | GRenderWindow::~GRenderWindow() { | 285 | GRenderWindow::~GRenderWindow() { |
| 241 | InputCommon::Shutdown(); | 286 | InputCommon::Shutdown(); |
| 242 | |||
| 243 | // Avoid an unordered destruction that generates a segfault | ||
| 244 | delete child; | ||
| 245 | } | 287 | } |
| 246 | 288 | ||
| 247 | void GRenderWindow::moveContext() { | 289 | void GRenderWindow::MakeCurrent() { |
| 248 | if (!context) { | 290 | if (core_context) { |
| 249 | return; | 291 | core_context->MakeCurrent(); |
| 250 | } | 292 | } |
| 251 | DoneCurrent(); | ||
| 252 | |||
| 253 | // If the thread started running, move the GL Context to the new thread. Otherwise, move it | ||
| 254 | // back. | ||
| 255 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) | ||
| 256 | ? emu_thread | ||
| 257 | : qApp->thread(); | ||
| 258 | context->moveToThread(thread); | ||
| 259 | } | 293 | } |
| 260 | 294 | ||
| 261 | void GRenderWindow::SwapBuffers() { | 295 | void GRenderWindow::DoneCurrent() { |
| 262 | if (context) { | 296 | if (core_context) { |
| 263 | context->swapBuffers(child); | 297 | core_context->DoneCurrent(); |
| 264 | } | 298 | } |
| 299 | } | ||
| 300 | |||
| 301 | void GRenderWindow::PollEvents() { | ||
| 265 | if (!first_frame) { | 302 | if (!first_frame) { |
| 266 | first_frame = true; | 303 | first_frame = true; |
| 267 | emit FirstFrameDisplayed(); | 304 | emit FirstFrameDisplayed(); |
| 268 | } | 305 | } |
| 269 | } | 306 | } |
| 270 | 307 | ||
| 271 | void GRenderWindow::MakeCurrent() { | ||
| 272 | if (context) { | ||
| 273 | context->makeCurrent(child); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | void GRenderWindow::DoneCurrent() { | ||
| 278 | if (context) { | ||
| 279 | context->doneCurrent(); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | void GRenderWindow::PollEvents() {} | ||
| 284 | |||
| 285 | bool GRenderWindow::IsShown() const { | 308 | bool GRenderWindow::IsShown() const { |
| 286 | return !isMinimized(); | 309 | return !isMinimized(); |
| 287 | } | 310 | } |
| @@ -291,7 +314,7 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i | |||
| 291 | #ifdef HAS_VULKAN | 314 | #ifdef HAS_VULKAN |
| 292 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | 315 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); |
| 293 | const VkInstance instance_copy = vk_instance->vkInstance(); | 316 | const VkInstance instance_copy = vk_instance->vkInstance(); |
| 294 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); | 317 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window); |
| 295 | 318 | ||
| 296 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | 319 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); |
| 297 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | 320 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); |
| @@ -309,21 +332,10 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i | |||
| 309 | void GRenderWindow::OnFramebufferSizeChanged() { | 332 | void GRenderWindow::OnFramebufferSizeChanged() { |
| 310 | // Screen changes potentially incur a change in screen DPI, hence we should update the | 333 | // Screen changes potentially incur a change in screen DPI, hence we should update the |
| 311 | // framebuffer size | 334 | // framebuffer size |
| 312 | const qreal pixelRatio{GetWindowPixelRatio()}; | 335 | const qreal pixel_ratio = windowPixelRatio(); |
| 313 | const auto size{child->GetSize()}; | 336 | const u32 width = this->width() * pixel_ratio; |
| 314 | UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); | 337 | const u32 height = this->height() * pixel_ratio; |
| 315 | } | 338 | UpdateCurrentFramebufferLayout(width, height); |
| 316 | |||
| 317 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | ||
| 318 | if (child) { | ||
| 319 | child->keyPressEvent(event); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { | ||
| 324 | if (child) { | ||
| 325 | child->keyReleaseEvent(event); | ||
| 326 | } | ||
| 327 | } | 339 | } |
| 328 | 340 | ||
| 329 | void GRenderWindow::BackupGeometry() { | 341 | void GRenderWindow::BackupGeometry() { |
| @@ -351,13 +363,12 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 351 | return geometry; | 363 | return geometry; |
| 352 | } | 364 | } |
| 353 | 365 | ||
| 354 | qreal GRenderWindow::GetWindowPixelRatio() const { | 366 | qreal GRenderWindow::windowPixelRatio() const { |
| 355 | // windowHandle() might not be accessible until the window is displayed to screen. | 367 | return devicePixelRatio(); |
| 356 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | ||
| 357 | } | 368 | } |
| 358 | 369 | ||
| 359 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 370 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { |
| 360 | const qreal pixel_ratio{GetWindowPixelRatio()}; | 371 | const qreal pixel_ratio = windowPixelRatio(); |
| 361 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 372 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 362 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 373 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| 363 | } | 374 | } |
| @@ -367,6 +378,47 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 367 | QWidget::closeEvent(event); | 378 | QWidget::closeEvent(event); |
| 368 | } | 379 | } |
| 369 | 380 | ||
| 381 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||
| 382 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 383 | } | ||
| 384 | |||
| 385 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 386 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 387 | } | ||
| 388 | |||
| 389 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||
| 390 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 391 | return; // touch input is handled in TouchBeginEvent | ||
| 392 | |||
| 393 | auto pos = event->pos(); | ||
| 394 | if (event->button() == Qt::LeftButton) { | ||
| 395 | const auto [x, y] = ScaleTouch(pos); | ||
| 396 | this->TouchPressed(x, y); | ||
| 397 | } else if (event->button() == Qt::RightButton) { | ||
| 398 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||
| 403 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 404 | return; // touch input is handled in TouchUpdateEvent | ||
| 405 | |||
| 406 | auto pos = event->pos(); | ||
| 407 | const auto [x, y] = ScaleTouch(pos); | ||
| 408 | this->TouchMoved(x, y); | ||
| 409 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 410 | } | ||
| 411 | |||
| 412 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||
| 413 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 414 | return; // touch input is handled in TouchEndEvent | ||
| 415 | |||
| 416 | if (event->button() == Qt::LeftButton) | ||
| 417 | this->TouchReleased(); | ||
| 418 | else if (event->button() == Qt::RightButton) | ||
| 419 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 420 | } | ||
| 421 | |||
| 370 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 422 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| 371 | // TouchBegin always has exactly one touch point, so take the .first() | 423 | // TouchBegin always has exactly one touch point, so take the .first() |
| 372 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | 424 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); |
| @@ -415,26 +467,20 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) { | |||
| 415 | InputCommon::GetKeyboard()->ReleaseAllKeys(); | 467 | InputCommon::GetKeyboard()->ReleaseAllKeys(); |
| 416 | } | 468 | } |
| 417 | 469 | ||
| 418 | void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { | 470 | void GRenderWindow::resizeEvent(QResizeEvent* event) { |
| 419 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | 471 | QWidget::resizeEvent(event); |
| 472 | OnFramebufferSizeChanged(); | ||
| 420 | } | 473 | } |
| 421 | 474 | ||
| 422 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 475 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { |
| 423 | return std::make_unique<GGLContext>(context.get()); | 476 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 477 | return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); | ||
| 478 | } | ||
| 479 | return {}; | ||
| 424 | } | 480 | } |
| 425 | 481 | ||
| 426 | bool GRenderWindow::InitRenderTarget() { | 482 | bool GRenderWindow::InitRenderTarget() { |
| 427 | shared_context.reset(); | 483 | ReleaseRenderTarget(); |
| 428 | context.reset(); | ||
| 429 | if (child) { | ||
| 430 | delete child; | ||
| 431 | } | ||
| 432 | if (container) { | ||
| 433 | delete container; | ||
| 434 | } | ||
| 435 | if (layout()) { | ||
| 436 | delete layout(); | ||
| 437 | } | ||
| 438 | 484 | ||
| 439 | first_frame = false; | 485 | first_frame = false; |
| 440 | 486 | ||
| @@ -451,13 +497,6 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 451 | break; | 497 | break; |
| 452 | } | 498 | } |
| 453 | 499 | ||
| 454 | container = QWidget::createWindowContainer(child, this); | ||
| 455 | QBoxLayout* layout = new QHBoxLayout(this); | ||
| 456 | |||
| 457 | layout->addWidget(container); | ||
| 458 | layout->setMargin(0); | ||
| 459 | setLayout(layout); | ||
| 460 | |||
| 461 | // Reset minimum required size to avoid resizing issues on the main window after restarting. | 500 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 462 | setMinimumSize(1, 1); | 501 | setMinimumSize(1, 1); |
| 463 | 502 | ||
| @@ -467,14 +506,9 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 467 | hide(); | 506 | hide(); |
| 468 | 507 | ||
| 469 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | 508 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); |
| 470 | child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 471 | container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 472 | 509 | ||
| 473 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 510 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 474 | |||
| 475 | OnFramebufferSizeChanged(); | 511 | OnFramebufferSizeChanged(); |
| 476 | NotifyClientAreaSizeChanged(child->GetSize()); | ||
| 477 | |||
| 478 | BackupGeometry(); | 512 | BackupGeometry(); |
| 479 | 513 | ||
| 480 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | 514 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| @@ -486,6 +520,14 @@ bool GRenderWindow::InitRenderTarget() { | |||
| 486 | return true; | 520 | return true; |
| 487 | } | 521 | } |
| 488 | 522 | ||
| 523 | void GRenderWindow::ReleaseRenderTarget() { | ||
| 524 | if (child_widget) { | ||
| 525 | layout()->removeWidget(child_widget); | ||
| 526 | delete child_widget; | ||
| 527 | child_widget = nullptr; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 489 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 531 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| 490 | auto& renderer = Core::System::GetInstance().Renderer(); | 532 | auto& renderer = Core::System::GetInstance().Renderer(); |
| 491 | 533 | ||
| @@ -521,16 +563,19 @@ bool GRenderWindow::InitializeOpenGL() { | |||
| 521 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 563 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); |
| 522 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 564 | // TODO: expose a setting for buffer value (ie default/single/double/triple) |
| 523 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 565 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); |
| 524 | shared_context = std::make_unique<QOpenGLContext>(); | 566 | fmt.setSwapInterval(0); |
| 525 | shared_context->setFormat(fmt); | 567 | QSurfaceFormat::setDefaultFormat(fmt); |
| 526 | shared_context->create(); | 568 | |
| 527 | context = std::make_unique<QOpenGLContext>(); | 569 | GMainWindow* parent = GetMainWindow(); |
| 528 | context->setShareContext(shared_context.get()); | 570 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; |
| 529 | context->setFormat(fmt); | 571 | child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); |
| 530 | context->create(); | 572 | child_window->create(); |
| 531 | fmt.setSwapInterval(false); | 573 | child_widget = createWindowContainer(child_window, this); |
| 532 | 574 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | |
| 533 | child = new GGLWidgetInternal(this, shared_context.get()); | 575 | layout()->addWidget(child_widget); |
| 576 | |||
| 577 | core_context = CreateSharedContext(); | ||
| 578 | |||
| 534 | return true; | 579 | return true; |
| 535 | } | 580 | } |
| 536 | 581 | ||
| @@ -559,7 +604,14 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 559 | return false; | 604 | return false; |
| 560 | } | 605 | } |
| 561 | 606 | ||
| 562 | child = new GVKWidgetInternal(this, vk_instance.get()); | 607 | GMainWindow* parent = GetMainWindow(); |
| 608 | QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; | ||
| 609 | child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); | ||
| 610 | child_window->create(); | ||
| 611 | child_widget = createWindowContainer(child_window, this); | ||
| 612 | child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 613 | layout()->addWidget(child_widget); | ||
| 614 | |||
| 563 | return true; | 615 | return true; |
| 564 | #else | 616 | #else |
| 565 | QMessageBox::critical(this, tr("Vulkan not available!"), | 617 | QMessageBox::critical(this, tr("Vulkan not available!"), |
| @@ -569,7 +621,7 @@ bool GRenderWindow::InitializeVulkan() { | |||
| 569 | } | 621 | } |
| 570 | 622 | ||
| 571 | bool GRenderWindow::LoadOpenGL() { | 623 | bool GRenderWindow::LoadOpenGL() { |
| 572 | Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; | 624 | Core::Frontend::ScopeAcquireContext acquire_context{*this}; |
| 573 | if (!gladLoadGL()) { | 625 | if (!gladLoadGL()) { |
| 574 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), | 626 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), |
| 575 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " | 627 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " |
| @@ -621,12 +673,10 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const { | |||
| 621 | 673 | ||
| 622 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | 674 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { |
| 623 | this->emu_thread = emu_thread; | 675 | this->emu_thread = emu_thread; |
| 624 | child->DisablePainting(); | ||
| 625 | } | 676 | } |
| 626 | 677 | ||
| 627 | void GRenderWindow::OnEmulationStopping() { | 678 | void GRenderWindow::OnEmulationStopping() { |
| 628 | emu_thread = nullptr; | 679 | emu_thread = nullptr; |
| 629 | child->EnablePainting(); | ||
| 630 | } | 680 | } |
| 631 | 681 | ||
| 632 | void GRenderWindow::showEvent(QShowEvent* event) { | 682 | void GRenderWindow::showEvent(QShowEvent* event) { |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 71a2fa321..79b030304 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -11,11 +11,13 @@ | |||
| 11 | #include <QImage> | 11 | #include <QImage> |
| 12 | #include <QThread> | 12 | #include <QThread> |
| 13 | #include <QWidget> | 13 | #include <QWidget> |
| 14 | #include <QWindow> | ||
| 14 | 15 | ||
| 15 | #include "common/thread.h" | 16 | #include "common/thread.h" |
| 16 | #include "core/core.h" | 17 | #include "core/core.h" |
| 17 | #include "core/frontend/emu_window.h" | 18 | #include "core/frontend/emu_window.h" |
| 18 | 19 | ||
| 20 | class GRenderWindow; | ||
| 19 | class QKeyEvent; | 21 | class QKeyEvent; |
| 20 | class QScreen; | 22 | class QScreen; |
| 21 | class QTouchEvent; | 23 | class QTouchEvent; |
| @@ -26,14 +28,6 @@ class QOpenGLContext; | |||
| 26 | class QVulkanInstance; | 28 | class QVulkanInstance; |
| 27 | #endif | 29 | #endif |
| 28 | 30 | ||
| 29 | class GWidgetInternal; | ||
| 30 | class GGLWidgetInternal; | ||
| 31 | class GVKWidgetInternal; | ||
| 32 | class GMainWindow; | ||
| 33 | class GRenderWindow; | ||
| 34 | class QSurface; | ||
| 35 | class QOpenGLContext; | ||
| 36 | |||
| 37 | namespace VideoCore { | 31 | namespace VideoCore { |
| 38 | enum class LoadCallbackStage; | 32 | enum class LoadCallbackStage; |
| 39 | } | 33 | } |
| @@ -42,7 +36,7 @@ class EmuThread final : public QThread { | |||
| 42 | Q_OBJECT | 36 | Q_OBJECT |
| 43 | 37 | ||
| 44 | public: | 38 | public: |
| 45 | explicit EmuThread(GRenderWindow* render_window); | 39 | explicit EmuThread(GRenderWindow& window); |
| 46 | ~EmuThread() override; | 40 | ~EmuThread() override; |
| 47 | 41 | ||
| 48 | /** | 42 | /** |
| @@ -96,7 +90,11 @@ private: | |||
| 96 | std::mutex running_mutex; | 90 | std::mutex running_mutex; |
| 97 | std::condition_variable running_cv; | 91 | std::condition_variable running_cv; |
| 98 | 92 | ||
| 99 | GRenderWindow* render_window; | 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; | ||
| 100 | 98 | ||
| 101 | signals: | 99 | signals: |
| 102 | /** | 100 | /** |
| @@ -126,11 +124,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | |||
| 126 | Q_OBJECT | 124 | Q_OBJECT |
| 127 | 125 | ||
| 128 | public: | 126 | public: |
| 129 | GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); | 127 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); |
| 130 | ~GRenderWindow() override; | 128 | ~GRenderWindow() override; |
| 131 | 129 | ||
| 132 | // EmuWindow implementation | 130 | // EmuWindow implementation. |
| 133 | void SwapBuffers() override; | ||
| 134 | void MakeCurrent() override; | 131 | void MakeCurrent() override; |
| 135 | void DoneCurrent() override; | 132 | void DoneCurrent() override; |
| 136 | void PollEvents() override; | 133 | void PollEvents() override; |
| @@ -139,30 +136,36 @@ public: | |||
| 139 | void* surface) const override; | 136 | void* surface) const override; |
| 140 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 137 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 141 | 138 | ||
| 142 | void ForwardKeyPressEvent(QKeyEvent* event); | ||
| 143 | void ForwardKeyReleaseEvent(QKeyEvent* event); | ||
| 144 | |||
| 145 | void BackupGeometry(); | 139 | void BackupGeometry(); |
| 146 | void RestoreGeometry(); | 140 | void RestoreGeometry(); |
| 147 | void restoreGeometry(const QByteArray& geometry); // overridden | 141 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 148 | QByteArray saveGeometry(); // overridden | 142 | QByteArray saveGeometry(); // overridden |
| 149 | 143 | ||
| 150 | qreal GetWindowPixelRatio() const; | 144 | qreal windowPixelRatio() const; |
| 151 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 152 | 145 | ||
| 153 | void closeEvent(QCloseEvent* event) override; | 146 | void closeEvent(QCloseEvent* event) override; |
| 147 | |||
| 148 | void resizeEvent(QResizeEvent* event) override; | ||
| 149 | |||
| 150 | void keyPressEvent(QKeyEvent* event) override; | ||
| 151 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 152 | |||
| 153 | void mousePressEvent(QMouseEvent* event) override; | ||
| 154 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 155 | void mouseReleaseEvent(QMouseEvent* event) override; | ||
| 156 | |||
| 154 | bool event(QEvent* event) override; | 157 | bool event(QEvent* event) override; |
| 155 | void focusOutEvent(QFocusEvent* event) override; | ||
| 156 | 158 | ||
| 157 | void OnClientAreaResized(u32 width, u32 height); | 159 | void focusOutEvent(QFocusEvent* event) override; |
| 158 | 160 | ||
| 159 | bool InitRenderTarget(); | 161 | bool InitRenderTarget(); |
| 160 | 162 | ||
| 163 | /// Destroy the previous run's child_widget which should also destroy the child_window | ||
| 164 | void ReleaseRenderTarget(); | ||
| 165 | |||
| 161 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 166 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 162 | 167 | ||
| 163 | public slots: | 168 | public slots: |
| 164 | void moveContext(); // overridden | ||
| 165 | |||
| 166 | void OnEmulationStarting(EmuThread* emu_thread); | 169 | void OnEmulationStarting(EmuThread* emu_thread); |
| 167 | void OnEmulationStopping(); | 170 | void OnEmulationStopping(); |
| 168 | void OnFramebufferSizeChanged(); | 171 | void OnFramebufferSizeChanged(); |
| @@ -173,6 +176,7 @@ signals: | |||
| 173 | void FirstFrameDisplayed(); | 176 | void FirstFrameDisplayed(); |
| 174 | 177 | ||
| 175 | private: | 178 | private: |
| 179 | std::pair<u32, u32> ScaleTouch(QPointF pos) const; | ||
| 176 | void TouchBeginEvent(const QTouchEvent* event); | 180 | void TouchBeginEvent(const QTouchEvent* event); |
| 177 | void TouchUpdateEvent(const QTouchEvent* event); | 181 | void TouchUpdateEvent(const QTouchEvent* event); |
| 178 | void TouchEndEvent(); | 182 | void TouchEndEvent(); |
| @@ -184,15 +188,9 @@ private: | |||
| 184 | bool LoadOpenGL(); | 188 | bool LoadOpenGL(); |
| 185 | QStringList GetUnsupportedGLExtensions() const; | 189 | QStringList GetUnsupportedGLExtensions() const; |
| 186 | 190 | ||
| 187 | QWidget* container = nullptr; | ||
| 188 | GWidgetInternal* child = nullptr; | ||
| 189 | |||
| 190 | EmuThread* emu_thread; | 191 | EmuThread* emu_thread; |
| 191 | // Context that backs the GGLWidgetInternal (and will be used by core to render) | 192 | |
| 192 | std::unique_ptr<QOpenGLContext> context; | 193 | std::unique_ptr<GraphicsContext> core_context; |
| 193 | // Context that will be shared between all newly created contexts. This should never be made | ||
| 194 | // current | ||
| 195 | std::unique_ptr<QOpenGLContext> shared_context; | ||
| 196 | 194 | ||
| 197 | #ifdef HAS_VULKAN | 195 | #ifdef HAS_VULKAN |
| 198 | std::unique_ptr<QVulkanInstance> vk_instance; | 196 | std::unique_ptr<QVulkanInstance> vk_instance; |
| @@ -202,6 +200,15 @@ private: | |||
| 202 | QImage screenshot_image; | 200 | QImage screenshot_image; |
| 203 | 201 | ||
| 204 | QByteArray geometry; | 202 | QByteArray geometry; |
| 203 | |||
| 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; | ||
| 211 | |||
| 205 | bool first_frame = false; | 212 | bool first_frame = false; |
| 206 | 213 | ||
| 207 | protected: | 214 | protected: |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 6209fff75..d0f574147 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -640,6 +640,7 @@ void Config::ReadRendererValues() { | |||
| 640 | ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); | 640 | ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); |
| 641 | Settings::values.use_asynchronous_gpu_emulation = | 641 | Settings::values.use_asynchronous_gpu_emulation = |
| 642 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); | 642 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); |
| 643 | Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); | ||
| 643 | Settings::values.force_30fps_mode = | 644 | Settings::values.force_30fps_mode = |
| 644 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); | 645 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); |
| 645 | 646 | ||
| @@ -1074,6 +1075,7 @@ void Config::SaveRendererValues() { | |||
| 1074 | Settings::values.use_accurate_gpu_emulation, false); | 1075 | Settings::values.use_accurate_gpu_emulation, false); |
| 1075 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), | 1076 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), |
| 1076 | Settings::values.use_asynchronous_gpu_emulation, false); | 1077 | Settings::values.use_asynchronous_gpu_emulation, false); |
| 1078 | WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); | ||
| 1077 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); | 1079 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); |
| 1078 | 1080 | ||
| 1079 | // Cast to double because Qt's written float values are not human-readable | 1081 | // Cast to double because Qt's written float values are not human-readable |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index ea899c080..fe64c7d81 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -103,6 +103,8 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 103 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 103 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 104 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); | 104 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); |
| 105 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 105 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 106 | ui->use_vsync->setEnabled(runtime_lock); | ||
| 107 | ui->use_vsync->setChecked(Settings::values.use_vsync); | ||
| 106 | ui->force_30fps_mode->setEnabled(runtime_lock); | 108 | ui->force_30fps_mode->setEnabled(runtime_lock); |
| 107 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | 109 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); |
| 108 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 110 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| @@ -120,6 +122,7 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 120 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 122 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 121 | Settings::values.use_asynchronous_gpu_emulation = | 123 | Settings::values.use_asynchronous_gpu_emulation = |
| 122 | ui->use_asynchronous_gpu_emulation->isChecked(); | 124 | ui->use_asynchronous_gpu_emulation->isChecked(); |
| 125 | Settings::values.use_vsync = ui->use_vsync->isChecked(); | ||
| 123 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); | 126 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); |
| 124 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | 127 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); |
| 125 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | 128 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index db60426ab..9acc7dd93 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -85,6 +85,16 @@ | |||
| 85 | </widget> | 85 | </widget> |
| 86 | </item> | 86 | </item> |
| 87 | <item> | 87 | <item> |
| 88 | <widget class="QCheckBox" name="use_vsync"> | ||
| 89 | <property name="toolTip"> | ||
| 90 | <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string> | ||
| 91 | </property> | ||
| 92 | <property name="text"> | ||
| 93 | <string>Use VSync (OpenGL only)</string> | ||
| 94 | </property> | ||
| 95 | </widget> | ||
| 96 | </item> | ||
| 97 | <item> | ||
| 88 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> | 98 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> |
| 89 | <property name="text"> | 99 | <property name="text"> |
| 90 | <string>Use accurate GPU emulation (slow)</string> | 100 | <string>Use accurate GPU emulation (slow)</string> |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 54ca2dc1d..47615adfe 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -20,7 +20,6 @@ | |||
| 20 | #include "core/file_sys/vfs.h" | 20 | #include "core/file_sys/vfs.h" |
| 21 | #include "core/file_sys/vfs_real.h" | 21 | #include "core/file_sys/vfs_real.h" |
| 22 | #include "core/frontend/applets/general_frontend.h" | 22 | #include "core/frontend/applets/general_frontend.h" |
| 23 | #include "core/frontend/scope_acquire_window_context.h" | ||
| 24 | #include "core/hle/service/acc/profile_manager.h" | 23 | #include "core/hle/service/acc/profile_manager.h" |
| 25 | #include "core/hle/service/am/applet_ae.h" | 24 | #include "core/hle/service/am/applet_ae.h" |
| 26 | #include "core/hle/service/am/applet_oe.h" | 25 | #include "core/hle/service/am/applet_oe.h" |
| @@ -985,11 +984,8 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 985 | return; | 984 | return; |
| 986 | 985 | ||
| 987 | // Create and start the emulation thread | 986 | // Create and start the emulation thread |
| 988 | emu_thread = std::make_unique<EmuThread>(render_window); | 987 | emu_thread = std::make_unique<EmuThread>(*render_window); |
| 989 | emit EmulationStarting(emu_thread.get()); | 988 | emit EmulationStarting(emu_thread.get()); |
| 990 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | ||
| 991 | render_window->moveContext(); | ||
| 992 | } | ||
| 993 | emu_thread->start(); | 989 | emu_thread->start(); |
| 994 | 990 | ||
| 995 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 991 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| @@ -1087,6 +1083,9 @@ void GMainWindow::ShutdownGame() { | |||
| 1087 | emulation_running = false; | 1083 | emulation_running = false; |
| 1088 | 1084 | ||
| 1089 | game_path.clear(); | 1085 | game_path.clear(); |
| 1086 | |||
| 1087 | // When closing the game, destroy the GLWindow to clear the context after the game is closed | ||
| 1088 | render_window->ReleaseRenderTarget(); | ||
| 1090 | } | 1089 | } |
| 1091 | 1090 | ||
| 1092 | void GMainWindow::StoreRecentFile(const QString& filename) { | 1091 | void GMainWindow::StoreRecentFile(const QString& filename) { |
| @@ -2215,48 +2214,47 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 2215 | QWidget::closeEvent(event); | 2214 | QWidget::closeEvent(event); |
| 2216 | } | 2215 | } |
| 2217 | 2216 | ||
| 2218 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | 2217 | static bool IsSingleFileDropEvent(const QMimeData* mime) { |
| 2219 | if (render_window) { | 2218 | return mime->hasUrls() && mime->urls().length() == 1; |
| 2220 | render_window->ForwardKeyPressEvent(event); | ||
| 2221 | } | ||
| 2222 | } | 2219 | } |
| 2223 | 2220 | ||
| 2224 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | 2221 | void GMainWindow::AcceptDropEvent(QDropEvent* event) { |
| 2225 | if (render_window) { | 2222 | if (IsSingleFileDropEvent(event->mimeData())) { |
| 2226 | render_window->ForwardKeyReleaseEvent(event); | 2223 | event->setDropAction(Qt::DropAction::LinkAction); |
| 2224 | event->accept(); | ||
| 2227 | } | 2225 | } |
| 2228 | } | 2226 | } |
| 2229 | 2227 | ||
| 2230 | static bool IsSingleFileDropEvent(QDropEvent* event) { | 2228 | bool GMainWindow::DropAction(QDropEvent* event) { |
| 2231 | const QMimeData* mimeData = event->mimeData(); | 2229 | if (!IsSingleFileDropEvent(event->mimeData())) { |
| 2232 | return mimeData->hasUrls() && mimeData->urls().length() == 1; | 2230 | return false; |
| 2233 | } | ||
| 2234 | |||
| 2235 | void GMainWindow::dropEvent(QDropEvent* event) { | ||
| 2236 | if (!IsSingleFileDropEvent(event)) { | ||
| 2237 | return; | ||
| 2238 | } | 2231 | } |
| 2239 | 2232 | ||
| 2240 | const QMimeData* mime_data = event->mimeData(); | 2233 | const QMimeData* mime_data = event->mimeData(); |
| 2241 | const QString filename = mime_data->urls().at(0).toLocalFile(); | 2234 | const QString& filename = mime_data->urls().at(0).toLocalFile(); |
| 2242 | 2235 | ||
| 2243 | if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) { | 2236 | if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) { |
| 2237 | // Amiibo | ||
| 2244 | LoadAmiibo(filename); | 2238 | LoadAmiibo(filename); |
| 2245 | } else { | 2239 | } else { |
| 2240 | // Game | ||
| 2246 | if (ConfirmChangeGame()) { | 2241 | if (ConfirmChangeGame()) { |
| 2247 | BootGame(filename); | 2242 | BootGame(filename); |
| 2248 | } | 2243 | } |
| 2249 | } | 2244 | } |
| 2245 | return true; | ||
| 2246 | } | ||
| 2247 | |||
| 2248 | void GMainWindow::dropEvent(QDropEvent* event) { | ||
| 2249 | DropAction(event); | ||
| 2250 | } | 2250 | } |
| 2251 | 2251 | ||
| 2252 | void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { | 2252 | void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { |
| 2253 | if (IsSingleFileDropEvent(event)) { | 2253 | AcceptDropEvent(event); |
| 2254 | event->acceptProposedAction(); | ||
| 2255 | } | ||
| 2256 | } | 2254 | } |
| 2257 | 2255 | ||
| 2258 | void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | 2256 | void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { |
| 2259 | event->acceptProposedAction(); | 2257 | AcceptDropEvent(event); |
| 2260 | } | 2258 | } |
| 2261 | 2259 | ||
| 2262 | bool GMainWindow::ConfirmChangeGame() { | 2260 | bool GMainWindow::ConfirmChangeGame() { |
| @@ -2377,6 +2375,7 @@ int main(int argc, char* argv[]) { | |||
| 2377 | 2375 | ||
| 2378 | // Enables the core to make the qt created contexts current on std::threads | 2376 | // Enables the core to make the qt created contexts current on std::threads |
| 2379 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2377 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 2378 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); | ||
| 2380 | QApplication app(argc, argv); | 2379 | QApplication app(argc, argv); |
| 2381 | 2380 | ||
| 2382 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2381 | // Qt changes the locale and causes issues in float conversion using std::to_string() when |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8eba2172c..a67125567 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -78,6 +78,9 @@ public: | |||
| 78 | 78 | ||
| 79 | std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; | 79 | std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; |
| 80 | 80 | ||
| 81 | bool DropAction(QDropEvent* event); | ||
| 82 | void AcceptDropEvent(QDropEvent* event); | ||
| 83 | |||
| 81 | signals: | 84 | signals: |
| 82 | 85 | ||
| 83 | /** | 86 | /** |
| @@ -264,8 +267,4 @@ protected: | |||
| 264 | void dropEvent(QDropEvent* event) override; | 267 | void dropEvent(QDropEvent* event) override; |
| 265 | void dragEnterEvent(QDragEnterEvent* event) override; | 268 | void dragEnterEvent(QDragEnterEvent* event) override; |
| 266 | void dragMoveEvent(QDragMoveEvent* event) override; | 269 | void dragMoveEvent(QDragMoveEvent* event) override; |
| 267 | |||
| 268 | // Overrides used to forward signals to the render window when the focus moves out. | ||
| 269 | void keyPressEvent(QKeyEvent* event) override; | ||
| 270 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 271 | }; | 270 | }; |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 96f1ce3af..b77c12baf 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -390,6 +390,8 @@ void Config::ReadValues() { | |||
| 390 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | 390 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); |
| 391 | Settings::values.use_asynchronous_gpu_emulation = | 391 | Settings::values.use_asynchronous_gpu_emulation = |
| 392 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | 392 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); |
| 393 | Settings::values.use_vsync = | ||
| 394 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); | ||
| 393 | 395 | ||
| 394 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); | 396 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); |
| 395 | Settings::values.bg_green = | 397 | Settings::values.bg_green = |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 8a2b658cd..df7473858 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -150,6 +150,11 @@ use_accurate_gpu_emulation = | |||
| 150 | # 0 : Off (slow), 1 (default): On (fast) | 150 | # 0 : Off (slow), 1 (default): On (fast) |
| 151 | use_asynchronous_gpu_emulation = | 151 | use_asynchronous_gpu_emulation = |
| 152 | 152 | ||
| 153 | # Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can | ||
| 154 | # so only turn this off if you notice a speed difference. | ||
| 155 | # 0: Off, 1 (default): On | ||
| 156 | use_vsync = | ||
| 157 | |||
| 153 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | 158 | # The clear color for the renderer. What shows up on the sides of the bottom screen. |
| 154 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. | 159 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. |
| 155 | bg_red = | 160 | bg_red = |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index e96139885..19584360c 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include "input_common/sdl/sdl.h" | 13 | #include "input_common/sdl/sdl.h" |
| 14 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 14 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 15 | 15 | ||
| 16 | EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | 16 | EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} { |
| 17 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { | 17 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { |
| 18 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); | 18 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); |
| 19 | exit(1); | 19 | exit(1); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index b38f56661..fffac4252 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -10,9 +10,13 @@ | |||
| 10 | 10 | ||
| 11 | struct SDL_Window; | 11 | struct SDL_Window; |
| 12 | 12 | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 13 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { | 17 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { |
| 14 | public: | 18 | public: |
| 15 | explicit EmuWindow_SDL2(bool fullscreen); | 19 | explicit EmuWindow_SDL2(Core::System& system, bool fullscreen); |
| 16 | ~EmuWindow_SDL2(); | 20 | ~EmuWindow_SDL2(); |
| 17 | 21 | ||
| 18 | /// Polls window events | 22 | /// Polls window events |
| @@ -24,6 +28,9 @@ public: | |||
| 24 | /// Returns if window is shown (not minimized) | 28 | /// Returns if window is shown (not minimized) |
| 25 | bool IsShown() const override; | 29 | bool IsShown() const override; |
| 26 | 30 | ||
| 31 | /// Presents the next frame | ||
| 32 | virtual void Present() = 0; | ||
| 33 | |||
| 27 | protected: | 34 | protected: |
| 28 | /// Called by PollEvents when a key is pressed or released. | 35 | /// Called by PollEvents when a key is pressed or released. |
| 29 | void OnKeyEvent(int key, u8 state); | 36 | void OnKeyEvent(int key, u8 state); |
| @@ -55,6 +62,9 @@ protected: | |||
| 55 | /// Called when a configuration change affects the minimal size of the window | 62 | /// Called when a configuration change affects the minimal size of the window |
| 56 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; | 63 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; |
| 57 | 64 | ||
| 65 | /// Instance of the system, used to access renderer for the presentation thread | ||
| 66 | Core::System& system; | ||
| 67 | |||
| 58 | /// Is the window still open? | 68 | /// Is the window still open? |
| 59 | bool is_open = true; | 69 | bool is_open = true; |
| 60 | 70 | ||
| @@ -62,7 +72,7 @@ protected: | |||
| 62 | bool is_shown = true; | 72 | bool is_shown = true; |
| 63 | 73 | ||
| 64 | /// Internal SDL2 render window | 74 | /// Internal SDL2 render window |
| 65 | SDL_Window* render_window; | 75 | SDL_Window* render_window{}; |
| 66 | 76 | ||
| 67 | /// Keeps track of how often to update the title bar during gameplay | 77 | /// Keeps track of how often to update the title bar during gameplay |
| 68 | u32 last_time = 0; | 78 | u32 last_time = 0; |
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 7ffa0ac09..c0d373477 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -13,24 +13,25 @@ | |||
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "common/scm_rev.h" | 14 | #include "common/scm_rev.h" |
| 15 | #include "common/string_util.h" | 15 | #include "common/string_util.h" |
| 16 | #include "core/core.h" | ||
| 16 | #include "core/settings.h" | 17 | #include "core/settings.h" |
| 17 | #include "input_common/keyboard.h" | 18 | #include "input_common/keyboard.h" |
| 18 | #include "input_common/main.h" | 19 | #include "input_common/main.h" |
| 19 | #include "input_common/motion_emu.h" | 20 | #include "input_common/motion_emu.h" |
| 21 | #include "video_core/renderer_base.h" | ||
| 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | 22 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" |
| 21 | 23 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | 24 | class SDLGLContext : public Core::Frontend::GraphicsContext { |
| 23 | public: | 25 | public: |
| 24 | explicit SDLGLContext() { | 26 | explicit SDLGLContext() { |
| 25 | // create a hidden window to make the shared context against | 27 | // create a hidden window to make the shared context against |
| 26 | window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position | 28 | window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, |
| 27 | SDL_WINDOWPOS_UNDEFINED, // y position | 29 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); |
| 28 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 29 | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); | ||
| 30 | context = SDL_GL_CreateContext(window); | 30 | context = SDL_GL_CreateContext(window); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | ~SDLGLContext() { | 33 | ~SDLGLContext() { |
| 34 | DoneCurrent(); | ||
| 34 | SDL_GL_DeleteContext(context); | 35 | SDL_GL_DeleteContext(context); |
| 35 | SDL_DestroyWindow(window); | 36 | SDL_DestroyWindow(window); |
| 36 | } | 37 | } |
| @@ -43,8 +44,6 @@ public: | |||
| 43 | SDL_GL_MakeCurrent(window, nullptr); | 44 | SDL_GL_MakeCurrent(window, nullptr); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | void SwapBuffers() override {} | ||
| 47 | |||
| 48 | private: | 47 | private: |
| 49 | SDL_Window* window; | 48 | SDL_Window* window; |
| 50 | SDL_GLContext context; | 49 | SDL_GLContext context; |
| @@ -80,7 +79,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { | |||
| 80 | return unsupported_ext.empty(); | 79 | return unsupported_ext.empty(); |
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | 82 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen) |
| 83 | : EmuWindow_SDL2{system, fullscreen} { | ||
| 84 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | 84 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); |
| 85 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | 85 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |
| 86 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); | 86 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); |
| @@ -90,6 +90,7 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree | |||
| 90 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 90 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
| 91 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | 91 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |
| 92 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | 92 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); |
| 93 | SDL_GL_SetSwapInterval(0); | ||
| 93 | 94 | ||
| 94 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | 95 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, |
| 95 | Common::g_scm_branch, Common::g_scm_desc); | 96 | Common::g_scm_branch, Common::g_scm_desc); |
| @@ -105,13 +106,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree | |||
| 105 | exit(1); | 106 | exit(1); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 109 | dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, | ||
| 110 | SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); | ||
| 111 | |||
| 108 | if (fullscreen) { | 112 | if (fullscreen) { |
| 109 | Fullscreen(); | 113 | Fullscreen(); |
| 110 | } | 114 | } |
| 111 | gl_context = SDL_GL_CreateContext(render_window); | ||
| 112 | 115 | ||
| 113 | if (gl_context == nullptr) { | 116 | window_context = SDL_GL_CreateContext(render_window); |
| 114 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); | 117 | core_context = CreateSharedContext(); |
| 118 | |||
| 119 | if (window_context == nullptr) { | ||
| 120 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError()); | ||
| 121 | exit(1); | ||
| 122 | } | ||
| 123 | if (core_context == nullptr) { | ||
| 124 | LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError()); | ||
| 115 | exit(1); | 125 | exit(1); |
| 116 | } | 126 | } |
| 117 | 127 | ||
| @@ -128,28 +138,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree | |||
| 128 | OnResize(); | 138 | OnResize(); |
| 129 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 139 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 130 | SDL_PumpEvents(); | 140 | SDL_PumpEvents(); |
| 131 | SDL_GL_SetSwapInterval(false); | ||
| 132 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, | 141 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, |
| 133 | Common::g_scm_desc); | 142 | Common::g_scm_desc); |
| 134 | Settings::LogSettings(); | 143 | Settings::LogSettings(); |
| 135 | |||
| 136 | DoneCurrent(); | ||
| 137 | } | 144 | } |
| 138 | 145 | ||
| 139 | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | 146 | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { |
| 140 | SDL_GL_DeleteContext(gl_context); | 147 | core_context.reset(); |
| 141 | } | 148 | SDL_GL_DeleteContext(window_context); |
| 142 | |||
| 143 | void EmuWindow_SDL2_GL::SwapBuffers() { | ||
| 144 | SDL_GL_SwapWindow(render_window); | ||
| 145 | } | 149 | } |
| 146 | 150 | ||
| 147 | void EmuWindow_SDL2_GL::MakeCurrent() { | 151 | void EmuWindow_SDL2_GL::MakeCurrent() { |
| 148 | SDL_GL_MakeCurrent(render_window, gl_context); | 152 | core_context->MakeCurrent(); |
| 149 | } | 153 | } |
| 150 | 154 | ||
| 151 | void EmuWindow_SDL2_GL::DoneCurrent() { | 155 | void EmuWindow_SDL2_GL::DoneCurrent() { |
| 152 | SDL_GL_MakeCurrent(render_window, nullptr); | 156 | core_context->DoneCurrent(); |
| 153 | } | 157 | } |
| 154 | 158 | ||
| 155 | 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, |
| @@ -161,3 +165,13 @@ void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, voi | |||
| 161 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | 165 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { |
| 162 | return std::make_unique<SDLGLContext>(); | 166 | return std::make_unique<SDLGLContext>(); |
| 163 | } | 167 | } |
| 168 | |||
| 169 | void EmuWindow_SDL2_GL::Present() { | ||
| 170 | SDL_GL_MakeCurrent(render_window, window_context); | ||
| 171 | SDL_GL_SetSwapInterval(Settings::values.use_vsync ? 1 : 0); | ||
| 172 | while (IsOpen()) { | ||
| 173 | system.Renderer().TryPresent(100); | ||
| 174 | SDL_GL_SwapWindow(render_window); | ||
| 175 | } | ||
| 176 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 177 | } | ||
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 c753085a8..b80669ff0 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -10,17 +10,12 @@ | |||
| 10 | 10 | ||
| 11 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { | 11 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { |
| 12 | public: | 12 | public: |
| 13 | explicit EmuWindow_SDL2_GL(bool fullscreen); | 13 | explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_GL(); | 14 | ~EmuWindow_SDL2_GL(); |
| 15 | 15 | ||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | 16 | void MakeCurrent() override; |
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | 17 | void DoneCurrent() override; |
| 18 | void Present() override; | ||
| 24 | 19 | ||
| 25 | /// Ignored in OpenGL | 20 | /// Ignored in OpenGL |
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 21 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| @@ -29,10 +24,17 @@ public: | |||
| 29 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 24 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 30 | 25 | ||
| 31 | private: | 26 | private: |
| 27 | /// Fake hidden window for the core context | ||
| 28 | SDL_Window* dummy_window{}; | ||
| 29 | |||
| 32 | /// Whether the GPU and driver supports the OpenGL extension required | 30 | /// Whether the GPU and driver supports the OpenGL extension required |
| 33 | bool SupportsRequiredGLExtensions(); | 31 | bool SupportsRequiredGLExtensions(); |
| 34 | 32 | ||
| 35 | using SDL_GLContext = void*; | 33 | using SDL_GLContext = void*; |
| 34 | |||
| 36 | /// The OpenGL context associated with the window | 35 | /// The OpenGL context associated with the window |
| 37 | SDL_GLContext gl_context; | 36 | SDL_GLContext window_context; |
| 37 | |||
| 38 | /// The OpenGL context associated with the core | ||
| 39 | std::unique_ptr<Core::Frontend::GraphicsContext> core_context; | ||
| 38 | }; | 40 | }; |
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 a203f0da9..abcc58165 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -15,7 +15,8 @@ | |||
| 15 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" | 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" |
| 17 | 17 | ||
| 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) |
| 19 | : EmuWindow_SDL2{system, fullscreen} { | ||
| 19 | if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { | 20 | if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { |
| 20 | LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); | 21 | LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); |
| 21 | exit(EXIT_FAILURE); | 22 | exit(EXIT_FAILURE); |
| @@ -110,8 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { | |||
| 110 | vkDestroyInstance(vk_instance, nullptr); | 111 | vkDestroyInstance(vk_instance, nullptr); |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 113 | void EmuWindow_SDL2_VK::SwapBuffers() {} | ||
| 114 | |||
| 115 | void EmuWindow_SDL2_VK::MakeCurrent() { | 114 | void EmuWindow_SDL2_VK::MakeCurrent() { |
| 116 | // Unused on Vulkan | 115 | // Unused on Vulkan |
| 117 | } | 116 | } |
| @@ -160,3 +159,7 @@ bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanc | |||
| 160 | return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); | 159 | return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); |
| 161 | }) != layers.end(); | 160 | }) != layers.end(); |
| 162 | } | 161 | } |
| 162 | |||
| 163 | void EmuWindow_SDL2_VK::Present() { | ||
| 164 | // TODO (bunnei): ImplementMe | ||
| 165 | } | ||
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 2a7c06a24..1eb8c0868 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -10,19 +10,12 @@ | |||
| 10 | 10 | ||
| 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { |
| 12 | public: | 12 | public: |
| 13 | explicit EmuWindow_SDL2_VK(bool fullscreen); | 13 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen); |
| 14 | ~EmuWindow_SDL2_VK(); | 14 | ~EmuWindow_SDL2_VK(); |
| 15 | 15 | ||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | 16 | void MakeCurrent() override; |
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | 17 | void DoneCurrent() override; |
| 24 | 18 | void Present() override; | |
| 25 | /// Retrieves Vulkan specific handlers from the window | ||
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 19 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, |
| 27 | void* surface) const override; | 20 | void* surface) const override; |
| 28 | 21 | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 325795321..babf4c3a4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -177,14 +177,16 @@ int main(int argc, char** argv) { | |||
| 177 | Settings::values.use_gdbstub = use_gdbstub; | 177 | Settings::values.use_gdbstub = use_gdbstub; |
| 178 | Settings::Apply(); | 178 | Settings::Apply(); |
| 179 | 179 | ||
| 180 | Core::System& system{Core::System::GetInstance()}; | ||
| 181 | |||
| 180 | std::unique_ptr<EmuWindow_SDL2> emu_window; | 182 | std::unique_ptr<EmuWindow_SDL2> emu_window; |
| 181 | switch (Settings::values.renderer_backend) { | 183 | switch (Settings::values.renderer_backend) { |
| 182 | case Settings::RendererBackend::OpenGL: | 184 | case Settings::RendererBackend::OpenGL: |
| 183 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen); | 185 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen); |
| 184 | break; | 186 | break; |
| 185 | case Settings::RendererBackend::Vulkan: | 187 | case Settings::RendererBackend::Vulkan: |
| 186 | #ifdef HAS_VULKAN | 188 | #ifdef HAS_VULKAN |
| 187 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen); | 189 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen); |
| 188 | break; | 190 | break; |
| 189 | #else | 191 | #else |
| 190 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); | 192 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); |
| @@ -192,12 +194,6 @@ int main(int argc, char** argv) { | |||
| 192 | #endif | 194 | #endif |
| 193 | } | 195 | } |
| 194 | 196 | ||
| 195 | if (!Settings::values.use_multi_core) { | ||
| 196 | // Single core mode must acquire OpenGL context for entire emulation session | ||
| 197 | emu_window->MakeCurrent(); | ||
| 198 | } | ||
| 199 | |||
| 200 | Core::System& system{Core::System::GetInstance()}; | ||
| 201 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); | 197 | system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); |
| 202 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 198 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 203 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); | 199 | system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); |
| @@ -234,12 +230,23 @@ int main(int argc, char** argv) { | |||
| 234 | 230 | ||
| 235 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); | 231 | system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); |
| 236 | 232 | ||
| 237 | emu_window->MakeCurrent(); | ||
| 238 | system.Renderer().Rasterizer().LoadDiskResources(); | 233 | system.Renderer().Rasterizer().LoadDiskResources(); |
| 239 | 234 | ||
| 235 | // Acquire render context for duration of the thread if this is the rendering thread | ||
| 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 | |||
| 245 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | ||
| 240 | while (emu_window->IsOpen()) { | 246 | while (emu_window->IsOpen()) { |
| 241 | system.RunLoop(); | 247 | system.RunLoop(); |
| 242 | } | 248 | } |
| 249 | render_thread.join(); | ||
| 243 | 250 | ||
| 244 | system.Shutdown(); | 251 | system.Shutdown(); |
| 245 | 252 | ||
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 f2cc4a797..a1bdb1a12 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | |||
| @@ -112,10 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | |||
| 112 | SDL_Quit(); | 112 | SDL_Quit(); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | void EmuWindow_SDL2_Hide::SwapBuffers() { | ||
| 116 | SDL_GL_SwapWindow(render_window); | ||
| 117 | } | ||
| 118 | |||
| 119 | void EmuWindow_SDL2_Hide::PollEvents() {} | 115 | void EmuWindow_SDL2_Hide::PollEvents() {} |
| 120 | 116 | ||
| 121 | void EmuWindow_SDL2_Hide::MakeCurrent() { | 117 | void EmuWindow_SDL2_Hide::MakeCurrent() { |
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 c7fccc002..b13e15309 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -13,9 +13,6 @@ public: | |||
| 13 | explicit EmuWindow_SDL2_Hide(); | 13 | explicit EmuWindow_SDL2_Hide(); |
| 14 | ~EmuWindow_SDL2_Hide(); | 14 | ~EmuWindow_SDL2_Hide(); |
| 15 | 15 | ||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Polls window events | 16 | /// Polls window events |
| 20 | void PollEvents() override; | 17 | void PollEvents() override; |
| 21 | 18 | ||