diff options
Diffstat (limited to 'src')
102 files changed, 2825 insertions, 883 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1a3647a67..d342cafe0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -15,14 +15,14 @@ add_library(core STATIC | |||
| 15 | constants.h | 15 | constants.h |
| 16 | core.cpp | 16 | core.cpp |
| 17 | core.h | 17 | core.h |
| 18 | core_cpu.cpp | 18 | core_manager.cpp |
| 19 | core_cpu.h | 19 | core_manager.h |
| 20 | core_timing.cpp | 20 | core_timing.cpp |
| 21 | core_timing.h | 21 | core_timing.h |
| 22 | core_timing_util.cpp | 22 | core_timing_util.cpp |
| 23 | core_timing_util.h | 23 | core_timing_util.h |
| 24 | cpu_core_manager.cpp | 24 | cpu_manager.cpp |
| 25 | cpu_core_manager.h | 25 | cpu_manager.h |
| 26 | crypto/aes_util.cpp | 26 | crypto/aes_util.cpp |
| 27 | crypto/aes_util.h | 27 | crypto/aes_util.h |
| 28 | crypto/encryption_layer.cpp | 28 | crypto/encryption_layer.cpp |
| @@ -158,6 +158,8 @@ add_library(core STATIC | |||
| 158 | hle/kernel/mutex.h | 158 | hle/kernel/mutex.h |
| 159 | hle/kernel/object.cpp | 159 | hle/kernel/object.cpp |
| 160 | hle/kernel/object.h | 160 | hle/kernel/object.h |
| 161 | hle/kernel/physical_core.cpp | ||
| 162 | hle/kernel/physical_core.h | ||
| 161 | hle/kernel/process.cpp | 163 | hle/kernel/process.cpp |
| 162 | hle/kernel/process.h | 164 | hle/kernel/process.h |
| 163 | hle/kernel/process_capability.cpp | 165 | hle/kernel/process_capability.cpp |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index e825c0526..791640a3a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -10,11 +10,12 @@ | |||
| 10 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "core/arm/dynarmic/arm_dynarmic.h" | 11 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/core_cpu.h" | 13 | #include "core/core_manager.h" |
| 14 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 15 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| 16 | #include "core/gdbstub/gdbstub.h" | 16 | #include "core/gdbstub/gdbstub.h" |
| 17 | #include "core/hle/kernel/process.h" | 17 | #include "core/hle/kernel/process.h" |
| 18 | #include "core/hle/kernel/scheduler.h" | ||
| 18 | #include "core/hle/kernel/svc.h" | 19 | #include "core/hle/kernel/svc.h" |
| 19 | #include "core/hle/kernel/vm_manager.h" | 20 | #include "core/hle/kernel/vm_manager.h" |
| 20 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| @@ -87,7 +88,7 @@ public: | |||
| 87 | if (GDBStub::IsServerEnabled()) { | 88 | if (GDBStub::IsServerEnabled()) { |
| 88 | parent.jit->HaltExecution(); | 89 | parent.jit->HaltExecution(); |
| 89 | parent.SetPC(pc); | 90 | parent.SetPC(pc); |
| 90 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 91 | Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread(); |
| 91 | parent.SaveContext(thread->GetContext()); | 92 | parent.SaveContext(thread->GetContext()); |
| 92 | GDBStub::Break(); | 93 | GDBStub::Break(); |
| 93 | GDBStub::SendTrap(thread, 5); | 94 | GDBStub::SendTrap(thread, 5); |
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index abd59ff4b..94570e520 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp | |||
| @@ -2,10 +2,24 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #ifdef ARCHITECTURE_x86_64 | ||
| 6 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 7 | #endif | ||
| 5 | #include "core/arm/exclusive_monitor.h" | 8 | #include "core/arm/exclusive_monitor.h" |
| 9 | #include "core/memory.h" | ||
| 6 | 10 | ||
| 7 | namespace Core { | 11 | namespace Core { |
| 8 | 12 | ||
| 9 | ExclusiveMonitor::~ExclusiveMonitor() = default; | 13 | ExclusiveMonitor::~ExclusiveMonitor() = default; |
| 10 | 14 | ||
| 15 | std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | ||
| 16 | std::size_t num_cores) { | ||
| 17 | #ifdef ARCHITECTURE_x86_64 | ||
| 18 | return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); | ||
| 19 | #else | ||
| 20 | // TODO(merry): Passthrough exclusive monitor | ||
| 21 | return nullptr; | ||
| 22 | #endif | ||
| 23 | } | ||
| 24 | |||
| 11 | } // namespace Core | 25 | } // namespace Core |
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index f59aca667..4ef418b90 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h | |||
| @@ -4,8 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 11 | namespace Memory { | ||
| 12 | class Memory; | ||
| 13 | } | ||
| 14 | |||
| 9 | namespace Core { | 15 | namespace Core { |
| 10 | 16 | ||
| 11 | class ExclusiveMonitor { | 17 | class ExclusiveMonitor { |
| @@ -22,4 +28,7 @@ public: | |||
| 22 | virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; | 28 | virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; |
| 23 | }; | 29 | }; |
| 24 | 30 | ||
| 31 | std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | ||
| 32 | std::size_t num_cores); | ||
| 33 | |||
| 25 | } // namespace Core | 34 | } // namespace Core |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 48182c99a..f99ad5802 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/arm/unicorn/arm_unicorn.h" | 9 | #include "core/arm/unicorn/arm_unicorn.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 12 | #include "core/hle/kernel/scheduler.h" | ||
| 12 | #include "core/hle/kernel/svc.h" | 13 | #include "core/hle/kernel/svc.h" |
| 13 | 14 | ||
| 14 | namespace Core { | 15 | namespace Core { |
| @@ -177,7 +178,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { | |||
| 177 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 178 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 181 | Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread(); |
| 181 | SaveContext(thread->GetContext()); | 182 | SaveContext(thread->GetContext()); |
| 182 | if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { | 183 | if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { |
| 183 | last_bkpt_hit = false; | 184 | last_bkpt_hit = false; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index d697b80ef..0eb0c0dca 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -11,9 +11,9 @@ | |||
| 11 | #include "common/string_util.h" | 11 | #include "common/string_util.h" |
| 12 | #include "core/arm/exclusive_monitor.h" | 12 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
| 14 | #include "core/core_cpu.h" | 14 | #include "core/core_manager.h" |
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/cpu_core_manager.h" | 16 | #include "core/cpu_manager.h" |
| 17 | #include "core/file_sys/bis_factory.h" | 17 | #include "core/file_sys/bis_factory.h" |
| 18 | #include "core/file_sys/card_image.h" | 18 | #include "core/file_sys/card_image.h" |
| 19 | #include "core/file_sys/mode.h" | 19 | #include "core/file_sys/mode.h" |
| @@ -28,6 +28,7 @@ | |||
| 28 | #include "core/hardware_interrupt_manager.h" | 28 | #include "core/hardware_interrupt_manager.h" |
| 29 | #include "core/hle/kernel/client_port.h" | 29 | #include "core/hle/kernel/client_port.h" |
| 30 | #include "core/hle/kernel/kernel.h" | 30 | #include "core/hle/kernel/kernel.h" |
| 31 | #include "core/hle/kernel/physical_core.h" | ||
| 31 | #include "core/hle/kernel/process.h" | 32 | #include "core/hle/kernel/process.h" |
| 32 | #include "core/hle/kernel/scheduler.h" | 33 | #include "core/hle/kernel/scheduler.h" |
| 33 | #include "core/hle/kernel/thread.h" | 34 | #include "core/hle/kernel/thread.h" |
| @@ -113,16 +114,25 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 113 | struct System::Impl { | 114 | struct System::Impl { |
| 114 | explicit Impl(System& system) | 115 | explicit Impl(System& system) |
| 115 | : kernel{system}, fs_controller{system}, memory{system}, | 116 | : kernel{system}, fs_controller{system}, memory{system}, |
| 116 | cpu_core_manager{system}, reporter{system}, applet_manager{system} {} | 117 | cpu_manager{system}, reporter{system}, applet_manager{system} {} |
| 117 | 118 | ||
| 118 | Cpu& CurrentCpuCore() { | 119 | CoreManager& CurrentCoreManager() { |
| 119 | return cpu_core_manager.GetCurrentCore(); | 120 | return cpu_manager.GetCurrentCoreManager(); |
| 121 | } | ||
| 122 | |||
| 123 | Kernel::PhysicalCore& CurrentPhysicalCore() { | ||
| 124 | const auto index = cpu_manager.GetActiveCoreIndex(); | ||
| 125 | return kernel.PhysicalCore(index); | ||
| 126 | } | ||
| 127 | |||
| 128 | Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { | ||
| 129 | return kernel.PhysicalCore(index); | ||
| 120 | } | 130 | } |
| 121 | 131 | ||
| 122 | ResultStatus RunLoop(bool tight_loop) { | 132 | ResultStatus RunLoop(bool tight_loop) { |
| 123 | status = ResultStatus::Success; | 133 | status = ResultStatus::Success; |
| 124 | 134 | ||
| 125 | cpu_core_manager.RunLoop(tight_loop); | 135 | cpu_manager.RunLoop(tight_loop); |
| 126 | 136 | ||
| 127 | return status; | 137 | return status; |
| 128 | } | 138 | } |
| @@ -131,8 +141,8 @@ struct System::Impl { | |||
| 131 | LOG_DEBUG(HW_Memory, "initialized OK"); | 141 | LOG_DEBUG(HW_Memory, "initialized OK"); |
| 132 | 142 | ||
| 133 | core_timing.Initialize(); | 143 | core_timing.Initialize(); |
| 134 | cpu_core_manager.Initialize(); | ||
| 135 | kernel.Initialize(); | 144 | kernel.Initialize(); |
| 145 | cpu_manager.Initialize(); | ||
| 136 | 146 | ||
| 137 | const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( | 147 | const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( |
| 138 | std::chrono::system_clock::now().time_since_epoch()); | 148 | std::chrono::system_clock::now().time_since_epoch()); |
| @@ -205,7 +215,6 @@ struct System::Impl { | |||
| 205 | // Main process has been loaded and been made current. | 215 | // Main process has been loaded and been made current. |
| 206 | // Begin GPU and CPU execution. | 216 | // Begin GPU and CPU execution. |
| 207 | gpu_core->Start(); | 217 | gpu_core->Start(); |
| 208 | cpu_core_manager.StartThreads(); | ||
| 209 | 218 | ||
| 210 | // Initialize cheat engine | 219 | // Initialize cheat engine |
| 211 | if (cheat_engine) { | 220 | if (cheat_engine) { |
| @@ -259,7 +268,9 @@ struct System::Impl { | |||
| 259 | is_powered_on = false; | 268 | is_powered_on = false; |
| 260 | exit_lock = false; | 269 | exit_lock = false; |
| 261 | 270 | ||
| 262 | gpu_core->WaitIdle(); | 271 | if (gpu_core) { |
| 272 | gpu_core->WaitIdle(); | ||
| 273 | } | ||
| 263 | 274 | ||
| 264 | // Shutdown emulation session | 275 | // Shutdown emulation session |
| 265 | renderer.reset(); | 276 | renderer.reset(); |
| @@ -272,7 +283,7 @@ struct System::Impl { | |||
| 272 | gpu_core.reset(); | 283 | gpu_core.reset(); |
| 273 | 284 | ||
| 274 | // Close all CPU/threading state | 285 | // Close all CPU/threading state |
| 275 | cpu_core_manager.Shutdown(); | 286 | cpu_manager.Shutdown(); |
| 276 | 287 | ||
| 277 | // Shutdown kernel and core timing | 288 | // Shutdown kernel and core timing |
| 278 | kernel.Shutdown(); | 289 | kernel.Shutdown(); |
| @@ -342,7 +353,7 @@ struct System::Impl { | |||
| 342 | std::unique_ptr<Tegra::GPU> gpu_core; | 353 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 343 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 354 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; |
| 344 | Memory::Memory memory; | 355 | Memory::Memory memory; |
| 345 | CpuCoreManager cpu_core_manager; | 356 | CpuManager cpu_manager; |
| 346 | bool is_powered_on = false; | 357 | bool is_powered_on = false; |
| 347 | bool exit_lock = false; | 358 | bool exit_lock = false; |
| 348 | 359 | ||
| @@ -377,12 +388,12 @@ struct System::Impl { | |||
| 377 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 388 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| 378 | System::~System() = default; | 389 | System::~System() = default; |
| 379 | 390 | ||
| 380 | Cpu& System::CurrentCpuCore() { | 391 | CoreManager& System::CurrentCoreManager() { |
| 381 | return impl->CurrentCpuCore(); | 392 | return impl->CurrentCoreManager(); |
| 382 | } | 393 | } |
| 383 | 394 | ||
| 384 | const Cpu& System::CurrentCpuCore() const { | 395 | const CoreManager& System::CurrentCoreManager() const { |
| 385 | return impl->CurrentCpuCore(); | 396 | return impl->CurrentCoreManager(); |
| 386 | } | 397 | } |
| 387 | 398 | ||
| 388 | System::ResultStatus System::RunLoop(bool tight_loop) { | 399 | System::ResultStatus System::RunLoop(bool tight_loop) { |
| @@ -394,7 +405,7 @@ System::ResultStatus System::SingleStep() { | |||
| 394 | } | 405 | } |
| 395 | 406 | ||
| 396 | void System::InvalidateCpuInstructionCaches() { | 407 | void System::InvalidateCpuInstructionCaches() { |
| 397 | impl->cpu_core_manager.InvalidateAllInstructionCaches(); | 408 | impl->kernel.InvalidateAllInstructionCaches(); |
| 398 | } | 409 | } |
| 399 | 410 | ||
| 400 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { | 411 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { |
| @@ -406,13 +417,11 @@ bool System::IsPoweredOn() const { | |||
| 406 | } | 417 | } |
| 407 | 418 | ||
| 408 | void System::PrepareReschedule() { | 419 | void System::PrepareReschedule() { |
| 409 | CurrentCpuCore().PrepareReschedule(); | 420 | impl->CurrentPhysicalCore().Stop(); |
| 410 | } | 421 | } |
| 411 | 422 | ||
| 412 | void System::PrepareReschedule(const u32 core_index) { | 423 | void System::PrepareReschedule(const u32 core_index) { |
| 413 | if (core_index < GlobalScheduler().CpuCoresCount()) { | 424 | impl->kernel.PrepareReschedule(core_index); |
| 414 | CpuCore(core_index).PrepareReschedule(); | ||
| 415 | } | ||
| 416 | } | 425 | } |
| 417 | 426 | ||
| 418 | PerfStatsResults System::GetAndResetPerfStats() { | 427 | PerfStatsResults System::GetAndResetPerfStats() { |
| @@ -428,31 +437,31 @@ const TelemetrySession& System::TelemetrySession() const { | |||
| 428 | } | 437 | } |
| 429 | 438 | ||
| 430 | ARM_Interface& System::CurrentArmInterface() { | 439 | ARM_Interface& System::CurrentArmInterface() { |
| 431 | return CurrentCpuCore().ArmInterface(); | 440 | return impl->CurrentPhysicalCore().ArmInterface(); |
| 432 | } | 441 | } |
| 433 | 442 | ||
| 434 | const ARM_Interface& System::CurrentArmInterface() const { | 443 | const ARM_Interface& System::CurrentArmInterface() const { |
| 435 | return CurrentCpuCore().ArmInterface(); | 444 | return impl->CurrentPhysicalCore().ArmInterface(); |
| 436 | } | 445 | } |
| 437 | 446 | ||
| 438 | std::size_t System::CurrentCoreIndex() const { | 447 | std::size_t System::CurrentCoreIndex() const { |
| 439 | return CurrentCpuCore().CoreIndex(); | 448 | return impl->cpu_manager.GetActiveCoreIndex(); |
| 440 | } | 449 | } |
| 441 | 450 | ||
| 442 | Kernel::Scheduler& System::CurrentScheduler() { | 451 | Kernel::Scheduler& System::CurrentScheduler() { |
| 443 | return CurrentCpuCore().Scheduler(); | 452 | return impl->CurrentPhysicalCore().Scheduler(); |
| 444 | } | 453 | } |
| 445 | 454 | ||
| 446 | const Kernel::Scheduler& System::CurrentScheduler() const { | 455 | const Kernel::Scheduler& System::CurrentScheduler() const { |
| 447 | return CurrentCpuCore().Scheduler(); | 456 | return impl->CurrentPhysicalCore().Scheduler(); |
| 448 | } | 457 | } |
| 449 | 458 | ||
| 450 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { | 459 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { |
| 451 | return CpuCore(core_index).Scheduler(); | 460 | return impl->GetPhysicalCore(core_index).Scheduler(); |
| 452 | } | 461 | } |
| 453 | 462 | ||
| 454 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { | 463 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { |
| 455 | return CpuCore(core_index).Scheduler(); | 464 | return impl->GetPhysicalCore(core_index).Scheduler(); |
| 456 | } | 465 | } |
| 457 | 466 | ||
| 458 | /// Gets the global scheduler | 467 | /// Gets the global scheduler |
| @@ -474,28 +483,28 @@ const Kernel::Process* System::CurrentProcess() const { | |||
| 474 | } | 483 | } |
| 475 | 484 | ||
| 476 | ARM_Interface& System::ArmInterface(std::size_t core_index) { | 485 | ARM_Interface& System::ArmInterface(std::size_t core_index) { |
| 477 | return CpuCore(core_index).ArmInterface(); | 486 | return impl->GetPhysicalCore(core_index).ArmInterface(); |
| 478 | } | 487 | } |
| 479 | 488 | ||
| 480 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { | 489 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { |
| 481 | return CpuCore(core_index).ArmInterface(); | 490 | return impl->GetPhysicalCore(core_index).ArmInterface(); |
| 482 | } | 491 | } |
| 483 | 492 | ||
| 484 | Cpu& System::CpuCore(std::size_t core_index) { | 493 | CoreManager& System::GetCoreManager(std::size_t core_index) { |
| 485 | return impl->cpu_core_manager.GetCore(core_index); | 494 | return impl->cpu_manager.GetCoreManager(core_index); |
| 486 | } | 495 | } |
| 487 | 496 | ||
| 488 | const Cpu& System::CpuCore(std::size_t core_index) const { | 497 | const CoreManager& System::GetCoreManager(std::size_t core_index) const { |
| 489 | ASSERT(core_index < NUM_CPU_CORES); | 498 | ASSERT(core_index < NUM_CPU_CORES); |
| 490 | return impl->cpu_core_manager.GetCore(core_index); | 499 | return impl->cpu_manager.GetCoreManager(core_index); |
| 491 | } | 500 | } |
| 492 | 501 | ||
| 493 | ExclusiveMonitor& System::Monitor() { | 502 | ExclusiveMonitor& System::Monitor() { |
| 494 | return impl->cpu_core_manager.GetExclusiveMonitor(); | 503 | return impl->kernel.GetExclusiveMonitor(); |
| 495 | } | 504 | } |
| 496 | 505 | ||
| 497 | const ExclusiveMonitor& System::Monitor() const { | 506 | const ExclusiveMonitor& System::Monitor() const { |
| 498 | return impl->cpu_core_manager.GetExclusiveMonitor(); | 507 | return impl->kernel.GetExclusiveMonitor(); |
| 499 | } | 508 | } |
| 500 | 509 | ||
| 501 | Memory::Memory& System::Memory() { | 510 | Memory::Memory& System::Memory() { |
diff --git a/src/core/core.h b/src/core/core.h index e240c5c58..e69d68fcf 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -93,7 +93,7 @@ class Memory; | |||
| 93 | namespace Core { | 93 | namespace Core { |
| 94 | 94 | ||
| 95 | class ARM_Interface; | 95 | class ARM_Interface; |
| 96 | class Cpu; | 96 | class CoreManager; |
| 97 | class ExclusiveMonitor; | 97 | class ExclusiveMonitor; |
| 98 | class FrameLimiter; | 98 | class FrameLimiter; |
| 99 | class PerfStats; | 99 | class PerfStats; |
| @@ -218,10 +218,10 @@ public: | |||
| 218 | const ARM_Interface& ArmInterface(std::size_t core_index) const; | 218 | const ARM_Interface& ArmInterface(std::size_t core_index) const; |
| 219 | 219 | ||
| 220 | /// Gets a CPU interface to the CPU core with the specified index | 220 | /// Gets a CPU interface to the CPU core with the specified index |
| 221 | Cpu& CpuCore(std::size_t core_index); | 221 | CoreManager& GetCoreManager(std::size_t core_index); |
| 222 | 222 | ||
| 223 | /// Gets a CPU interface to the CPU core with the specified index | 223 | /// Gets a CPU interface to the CPU core with the specified index |
| 224 | const Cpu& CpuCore(std::size_t core_index) const; | 224 | const CoreManager& GetCoreManager(std::size_t core_index) const; |
| 225 | 225 | ||
| 226 | /// Gets a reference to the exclusive monitor | 226 | /// Gets a reference to the exclusive monitor |
| 227 | ExclusiveMonitor& Monitor(); | 227 | ExclusiveMonitor& Monitor(); |
| @@ -364,10 +364,10 @@ private: | |||
| 364 | System(); | 364 | System(); |
| 365 | 365 | ||
| 366 | /// Returns the currently running CPU core | 366 | /// Returns the currently running CPU core |
| 367 | Cpu& CurrentCpuCore(); | 367 | CoreManager& CurrentCoreManager(); |
| 368 | 368 | ||
| 369 | /// Returns the currently running CPU core | 369 | /// Returns the currently running CPU core |
| 370 | const Cpu& CurrentCpuCore() const; | 370 | const CoreManager& CurrentCoreManager() const; |
| 371 | 371 | ||
| 372 | /** | 372 | /** |
| 373 | * Initialize the emulated system. | 373 | * Initialize the emulated system. |
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp deleted file mode 100644 index 630cd4feb..000000000 --- a/src/core/core_cpu.cpp +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <condition_variable> | ||
| 6 | #include <mutex> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #ifdef ARCHITECTURE_x86_64 | ||
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 11 | #endif | ||
| 12 | #include "core/arm/exclusive_monitor.h" | ||
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_cpu.h" | ||
| 16 | #include "core/core_timing.h" | ||
| 17 | #include "core/hle/kernel/scheduler.h" | ||
| 18 | #include "core/hle/kernel/thread.h" | ||
| 19 | #include "core/hle/lock.h" | ||
| 20 | #include "core/settings.h" | ||
| 21 | |||
| 22 | namespace Core { | ||
| 23 | |||
| 24 | void CpuBarrier::NotifyEnd() { | ||
| 25 | std::unique_lock lock{mutex}; | ||
| 26 | end = true; | ||
| 27 | condition.notify_all(); | ||
| 28 | } | ||
| 29 | |||
| 30 | bool CpuBarrier::Rendezvous() { | ||
| 31 | if (!Settings::values.use_multi_core) { | ||
| 32 | // Meaningless when running in single-core mode | ||
| 33 | return true; | ||
| 34 | } | ||
| 35 | |||
| 36 | if (!end) { | ||
| 37 | std::unique_lock lock{mutex}; | ||
| 38 | |||
| 39 | --cores_waiting; | ||
| 40 | if (!cores_waiting) { | ||
| 41 | cores_waiting = NUM_CPU_CORES; | ||
| 42 | condition.notify_all(); | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | condition.wait(lock); | ||
| 47 | return true; | ||
| 48 | } | ||
| 49 | |||
| 50 | return false; | ||
| 51 | } | ||
| 52 | |||
| 53 | Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, | ||
| 54 | std::size_t core_index) | ||
| 55 | : cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()}, | ||
| 56 | core_timing{system.CoreTiming()}, core_index{core_index} { | ||
| 57 | #ifdef ARCHITECTURE_x86_64 | ||
| 58 | arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); | ||
| 59 | #else | ||
| 60 | arm_interface = std::make_unique<ARM_Unicorn>(system); | ||
| 61 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 62 | #endif | ||
| 63 | |||
| 64 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); | ||
| 65 | } | ||
| 66 | |||
| 67 | Cpu::~Cpu() = default; | ||
| 68 | |||
| 69 | std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor( | ||
| 70 | [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) { | ||
| 71 | #ifdef ARCHITECTURE_x86_64 | ||
| 72 | return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores); | ||
| 73 | #else | ||
| 74 | // TODO(merry): Passthrough exclusive monitor | ||
| 75 | return nullptr; | ||
| 76 | #endif | ||
| 77 | } | ||
| 78 | |||
| 79 | void Cpu::RunLoop(bool tight_loop) { | ||
| 80 | // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step | ||
| 81 | if (!cpu_barrier.Rendezvous()) { | ||
| 82 | // If rendezvous failed, session has been killed | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | Reschedule(); | ||
| 87 | |||
| 88 | // If we don't have a currently active thread then don't execute instructions, | ||
| 89 | // instead advance to the next event and try to yield to the next thread | ||
| 90 | if (Kernel::GetCurrentThread() == nullptr) { | ||
| 91 | LOG_TRACE(Core, "Core-{} idling", core_index); | ||
| 92 | core_timing.Idle(); | ||
| 93 | } else { | ||
| 94 | if (tight_loop) { | ||
| 95 | arm_interface->Run(); | ||
| 96 | } else { | ||
| 97 | arm_interface->Step(); | ||
| 98 | } | ||
| 99 | // We are stopping a run, exclusive state must be cleared | ||
| 100 | arm_interface->ClearExclusiveState(); | ||
| 101 | } | ||
| 102 | core_timing.Advance(); | ||
| 103 | |||
| 104 | Reschedule(); | ||
| 105 | } | ||
| 106 | |||
| 107 | void Cpu::SingleStep() { | ||
| 108 | return RunLoop(false); | ||
| 109 | } | ||
| 110 | |||
| 111 | void Cpu::PrepareReschedule() { | ||
| 112 | arm_interface->PrepareReschedule(); | ||
| 113 | } | ||
| 114 | |||
| 115 | void Cpu::Reschedule() { | ||
| 116 | // Lock the global kernel mutex when we manipulate the HLE state | ||
| 117 | std::lock_guard lock(HLE::g_hle_lock); | ||
| 118 | |||
| 119 | global_scheduler.SelectThread(core_index); | ||
| 120 | scheduler->TryDoContextSwitch(); | ||
| 121 | } | ||
| 122 | |||
| 123 | void Cpu::Shutdown() { | ||
| 124 | scheduler->Shutdown(); | ||
| 125 | } | ||
| 126 | |||
| 127 | } // namespace Core | ||
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h deleted file mode 100644 index 78f5021a2..000000000 --- a/src/core/core_cpu.h +++ /dev/null | |||
| @@ -1,120 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <condition_variable> | ||
| 9 | #include <cstddef> | ||
| 10 | #include <memory> | ||
| 11 | #include <mutex> | ||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | class GlobalScheduler; | ||
| 16 | class Scheduler; | ||
| 17 | } // namespace Kernel | ||
| 18 | |||
| 19 | namespace Core { | ||
| 20 | class System; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Core::Timing { | ||
| 24 | class CoreTiming; | ||
| 25 | } | ||
| 26 | |||
| 27 | namespace Memory { | ||
| 28 | class Memory; | ||
| 29 | } | ||
| 30 | |||
| 31 | namespace Core { | ||
| 32 | |||
| 33 | class ARM_Interface; | ||
| 34 | class ExclusiveMonitor; | ||
| 35 | |||
| 36 | constexpr unsigned NUM_CPU_CORES{4}; | ||
| 37 | |||
| 38 | class CpuBarrier { | ||
| 39 | public: | ||
| 40 | bool IsAlive() const { | ||
| 41 | return !end; | ||
| 42 | } | ||
| 43 | |||
| 44 | void NotifyEnd(); | ||
| 45 | |||
| 46 | bool Rendezvous(); | ||
| 47 | |||
| 48 | private: | ||
| 49 | unsigned cores_waiting{NUM_CPU_CORES}; | ||
| 50 | std::mutex mutex; | ||
| 51 | std::condition_variable condition; | ||
| 52 | std::atomic<bool> end{}; | ||
| 53 | }; | ||
| 54 | |||
| 55 | class Cpu { | ||
| 56 | public: | ||
| 57 | Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, | ||
| 58 | std::size_t core_index); | ||
| 59 | ~Cpu(); | ||
| 60 | |||
| 61 | void RunLoop(bool tight_loop = true); | ||
| 62 | |||
| 63 | void SingleStep(); | ||
| 64 | |||
| 65 | void PrepareReschedule(); | ||
| 66 | |||
| 67 | ARM_Interface& ArmInterface() { | ||
| 68 | return *arm_interface; | ||
| 69 | } | ||
| 70 | |||
| 71 | const ARM_Interface& ArmInterface() const { | ||
| 72 | return *arm_interface; | ||
| 73 | } | ||
| 74 | |||
| 75 | Kernel::Scheduler& Scheduler() { | ||
| 76 | return *scheduler; | ||
| 77 | } | ||
| 78 | |||
| 79 | const Kernel::Scheduler& Scheduler() const { | ||
| 80 | return *scheduler; | ||
| 81 | } | ||
| 82 | |||
| 83 | bool IsMainCore() const { | ||
| 84 | return core_index == 0; | ||
| 85 | } | ||
| 86 | |||
| 87 | std::size_t CoreIndex() const { | ||
| 88 | return core_index; | ||
| 89 | } | ||
| 90 | |||
| 91 | void Shutdown(); | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Creates an exclusive monitor to handle exclusive reads/writes. | ||
| 95 | * | ||
| 96 | * @param memory The current memory subsystem that the monitor may wish | ||
| 97 | * to keep track of. | ||
| 98 | * | ||
| 99 | * @param num_cores The number of cores to assume about the CPU. | ||
| 100 | * | ||
| 101 | * @returns The constructed exclusive monitor instance, or nullptr if the current | ||
| 102 | * CPU backend is unable to use an exclusive monitor. | ||
| 103 | */ | ||
| 104 | static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | ||
| 105 | std::size_t num_cores); | ||
| 106 | |||
| 107 | private: | ||
| 108 | void Reschedule(); | ||
| 109 | |||
| 110 | std::unique_ptr<ARM_Interface> arm_interface; | ||
| 111 | CpuBarrier& cpu_barrier; | ||
| 112 | Kernel::GlobalScheduler& global_scheduler; | ||
| 113 | std::unique_ptr<Kernel::Scheduler> scheduler; | ||
| 114 | Timing::CoreTiming& core_timing; | ||
| 115 | |||
| 116 | std::atomic<bool> reschedule_pending = false; | ||
| 117 | std::size_t core_index; | ||
| 118 | }; | ||
| 119 | |||
| 120 | } // namespace Core | ||
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp new file mode 100644 index 000000000..8eacf92dd --- /dev/null +++ b/src/core/core_manager.cpp | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <condition_variable> | ||
| 6 | #include <mutex> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #ifdef ARCHITECTURE_x86_64 | ||
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 11 | #endif | ||
| 12 | #include "core/arm/exclusive_monitor.h" | ||
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_manager.h" | ||
| 16 | #include "core/core_timing.h" | ||
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 18 | #include "core/hle/kernel/physical_core.h" | ||
| 19 | #include "core/hle/kernel/scheduler.h" | ||
| 20 | #include "core/hle/kernel/thread.h" | ||
| 21 | #include "core/hle/lock.h" | ||
| 22 | #include "core/settings.h" | ||
| 23 | |||
| 24 | namespace Core { | ||
| 25 | |||
| 26 | CoreManager::CoreManager(System& system, std::size_t core_index) | ||
| 27 | : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore( | ||
| 28 | core_index)}, | ||
| 29 | core_timing{system.CoreTiming()}, core_index{core_index} {} | ||
| 30 | |||
| 31 | CoreManager::~CoreManager() = default; | ||
| 32 | |||
| 33 | void CoreManager::RunLoop(bool tight_loop) { | ||
| 34 | Reschedule(); | ||
| 35 | |||
| 36 | // If we don't have a currently active thread then don't execute instructions, | ||
| 37 | // instead advance to the next event and try to yield to the next thread | ||
| 38 | if (Kernel::GetCurrentThread() == nullptr) { | ||
| 39 | LOG_TRACE(Core, "Core-{} idling", core_index); | ||
| 40 | core_timing.Idle(); | ||
| 41 | } else { | ||
| 42 | if (tight_loop) { | ||
| 43 | physical_core.Run(); | ||
| 44 | } else { | ||
| 45 | physical_core.Step(); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | core_timing.Advance(); | ||
| 49 | |||
| 50 | Reschedule(); | ||
| 51 | } | ||
| 52 | |||
| 53 | void CoreManager::SingleStep() { | ||
| 54 | return RunLoop(false); | ||
| 55 | } | ||
| 56 | |||
| 57 | void CoreManager::PrepareReschedule() { | ||
| 58 | physical_core.Stop(); | ||
| 59 | } | ||
| 60 | |||
| 61 | void CoreManager::Reschedule() { | ||
| 62 | // Lock the global kernel mutex when we manipulate the HLE state | ||
| 63 | std::lock_guard lock(HLE::g_hle_lock); | ||
| 64 | |||
| 65 | global_scheduler.SelectThread(core_index); | ||
| 66 | |||
| 67 | physical_core.Scheduler().TryDoContextSwitch(); | ||
| 68 | } | ||
| 69 | |||
| 70 | } // namespace Core | ||
diff --git a/src/core/core_manager.h b/src/core/core_manager.h new file mode 100644 index 000000000..b14e723d7 --- /dev/null +++ b/src/core/core_manager.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <cstddef> | ||
| 9 | #include <memory> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | class GlobalScheduler; | ||
| 14 | class PhysicalCore; | ||
| 15 | } // namespace Kernel | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core::Timing { | ||
| 22 | class CoreTiming; | ||
| 23 | } | ||
| 24 | |||
| 25 | namespace Memory { | ||
| 26 | class Memory; | ||
| 27 | } | ||
| 28 | |||
| 29 | namespace Core { | ||
| 30 | |||
| 31 | constexpr unsigned NUM_CPU_CORES{4}; | ||
| 32 | |||
| 33 | class CoreManager { | ||
| 34 | public: | ||
| 35 | CoreManager(System& system, std::size_t core_index); | ||
| 36 | ~CoreManager(); | ||
| 37 | |||
| 38 | void RunLoop(bool tight_loop = true); | ||
| 39 | |||
| 40 | void SingleStep(); | ||
| 41 | |||
| 42 | void PrepareReschedule(); | ||
| 43 | |||
| 44 | bool IsMainCore() const { | ||
| 45 | return core_index == 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | std::size_t CoreIndex() const { | ||
| 49 | return core_index; | ||
| 50 | } | ||
| 51 | |||
| 52 | private: | ||
| 53 | void Reschedule(); | ||
| 54 | |||
| 55 | Kernel::GlobalScheduler& global_scheduler; | ||
| 56 | Kernel::PhysicalCore& physical_core; | ||
| 57 | Timing::CoreTiming& core_timing; | ||
| 58 | |||
| 59 | std::atomic<bool> reschedule_pending = false; | ||
| 60 | std::size_t core_index; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace Core | ||
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp deleted file mode 100644 index f04a34133..000000000 --- a/src/core/cpu_core_manager.cpp +++ /dev/null | |||
| @@ -1,152 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 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/arm/exclusive_monitor.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/core_cpu.h" | ||
| 9 | #include "core/core_timing.h" | ||
| 10 | #include "core/cpu_core_manager.h" | ||
| 11 | #include "core/gdbstub/gdbstub.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | namespace { | ||
| 16 | void RunCpuCore(const System& system, Cpu& cpu_state) { | ||
| 17 | while (system.IsPoweredOn()) { | ||
| 18 | cpu_state.RunLoop(true); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | } // Anonymous namespace | ||
| 22 | |||
| 23 | CpuCoreManager::CpuCoreManager(System& system) : system{system} {} | ||
| 24 | CpuCoreManager::~CpuCoreManager() = default; | ||
| 25 | |||
| 26 | void CpuCoreManager::Initialize() { | ||
| 27 | barrier = std::make_unique<CpuBarrier>(); | ||
| 28 | exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size()); | ||
| 29 | |||
| 30 | for (std::size_t index = 0; index < cores.size(); ++index) { | ||
| 31 | cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | void CpuCoreManager::StartThreads() { | ||
| 36 | // Create threads for CPU cores 1-3, and build thread_to_cpu map | ||
| 37 | // CPU core 0 is run on the main thread | ||
| 38 | thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); | ||
| 39 | if (!Settings::values.use_multi_core) { | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | |||
| 43 | for (std::size_t index = 0; index < core_threads.size(); ++index) { | ||
| 44 | core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system), | ||
| 45 | std::ref(*cores[index + 1])); | ||
| 46 | thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void CpuCoreManager::Shutdown() { | ||
| 51 | barrier->NotifyEnd(); | ||
| 52 | if (Settings::values.use_multi_core) { | ||
| 53 | for (auto& thread : core_threads) { | ||
| 54 | thread->join(); | ||
| 55 | thread.reset(); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | thread_to_cpu.clear(); | ||
| 60 | for (auto& cpu_core : cores) { | ||
| 61 | cpu_core->Shutdown(); | ||
| 62 | cpu_core.reset(); | ||
| 63 | } | ||
| 64 | |||
| 65 | exclusive_monitor.reset(); | ||
| 66 | barrier.reset(); | ||
| 67 | } | ||
| 68 | |||
| 69 | Cpu& CpuCoreManager::GetCore(std::size_t index) { | ||
| 70 | return *cores.at(index); | ||
| 71 | } | ||
| 72 | |||
| 73 | const Cpu& CpuCoreManager::GetCore(std::size_t index) const { | ||
| 74 | return *cores.at(index); | ||
| 75 | } | ||
| 76 | |||
| 77 | ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() { | ||
| 78 | return *exclusive_monitor; | ||
| 79 | } | ||
| 80 | |||
| 81 | const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const { | ||
| 82 | return *exclusive_monitor; | ||
| 83 | } | ||
| 84 | |||
| 85 | Cpu& CpuCoreManager::GetCurrentCore() { | ||
| 86 | if (Settings::values.use_multi_core) { | ||
| 87 | const auto& search = thread_to_cpu.find(std::this_thread::get_id()); | ||
| 88 | ASSERT(search != thread_to_cpu.end()); | ||
| 89 | ASSERT(search->second); | ||
| 90 | return *search->second; | ||
| 91 | } | ||
| 92 | |||
| 93 | // Otherwise, use single-threaded mode active_core variable | ||
| 94 | return *cores[active_core]; | ||
| 95 | } | ||
| 96 | |||
| 97 | const Cpu& CpuCoreManager::GetCurrentCore() const { | ||
| 98 | if (Settings::values.use_multi_core) { | ||
| 99 | const auto& search = thread_to_cpu.find(std::this_thread::get_id()); | ||
| 100 | ASSERT(search != thread_to_cpu.end()); | ||
| 101 | ASSERT(search->second); | ||
| 102 | return *search->second; | ||
| 103 | } | ||
| 104 | |||
| 105 | // Otherwise, use single-threaded mode active_core variable | ||
| 106 | return *cores[active_core]; | ||
| 107 | } | ||
| 108 | |||
| 109 | void CpuCoreManager::RunLoop(bool tight_loop) { | ||
| 110 | // Update thread_to_cpu in case Core 0 is run from a different host thread | ||
| 111 | thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); | ||
| 112 | |||
| 113 | if (GDBStub::IsServerEnabled()) { | ||
| 114 | GDBStub::HandlePacket(); | ||
| 115 | |||
| 116 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | ||
| 117 | // execute. Otherwise, get out of the loop function. | ||
| 118 | if (GDBStub::GetCpuHaltFlag()) { | ||
| 119 | if (GDBStub::GetCpuStepFlag()) { | ||
| 120 | tight_loop = false; | ||
| 121 | } else { | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | auto& core_timing = system.CoreTiming(); | ||
| 128 | core_timing.ResetRun(); | ||
| 129 | bool keep_running{}; | ||
| 130 | do { | ||
| 131 | keep_running = false; | ||
| 132 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | ||
| 133 | core_timing.SwitchContext(active_core); | ||
| 134 | if (core_timing.CanCurrentContextRun()) { | ||
| 135 | cores[active_core]->RunLoop(tight_loop); | ||
| 136 | } | ||
| 137 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 138 | } | ||
| 139 | } while (keep_running); | ||
| 140 | |||
| 141 | if (GDBStub::IsServerEnabled()) { | ||
| 142 | GDBStub::SetCpuStepFlag(false); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | void CpuCoreManager::InvalidateAllInstructionCaches() { | ||
| 147 | for (auto& cpu : cores) { | ||
| 148 | cpu->ArmInterface().ClearInstructionCache(); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | } // namespace Core | ||
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h deleted file mode 100644 index 2cbbf8216..000000000 --- a/src/core/cpu_core_manager.h +++ /dev/null | |||
| @@ -1,62 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <map> | ||
| 9 | #include <memory> | ||
| 10 | #include <thread> | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | |||
| 14 | class Cpu; | ||
| 15 | class CpuBarrier; | ||
| 16 | class ExclusiveMonitor; | ||
| 17 | class System; | ||
| 18 | |||
| 19 | class CpuCoreManager { | ||
| 20 | public: | ||
| 21 | explicit CpuCoreManager(System& system); | ||
| 22 | CpuCoreManager(const CpuCoreManager&) = delete; | ||
| 23 | CpuCoreManager(CpuCoreManager&&) = delete; | ||
| 24 | |||
| 25 | ~CpuCoreManager(); | ||
| 26 | |||
| 27 | CpuCoreManager& operator=(const CpuCoreManager&) = delete; | ||
| 28 | CpuCoreManager& operator=(CpuCoreManager&&) = delete; | ||
| 29 | |||
| 30 | void Initialize(); | ||
| 31 | void StartThreads(); | ||
| 32 | void Shutdown(); | ||
| 33 | |||
| 34 | Cpu& GetCore(std::size_t index); | ||
| 35 | const Cpu& GetCore(std::size_t index) const; | ||
| 36 | |||
| 37 | Cpu& GetCurrentCore(); | ||
| 38 | const Cpu& GetCurrentCore() const; | ||
| 39 | |||
| 40 | ExclusiveMonitor& GetExclusiveMonitor(); | ||
| 41 | const ExclusiveMonitor& GetExclusiveMonitor() const; | ||
| 42 | |||
| 43 | void RunLoop(bool tight_loop); | ||
| 44 | |||
| 45 | void InvalidateAllInstructionCaches(); | ||
| 46 | |||
| 47 | private: | ||
| 48 | static constexpr std::size_t NUM_CPU_CORES = 4; | ||
| 49 | |||
| 50 | std::unique_ptr<ExclusiveMonitor> exclusive_monitor; | ||
| 51 | std::unique_ptr<CpuBarrier> barrier; | ||
| 52 | std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores; | ||
| 53 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads; | ||
| 54 | std::size_t active_core{}; ///< Active core, only used in single thread mode | ||
| 55 | |||
| 56 | /// Map of guest threads to CPU cores | ||
| 57 | std::map<std::thread::id, Cpu*> thread_to_cpu; | ||
| 58 | |||
| 59 | System& system; | ||
| 60 | }; | ||
| 61 | |||
| 62 | } // namespace Core | ||
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp new file mode 100644 index 000000000..70ddbdcca --- /dev/null +++ b/src/core/cpu_manager.cpp | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/arm/exclusive_monitor.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/core_manager.h" | ||
| 8 | #include "core/core_timing.h" | ||
| 9 | #include "core/cpu_manager.h" | ||
| 10 | #include "core/gdbstub/gdbstub.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | |||
| 14 | CpuManager::CpuManager(System& system) : system{system} {} | ||
| 15 | CpuManager::~CpuManager() = default; | ||
| 16 | |||
| 17 | void CpuManager::Initialize() { | ||
| 18 | for (std::size_t index = 0; index < core_managers.size(); ++index) { | ||
| 19 | core_managers[index] = std::make_unique<CoreManager>(system, index); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | void CpuManager::Shutdown() { | ||
| 24 | for (auto& cpu_core : core_managers) { | ||
| 25 | cpu_core.reset(); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | CoreManager& CpuManager::GetCoreManager(std::size_t index) { | ||
| 30 | return *core_managers.at(index); | ||
| 31 | } | ||
| 32 | |||
| 33 | const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { | ||
| 34 | return *core_managers.at(index); | ||
| 35 | } | ||
| 36 | |||
| 37 | CoreManager& CpuManager::GetCurrentCoreManager() { | ||
| 38 | // Otherwise, use single-threaded mode active_core variable | ||
| 39 | return *core_managers[active_core]; | ||
| 40 | } | ||
| 41 | |||
| 42 | const CoreManager& CpuManager::GetCurrentCoreManager() const { | ||
| 43 | // Otherwise, use single-threaded mode active_core variable | ||
| 44 | return *core_managers[active_core]; | ||
| 45 | } | ||
| 46 | |||
| 47 | void CpuManager::RunLoop(bool tight_loop) { | ||
| 48 | if (GDBStub::IsServerEnabled()) { | ||
| 49 | GDBStub::HandlePacket(); | ||
| 50 | |||
| 51 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | ||
| 52 | // execute. Otherwise, get out of the loop function. | ||
| 53 | if (GDBStub::GetCpuHaltFlag()) { | ||
| 54 | if (GDBStub::GetCpuStepFlag()) { | ||
| 55 | tight_loop = false; | ||
| 56 | } else { | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | auto& core_timing = system.CoreTiming(); | ||
| 63 | core_timing.ResetRun(); | ||
| 64 | bool keep_running{}; | ||
| 65 | do { | ||
| 66 | keep_running = false; | ||
| 67 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | ||
| 68 | core_timing.SwitchContext(active_core); | ||
| 69 | if (core_timing.CanCurrentContextRun()) { | ||
| 70 | core_managers[active_core]->RunLoop(tight_loop); | ||
| 71 | } | ||
| 72 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 73 | } | ||
| 74 | } while (keep_running); | ||
| 75 | |||
| 76 | if (GDBStub::IsServerEnabled()) { | ||
| 77 | GDBStub::SetCpuStepFlag(false); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | } // namespace Core | ||
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h new file mode 100644 index 000000000..feb619e1b --- /dev/null +++ b/src/core/cpu_manager.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | namespace Core { | ||
| 11 | |||
| 12 | class CoreManager; | ||
| 13 | class System; | ||
| 14 | |||
| 15 | class CpuManager { | ||
| 16 | public: | ||
| 17 | explicit CpuManager(System& system); | ||
| 18 | CpuManager(const CpuManager&) = delete; | ||
| 19 | CpuManager(CpuManager&&) = delete; | ||
| 20 | |||
| 21 | ~CpuManager(); | ||
| 22 | |||
| 23 | CpuManager& operator=(const CpuManager&) = delete; | ||
| 24 | CpuManager& operator=(CpuManager&&) = delete; | ||
| 25 | |||
| 26 | void Initialize(); | ||
| 27 | void Shutdown(); | ||
| 28 | |||
| 29 | CoreManager& GetCoreManager(std::size_t index); | ||
| 30 | const CoreManager& GetCoreManager(std::size_t index) const; | ||
| 31 | |||
| 32 | CoreManager& GetCurrentCoreManager(); | ||
| 33 | const CoreManager& GetCurrentCoreManager() const; | ||
| 34 | |||
| 35 | std::size_t GetActiveCoreIndex() const { | ||
| 36 | return active_core; | ||
| 37 | } | ||
| 38 | |||
| 39 | void RunLoop(bool tight_loop); | ||
| 40 | |||
| 41 | private: | ||
| 42 | static constexpr std::size_t NUM_CPU_CORES = 4; | ||
| 43 | |||
| 44 | std::array<std::unique_ptr<CoreManager>, NUM_CPU_CORES> core_managers; | ||
| 45 | std::size_t active_core{}; ///< Active core, only used in single thread mode | ||
| 46 | |||
| 47 | System& system; | ||
| 48 | }; | ||
| 49 | |||
| 50 | } // namespace Core | ||
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 4a9912641..3376eedc5 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -75,6 +75,13 @@ public: | |||
| 75 | return nullptr; | 75 | return nullptr; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | /// Returns if window is shown (not minimized) | ||
| 79 | virtual bool IsShown() const = 0; | ||
| 80 | |||
| 81 | /// Retrieves Vulkan specific handlers from the window | ||
| 82 | virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 83 | void* surface) const = 0; | ||
| 84 | |||
| 78 | /** | 85 | /** |
| 79 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 86 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
| 80 | * @param framebuffer_x Framebuffer x-coordinate that was pressed | 87 | * @param framebuffer_x Framebuffer x-coordinate that was pressed |
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 7c11d7546..2b098b7c6 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h | |||
| @@ -15,6 +15,13 @@ | |||
| 15 | 15 | ||
| 16 | namespace Input { | 16 | namespace Input { |
| 17 | 17 | ||
| 18 | enum class AnalogDirection : u8 { | ||
| 19 | RIGHT, | ||
| 20 | LEFT, | ||
| 21 | UP, | ||
| 22 | DOWN, | ||
| 23 | }; | ||
| 24 | |||
| 18 | /// An abstract class template for an input device (a button, an analog input, etc.). | 25 | /// An abstract class template for an input device (a button, an analog input, etc.). |
| 19 | template <typename StatusType> | 26 | template <typename StatusType> |
| 20 | class InputDevice { | 27 | class InputDevice { |
| @@ -23,6 +30,9 @@ public: | |||
| 23 | virtual StatusType GetStatus() const { | 30 | virtual StatusType GetStatus() const { |
| 24 | return {}; | 31 | return {}; |
| 25 | } | 32 | } |
| 33 | virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { | ||
| 34 | return {}; | ||
| 35 | } | ||
| 26 | }; | 36 | }; |
| 27 | 37 | ||
| 28 | /// An abstract class template for a factory that can create input devices. | 38 | /// An abstract class template for a factory that can create input devices. |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 37cb28848..67e95999d 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -35,7 +35,7 @@ | |||
| 35 | #include "common/swap.h" | 35 | #include "common/swap.h" |
| 36 | #include "core/arm/arm_interface.h" | 36 | #include "core/arm/arm_interface.h" |
| 37 | #include "core/core.h" | 37 | #include "core/core.h" |
| 38 | #include "core/core_cpu.h" | 38 | #include "core/core_manager.h" |
| 39 | #include "core/gdbstub/gdbstub.h" | 39 | #include "core/gdbstub/gdbstub.h" |
| 40 | #include "core/hle/kernel/process.h" | 40 | #include "core/hle/kernel/process.h" |
| 41 | #include "core/hle/kernel/scheduler.h" | 41 | #include "core/hle/kernel/scheduler.h" |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index db189c8e3..2ea3dcb61 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/core_cpu.h" | ||
| 12 | #include "core/hle/kernel/address_arbiter.h" | 11 | #include "core/hle/kernel/address_arbiter.h" |
| 13 | #include "core/hle/kernel/errors.h" | 12 | #include "core/hle/kernel/errors.h" |
| 14 | #include "core/hle/kernel/scheduler.h" | 13 | #include "core/hle/kernel/scheduler.h" |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1d0783bd3..edd4c4259 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -3,13 +3,15 @@ | |||
| 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 <functional> | ||
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <mutex> | 8 | #include <mutex> |
| 8 | #include <utility> | 9 | #include <utility> |
| 9 | 10 | ||
| 10 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 11 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 12 | 13 | #include "core/arm/arm_interface.h" | |
| 14 | #include "core/arm/exclusive_monitor.h" | ||
| 13 | #include "core/core.h" | 15 | #include "core/core.h" |
| 14 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 15 | #include "core/core_timing_util.h" | 17 | #include "core/core_timing_util.h" |
| @@ -17,6 +19,7 @@ | |||
| 17 | #include "core/hle/kernel/errors.h" | 19 | #include "core/hle/kernel/errors.h" |
| 18 | #include "core/hle/kernel/handle_table.h" | 20 | #include "core/hle/kernel/handle_table.h" |
| 19 | #include "core/hle/kernel/kernel.h" | 21 | #include "core/hle/kernel/kernel.h" |
| 22 | #include "core/hle/kernel/physical_core.h" | ||
| 20 | #include "core/hle/kernel/process.h" | 23 | #include "core/hle/kernel/process.h" |
| 21 | #include "core/hle/kernel/resource_limit.h" | 24 | #include "core/hle/kernel/resource_limit.h" |
| 22 | #include "core/hle/kernel/scheduler.h" | 25 | #include "core/hle/kernel/scheduler.h" |
| @@ -98,6 +101,7 @@ struct KernelCore::Impl { | |||
| 98 | void Initialize(KernelCore& kernel) { | 101 | void Initialize(KernelCore& kernel) { |
| 99 | Shutdown(); | 102 | Shutdown(); |
| 100 | 103 | ||
| 104 | InitializePhysicalCores(); | ||
| 101 | InitializeSystemResourceLimit(kernel); | 105 | InitializeSystemResourceLimit(kernel); |
| 102 | InitializeThreads(); | 106 | InitializeThreads(); |
| 103 | InitializePreemption(); | 107 | InitializePreemption(); |
| @@ -121,6 +125,21 @@ struct KernelCore::Impl { | |||
| 121 | global_scheduler.Shutdown(); | 125 | global_scheduler.Shutdown(); |
| 122 | 126 | ||
| 123 | named_ports.clear(); | 127 | named_ports.clear(); |
| 128 | |||
| 129 | for (auto& core : cores) { | ||
| 130 | core.Shutdown(); | ||
| 131 | } | ||
| 132 | cores.clear(); | ||
| 133 | |||
| 134 | exclusive_monitor.reset(); | ||
| 135 | } | ||
| 136 | |||
| 137 | void InitializePhysicalCores() { | ||
| 138 | exclusive_monitor = | ||
| 139 | Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount()); | ||
| 140 | for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) { | ||
| 141 | cores.emplace_back(system, i, *exclusive_monitor); | ||
| 142 | } | ||
| 124 | } | 143 | } |
| 125 | 144 | ||
| 126 | // Creates the default system resource limit | 145 | // Creates the default system resource limit |
| @@ -186,6 +205,9 @@ struct KernelCore::Impl { | |||
| 186 | /// the ConnectToPort SVC. | 205 | /// the ConnectToPort SVC. |
| 187 | NamedPortTable named_ports; | 206 | NamedPortTable named_ports; |
| 188 | 207 | ||
| 208 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; | ||
| 209 | std::vector<Kernel::PhysicalCore> cores; | ||
| 210 | |||
| 189 | // System context | 211 | // System context |
| 190 | Core::System& system; | 212 | Core::System& system; |
| 191 | }; | 213 | }; |
| @@ -240,6 +262,34 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { | |||
| 240 | return impl->global_scheduler; | 262 | return impl->global_scheduler; |
| 241 | } | 263 | } |
| 242 | 264 | ||
| 265 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | ||
| 266 | return impl->cores[id]; | ||
| 267 | } | ||
| 268 | |||
| 269 | const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | ||
| 270 | return impl->cores[id]; | ||
| 271 | } | ||
| 272 | |||
| 273 | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | ||
| 274 | return *impl->exclusive_monitor; | ||
| 275 | } | ||
| 276 | |||
| 277 | const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { | ||
| 278 | return *impl->exclusive_monitor; | ||
| 279 | } | ||
| 280 | |||
| 281 | void KernelCore::InvalidateAllInstructionCaches() { | ||
| 282 | for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) { | ||
| 283 | PhysicalCore(i).ArmInterface().ClearInstructionCache(); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | void KernelCore::PrepareReschedule(std::size_t id) { | ||
| 288 | if (id < impl->global_scheduler.CpuCoresCount()) { | ||
| 289 | impl->cores[id].Stop(); | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 243 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { | 293 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { |
| 244 | impl->named_ports.emplace(std::move(name), std::move(port)); | 294 | impl->named_ports.emplace(std::move(name), std::move(port)); |
| 245 | } | 295 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3bf0068ed..fccffaf3a 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -11,8 +11,9 @@ | |||
| 11 | #include "core/hle/kernel/object.h" | 11 | #include "core/hle/kernel/object.h" |
| 12 | 12 | ||
| 13 | namespace Core { | 13 | namespace Core { |
| 14 | class ExclusiveMonitor; | ||
| 14 | class System; | 15 | class System; |
| 15 | } | 16 | } // namespace Core |
| 16 | 17 | ||
| 17 | namespace Core::Timing { | 18 | namespace Core::Timing { |
| 18 | class CoreTiming; | 19 | class CoreTiming; |
| @@ -25,6 +26,7 @@ class AddressArbiter; | |||
| 25 | class ClientPort; | 26 | class ClientPort; |
| 26 | class GlobalScheduler; | 27 | class GlobalScheduler; |
| 27 | class HandleTable; | 28 | class HandleTable; |
| 29 | class PhysicalCore; | ||
| 28 | class Process; | 30 | class Process; |
| 29 | class ResourceLimit; | 31 | class ResourceLimit; |
| 30 | class Thread; | 32 | class Thread; |
| @@ -84,6 +86,21 @@ public: | |||
| 84 | /// Gets the sole instance of the global scheduler | 86 | /// Gets the sole instance of the global scheduler |
| 85 | const Kernel::GlobalScheduler& GlobalScheduler() const; | 87 | const Kernel::GlobalScheduler& GlobalScheduler() const; |
| 86 | 88 | ||
| 89 | /// Gets the an instance of the respective physical CPU core. | ||
| 90 | Kernel::PhysicalCore& PhysicalCore(std::size_t id); | ||
| 91 | |||
| 92 | /// Gets the an instance of the respective physical CPU core. | ||
| 93 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; | ||
| 94 | |||
| 95 | /// Stops execution of 'id' core, in order to reschedule a new thread. | ||
| 96 | void PrepareReschedule(std::size_t id); | ||
| 97 | |||
| 98 | Core::ExclusiveMonitor& GetExclusiveMonitor(); | ||
| 99 | |||
| 100 | const Core::ExclusiveMonitor& GetExclusiveMonitor() const; | ||
| 101 | |||
| 102 | void InvalidateAllInstructionCaches(); | ||
| 103 | |||
| 87 | /// Adds a port to the named port table | 104 | /// Adds a port to the named port table |
| 88 | void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port); | 105 | void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port); |
| 89 | 106 | ||
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp new file mode 100644 index 000000000..9303dd273 --- /dev/null +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -0,0 +1,51 @@ | |||
| 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/logging/log.h" | ||
| 6 | #include "core/arm/arm_interface.h" | ||
| 7 | #ifdef ARCHITECTURE_x86_64 | ||
| 8 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 9 | #endif | ||
| 10 | #include "core/arm/exclusive_monitor.h" | ||
| 11 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 12 | #include "core/core.h" | ||
| 13 | #include "core/hle/kernel/physical_core.h" | ||
| 14 | #include "core/hle/kernel/scheduler.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | ||
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | |||
| 19 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, | ||
| 20 | Core::ExclusiveMonitor& exclusive_monitor) | ||
| 21 | : core_index{id} { | ||
| 22 | #ifdef ARCHITECTURE_x86_64 | ||
| 23 | arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index); | ||
| 24 | #else | ||
| 25 | arm_interface = std::make_shared<Core::ARM_Unicorn>(system); | ||
| 26 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 27 | #endif | ||
| 28 | |||
| 29 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); | ||
| 30 | } | ||
| 31 | |||
| 32 | PhysicalCore::~PhysicalCore() = default; | ||
| 33 | |||
| 34 | void PhysicalCore::Run() { | ||
| 35 | arm_interface->Run(); | ||
| 36 | arm_interface->ClearExclusiveState(); | ||
| 37 | } | ||
| 38 | |||
| 39 | void PhysicalCore::Step() { | ||
| 40 | arm_interface->Step(); | ||
| 41 | } | ||
| 42 | |||
| 43 | void PhysicalCore::Stop() { | ||
| 44 | arm_interface->PrepareReschedule(); | ||
| 45 | } | ||
| 46 | |||
| 47 | void PhysicalCore::Shutdown() { | ||
| 48 | scheduler->Shutdown(); | ||
| 49 | } | ||
| 50 | |||
| 51 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h new file mode 100644 index 000000000..4c32c0f1b --- /dev/null +++ b/src/core/hle/kernel/physical_core.h | |||
| @@ -0,0 +1,77 @@ | |||
| 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 <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | class Scheduler; | ||
| 12 | } // namespace Kernel | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class ARM_Interface; | ||
| 16 | class ExclusiveMonitor; | ||
| 17 | class System; | ||
| 18 | } // namespace Core | ||
| 19 | |||
| 20 | namespace Kernel { | ||
| 21 | |||
| 22 | class PhysicalCore { | ||
| 23 | public: | ||
| 24 | PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor); | ||
| 25 | ~PhysicalCore(); | ||
| 26 | |||
| 27 | PhysicalCore(const PhysicalCore&) = delete; | ||
| 28 | PhysicalCore& operator=(const PhysicalCore&) = delete; | ||
| 29 | |||
| 30 | PhysicalCore(PhysicalCore&&) = default; | ||
| 31 | PhysicalCore& operator=(PhysicalCore&&) = default; | ||
| 32 | |||
| 33 | /// Execute current jit state | ||
| 34 | void Run(); | ||
| 35 | /// Execute a single instruction in current jit. | ||
| 36 | void Step(); | ||
| 37 | /// Stop JIT execution/exit | ||
| 38 | void Stop(); | ||
| 39 | |||
| 40 | // Shutdown this physical core. | ||
| 41 | void Shutdown(); | ||
| 42 | |||
| 43 | Core::ARM_Interface& ArmInterface() { | ||
| 44 | return *arm_interface; | ||
| 45 | } | ||
| 46 | |||
| 47 | const Core::ARM_Interface& ArmInterface() const { | ||
| 48 | return *arm_interface; | ||
| 49 | } | ||
| 50 | |||
| 51 | bool IsMainCore() const { | ||
| 52 | return core_index == 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool IsSystemCore() const { | ||
| 56 | return core_index == 3; | ||
| 57 | } | ||
| 58 | |||
| 59 | std::size_t CoreIndex() const { | ||
| 60 | return core_index; | ||
| 61 | } | ||
| 62 | |||
| 63 | Kernel::Scheduler& Scheduler() { | ||
| 64 | return *scheduler; | ||
| 65 | } | ||
| 66 | |||
| 67 | const Kernel::Scheduler& Scheduler() const { | ||
| 68 | return *scheduler; | ||
| 69 | } | ||
| 70 | |||
| 71 | private: | ||
| 72 | std::size_t core_index; | ||
| 73 | std::unique_ptr<Core::ARM_Interface> arm_interface; | ||
| 74 | std::unique_ptr<Kernel::Scheduler> scheduler; | ||
| 75 | }; | ||
| 76 | |||
| 77 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index d36fcd7d9..eb196a690 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -14,7 +14,6 @@ | |||
| 14 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 15 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/core.h" | 16 | #include "core/core.h" |
| 17 | #include "core/core_cpu.h" | ||
| 18 | #include "core/core_timing.h" | 17 | #include "core/core_timing.h" |
| 19 | #include "core/hle/kernel/kernel.h" | 18 | #include "core/hle/kernel/kernel.h" |
| 20 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index dbcdb0b88..1d99bf7a2 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | #include "common/string_util.h" | 15 | #include "common/string_util.h" |
| 16 | #include "core/arm/exclusive_monitor.h" | 16 | #include "core/arm/exclusive_monitor.h" |
| 17 | #include "core/core.h" | 17 | #include "core/core.h" |
| 18 | #include "core/core_cpu.h" | 18 | #include "core/core_manager.h" |
| 19 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 20 | #include "core/core_timing_util.h" |
| 21 | #include "core/hle/kernel/address_arbiter.h" | 21 | #include "core/hle/kernel/address_arbiter.h" |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e84e5ce0d..e965b5b04 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -13,7 +13,6 @@ | |||
| 13 | #include "common/thread_queue_list.h" | 13 | #include "common/thread_queue_list.h" |
| 14 | #include "core/arm/arm_interface.h" | 14 | #include "core/arm/arm_interface.h" |
| 15 | #include "core/core.h" | 15 | #include "core/core.h" |
| 16 | #include "core/core_cpu.h" | ||
| 17 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 18 | #include "core/core_timing_util.h" | 17 | #include "core/core_timing_util.h" |
| 19 | #include "core/hle/kernel/errors.h" | 18 | #include "core/hle/kernel/errors.h" |
| @@ -356,7 +355,7 @@ void Thread::SetActivity(ThreadActivity value) { | |||
| 356 | // Set status if not waiting | 355 | // Set status if not waiting |
| 357 | if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { | 356 | if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { |
| 358 | SetStatus(ThreadStatus::Paused); | 357 | SetStatus(ThreadStatus::Paused); |
| 359 | Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); | 358 | kernel.PrepareReschedule(processor_id); |
| 360 | } | 359 | } |
| 361 | } else if (status == ThreadStatus::Paused) { | 360 | } else if (status == ThreadStatus::Paused) { |
| 362 | // Ready to reschedule | 361 | // Ready to reschedule |
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 745f2c4e8..a0c806e8f 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/core_cpu.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/object.h" | 11 | #include "core/hle/kernel/object.h" |
| 13 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| @@ -96,7 +95,7 @@ void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { | |||
| 96 | } | 95 | } |
| 97 | if (resume) { | 96 | if (resume) { |
| 98 | thread->ResumeFromWait(); | 97 | thread->ResumeFromWait(); |
| 99 | Core::System::GetInstance().PrepareReschedule(thread->GetProcessorID()); | 98 | kernel.PrepareReschedule(thread->GetProcessorID()); |
| 100 | } | 99 | } |
| 101 | } | 100 | } |
| 102 | 101 | ||
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 4d952adc0..15c09f04c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -250,6 +250,10 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | |||
| 250 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; | 250 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; |
| 251 | const auto& button_state = buttons[controller_idx]; | 251 | const auto& button_state = buttons[controller_idx]; |
| 252 | const auto& analog_state = sticks[controller_idx]; | 252 | const auto& analog_state = sticks[controller_idx]; |
| 253 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 254 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 255 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 256 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 253 | 257 | ||
| 254 | using namespace Settings::NativeButton; | 258 | using namespace Settings::NativeButton; |
| 255 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); | 259 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); |
| @@ -270,23 +274,32 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | |||
| 270 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); | 274 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); |
| 271 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); | 275 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); |
| 272 | 276 | ||
| 273 | pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | 277 | pad_state.l_stick_right.Assign( |
| 274 | pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | 278 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( |
| 275 | pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | 279 | Input::AnalogDirection::RIGHT)); |
| 276 | pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | 280 | pad_state.l_stick_left.Assign( |
| 277 | 281 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | |
| 278 | pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | 282 | Input::AnalogDirection::LEFT)); |
| 279 | pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | 283 | pad_state.l_stick_up.Assign( |
| 280 | pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | 284 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( |
| 281 | pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | 285 | Input::AnalogDirection::UP)); |
| 286 | pad_state.l_stick_down.Assign( | ||
| 287 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | ||
| 288 | Input::AnalogDirection::DOWN)); | ||
| 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( | ||
| 295 | 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)] | ||
| 298 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); | ||
| 282 | 299 | ||
| 283 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); | 300 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); |
| 284 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); | 301 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); |
| 285 | 302 | ||
| 286 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 287 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 288 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 289 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 290 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | 303 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); |
| 291 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | 304 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); |
| 292 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | 305 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 884ad173b..f67fab2f9 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -42,6 +42,26 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) { | |||
| 42 | rb.Push<u32>(0); // bsd errno | 42 | rb.Push<u32>(0); // bsd errno |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | void BSD::Select(Kernel::HLERequestContext& ctx) { | ||
| 46 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 47 | |||
| 48 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 49 | |||
| 50 | rb.Push(RESULT_SUCCESS); | ||
| 51 | rb.Push<u32>(0); // ret | ||
| 52 | rb.Push<u32>(0); // bsd errno | ||
| 53 | } | ||
| 54 | |||
| 55 | void BSD::Bind(Kernel::HLERequestContext& ctx) { | ||
| 56 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 57 | |||
| 58 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 59 | |||
| 60 | rb.Push(RESULT_SUCCESS); | ||
| 61 | rb.Push<u32>(0); // ret | ||
| 62 | rb.Push<u32>(0); // bsd errno | ||
| 63 | } | ||
| 64 | |||
| 45 | void BSD::Connect(Kernel::HLERequestContext& ctx) { | 65 | void BSD::Connect(Kernel::HLERequestContext& ctx) { |
| 46 | LOG_WARNING(Service, "(STUBBED) called"); | 66 | LOG_WARNING(Service, "(STUBBED) called"); |
| 47 | 67 | ||
| @@ -52,6 +72,26 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) { | |||
| 52 | rb.Push<u32>(0); // bsd errno | 72 | rb.Push<u32>(0); // bsd errno |
| 53 | } | 73 | } |
| 54 | 74 | ||
| 75 | void BSD::Listen(Kernel::HLERequestContext& ctx) { | ||
| 76 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 77 | |||
| 78 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 79 | |||
| 80 | rb.Push(RESULT_SUCCESS); | ||
| 81 | rb.Push<u32>(0); // ret | ||
| 82 | rb.Push<u32>(0); // bsd errno | ||
| 83 | } | ||
| 84 | |||
| 85 | void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) { | ||
| 86 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 87 | |||
| 88 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 89 | |||
| 90 | rb.Push(RESULT_SUCCESS); | ||
| 91 | rb.Push<u32>(0); // ret | ||
| 92 | rb.Push<u32>(0); // bsd errno | ||
| 93 | } | ||
| 94 | |||
| 55 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { | 95 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { |
| 56 | LOG_WARNING(Service, "(STUBBED) called"); | 96 | LOG_WARNING(Service, "(STUBBED) called"); |
| 57 | 97 | ||
| @@ -80,7 +120,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 80 | {2, &BSD::Socket, "Socket"}, | 120 | {2, &BSD::Socket, "Socket"}, |
| 81 | {3, nullptr, "SocketExempt"}, | 121 | {3, nullptr, "SocketExempt"}, |
| 82 | {4, nullptr, "Open"}, | 122 | {4, nullptr, "Open"}, |
| 83 | {5, nullptr, "Select"}, | 123 | {5, &BSD::Select, "Select"}, |
| 84 | {6, nullptr, "Poll"}, | 124 | {6, nullptr, "Poll"}, |
| 85 | {7, nullptr, "Sysctl"}, | 125 | {7, nullptr, "Sysctl"}, |
| 86 | {8, nullptr, "Recv"}, | 126 | {8, nullptr, "Recv"}, |
| @@ -88,15 +128,15 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 88 | {10, nullptr, "Send"}, | 128 | {10, nullptr, "Send"}, |
| 89 | {11, &BSD::SendTo, "SendTo"}, | 129 | {11, &BSD::SendTo, "SendTo"}, |
| 90 | {12, nullptr, "Accept"}, | 130 | {12, nullptr, "Accept"}, |
| 91 | {13, nullptr, "Bind"}, | 131 | {13, &BSD::Bind, "Bind"}, |
| 92 | {14, &BSD::Connect, "Connect"}, | 132 | {14, &BSD::Connect, "Connect"}, |
| 93 | {15, nullptr, "GetPeerName"}, | 133 | {15, nullptr, "GetPeerName"}, |
| 94 | {16, nullptr, "GetSockName"}, | 134 | {16, nullptr, "GetSockName"}, |
| 95 | {17, nullptr, "GetSockOpt"}, | 135 | {17, nullptr, "GetSockOpt"}, |
| 96 | {18, nullptr, "Listen"}, | 136 | {18, &BSD::Listen, "Listen"}, |
| 97 | {19, nullptr, "Ioctl"}, | 137 | {19, nullptr, "Ioctl"}, |
| 98 | {20, nullptr, "Fcntl"}, | 138 | {20, nullptr, "Fcntl"}, |
| 99 | {21, nullptr, "SetSockOpt"}, | 139 | {21, &BSD::SetSockOpt, "SetSockOpt"}, |
| 100 | {22, nullptr, "Shutdown"}, | 140 | {22, nullptr, "Shutdown"}, |
| 101 | {23, nullptr, "ShutdownAllSockets"}, | 141 | {23, nullptr, "ShutdownAllSockets"}, |
| 102 | {24, nullptr, "Write"}, | 142 | {24, nullptr, "Write"}, |
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 0fe0e65c6..3098e3baf 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h | |||
| @@ -18,7 +18,11 @@ private: | |||
| 18 | void RegisterClient(Kernel::HLERequestContext& ctx); | 18 | void RegisterClient(Kernel::HLERequestContext& ctx); |
| 19 | void StartMonitoring(Kernel::HLERequestContext& ctx); | 19 | void StartMonitoring(Kernel::HLERequestContext& ctx); |
| 20 | void Socket(Kernel::HLERequestContext& ctx); | 20 | void Socket(Kernel::HLERequestContext& ctx); |
| 21 | void Select(Kernel::HLERequestContext& ctx); | ||
| 22 | void Bind(Kernel::HLERequestContext& ctx); | ||
| 21 | void Connect(Kernel::HLERequestContext& ctx); | 23 | void Connect(Kernel::HLERequestContext& ctx); |
| 24 | void Listen(Kernel::HLERequestContext& ctx); | ||
| 25 | void SetSockOpt(Kernel::HLERequestContext& ctx); | ||
| 22 | void SendTo(Kernel::HLERequestContext& ctx); | 26 | void SendTo(Kernel::HLERequestContext& ctx); |
| 23 | void Close(Kernel::HLERequestContext& ctx); | 27 | void Close(Kernel::HLERequestContext& ctx); |
| 24 | 28 | ||
diff --git a/src/core/settings.h b/src/core/settings.h index 421e76f5f..e1a9a0ffa 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -371,6 +371,11 @@ enum class SDMCSize : u64 { | |||
| 371 | S1TB = 0x10000000000ULL, | 371 | S1TB = 0x10000000000ULL, |
| 372 | }; | 372 | }; |
| 373 | 373 | ||
| 374 | enum class RendererBackend { | ||
| 375 | OpenGL = 0, | ||
| 376 | Vulkan = 1, | ||
| 377 | }; | ||
| 378 | |||
| 374 | struct Values { | 379 | struct Values { |
| 375 | // System | 380 | // System |
| 376 | bool use_docked_mode; | 381 | bool use_docked_mode; |
| @@ -419,6 +424,10 @@ struct Values { | |||
| 419 | SDMCSize sdmc_size; | 424 | SDMCSize sdmc_size; |
| 420 | 425 | ||
| 421 | // Renderer | 426 | // Renderer |
| 427 | RendererBackend renderer_backend; | ||
| 428 | bool renderer_debug; | ||
| 429 | int vulkan_device; | ||
| 430 | |||
| 422 | float resolution_factor; | 431 | float resolution_factor; |
| 423 | bool use_frame_limit; | 432 | bool use_frame_limit; |
| 424 | u16 frame_limit; | 433 | u16 frame_limit; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 320e8ad73..0e72d31cd 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -46,6 +46,16 @@ static u64 GenerateTelemetryId() { | |||
| 46 | return telemetry_id; | 46 | return telemetry_id; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | static const char* TranslateRenderer(Settings::RendererBackend backend) { | ||
| 50 | switch (backend) { | ||
| 51 | case Settings::RendererBackend::OpenGL: | ||
| 52 | return "OpenGL"; | ||
| 53 | case Settings::RendererBackend::Vulkan: | ||
| 54 | return "Vulkan"; | ||
| 55 | } | ||
| 56 | return "Unknown"; | ||
| 57 | } | ||
| 58 | |||
| 49 | u64 GetTelemetryId() { | 59 | u64 GetTelemetryId() { |
| 50 | u64 telemetry_id{}; | 60 | u64 telemetry_id{}; |
| 51 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + | 61 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + |
| @@ -169,7 +179,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | |||
| 169 | AddField(field_type, "Audio_SinkId", Settings::values.sink_id); | 179 | AddField(field_type, "Audio_SinkId", Settings::values.sink_id); |
| 170 | AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | 180 | AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); |
| 171 | AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); | 181 | AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); |
| 172 | AddField(field_type, "Renderer_Backend", "OpenGL"); | 182 | AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend)); |
| 173 | AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); | 183 | AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); |
| 174 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); | 184 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); |
| 175 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); | 185 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); |
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 9e028da89..c98c848cf 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -41,6 +41,7 @@ void Shutdown() { | |||
| 41 | Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); | 41 | Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); |
| 42 | motion_emu.reset(); | 42 | motion_emu.reset(); |
| 43 | sdl.reset(); | 43 | sdl.reset(); |
| 44 | udp.reset(); | ||
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | Keyboard* GetKeyboard() { | 47 | Keyboard* GetKeyboard() { |
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d2e9d278f..a2e0c0bd2 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp | |||
| @@ -342,6 +342,22 @@ public: | |||
| 342 | return std::make_tuple<float, float>(0.0f, 0.0f); | 342 | return std::make_tuple<float, float>(0.0f, 0.0f); |
| 343 | } | 343 | } |
| 344 | 344 | ||
| 345 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 346 | const auto [x, y] = GetStatus(); | ||
| 347 | const float directional_deadzone = 0.4f; | ||
| 348 | switch (direction) { | ||
| 349 | case Input::AnalogDirection::RIGHT: | ||
| 350 | return x > directional_deadzone; | ||
| 351 | case Input::AnalogDirection::LEFT: | ||
| 352 | return x < -directional_deadzone; | ||
| 353 | case Input::AnalogDirection::UP: | ||
| 354 | return y > directional_deadzone; | ||
| 355 | case Input::AnalogDirection::DOWN: | ||
| 356 | return y < -directional_deadzone; | ||
| 357 | } | ||
| 358 | return false; | ||
| 359 | } | ||
| 360 | |||
| 345 | private: | 361 | private: |
| 346 | std::shared_ptr<SDLJoystick> joystick; | 362 | std::shared_ptr<SDLJoystick> joystick; |
| 347 | const int axis_x; | 363 | const int axis_x; |
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index 5f5a9989c..2228571a6 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp | |||
| @@ -14,7 +14,6 @@ | |||
| 14 | #include "input_common/udp/client.h" | 14 | #include "input_common/udp/client.h" |
| 15 | #include "input_common/udp/protocol.h" | 15 | #include "input_common/udp/protocol.h" |
| 16 | 16 | ||
| 17 | using boost::asio::ip::address_v4; | ||
| 18 | using boost::asio::ip::udp; | 17 | using boost::asio::ip::udp; |
| 19 | 18 | ||
| 20 | namespace InputCommon::CemuhookUDP { | 19 | namespace InputCommon::CemuhookUDP { |
| @@ -31,10 +30,10 @@ public: | |||
| 31 | 30 | ||
| 32 | explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, | 31 | explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, |
| 33 | SocketCallback callback) | 32 | SocketCallback callback) |
| 34 | : client_id(client_id), timer(io_service), | 33 | : callback(std::move(callback)), timer(io_service), |
| 35 | send_endpoint(udp::endpoint(address_v4::from_string(host), port)), | 34 | socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), |
| 36 | socket(io_service, udp::endpoint(udp::v4(), 0)), pad_index(pad_index), | 35 | pad_index(pad_index), |
| 37 | callback(std::move(callback)) {} | 36 | send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {} |
| 38 | 37 | ||
| 39 | void Stop() { | 38 | void Stop() { |
| 40 | io_service.stop(); | 39 | io_service.stop(); |
| @@ -126,7 +125,7 @@ static void SocketLoop(Socket* socket) { | |||
| 126 | 125 | ||
| 127 | Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, | 126 | Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, |
| 128 | u8 pad_index, u32 client_id) | 127 | u8 pad_index, u32 client_id) |
| 129 | : status(status) { | 128 | : status(std::move(status)) { |
| 130 | StartCommunication(host, port, pad_index, client_id); | 129 | StartCommunication(host, port, pad_index, client_id); |
| 131 | } | 130 | } |
| 132 | 131 | ||
| @@ -207,7 +206,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie | |||
| 207 | Common::Event success_event; | 206 | Common::Event success_event; |
| 208 | SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, | 207 | SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, |
| 209 | [&](Response::PadData data) { success_event.Set(); }}; | 208 | [&](Response::PadData data) { success_event.Set(); }}; |
| 210 | Socket socket{host, port, pad_index, client_id, callback}; | 209 | Socket socket{host, port, pad_index, client_id, std::move(callback)}; |
| 211 | std::thread worker_thread{SocketLoop, &socket}; | 210 | std::thread worker_thread{SocketLoop, &socket}; |
| 212 | bool result = success_event.WaitFor(std::chrono::seconds(8)); | 211 | bool result = success_event.WaitFor(std::chrono::seconds(8)); |
| 213 | socket.Stop(); | 212 | socket.Stop(); |
| @@ -267,7 +266,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( | |||
| 267 | complete_event.Set(); | 266 | complete_event.Set(); |
| 268 | } | 267 | } |
| 269 | }}; | 268 | }}; |
| 270 | Socket socket{host, port, pad_index, client_id, callback}; | 269 | Socket socket{host, port, pad_index, client_id, std::move(callback)}; |
| 271 | std::thread worker_thread{SocketLoop, &socket}; | 270 | std::thread worker_thread{SocketLoop, &socket}; |
| 272 | complete_event.Wait(); | 271 | complete_event.Wait(); |
| 273 | socket.Stop(); | 272 | socket.Stop(); |
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 0b21f4da6..b8c654755 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include <string> | 11 | #include <string> |
| 12 | #include <thread> | 12 | #include <thread> |
| 13 | #include <tuple> | 13 | #include <tuple> |
| 14 | #include <vector> | ||
| 15 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 16 | #include "common/thread.h" | 15 | #include "common/thread.h" |
| 17 | #include "common/vector_math.h" | 16 | #include "common/vector_math.h" |
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index 1b521860a..3ba4d1fc8 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <optional> | 8 | #include <optional> |
| 9 | #include <type_traits> | 9 | #include <type_traits> |
| 10 | #include <vector> | ||
| 11 | #include <boost/crc.hpp> | 10 | #include <boost/crc.hpp> |
| 12 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| 13 | #include "common/swap.h" | 12 | #include "common/swap.h" |
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index a80f38614..ca99cc22f 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp | |||
| @@ -2,7 +2,9 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | 5 | #include <mutex> |
| 6 | #include <tuple> | ||
| 7 | |||
| 6 | #include "common/param_package.h" | 8 | #include "common/param_package.h" |
| 7 | #include "core/frontend/input.h" | 9 | #include "core/frontend/input.h" |
| 8 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| @@ -14,7 +16,7 @@ namespace InputCommon::CemuhookUDP { | |||
| 14 | class UDPTouchDevice final : public Input::TouchDevice { | 16 | class UDPTouchDevice final : public Input::TouchDevice { |
| 15 | public: | 17 | public: |
| 16 | explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | 18 | explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} |
| 17 | std::tuple<float, float, bool> GetStatus() const { | 19 | std::tuple<float, float, bool> GetStatus() const override { |
| 18 | std::lock_guard guard(status->update_mutex); | 20 | std::lock_guard guard(status->update_mutex); |
| 19 | return status->touch_status; | 21 | return status->touch_status; |
| 20 | } | 22 | } |
| @@ -26,7 +28,7 @@ private: | |||
| 26 | class UDPMotionDevice final : public Input::MotionDevice { | 28 | class UDPMotionDevice final : public Input::MotionDevice { |
| 27 | public: | 29 | public: |
| 28 | explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | 30 | explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} |
| 29 | std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const { | 31 | std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { |
| 30 | std::lock_guard guard(status->update_mutex); | 32 | std::lock_guard guard(status->update_mutex); |
| 31 | return status->motion_status; | 33 | return status->motion_status; |
| 32 | } | 34 | } |
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h index ea3de60bb..4f83f0441 100644 --- a/src/input_common/udp/udp.h +++ b/src/input_common/udp/udp.h | |||
| @@ -2,15 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | ||
| 6 | |||
| 5 | #include <memory> | 7 | #include <memory> |
| 6 | #include <unordered_map> | ||
| 7 | #include "input_common/main.h" | ||
| 8 | #include "input_common/udp/client.h" | ||
| 9 | 8 | ||
| 10 | namespace InputCommon::CemuhookUDP { | 9 | namespace InputCommon::CemuhookUDP { |
| 11 | 10 | ||
| 12 | class UDPTouchDevice; | 11 | class Client; |
| 13 | class UDPMotionDevice; | ||
| 14 | 12 | ||
| 15 | class State { | 13 | class State { |
| 16 | public: | 14 | public: |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index ccfed4f2e..db9332d00 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -29,6 +29,8 @@ add_library(video_core STATIC | |||
| 29 | gpu_synch.h | 29 | gpu_synch.h |
| 30 | gpu_thread.cpp | 30 | gpu_thread.cpp |
| 31 | gpu_thread.h | 31 | gpu_thread.h |
| 32 | guest_driver.cpp | ||
| 33 | guest_driver.h | ||
| 32 | macro_interpreter.cpp | 34 | macro_interpreter.cpp |
| 33 | macro_interpreter.h | 35 | macro_interpreter.h |
| 34 | memory_manager.cpp | 36 | memory_manager.cpp |
| @@ -154,6 +156,7 @@ if (ENABLE_VULKAN) | |||
| 154 | renderer_vulkan/maxwell_to_vk.cpp | 156 | renderer_vulkan/maxwell_to_vk.cpp |
| 155 | renderer_vulkan/maxwell_to_vk.h | 157 | renderer_vulkan/maxwell_to_vk.h |
| 156 | renderer_vulkan/renderer_vulkan.h | 158 | renderer_vulkan/renderer_vulkan.h |
| 159 | renderer_vulkan/renderer_vulkan.cpp | ||
| 157 | renderer_vulkan/vk_blit_screen.cpp | 160 | renderer_vulkan/vk_blit_screen.cpp |
| 158 | renderer_vulkan/vk_blit_screen.h | 161 | renderer_vulkan/vk_blit_screen.h |
| 159 | renderer_vulkan/vk_buffer_cache.cpp | 162 | renderer_vulkan/vk_buffer_cache.cpp |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 0510ed777..186aca61d 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -101,7 +101,10 @@ public: | |||
| 101 | void TickFrame() { | 101 | void TickFrame() { |
| 102 | ++epoch; | 102 | ++epoch; |
| 103 | while (!pending_destruction.empty()) { | 103 | while (!pending_destruction.empty()) { |
| 104 | if (pending_destruction.front()->GetEpoch() + 1 > epoch) { | 104 | // Delay at least 4 frames before destruction. |
| 105 | // This is due to triple buffering happening on some drivers. | ||
| 106 | static constexpr u64 epochs_to_destroy = 5; | ||
| 107 | if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) { | ||
| 105 | break; | 108 | break; |
| 106 | } | 109 | } |
| 107 | pending_destruction.pop_front(); | 110 | pending_destruction.pop_front(); |
diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h index 44b8b8d22..d56a47710 100644 --- a/src/video_core/engines/const_buffer_engine_interface.h +++ b/src/video_core/engines/const_buffer_engine_interface.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/engines/shader_bytecode.h" | 10 | #include "video_core/engines/shader_bytecode.h" |
| 11 | #include "video_core/engines/shader_type.h" | 11 | #include "video_core/engines/shader_type.h" |
| 12 | #include "video_core/guest_driver.h" | ||
| 12 | #include "video_core/textures/texture.h" | 13 | #include "video_core/textures/texture.h" |
| 13 | 14 | ||
| 14 | namespace Tegra::Engines { | 15 | namespace Tegra::Engines { |
| @@ -106,6 +107,9 @@ public: | |||
| 106 | virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, | 107 | virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, |
| 107 | u64 offset) const = 0; | 108 | u64 offset) const = 0; |
| 108 | virtual u32 GetBoundBuffer() const = 0; | 109 | virtual u32 GetBoundBuffer() const = 0; |
| 110 | |||
| 111 | virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0; | ||
| 112 | virtual const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const = 0; | ||
| 109 | }; | 113 | }; |
| 110 | 114 | ||
| 111 | } // namespace Tegra::Engines | 115 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 110406f2f..4b824aa4e 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -94,6 +94,14 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con | |||
| 94 | return result; | 94 | return result; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() { | ||
| 98 | return rasterizer.AccessGuestDriverProfile(); | ||
| 99 | } | ||
| 100 | |||
| 101 | const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const { | ||
| 102 | return rasterizer.AccessGuestDriverProfile(); | ||
| 103 | } | ||
| 104 | |||
| 97 | void KeplerCompute::ProcessLaunch() { | 105 | void KeplerCompute::ProcessLaunch() { |
| 98 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); | 106 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); |
| 99 | memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, | 107 | memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 4ef3e0613..eeb79c56f 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -218,6 +218,10 @@ public: | |||
| 218 | return regs.tex_cb_index; | 218 | return regs.tex_cb_index; |
| 219 | } | 219 | } |
| 220 | 220 | ||
| 221 | VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override; | ||
| 222 | |||
| 223 | const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; | ||
| 224 | |||
| 221 | private: | 225 | private: |
| 222 | Core::System& system; | 226 | Core::System& system; |
| 223 | VideoCore::RasterizerInterface& rasterizer; | 227 | VideoCore::RasterizerInterface& rasterizer; |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 58dfa8033..7cea146f0 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -784,4 +784,12 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b | |||
| 784 | return result; | 784 | return result; |
| 785 | } | 785 | } |
| 786 | 786 | ||
| 787 | VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() { | ||
| 788 | return rasterizer.AccessGuestDriverProfile(); | ||
| 789 | } | ||
| 790 | |||
| 791 | const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const { | ||
| 792 | return rasterizer.AccessGuestDriverProfile(); | ||
| 793 | } | ||
| 794 | |||
| 787 | } // namespace Tegra::Engines | 795 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index ee79260fc..8808bbf76 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -1306,6 +1306,10 @@ public: | |||
| 1306 | return regs.tex_cb_index; | 1306 | return regs.tex_cb_index; |
| 1307 | } | 1307 | } |
| 1308 | 1308 | ||
| 1309 | VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override; | ||
| 1310 | |||
| 1311 | const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; | ||
| 1312 | |||
| 1309 | /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than | 1313 | /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than |
| 1310 | /// we've seen used. | 1314 | /// we've seen used. |
| 1311 | using MacroMemory = std::array<u32, 0x40000>; | 1315 | using MacroMemory = std::array<u32, 0x40000>; |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index ab5c8e622..81b6d9eff 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -227,6 +227,28 @@ enum class AtomicOp : u64 { | |||
| 227 | Exch = 8, | 227 | Exch = 8, |
| 228 | }; | 228 | }; |
| 229 | 229 | ||
| 230 | enum class GlobalAtomicOp : u64 { | ||
| 231 | Add = 0, | ||
| 232 | Min = 1, | ||
| 233 | Max = 2, | ||
| 234 | Inc = 3, | ||
| 235 | Dec = 4, | ||
| 236 | And = 5, | ||
| 237 | Or = 6, | ||
| 238 | Xor = 7, | ||
| 239 | Exch = 8, | ||
| 240 | SafeAdd = 10, | ||
| 241 | }; | ||
| 242 | |||
| 243 | enum class GlobalAtomicType : u64 { | ||
| 244 | U32 = 0, | ||
| 245 | S32 = 1, | ||
| 246 | U64 = 2, | ||
| 247 | F32_FTZ_RN = 3, | ||
| 248 | F16x2_FTZ_RN = 4, | ||
| 249 | S64 = 5, | ||
| 250 | }; | ||
| 251 | |||
| 230 | enum class UniformType : u64 { | 252 | enum class UniformType : u64 { |
| 231 | UnsignedByte = 0, | 253 | UnsignedByte = 0, |
| 232 | SignedByte = 1, | 254 | SignedByte = 1, |
| @@ -958,6 +980,12 @@ union Instruction { | |||
| 958 | } stg; | 980 | } stg; |
| 959 | 981 | ||
| 960 | union { | 982 | union { |
| 983 | BitField<52, 4, GlobalAtomicOp> operation; | ||
| 984 | BitField<49, 3, GlobalAtomicType> type; | ||
| 985 | BitField<28, 20, s64> offset; | ||
| 986 | } atom; | ||
| 987 | |||
| 988 | union { | ||
| 961 | BitField<52, 4, AtomicOp> operation; | 989 | BitField<52, 4, AtomicOp> operation; |
| 962 | BitField<28, 2, AtomicType> type; | 990 | BitField<28, 2, AtomicType> type; |
| 963 | BitField<30, 22, s64> offset; | 991 | BitField<30, 22, s64> offset; |
| @@ -1096,6 +1124,11 @@ union Instruction { | |||
| 1096 | } fset; | 1124 | } fset; |
| 1097 | 1125 | ||
| 1098 | union { | 1126 | union { |
| 1127 | BitField<47, 1, u64> ftz; | ||
| 1128 | BitField<48, 4, PredCondition> cond; | ||
| 1129 | } fcmp; | ||
| 1130 | |||
| 1131 | union { | ||
| 1099 | BitField<49, 1, u64> bf; | 1132 | BitField<49, 1, u64> bf; |
| 1100 | BitField<35, 3, PredCondition> cond; | 1133 | BitField<35, 3, PredCondition> cond; |
| 1101 | BitField<50, 1, u64> ftz; | 1134 | BitField<50, 1, u64> ftz; |
| @@ -1691,6 +1724,7 @@ public: | |||
| 1691 | ST_S, | 1724 | ST_S, |
| 1692 | ST, // Store in generic memory | 1725 | ST, // Store in generic memory |
| 1693 | STG, // Store in global memory | 1726 | STG, // Store in global memory |
| 1727 | ATOM, // Atomic operation on global memory | ||
| 1694 | ATOMS, // Atomic operation on shared memory | 1728 | ATOMS, // Atomic operation on shared memory |
| 1695 | AL2P, // Transforms attribute memory into physical memory | 1729 | AL2P, // Transforms attribute memory into physical memory |
| 1696 | TEX, | 1730 | TEX, |
| @@ -1772,6 +1806,7 @@ public: | |||
| 1772 | ICMP_R, | 1806 | ICMP_R, |
| 1773 | ICMP_CR, | 1807 | ICMP_CR, |
| 1774 | ICMP_IMM, | 1808 | ICMP_IMM, |
| 1809 | FCMP_R, | ||
| 1775 | MUFU, // Multi-Function Operator | 1810 | MUFU, // Multi-Function Operator |
| 1776 | RRO_C, // Range Reduction Operator | 1811 | RRO_C, // Range Reduction Operator |
| 1777 | RRO_R, | 1812 | RRO_R, |
| @@ -1995,6 +2030,7 @@ private: | |||
| 1995 | INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), | 2030 | INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), |
| 1996 | INST("101-------------", Id::ST, Type::Memory, "ST"), | 2031 | INST("101-------------", Id::ST, Type::Memory, "ST"), |
| 1997 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), | 2032 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), |
| 2033 | INST("11101101--------", Id::ATOM, Type::Memory, "ATOM"), | ||
| 1998 | INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"), | 2034 | INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"), |
| 1999 | INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), | 2035 | INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), |
| 2000 | INST("110000----111---", Id::TEX, Type::Texture, "TEX"), | 2036 | INST("110000----111---", Id::TEX, Type::Texture, "TEX"), |
| @@ -2075,6 +2111,7 @@ private: | |||
| 2075 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), | 2111 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), |
| 2076 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), | 2112 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), |
| 2077 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), | 2113 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), |
| 2114 | INST("010110111010----", Id::FCMP_R, Type::Arithmetic, "FCMP_R"), | ||
| 2078 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | 2115 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), |
| 2079 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), | 2116 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), |
| 2080 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), | 2117 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), |
diff --git a/src/video_core/guest_driver.cpp b/src/video_core/guest_driver.cpp new file mode 100644 index 000000000..6adef459e --- /dev/null +++ b/src/video_core/guest_driver.cpp | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <limits> | ||
| 7 | |||
| 8 | #include "video_core/guest_driver.h" | ||
| 9 | |||
| 10 | namespace VideoCore { | ||
| 11 | |||
| 12 | void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) { | ||
| 13 | if (texture_handler_size_deduced) { | ||
| 14 | return; | ||
| 15 | } | ||
| 16 | const std::size_t size = bound_offsets.size(); | ||
| 17 | if (size < 2) { | ||
| 18 | return; | ||
| 19 | } | ||
| 20 | std::sort(bound_offsets.begin(), bound_offsets.end(), std::less{}); | ||
| 21 | u32 min_val = std::numeric_limits<u32>::max(); | ||
| 22 | for (std::size_t i = 1; i < size; ++i) { | ||
| 23 | if (bound_offsets[i] == bound_offsets[i - 1]) { | ||
| 24 | continue; | ||
| 25 | } | ||
| 26 | const u32 new_min = bound_offsets[i] - bound_offsets[i - 1]; | ||
| 27 | min_val = std::min(min_val, new_min); | ||
| 28 | } | ||
| 29 | if (min_val > 2) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | texture_handler_size_deduced = true; | ||
| 33 | texture_handler_size = min_texture_handler_size * min_val; | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace VideoCore | ||
diff --git a/src/video_core/guest_driver.h b/src/video_core/guest_driver.h new file mode 100644 index 000000000..fc1917347 --- /dev/null +++ b/src/video_core/guest_driver.h | |||
| @@ -0,0 +1,41 @@ | |||
| 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 <vector> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace VideoCore { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * The GuestDriverProfile class is used to learn about the GPU drivers behavior and collect | ||
| 15 | * information necessary for impossible to avoid HLE methods like shader tracks as they are | ||
| 16 | * Entscheidungsproblems. | ||
| 17 | */ | ||
| 18 | class GuestDriverProfile { | ||
| 19 | public: | ||
| 20 | void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets); | ||
| 21 | |||
| 22 | u32 GetTextureHandlerSize() const { | ||
| 23 | return texture_handler_size; | ||
| 24 | } | ||
| 25 | |||
| 26 | bool TextureHandlerSizeKnown() const { | ||
| 27 | return texture_handler_size_deduced; | ||
| 28 | } | ||
| 29 | |||
| 30 | private: | ||
| 31 | // Minimum size of texture handler any driver can use. | ||
| 32 | static constexpr u32 min_texture_handler_size = 4; | ||
| 33 | // This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily | ||
| 34 | // use 4 bytes instead. Thus, certain drivers may squish the size. | ||
| 35 | static constexpr u32 default_texture_handler_size = 8; | ||
| 36 | |||
| 37 | u32 texture_handler_size = default_texture_handler_size; | ||
| 38 | bool texture_handler_size_deduced = false; | ||
| 39 | }; | ||
| 40 | |||
| 41 | } // namespace VideoCore | ||
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 5b0eca9e2..c586cd6fe 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/engines/fermi_2d.h" | 10 | #include "video_core/engines/fermi_2d.h" |
| 11 | #include "video_core/gpu.h" | 11 | #include "video_core/gpu.h" |
| 12 | #include "video_core/guest_driver.h" | ||
| 12 | 13 | ||
| 13 | namespace Tegra { | 14 | namespace Tegra { |
| 14 | class MemoryManager; | 15 | class MemoryManager; |
| @@ -78,5 +79,18 @@ public: | |||
| 78 | /// Initialize disk cached resources for the game being emulated | 79 | /// Initialize disk cached resources for the game being emulated |
| 79 | virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, | 80 | virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, |
| 80 | const DiskResourceLoadCallback& callback = {}) {} | 81 | const DiskResourceLoadCallback& callback = {}) {} |
| 82 | |||
| 83 | /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. | ||
| 84 | GuestDriverProfile& AccessGuestDriverProfile() { | ||
| 85 | return guest_driver_profile; | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. | ||
| 89 | const GuestDriverProfile& AccessGuestDriverProfile() const { | ||
| 90 | return guest_driver_profile; | ||
| 91 | } | ||
| 92 | |||
| 93 | private: | ||
| 94 | GuestDriverProfile guest_driver_profile{}; | ||
| 81 | }; | 95 | }; |
| 82 | } // namespace VideoCore | 96 | } // namespace VideoCore |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c428f06e4..362942e09 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -55,16 +55,20 @@ namespace { | |||
| 55 | 55 | ||
| 56 | template <typename Engine, typename Entry> | 56 | template <typename Engine, typename Entry> |
| 57 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 57 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, |
| 58 | Tegra::Engines::ShaderType shader_type) { | 58 | Tegra::Engines::ShaderType shader_type, |
| 59 | std::size_t index = 0) { | ||
| 59 | if (entry.IsBindless()) { | 60 | if (entry.IsBindless()) { |
| 60 | const Tegra::Texture::TextureHandle tex_handle = | 61 | const Tegra::Texture::TextureHandle tex_handle = |
| 61 | engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); | 62 | engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); |
| 62 | return engine.GetTextureInfo(tex_handle); | 63 | return engine.GetTextureInfo(tex_handle); |
| 63 | } | 64 | } |
| 65 | const auto& gpu_profile = engine.AccessGuestDriverProfile(); | ||
| 66 | const u32 offset = | ||
| 67 | entry.GetOffset() + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize()); | ||
| 64 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { | 68 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { |
| 65 | return engine.GetStageTexture(shader_type, entry.GetOffset()); | 69 | return engine.GetStageTexture(shader_type, offset); |
| 66 | } else { | 70 | } else { |
| 67 | return engine.GetTexture(entry.GetOffset()); | 71 | return engine.GetTexture(offset); |
| 68 | } | 72 | } |
| 69 | } | 73 | } |
| 70 | 74 | ||
| @@ -942,8 +946,15 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader& | |||
| 942 | u32 binding = device.GetBaseBindings(stage_index).sampler; | 946 | u32 binding = device.GetBaseBindings(stage_index).sampler; |
| 943 | for (const auto& entry : shader->GetShaderEntries().samplers) { | 947 | for (const auto& entry : shader->GetShaderEntries().samplers) { |
| 944 | const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); | 948 | const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); |
| 945 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); | 949 | if (!entry.IsIndexed()) { |
| 946 | SetupTexture(binding++, texture, entry); | 950 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); |
| 951 | SetupTexture(binding++, texture, entry); | ||
| 952 | } else { | ||
| 953 | for (std::size_t i = 0; i < entry.Size(); ++i) { | ||
| 954 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i); | ||
| 955 | SetupTexture(binding++, texture, entry); | ||
| 956 | } | ||
| 957 | } | ||
| 947 | } | 958 | } |
| 948 | } | 959 | } |
| 949 | 960 | ||
| @@ -952,8 +963,17 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) { | |||
| 952 | const auto& compute = system.GPU().KeplerCompute(); | 963 | const auto& compute = system.GPU().KeplerCompute(); |
| 953 | u32 binding = 0; | 964 | u32 binding = 0; |
| 954 | for (const auto& entry : kernel->GetShaderEntries().samplers) { | 965 | for (const auto& entry : kernel->GetShaderEntries().samplers) { |
| 955 | const auto texture = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); | 966 | if (!entry.IsIndexed()) { |
| 956 | SetupTexture(binding++, texture, entry); | 967 | const auto texture = |
| 968 | GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); | ||
| 969 | SetupTexture(binding++, texture, entry); | ||
| 970 | } else { | ||
| 971 | for (std::size_t i = 0; i < entry.Size(); ++i) { | ||
| 972 | const auto texture = | ||
| 973 | GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute, i); | ||
| 974 | SetupTexture(binding++, texture, entry); | ||
| 975 | } | ||
| 976 | } | ||
| 957 | } | 977 | } |
| 958 | } | 978 | } |
| 959 | 979 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 3c5bdd377..489eb143c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -214,6 +214,7 @@ std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ShaderType s | |||
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) { | 216 | void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) { |
| 217 | locker.SetBoundBuffer(usage.bound_buffer); | ||
| 217 | for (const auto& key : usage.keys) { | 218 | for (const auto& key : usage.keys) { |
| 218 | const auto [buffer, offset] = key.first; | 219 | const auto [buffer, offset] = key.first; |
| 219 | locker.InsertKey(buffer, offset, key.second); | 220 | locker.InsertKey(buffer, offset, key.second); |
| @@ -418,7 +419,8 @@ bool CachedShader::EnsureValidLockerVariant() { | |||
| 418 | 419 | ||
| 419 | ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant, | 420 | ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant, |
| 420 | const ConstBufferLocker& locker) const { | 421 | const ConstBufferLocker& locker) const { |
| 421 | return ShaderDiskCacheUsage{unique_identifier, variant, locker.GetKeys(), | 422 | return ShaderDiskCacheUsage{unique_identifier, variant, |
| 423 | locker.GetBoundBuffer(), locker.GetKeys(), | ||
| 422 | locker.GetBoundSamplers(), locker.GetBindlessSamplers()}; | 424 | locker.GetBoundSamplers(), locker.GetBindlessSamplers()}; |
| 423 | } | 425 | } |
| 424 | 426 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 2996aaf08..4735000b5 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -391,6 +391,7 @@ public: | |||
| 391 | DeclareVertex(); | 391 | DeclareVertex(); |
| 392 | DeclareGeometry(); | 392 | DeclareGeometry(); |
| 393 | DeclareRegisters(); | 393 | DeclareRegisters(); |
| 394 | DeclareCustomVariables(); | ||
| 394 | DeclarePredicates(); | 395 | DeclarePredicates(); |
| 395 | DeclareLocalMemory(); | 396 | DeclareLocalMemory(); |
| 396 | DeclareInternalFlags(); | 397 | DeclareInternalFlags(); |
| @@ -503,6 +504,16 @@ private: | |||
| 503 | } | 504 | } |
| 504 | } | 505 | } |
| 505 | 506 | ||
| 507 | void DeclareCustomVariables() { | ||
| 508 | const u32 num_custom_variables = ir.GetNumCustomVariables(); | ||
| 509 | for (u32 i = 0; i < num_custom_variables; ++i) { | ||
| 510 | code.AddLine("float {} = 0.0f;", GetCustomVariable(i)); | ||
| 511 | } | ||
| 512 | if (num_custom_variables > 0) { | ||
| 513 | code.AddNewLine(); | ||
| 514 | } | ||
| 515 | } | ||
| 516 | |||
| 506 | void DeclarePredicates() { | 517 | void DeclarePredicates() { |
| 507 | const auto& predicates = ir.GetPredicates(); | 518 | const auto& predicates = ir.GetPredicates(); |
| 508 | for (const auto pred : predicates) { | 519 | for (const auto pred : predicates) { |
| @@ -655,7 +666,8 @@ private: | |||
| 655 | u32 binding = device.GetBaseBindings(stage).sampler; | 666 | u32 binding = device.GetBaseBindings(stage).sampler; |
| 656 | for (const auto& sampler : ir.GetSamplers()) { | 667 | for (const auto& sampler : ir.GetSamplers()) { |
| 657 | const std::string name = GetSampler(sampler); | 668 | const std::string name = GetSampler(sampler); |
| 658 | const std::string description = fmt::format("layout (binding = {}) uniform", binding++); | 669 | const std::string description = fmt::format("layout (binding = {}) uniform", binding); |
| 670 | binding += sampler.IsIndexed() ? sampler.Size() : 1; | ||
| 659 | 671 | ||
| 660 | std::string sampler_type = [&]() { | 672 | std::string sampler_type = [&]() { |
| 661 | if (sampler.IsBuffer()) { | 673 | if (sampler.IsBuffer()) { |
| @@ -682,7 +694,11 @@ private: | |||
| 682 | sampler_type += "Shadow"; | 694 | sampler_type += "Shadow"; |
| 683 | } | 695 | } |
| 684 | 696 | ||
| 685 | code.AddLine("{} {} {};", description, sampler_type, name); | 697 | if (!sampler.IsIndexed()) { |
| 698 | code.AddLine("{} {} {};", description, sampler_type, name); | ||
| 699 | } else { | ||
| 700 | code.AddLine("{} {} {}[{}];", description, sampler_type, name, sampler.Size()); | ||
| 701 | } | ||
| 686 | } | 702 | } |
| 687 | if (!ir.GetSamplers().empty()) { | 703 | if (!ir.GetSamplers().empty()) { |
| 688 | code.AddNewLine(); | 704 | code.AddNewLine(); |
| @@ -775,6 +791,11 @@ private: | |||
| 775 | return {GetRegister(index), Type::Float}; | 791 | return {GetRegister(index), Type::Float}; |
| 776 | } | 792 | } |
| 777 | 793 | ||
| 794 | if (const auto cv = std::get_if<CustomVarNode>(&*node)) { | ||
| 795 | const u32 index = cv->GetIndex(); | ||
| 796 | return {GetCustomVariable(index), Type::Float}; | ||
| 797 | } | ||
| 798 | |||
| 778 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { | 799 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { |
| 779 | const u32 value = immediate->GetValue(); | 800 | const u32 value = immediate->GetValue(); |
| 780 | if (value < 10) { | 801 | if (value < 10) { |
| @@ -1019,7 +1040,6 @@ private: | |||
| 1019 | } | 1040 | } |
| 1020 | return {{"gl_ViewportIndex", Type::Int}}; | 1041 | return {{"gl_ViewportIndex", Type::Int}}; |
| 1021 | case 3: | 1042 | case 3: |
| 1022 | UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader"); | ||
| 1023 | return {{"gl_PointSize", Type::Float}}; | 1043 | return {{"gl_PointSize", Type::Float}}; |
| 1024 | } | 1044 | } |
| 1025 | return {}; | 1045 | return {}; |
| @@ -1099,7 +1119,11 @@ private: | |||
| 1099 | } else if (!meta->ptp.empty()) { | 1119 | } else if (!meta->ptp.empty()) { |
| 1100 | expr += "Offsets"; | 1120 | expr += "Offsets"; |
| 1101 | } | 1121 | } |
| 1102 | expr += '(' + GetSampler(meta->sampler) + ", "; | 1122 | if (!meta->sampler.IsIndexed()) { |
| 1123 | expr += '(' + GetSampler(meta->sampler) + ", "; | ||
| 1124 | } else { | ||
| 1125 | expr += '(' + GetSampler(meta->sampler) + '[' + Visit(meta->index).AsUint() + "], "; | ||
| 1126 | } | ||
| 1103 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + | 1127 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + |
| 1104 | (has_shadow && !separate_dc ? 1 : 0) - 1); | 1128 | (has_shadow && !separate_dc ? 1 : 0) - 1); |
| 1105 | expr += '('; | 1129 | expr += '('; |
| @@ -1311,6 +1335,8 @@ private: | |||
| 1311 | const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); | 1335 | const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); |
| 1312 | target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), | 1336 | target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), |
| 1313 | Type::Uint}; | 1337 | Type::Uint}; |
| 1338 | } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) { | ||
| 1339 | target = {GetCustomVariable(cv->GetIndex()), Type::Float}; | ||
| 1314 | } else { | 1340 | } else { |
| 1315 | UNREACHABLE_MSG("Assign called without a proper target"); | 1341 | UNREACHABLE_MSG("Assign called without a proper target"); |
| 1316 | } | 1342 | } |
| @@ -1858,10 +1884,7 @@ private: | |||
| 1858 | 1884 | ||
| 1859 | template <const std::string_view& opname, Type type> | 1885 | template <const std::string_view& opname, Type type> |
| 1860 | Expression Atomic(Operation operation) { | 1886 | Expression Atomic(Operation operation) { |
| 1861 | ASSERT(stage == ShaderType::Compute); | 1887 | return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), |
| 1862 | auto& smem = std::get<SmemNode>(*operation[0]); | ||
| 1863 | |||
| 1864 | return {fmt::format("atomic{}(smem[{} >> 2], {})", opname, Visit(smem.GetAddress()).AsInt(), | ||
| 1865 | Visit(operation[1]).As(type)), | 1888 | Visit(operation[1]).As(type)), |
| 1866 | type}; | 1889 | type}; |
| 1867 | } | 1890 | } |
| @@ -2241,6 +2264,10 @@ private: | |||
| 2241 | return GetDeclarationWithSuffix(index, "gpr"); | 2264 | return GetDeclarationWithSuffix(index, "gpr"); |
| 2242 | } | 2265 | } |
| 2243 | 2266 | ||
| 2267 | std::string GetCustomVariable(u32 index) const { | ||
| 2268 | return GetDeclarationWithSuffix(index, "custom_var"); | ||
| 2269 | } | ||
| 2270 | |||
| 2244 | std::string GetPredicate(Tegra::Shader::Pred pred) const { | 2271 | std::string GetPredicate(Tegra::Shader::Pred pred) const { |
| 2245 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); | 2272 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); |
| 2246 | } | 2273 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index cf874a09a..1fc204f6f 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -53,7 +53,7 @@ struct BindlessSamplerKey { | |||
| 53 | Tegra::Engines::SamplerDescriptor sampler{}; | 53 | Tegra::Engines::SamplerDescriptor sampler{}; |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | constexpr u32 NativeVersion = 11; | 56 | constexpr u32 NativeVersion = 12; |
| 57 | 57 | ||
| 58 | // Making sure sizes doesn't change by accident | 58 | // Making sure sizes doesn't change by accident |
| 59 | static_assert(sizeof(ProgramVariant) == 20); | 59 | static_assert(sizeof(ProgramVariant) == 20); |
| @@ -186,7 +186,8 @@ ShaderDiskCacheOpenGL::LoadTransferable() { | |||
| 186 | u32 num_bound_samplers{}; | 186 | u32 num_bound_samplers{}; |
| 187 | u32 num_bindless_samplers{}; | 187 | u32 num_bindless_samplers{}; |
| 188 | if (file.ReadArray(&usage.unique_identifier, 1) != 1 || | 188 | if (file.ReadArray(&usage.unique_identifier, 1) != 1 || |
| 189 | file.ReadArray(&usage.variant, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 || | 189 | file.ReadArray(&usage.variant, 1) != 1 || |
| 190 | file.ReadArray(&usage.bound_buffer, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 || | ||
| 190 | file.ReadArray(&num_bound_samplers, 1) != 1 || | 191 | file.ReadArray(&num_bound_samplers, 1) != 1 || |
| 191 | file.ReadArray(&num_bindless_samplers, 1) != 1) { | 192 | file.ReadArray(&num_bindless_samplers, 1) != 1) { |
| 192 | LOG_ERROR(Render_OpenGL, error_loading); | 193 | LOG_ERROR(Render_OpenGL, error_loading); |
| @@ -281,7 +282,9 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | |||
| 281 | u32 num_bindless_samplers{}; | 282 | u32 num_bindless_samplers{}; |
| 282 | ShaderDiskCacheUsage usage; | 283 | ShaderDiskCacheUsage usage; |
| 283 | if (!LoadObjectFromPrecompiled(usage.unique_identifier) || | 284 | if (!LoadObjectFromPrecompiled(usage.unique_identifier) || |
| 284 | !LoadObjectFromPrecompiled(usage.variant) || !LoadObjectFromPrecompiled(num_keys) || | 285 | !LoadObjectFromPrecompiled(usage.variant) || |
| 286 | !LoadObjectFromPrecompiled(usage.bound_buffer) || | ||
| 287 | !LoadObjectFromPrecompiled(num_keys) || | ||
| 285 | !LoadObjectFromPrecompiled(num_bound_samplers) || | 288 | !LoadObjectFromPrecompiled(num_bound_samplers) || |
| 286 | !LoadObjectFromPrecompiled(num_bindless_samplers)) { | 289 | !LoadObjectFromPrecompiled(num_bindless_samplers)) { |
| 287 | return {}; | 290 | return {}; |
| @@ -393,6 +396,7 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { | |||
| 393 | 396 | ||
| 394 | if (file.WriteObject(TransferableEntryKind::Usage) != 1 || | 397 | if (file.WriteObject(TransferableEntryKind::Usage) != 1 || |
| 395 | file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 || | 398 | file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 || |
| 399 | file.WriteObject(usage.bound_buffer) != 1 || | ||
| 396 | file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 || | 400 | file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 || |
| 397 | file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 || | 401 | file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 || |
| 398 | file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) { | 402 | file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) { |
| @@ -447,7 +451,7 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p | |||
| 447 | }; | 451 | }; |
| 448 | 452 | ||
| 449 | if (!SaveObjectToPrecompiled(usage.unique_identifier) || | 453 | if (!SaveObjectToPrecompiled(usage.unique_identifier) || |
| 450 | !SaveObjectToPrecompiled(usage.variant) || | 454 | !SaveObjectToPrecompiled(usage.variant) || !SaveObjectToPrecompiled(usage.bound_buffer) || |
| 451 | !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) || | 455 | !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) || |
| 452 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) || | 456 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) || |
| 453 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) { | 457 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index 69a2fbdda..ef2371f6d 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -79,6 +79,7 @@ static_assert(std::is_trivially_copyable_v<ProgramVariant>); | |||
| 79 | struct ShaderDiskCacheUsage { | 79 | struct ShaderDiskCacheUsage { |
| 80 | u64 unique_identifier{}; | 80 | u64 unique_identifier{}; |
| 81 | ProgramVariant variant; | 81 | ProgramVariant variant; |
| 82 | u32 bound_buffer{}; | ||
| 82 | VideoCommon::Shader::KeyMap keys; | 83 | VideoCommon::Shader::KeyMap keys; |
| 83 | VideoCommon::Shader::BoundSamplerMap bound_samplers; | 84 | VideoCommon::Shader::BoundSamplerMap bound_samplers; |
| 84 | VideoCommon::Shader::BindlessSamplerMap bindless_samplers; | 85 | VideoCommon::Shader::BindlessSamplerMap bindless_samplers; |
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index e95eb069e..d4b81cd87 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -176,6 +176,19 @@ GLint GetSwizzleSource(SwizzleSource source) { | |||
| 176 | return GL_NONE; | 176 | return GL_NONE; |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | GLenum GetComponent(PixelFormat format, bool is_first) { | ||
| 180 | switch (format) { | ||
| 181 | case PixelFormat::Z24S8: | ||
| 182 | case PixelFormat::Z32FS8: | ||
| 183 | return is_first ? GL_DEPTH_COMPONENT : GL_STENCIL_INDEX; | ||
| 184 | case PixelFormat::S8Z24: | ||
| 185 | return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT; | ||
| 186 | default: | ||
| 187 | UNREACHABLE(); | ||
| 188 | return GL_DEPTH_COMPONENT; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 179 | void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { | 192 | void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { |
| 180 | if (params.IsBuffer()) { | 193 | if (params.IsBuffer()) { |
| 181 | return; | 194 | return; |
| @@ -184,7 +197,7 @@ void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { | |||
| 184 | glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 197 | glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 185 | glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 198 | glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 186 | glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 199 | glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 187 | glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.num_levels - 1); | 200 | glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(params.num_levels - 1)); |
| 188 | if (params.num_levels == 1) { | 201 | if (params.num_levels == 1) { |
| 189 | glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); | 202 | glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); |
| 190 | } | 203 | } |
| @@ -416,11 +429,21 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou | |||
| 416 | if (new_swizzle == swizzle) | 429 | if (new_swizzle == swizzle) |
| 417 | return; | 430 | return; |
| 418 | swizzle = new_swizzle; | 431 | swizzle = new_swizzle; |
| 419 | const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), | 432 | const std::array gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), |
| 420 | GetSwizzleSource(z_source), | 433 | GetSwizzleSource(z_source), GetSwizzleSource(w_source)}; |
| 421 | GetSwizzleSource(w_source)}; | ||
| 422 | const GLuint handle = GetTexture(); | 434 | const GLuint handle = GetTexture(); |
| 423 | glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); | 435 | const PixelFormat format = surface.GetSurfaceParams().pixel_format; |
| 436 | switch (format) { | ||
| 437 | case PixelFormat::Z24S8: | ||
| 438 | case PixelFormat::Z32FS8: | ||
| 439 | case PixelFormat::S8Z24: | ||
| 440 | glTextureParameteri(handle, GL_DEPTH_STENCIL_TEXTURE_MODE, | ||
| 441 | GetComponent(format, x_source == SwizzleSource::R)); | ||
| 442 | break; | ||
| 443 | default: | ||
| 444 | glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); | ||
| 445 | break; | ||
| 446 | } | ||
| 424 | } | 447 | } |
| 425 | 448 | ||
| 426 | OGLTextureView CachedSurfaceView::CreateTextureView() const { | 449 | OGLTextureView CachedSurfaceView::CreateTextureView() const { |
| @@ -529,8 +552,11 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view, | |||
| 529 | const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect; | 552 | const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect; |
| 530 | const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear; | 553 | const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear; |
| 531 | 554 | ||
| 532 | glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, | 555 | glBlitFramebuffer(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.top), |
| 533 | dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, | 556 | static_cast<GLint>(src_rect.right), static_cast<GLint>(src_rect.bottom), |
| 557 | static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.top), | ||
| 558 | static_cast<GLint>(dst_rect.right), static_cast<GLint>(dst_rect.bottom), | ||
| 559 | buffers, | ||
| 534 | is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST); | 560 | is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST); |
| 535 | } | 561 | } |
| 536 | 562 | ||
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp new file mode 100644 index 000000000..d5032b432 --- /dev/null +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -0,0 +1,265 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <optional> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/telemetry.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_timing.h" | ||
| 16 | #include "core/frontend/emu_window.h" | ||
| 17 | #include "core/memory.h" | ||
| 18 | #include "core/perf_stats.h" | ||
| 19 | #include "core/settings.h" | ||
| 20 | #include "core/telemetry_session.h" | ||
| 21 | #include "video_core/gpu.h" | ||
| 22 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 23 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||
| 24 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||
| 25 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 26 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 27 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | ||
| 28 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||
| 29 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 30 | #include "video_core/renderer_vulkan/vk_swapchain.h" | ||
| 31 | |||
| 32 | namespace Vulkan { | ||
| 33 | |||
| 34 | namespace { | ||
| 35 | |||
| 36 | VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, | ||
| 37 | VkDebugUtilsMessageTypeFlagsEXT type, | ||
| 38 | const VkDebugUtilsMessengerCallbackDataEXT* data, | ||
| 39 | [[maybe_unused]] void* user_data) { | ||
| 40 | const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_}; | ||
| 41 | const char* message{data->pMessage}; | ||
| 42 | |||
| 43 | if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) { | ||
| 44 | LOG_CRITICAL(Render_Vulkan, "{}", message); | ||
| 45 | } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) { | ||
| 46 | LOG_WARNING(Render_Vulkan, "{}", message); | ||
| 47 | } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) { | ||
| 48 | LOG_INFO(Render_Vulkan, "{}", message); | ||
| 49 | } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) { | ||
| 50 | LOG_DEBUG(Render_Vulkan, "{}", message); | ||
| 51 | } | ||
| 52 | return VK_FALSE; | ||
| 53 | } | ||
| 54 | |||
| 55 | std::string GetReadableVersion(u32 version) { | ||
| 56 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), | ||
| 57 | VK_VERSION_PATCH(version)); | ||
| 58 | } | ||
| 59 | |||
| 60 | std::string GetDriverVersion(const VKDevice& device) { | ||
| 61 | // Extracted from | ||
| 62 | // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 | ||
| 63 | const u32 version = device.GetDriverVersion(); | ||
| 64 | |||
| 65 | if (device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) { | ||
| 66 | const u32 major = (version >> 22) & 0x3ff; | ||
| 67 | const u32 minor = (version >> 14) & 0x0ff; | ||
| 68 | const u32 secondary = (version >> 6) & 0x0ff; | ||
| 69 | const u32 tertiary = version & 0x003f; | ||
| 70 | return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary); | ||
| 71 | } | ||
| 72 | if (device.GetDriverID() == vk::DriverIdKHR::eIntelProprietaryWindows) { | ||
| 73 | const u32 major = version >> 14; | ||
| 74 | const u32 minor = version & 0x3fff; | ||
| 75 | return fmt::format("{}.{}", major, minor); | ||
| 76 | } | ||
| 77 | |||
| 78 | return GetReadableVersion(version); | ||
| 79 | } | ||
| 80 | |||
| 81 | std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) { | ||
| 82 | std::sort(std::begin(available_extensions), std::end(available_extensions)); | ||
| 83 | |||
| 84 | static constexpr std::size_t AverageExtensionSize = 64; | ||
| 85 | std::string separated_extensions; | ||
| 86 | separated_extensions.reserve(available_extensions.size() * AverageExtensionSize); | ||
| 87 | |||
| 88 | const auto end = std::end(available_extensions); | ||
| 89 | for (auto extension = std::begin(available_extensions); extension != end; ++extension) { | ||
| 90 | if (const bool is_last = extension + 1 == end; is_last) { | ||
| 91 | separated_extensions += *extension; | ||
| 92 | } else { | ||
| 93 | separated_extensions += fmt::format("{},", *extension); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | return separated_extensions; | ||
| 97 | } | ||
| 98 | |||
| 99 | } // Anonymous namespace | ||
| 100 | |||
| 101 | RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system) | ||
| 102 | : RendererBase(window), system{system} {} | ||
| 103 | |||
| 104 | RendererVulkan::~RendererVulkan() { | ||
| 105 | ShutDown(); | ||
| 106 | } | ||
| 107 | |||
| 108 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 109 | const auto& layout = render_window.GetFramebufferLayout(); | ||
| 110 | if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) { | ||
| 111 | const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; | ||
| 112 | const bool use_accelerated = | ||
| 113 | rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); | ||
| 114 | const bool is_srgb = use_accelerated && screen_info.is_srgb; | ||
| 115 | if (swapchain->HasFramebufferChanged(layout) || swapchain->GetSrgbState() != is_srgb) { | ||
| 116 | swapchain->Create(layout.width, layout.height, is_srgb); | ||
| 117 | blit_screen->Recreate(); | ||
| 118 | } | ||
| 119 | |||
| 120 | scheduler->WaitWorker(); | ||
| 121 | |||
| 122 | swapchain->AcquireNextImage(); | ||
| 123 | const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated); | ||
| 124 | |||
| 125 | scheduler->Flush(false, render_semaphore); | ||
| 126 | |||
| 127 | if (swapchain->Present(render_semaphore, fence)) { | ||
| 128 | blit_screen->Recreate(); | ||
| 129 | } | ||
| 130 | |||
| 131 | render_window.SwapBuffers(); | ||
| 132 | rasterizer->TickFrame(); | ||
| 133 | } | ||
| 134 | |||
| 135 | render_window.PollEvents(); | ||
| 136 | } | ||
| 137 | |||
| 138 | bool RendererVulkan::Init() { | ||
| 139 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; | ||
| 140 | render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); | ||
| 141 | const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr); | ||
| 142 | |||
| 143 | std::optional<vk::DebugUtilsMessengerEXT> callback; | ||
| 144 | if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) { | ||
| 145 | callback = CreateDebugCallback(dldi); | ||
| 146 | if (!callback) { | ||
| 147 | return false; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | if (!PickDevices(dldi)) { | ||
| 152 | if (callback) { | ||
| 153 | instance.destroy(*callback, nullptr, dldi); | ||
| 154 | } | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | debug_callback = UniqueDebugUtilsMessengerEXT( | ||
| 158 | *callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>( | ||
| 159 | instance, nullptr, device->GetDispatchLoader())); | ||
| 160 | |||
| 161 | Report(); | ||
| 162 | |||
| 163 | memory_manager = std::make_unique<VKMemoryManager>(*device); | ||
| 164 | |||
| 165 | resource_manager = std::make_unique<VKResourceManager>(*device); | ||
| 166 | |||
| 167 | const auto& framebuffer = render_window.GetFramebufferLayout(); | ||
| 168 | swapchain = std::make_unique<VKSwapchain>(surface, *device); | ||
| 169 | swapchain->Create(framebuffer.width, framebuffer.height, false); | ||
| 170 | |||
| 171 | scheduler = std::make_unique<VKScheduler>(*device, *resource_manager); | ||
| 172 | |||
| 173 | rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device, | ||
| 174 | *resource_manager, *memory_manager, *scheduler); | ||
| 175 | |||
| 176 | blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device, | ||
| 177 | *resource_manager, *memory_manager, *swapchain, | ||
| 178 | *scheduler, screen_info); | ||
| 179 | |||
| 180 | return true; | ||
| 181 | } | ||
| 182 | |||
| 183 | void RendererVulkan::ShutDown() { | ||
| 184 | if (!device) { | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | const auto dev = device->GetLogical(); | ||
| 188 | const auto& dld = device->GetDispatchLoader(); | ||
| 189 | if (dev && dld.vkDeviceWaitIdle) { | ||
| 190 | dev.waitIdle(dld); | ||
| 191 | } | ||
| 192 | |||
| 193 | rasterizer.reset(); | ||
| 194 | blit_screen.reset(); | ||
| 195 | scheduler.reset(); | ||
| 196 | swapchain.reset(); | ||
| 197 | memory_manager.reset(); | ||
| 198 | resource_manager.reset(); | ||
| 199 | device.reset(); | ||
| 200 | } | ||
| 201 | |||
| 202 | std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback( | ||
| 203 | const vk::DispatchLoaderDynamic& dldi) { | ||
| 204 | const vk::DebugUtilsMessengerCreateInfoEXT callback_ci( | ||
| 205 | {}, | ||
| 206 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | | ||
| 207 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | | ||
| 208 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo | | ||
| 209 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose, | ||
| 210 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | | ||
| 211 | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | | ||
| 212 | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, | ||
| 213 | &DebugCallback, nullptr); | ||
| 214 | vk::DebugUtilsMessengerEXT callback; | ||
| 215 | if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) != | ||
| 216 | vk::Result::eSuccess) { | ||
| 217 | LOG_ERROR(Render_Vulkan, "Failed to create debug callback"); | ||
| 218 | return {}; | ||
| 219 | } | ||
| 220 | return callback; | ||
| 221 | } | ||
| 222 | |||
| 223 | bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) { | ||
| 224 | const auto devices = instance.enumeratePhysicalDevices(dldi); | ||
| 225 | |||
| 226 | // TODO(Rodrigo): Choose device from config file | ||
| 227 | const s32 device_index = Settings::values.vulkan_device; | ||
| 228 | if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) { | ||
| 229 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); | ||
| 230 | return false; | ||
| 231 | } | ||
| 232 | const vk::PhysicalDevice physical_device = devices[device_index]; | ||
| 233 | |||
| 234 | if (!VKDevice::IsSuitable(dldi, physical_device, surface)) { | ||
| 235 | return false; | ||
| 236 | } | ||
| 237 | |||
| 238 | device = std::make_unique<VKDevice>(dldi, physical_device, surface); | ||
| 239 | return device->Create(dldi, instance); | ||
| 240 | } | ||
| 241 | |||
| 242 | void RendererVulkan::Report() const { | ||
| 243 | const std::string vendor_name{device->GetVendorName()}; | ||
| 244 | const std::string model_name{device->GetModelName()}; | ||
| 245 | const std::string driver_version = GetDriverVersion(*device); | ||
| 246 | const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version); | ||
| 247 | |||
| 248 | const std::string api_version = GetReadableVersion(device->GetApiVersion()); | ||
| 249 | |||
| 250 | const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions()); | ||
| 251 | |||
| 252 | LOG_INFO(Render_Vulkan, "Driver: {}", driver_name); | ||
| 253 | LOG_INFO(Render_Vulkan, "Device: {}", model_name); | ||
| 254 | LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); | ||
| 255 | |||
| 256 | auto& telemetry_session = system.TelemetrySession(); | ||
| 257 | constexpr auto field = Telemetry::FieldType::UserSystem; | ||
| 258 | telemetry_session.AddField(field, "GPU_Vendor", vendor_name); | ||
| 259 | telemetry_session.AddField(field, "GPU_Model", model_name); | ||
| 260 | telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name); | ||
| 261 | telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version); | ||
| 262 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | ||
| 263 | } | ||
| 264 | |||
| 265 | } // namespace Vulkan \ No newline at end of file | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 939eebe83..9840f26e5 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -400,8 +400,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 400 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); | 400 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); |
| 401 | Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, | 401 | Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, |
| 402 | false); | 402 | false); |
| 403 | Test(extension, nv_device_diagnostic_checkpoints, | 403 | if (Settings::values.renderer_debug) { |
| 404 | VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); | 404 | Test(extension, nv_device_diagnostic_checkpoints, |
| 405 | VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); | ||
| 406 | } | ||
| 405 | } | 407 | } |
| 406 | 408 | ||
| 407 | if (khr_shader_float16_int8) { | 409 | if (khr_shader_float16_int8) { |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index b53078721..24a658dce 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -353,6 +353,7 @@ private: | |||
| 353 | DeclareFragment(); | 353 | DeclareFragment(); |
| 354 | DeclareCompute(); | 354 | DeclareCompute(); |
| 355 | DeclareRegisters(); | 355 | DeclareRegisters(); |
| 356 | DeclareCustomVariables(); | ||
| 356 | DeclarePredicates(); | 357 | DeclarePredicates(); |
| 357 | DeclareLocalMemory(); | 358 | DeclareLocalMemory(); |
| 358 | DeclareSharedMemory(); | 359 | DeclareSharedMemory(); |
| @@ -586,6 +587,15 @@ private: | |||
| 586 | } | 587 | } |
| 587 | } | 588 | } |
| 588 | 589 | ||
| 590 | void DeclareCustomVariables() { | ||
| 591 | const u32 num_custom_variables = ir.GetNumCustomVariables(); | ||
| 592 | for (u32 i = 0; i < num_custom_variables; ++i) { | ||
| 593 | const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero); | ||
| 594 | Name(id, fmt::format("custom_var_{}", i)); | ||
| 595 | custom_variables.emplace(i, AddGlobalVariable(id)); | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 589 | void DeclarePredicates() { | 599 | void DeclarePredicates() { |
| 590 | for (const auto pred : ir.GetPredicates()) { | 600 | for (const auto pred : ir.GetPredicates()) { |
| 591 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | 601 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); |
| @@ -982,6 +992,11 @@ private: | |||
| 982 | return {OpLoad(t_float, registers.at(index)), Type::Float}; | 992 | return {OpLoad(t_float, registers.at(index)), Type::Float}; |
| 983 | } | 993 | } |
| 984 | 994 | ||
| 995 | if (const auto cv = std::get_if<CustomVarNode>(&*node)) { | ||
| 996 | const u32 index = cv->GetIndex(); | ||
| 997 | return {OpLoad(t_float, custom_variables.at(index)), Type::Float}; | ||
| 998 | } | ||
| 999 | |||
| 985 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { | 1000 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { |
| 986 | return {Constant(t_uint, immediate->GetValue()), Type::Uint}; | 1001 | return {Constant(t_uint, immediate->GetValue()), Type::Uint}; |
| 987 | } | 1002 | } |
| @@ -1123,15 +1138,7 @@ private: | |||
| 1123 | } | 1138 | } |
| 1124 | 1139 | ||
| 1125 | if (const auto gmem = std::get_if<GmemNode>(&*node)) { | 1140 | if (const auto gmem = std::get_if<GmemNode>(&*node)) { |
| 1126 | const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); | 1141 | return {OpLoad(t_uint, GetGlobalMemoryPointer(*gmem)), Type::Uint}; |
| 1127 | const Id real = AsUint(Visit(gmem->GetRealAddress())); | ||
| 1128 | const Id base = AsUint(Visit(gmem->GetBaseAddress())); | ||
| 1129 | |||
| 1130 | Id offset = OpISub(t_uint, real, base); | ||
| 1131 | offset = OpUDiv(t_uint, offset, Constant(t_uint, 4U)); | ||
| 1132 | return {OpLoad(t_float, | ||
| 1133 | OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0U), offset)), | ||
| 1134 | Type::Float}; | ||
| 1135 | } | 1142 | } |
| 1136 | 1143 | ||
| 1137 | if (const auto lmem = std::get_if<LmemNode>(&*node)) { | 1144 | if (const auto lmem = std::get_if<LmemNode>(&*node)) { |
| @@ -1142,10 +1149,7 @@ private: | |||
| 1142 | } | 1149 | } |
| 1143 | 1150 | ||
| 1144 | if (const auto smem = std::get_if<SmemNode>(&*node)) { | 1151 | if (const auto smem = std::get_if<SmemNode>(&*node)) { |
| 1145 | Id address = AsUint(Visit(smem->GetAddress())); | 1152 | return {OpLoad(t_uint, GetSharedMemoryPointer(*smem)), Type::Uint}; |
| 1146 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | ||
| 1147 | const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address); | ||
| 1148 | return {OpLoad(t_uint, pointer), Type::Uint}; | ||
| 1149 | } | 1153 | } |
| 1150 | 1154 | ||
| 1151 | if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { | 1155 | if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { |
| @@ -1339,20 +1343,13 @@ private: | |||
| 1339 | target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float}; | 1343 | target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float}; |
| 1340 | 1344 | ||
| 1341 | } else if (const auto smem = std::get_if<SmemNode>(&*dest)) { | 1345 | } else if (const auto smem = std::get_if<SmemNode>(&*dest)) { |
| 1342 | ASSERT(stage == ShaderType::Compute); | 1346 | target = {GetSharedMemoryPointer(*smem), Type::Uint}; |
| 1343 | Id address = AsUint(Visit(smem->GetAddress())); | ||
| 1344 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | ||
| 1345 | target = {OpAccessChain(t_smem_uint, shared_memory, address), Type::Uint}; | ||
| 1346 | 1347 | ||
| 1347 | } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { | 1348 | } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { |
| 1348 | const Id real = AsUint(Visit(gmem->GetRealAddress())); | 1349 | target = {GetGlobalMemoryPointer(*gmem), Type::Uint}; |
| 1349 | const Id base = AsUint(Visit(gmem->GetBaseAddress())); | ||
| 1350 | const Id diff = OpISub(t_uint, real, base); | ||
| 1351 | const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2)); | ||
| 1352 | 1350 | ||
| 1353 | const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); | 1351 | } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) { |
| 1354 | target = {OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0), offset), | 1352 | target = {custom_variables.at(cv->GetIndex()), Type::Float}; |
| 1355 | Type::Float}; | ||
| 1356 | 1353 | ||
| 1357 | } else { | 1354 | } else { |
| 1358 | UNIMPLEMENTED(); | 1355 | UNIMPLEMENTED(); |
| @@ -1804,11 +1801,16 @@ private: | |||
| 1804 | return {}; | 1801 | return {}; |
| 1805 | } | 1802 | } |
| 1806 | 1803 | ||
| 1807 | Expression UAtomicAdd(Operation operation) { | 1804 | Expression AtomicAdd(Operation operation) { |
| 1808 | const auto& smem = std::get<SmemNode>(*operation[0]); | 1805 | Id pointer; |
| 1809 | Id address = AsUint(Visit(smem.GetAddress())); | 1806 | if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { |
| 1810 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | 1807 | pointer = GetSharedMemoryPointer(*smem); |
| 1811 | const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address); | 1808 | } else if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) { |
| 1809 | pointer = GetGlobalMemoryPointer(*gmem); | ||
| 1810 | } else { | ||
| 1811 | UNREACHABLE(); | ||
| 1812 | return {Constant(t_uint, 0), Type::Uint}; | ||
| 1813 | } | ||
| 1812 | 1814 | ||
| 1813 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); | 1815 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); |
| 1814 | const Id semantics = Constant(t_uint, 0U); | 1816 | const Id semantics = Constant(t_uint, 0U); |
| @@ -2243,6 +2245,22 @@ private: | |||
| 2243 | return {}; | 2245 | return {}; |
| 2244 | } | 2246 | } |
| 2245 | 2247 | ||
| 2248 | Id GetGlobalMemoryPointer(const GmemNode& gmem) { | ||
| 2249 | const Id real = AsUint(Visit(gmem.GetRealAddress())); | ||
| 2250 | const Id base = AsUint(Visit(gmem.GetBaseAddress())); | ||
| 2251 | const Id diff = OpISub(t_uint, real, base); | ||
| 2252 | const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2)); | ||
| 2253 | const Id buffer = global_buffers.at(gmem.GetDescriptor()); | ||
| 2254 | return OpAccessChain(t_gmem_uint, buffer, Constant(t_uint, 0), offset); | ||
| 2255 | } | ||
| 2256 | |||
| 2257 | Id GetSharedMemoryPointer(const SmemNode& smem) { | ||
| 2258 | ASSERT(stage == ShaderType::Compute); | ||
| 2259 | Id address = AsUint(Visit(smem.GetAddress())); | ||
| 2260 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | ||
| 2261 | return OpAccessChain(t_smem_uint, shared_memory, address); | ||
| 2262 | } | ||
| 2263 | |||
| 2246 | static constexpr std::array operation_decompilers = { | 2264 | static constexpr std::array operation_decompilers = { |
| 2247 | &SPIRVDecompiler::Assign, | 2265 | &SPIRVDecompiler::Assign, |
| 2248 | 2266 | ||
| @@ -2389,7 +2407,7 @@ private: | |||
| 2389 | &SPIRVDecompiler::AtomicImageXor, | 2407 | &SPIRVDecompiler::AtomicImageXor, |
| 2390 | &SPIRVDecompiler::AtomicImageExchange, | 2408 | &SPIRVDecompiler::AtomicImageExchange, |
| 2391 | 2409 | ||
| 2392 | &SPIRVDecompiler::UAtomicAdd, | 2410 | &SPIRVDecompiler::AtomicAdd, |
| 2393 | 2411 | ||
| 2394 | &SPIRVDecompiler::Branch, | 2412 | &SPIRVDecompiler::Branch, |
| 2395 | &SPIRVDecompiler::BranchIndirect, | 2413 | &SPIRVDecompiler::BranchIndirect, |
| @@ -2485,9 +2503,9 @@ private: | |||
| 2485 | 2503 | ||
| 2486 | Id t_smem_uint{}; | 2504 | Id t_smem_uint{}; |
| 2487 | 2505 | ||
| 2488 | const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float); | 2506 | const Id t_gmem_uint = TypePointer(spv::StorageClass::StorageBuffer, t_uint); |
| 2489 | const Id t_gmem_array = | 2507 | const Id t_gmem_array = |
| 2490 | Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4U), "GmemArray"); | 2508 | Name(Decorate(TypeRuntimeArray(t_uint), spv::Decoration::ArrayStride, 4U), "GmemArray"); |
| 2491 | const Id t_gmem_struct = MemberDecorate( | 2509 | const Id t_gmem_struct = MemberDecorate( |
| 2492 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); | 2510 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); |
| 2493 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); | 2511 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); |
| @@ -2508,6 +2526,7 @@ private: | |||
| 2508 | Id out_vertex{}; | 2526 | Id out_vertex{}; |
| 2509 | Id in_vertex{}; | 2527 | Id in_vertex{}; |
| 2510 | std::map<u32, Id> registers; | 2528 | std::map<u32, Id> registers; |
| 2529 | std::map<u32, Id> custom_variables; | ||
| 2511 | std::map<Tegra::Shader::Pred, Id> predicates; | 2530 | std::map<Tegra::Shader::Pred, Id> predicates; |
| 2512 | std::map<u32, Id> flow_variables; | 2531 | std::map<u32, Id> flow_variables; |
| 2513 | Id local_memory{}; | 2532 | Id local_memory{}; |
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index a2f0044ba..cca13bcde 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h | |||
| @@ -65,8 +65,8 @@ public: | |||
| 65 | void DetachSegment(ASTNode start, ASTNode end); | 65 | void DetachSegment(ASTNode start, ASTNode end); |
| 66 | void Remove(ASTNode node); | 66 | void Remove(ASTNode node); |
| 67 | 67 | ||
| 68 | ASTNode first{}; | 68 | ASTNode first; |
| 69 | ASTNode last{}; | 69 | ASTNode last; |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | class ASTProgram { | 72 | class ASTProgram { |
| @@ -299,9 +299,9 @@ private: | |||
| 299 | friend class ASTZipper; | 299 | friend class ASTZipper; |
| 300 | 300 | ||
| 301 | ASTData data; | 301 | ASTData data; |
| 302 | ASTNode parent{}; | 302 | ASTNode parent; |
| 303 | ASTNode next{}; | 303 | ASTNode next; |
| 304 | ASTNode previous{}; | 304 | ASTNode previous; |
| 305 | ASTZipper* manager{}; | 305 | ASTZipper* manager{}; |
| 306 | }; | 306 | }; |
| 307 | 307 | ||
diff --git a/src/video_core/shader/const_buffer_locker.cpp b/src/video_core/shader/const_buffer_locker.cpp index a4a0319eb..0638be8cb 100644 --- a/src/video_core/shader/const_buffer_locker.cpp +++ b/src/video_core/shader/const_buffer_locker.cpp | |||
| @@ -66,6 +66,18 @@ std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindle | |||
| 66 | return value; | 66 | return value; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | std::optional<u32> ConstBufferLocker::ObtainBoundBuffer() { | ||
| 70 | if (bound_buffer_saved) { | ||
| 71 | return bound_buffer; | ||
| 72 | } | ||
| 73 | if (!engine) { | ||
| 74 | return std::nullopt; | ||
| 75 | } | ||
| 76 | bound_buffer_saved = true; | ||
| 77 | bound_buffer = engine->GetBoundBuffer(); | ||
| 78 | return bound_buffer; | ||
| 79 | } | ||
| 80 | |||
| 69 | void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) { | 81 | void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) { |
| 70 | keys.insert_or_assign({buffer, offset}, value); | 82 | keys.insert_or_assign({buffer, offset}, value); |
| 71 | } | 83 | } |
| @@ -78,6 +90,11 @@ void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDes | |||
| 78 | bindless_samplers.insert_or_assign({buffer, offset}, sampler); | 90 | bindless_samplers.insert_or_assign({buffer, offset}, sampler); |
| 79 | } | 91 | } |
| 80 | 92 | ||
| 93 | void ConstBufferLocker::SetBoundBuffer(u32 buffer) { | ||
| 94 | bound_buffer_saved = true; | ||
| 95 | bound_buffer = buffer; | ||
| 96 | } | ||
| 97 | |||
| 81 | bool ConstBufferLocker::IsConsistent() const { | 98 | bool ConstBufferLocker::IsConsistent() const { |
| 82 | if (!engine) { | 99 | if (!engine) { |
| 83 | return false; | 100 | return false; |
diff --git a/src/video_core/shader/const_buffer_locker.h b/src/video_core/shader/const_buffer_locker.h index d32e2d657..d3ea11087 100644 --- a/src/video_core/shader/const_buffer_locker.h +++ b/src/video_core/shader/const_buffer_locker.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/hash.h" | 10 | #include "common/hash.h" |
| 11 | #include "video_core/engines/const_buffer_engine_interface.h" | 11 | #include "video_core/engines/const_buffer_engine_interface.h" |
| 12 | #include "video_core/engines/shader_type.h" | 12 | #include "video_core/engines/shader_type.h" |
| 13 | #include "video_core/guest_driver.h" | ||
| 13 | 14 | ||
| 14 | namespace VideoCommon::Shader { | 15 | namespace VideoCommon::Shader { |
| 15 | 16 | ||
| @@ -40,6 +41,8 @@ public: | |||
| 40 | 41 | ||
| 41 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); | 42 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); |
| 42 | 43 | ||
| 44 | std::optional<u32> ObtainBoundBuffer(); | ||
| 45 | |||
| 43 | /// Inserts a key. | 46 | /// Inserts a key. |
| 44 | void InsertKey(u32 buffer, u32 offset, u32 value); | 47 | void InsertKey(u32 buffer, u32 offset, u32 value); |
| 45 | 48 | ||
| @@ -49,6 +52,9 @@ public: | |||
| 49 | /// Inserts a bindless sampler key. | 52 | /// Inserts a bindless sampler key. |
| 50 | void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); | 53 | void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); |
| 51 | 54 | ||
| 55 | /// Set the bound buffer for this locker. | ||
| 56 | void SetBoundBuffer(u32 buffer); | ||
| 57 | |||
| 52 | /// Checks keys and samplers against engine's current const buffers. Returns true if they are | 58 | /// Checks keys and samplers against engine's current const buffers. Returns true if they are |
| 53 | /// the same value, false otherwise; | 59 | /// the same value, false otherwise; |
| 54 | bool IsConsistent() const; | 60 | bool IsConsistent() const; |
| @@ -71,12 +77,27 @@ public: | |||
| 71 | return bindless_samplers; | 77 | return bindless_samplers; |
| 72 | } | 78 | } |
| 73 | 79 | ||
| 80 | /// Gets bound buffer used on this shader | ||
| 81 | u32 GetBoundBuffer() const { | ||
| 82 | return bound_buffer; | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Obtains access to the guest driver's profile. | ||
| 86 | VideoCore::GuestDriverProfile* AccessGuestDriverProfile() const { | ||
| 87 | if (engine) { | ||
| 88 | return &engine->AccessGuestDriverProfile(); | ||
| 89 | } | ||
| 90 | return nullptr; | ||
| 91 | } | ||
| 92 | |||
| 74 | private: | 93 | private: |
| 75 | const Tegra::Engines::ShaderType stage; | 94 | const Tegra::Engines::ShaderType stage; |
| 76 | Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; | 95 | Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; |
| 77 | KeyMap keys; | 96 | KeyMap keys; |
| 78 | BoundSamplerMap bound_samplers; | 97 | BoundSamplerMap bound_samplers; |
| 79 | BindlessSamplerMap bindless_samplers; | 98 | BindlessSamplerMap bindless_samplers; |
| 99 | bool bound_buffer_saved{}; | ||
| 100 | u32 bound_buffer{}; | ||
| 80 | }; | 101 | }; |
| 81 | 102 | ||
| 82 | } // namespace VideoCommon::Shader | 103 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index 22c3e5120..6b697ed5d 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <limits> | ||
| 6 | #include <set> | 7 | #include <set> |
| 7 | 8 | ||
| 8 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| @@ -33,6 +34,52 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) { | |||
| 33 | return (absolute_offset % SchedPeriod) == 0; | 34 | return (absolute_offset % SchedPeriod) == 0; |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 37 | void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver, | ||
| 38 | const std::list<Sampler>& used_samplers) { | ||
| 39 | if (gpu_driver == nullptr) { | ||
| 40 | LOG_CRITICAL(HW_GPU, "GPU driver profile has not been created yet"); | ||
| 41 | return; | ||
| 42 | } | ||
| 43 | if (gpu_driver->TextureHandlerSizeKnown() || used_samplers.size() <= 1) { | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | u32 count{}; | ||
| 47 | std::vector<u32> bound_offsets; | ||
| 48 | for (const auto& sampler : used_samplers) { | ||
| 49 | if (sampler.IsBindless()) { | ||
| 50 | continue; | ||
| 51 | } | ||
| 52 | ++count; | ||
| 53 | bound_offsets.emplace_back(sampler.GetOffset()); | ||
| 54 | } | ||
| 55 | if (count > 1) { | ||
| 56 | gpu_driver->DeduceTextureHandlerSize(std::move(bound_offsets)); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce, | ||
| 61 | VideoCore::GuestDriverProfile* gpu_driver, | ||
| 62 | const std::list<Sampler>& used_samplers) { | ||
| 63 | if (gpu_driver == nullptr) { | ||
| 64 | LOG_CRITICAL(HW_GPU, "GPU Driver profile has not been created yet"); | ||
| 65 | return std::nullopt; | ||
| 66 | } | ||
| 67 | const u32 base_offset = sampler_to_deduce.GetOffset(); | ||
| 68 | u32 max_offset{std::numeric_limits<u32>::max()}; | ||
| 69 | for (const auto& sampler : used_samplers) { | ||
| 70 | if (sampler.IsBindless()) { | ||
| 71 | continue; | ||
| 72 | } | ||
| 73 | if (sampler.GetOffset() > base_offset) { | ||
| 74 | max_offset = std::min(sampler.GetOffset(), max_offset); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | if (max_offset == std::numeric_limits<u32>::max()) { | ||
| 78 | return std::nullopt; | ||
| 79 | } | ||
| 80 | return ((max_offset - base_offset) * 4) / gpu_driver->GetTextureHandlerSize(); | ||
| 81 | } | ||
| 82 | |||
| 36 | } // Anonymous namespace | 83 | } // Anonymous namespace |
| 37 | 84 | ||
| 38 | class ASTDecoder { | 85 | class ASTDecoder { |
| @@ -315,4 +362,25 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) { | |||
| 315 | return pc + 1; | 362 | return pc + 1; |
| 316 | } | 363 | } |
| 317 | 364 | ||
| 365 | void ShaderIR::PostDecode() { | ||
| 366 | // Deduce texture handler size if needed | ||
| 367 | auto gpu_driver = locker.AccessGuestDriverProfile(); | ||
| 368 | DeduceTextureHandlerSize(gpu_driver, used_samplers); | ||
| 369 | // Deduce Indexed Samplers | ||
| 370 | if (!uses_indexed_samplers) { | ||
| 371 | return; | ||
| 372 | } | ||
| 373 | for (auto& sampler : used_samplers) { | ||
| 374 | if (!sampler.IsIndexed()) { | ||
| 375 | continue; | ||
| 376 | } | ||
| 377 | if (const auto size = TryDeduceSamplerSize(sampler, gpu_driver, used_samplers)) { | ||
| 378 | sampler.SetSize(*size); | ||
| 379 | } else { | ||
| 380 | LOG_CRITICAL(HW_GPU, "Failed to deduce size of indexed sampler"); | ||
| 381 | sampler.SetSize(1); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 318 | } // namespace VideoCommon::Shader | 386 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp index fcedd2af6..90240c765 100644 --- a/src/video_core/shader/decode/arithmetic.cpp +++ b/src/video_core/shader/decode/arithmetic.cpp | |||
| @@ -21,7 +21,7 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { | |||
| 21 | 21 | ||
| 22 | Node op_a = GetRegister(instr.gpr8); | 22 | Node op_a = GetRegister(instr.gpr8); |
| 23 | 23 | ||
| 24 | Node op_b = [&]() -> Node { | 24 | Node op_b = [&] { |
| 25 | if (instr.is_b_imm) { | 25 | if (instr.is_b_imm) { |
| 26 | return GetImmediate19(instr); | 26 | return GetImmediate19(instr); |
| 27 | } else if (instr.is_b_gpr) { | 27 | } else if (instr.is_b_gpr) { |
| @@ -141,6 +141,15 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { | |||
| 141 | SetRegister(bb, instr.gpr0, value); | 141 | SetRegister(bb, instr.gpr0, value); |
| 142 | break; | 142 | break; |
| 143 | } | 143 | } |
| 144 | case OpCode::Id::FCMP_R: { | ||
| 145 | UNIMPLEMENTED_IF(instr.fcmp.ftz == 0); | ||
| 146 | Node op_c = GetRegister(instr.gpr39); | ||
| 147 | Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f)); | ||
| 148 | SetRegister( | ||
| 149 | bb, instr.gpr0, | ||
| 150 | Operation(OperationCode::Select, std::move(comp), std::move(op_a), std::move(op_b))); | ||
| 151 | break; | ||
| 152 | } | ||
| 144 | case OpCode::Id::RRO_C: | 153 | case OpCode::Id::RRO_C: |
| 145 | case OpCode::Id::RRO_R: | 154 | case OpCode::Id::RRO_R: |
| 146 | case OpCode::Id::RRO_IMM: { | 155 | case OpCode::Id::RRO_IMM: { |
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp index 371fae127..e60875cc4 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp | |||
| @@ -297,7 +297,7 @@ void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Nod | |||
| 297 | const Node one = Immediate(1); | 297 | const Node one = Immediate(1); |
| 298 | const Node two = Immediate(2); | 298 | const Node two = Immediate(2); |
| 299 | 299 | ||
| 300 | Node value{}; | 300 | Node value; |
| 301 | for (u32 i = 0; i < lop_iterations; ++i) { | 301 | for (u32 i = 0; i < lop_iterations; ++i) { |
| 302 | const Node shift_amount = Immediate(i); | 302 | const Node shift_amount = Immediate(i); |
| 303 | 303 | ||
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index 7591a715f..b5fbc4d58 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp | |||
| @@ -19,9 +19,12 @@ namespace VideoCommon::Shader { | |||
| 19 | using Tegra::Shader::AtomicOp; | 19 | using Tegra::Shader::AtomicOp; |
| 20 | using Tegra::Shader::AtomicType; | 20 | using Tegra::Shader::AtomicType; |
| 21 | using Tegra::Shader::Attribute; | 21 | using Tegra::Shader::Attribute; |
| 22 | using Tegra::Shader::GlobalAtomicOp; | ||
| 23 | using Tegra::Shader::GlobalAtomicType; | ||
| 22 | using Tegra::Shader::Instruction; | 24 | using Tegra::Shader::Instruction; |
| 23 | using Tegra::Shader::OpCode; | 25 | using Tegra::Shader::OpCode; |
| 24 | using Tegra::Shader::Register; | 26 | using Tegra::Shader::Register; |
| 27 | using Tegra::Shader::StoreType; | ||
| 25 | 28 | ||
| 26 | namespace { | 29 | namespace { |
| 27 | 30 | ||
| @@ -61,6 +64,27 @@ u32 GetMemorySize(Tegra::Shader::UniformType uniform_type) { | |||
| 61 | } | 64 | } |
| 62 | } | 65 | } |
| 63 | 66 | ||
| 67 | Node ExtractUnaligned(Node value, Node address, u32 mask, u32 size) { | ||
| 68 | Node offset = Operation(OperationCode::UBitwiseAnd, address, Immediate(mask)); | ||
| 69 | offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3)); | ||
| 70 | return Operation(OperationCode::UBitfieldExtract, std::move(value), std::move(offset), | ||
| 71 | Immediate(size)); | ||
| 72 | } | ||
| 73 | |||
| 74 | Node InsertUnaligned(Node dest, Node value, Node address, u32 mask, u32 size) { | ||
| 75 | Node offset = Operation(OperationCode::UBitwiseAnd, std::move(address), Immediate(mask)); | ||
| 76 | offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3)); | ||
| 77 | return Operation(OperationCode::UBitfieldInsert, std::move(dest), std::move(value), | ||
| 78 | std::move(offset), Immediate(size)); | ||
| 79 | } | ||
| 80 | |||
| 81 | Node Sign16Extend(Node value) { | ||
| 82 | Node sign = Operation(OperationCode::UBitwiseAnd, value, Immediate(1U << 15)); | ||
| 83 | Node is_sign = Operation(OperationCode::LogicalUEqual, std::move(sign), Immediate(1U << 15)); | ||
| 84 | Node extend = Operation(OperationCode::Select, is_sign, Immediate(0xFFFF0000), Immediate(0)); | ||
| 85 | return Operation(OperationCode::UBitwiseOr, std::move(value), std::move(extend)); | ||
| 86 | } | ||
| 87 | |||
| 64 | } // Anonymous namespace | 88 | } // Anonymous namespace |
| 65 | 89 | ||
| 66 | u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | 90 | u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { |
| @@ -136,26 +160,31 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 136 | LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown)); | 160 | LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown)); |
| 137 | [[fallthrough]]; | 161 | [[fallthrough]]; |
| 138 | case OpCode::Id::LD_S: { | 162 | case OpCode::Id::LD_S: { |
| 139 | const auto GetMemory = [&](s32 offset) { | 163 | const auto GetAddress = [&](s32 offset) { |
| 140 | ASSERT(offset % 4 == 0); | 164 | ASSERT(offset % 4 == 0); |
| 141 | const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset); | 165 | const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset); |
| 142 | const Node address = Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), | 166 | return Operation(OperationCode::IAdd, GetRegister(instr.gpr8), immediate_offset); |
| 143 | immediate_offset); | 167 | }; |
| 144 | return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(address) | 168 | const auto GetMemory = [&](s32 offset) { |
| 145 | : GetLocalMemory(address); | 169 | return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(GetAddress(offset)) |
| 170 | : GetLocalMemory(GetAddress(offset)); | ||
| 146 | }; | 171 | }; |
| 147 | 172 | ||
| 148 | switch (instr.ldst_sl.type.Value()) { | 173 | switch (instr.ldst_sl.type.Value()) { |
| 149 | case Tegra::Shader::StoreType::Bits32: | 174 | case StoreType::Signed16: |
| 150 | case Tegra::Shader::StoreType::Bits64: | 175 | SetRegister(bb, instr.gpr0, |
| 151 | case Tegra::Shader::StoreType::Bits128: { | 176 | Sign16Extend(ExtractUnaligned(GetMemory(0), GetAddress(0), 0b10, 16))); |
| 152 | const u32 count = [&]() { | 177 | break; |
| 178 | case StoreType::Bits32: | ||
| 179 | case StoreType::Bits64: | ||
| 180 | case StoreType::Bits128: { | ||
| 181 | const u32 count = [&] { | ||
| 153 | switch (instr.ldst_sl.type.Value()) { | 182 | switch (instr.ldst_sl.type.Value()) { |
| 154 | case Tegra::Shader::StoreType::Bits32: | 183 | case StoreType::Bits32: |
| 155 | return 1; | 184 | return 1; |
| 156 | case Tegra::Shader::StoreType::Bits64: | 185 | case StoreType::Bits64: |
| 157 | return 2; | 186 | return 2; |
| 158 | case Tegra::Shader::StoreType::Bits128: | 187 | case StoreType::Bits128: |
| 159 | return 4; | 188 | return 4; |
| 160 | default: | 189 | default: |
| 161 | UNREACHABLE(); | 190 | UNREACHABLE(); |
| @@ -212,12 +241,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 212 | // To handle unaligned loads get the bytes used to dereference global memory and extract | 241 | // To handle unaligned loads get the bytes used to dereference global memory and extract |
| 213 | // those bytes from the loaded u32. | 242 | // those bytes from the loaded u32. |
| 214 | if (IsUnaligned(type)) { | 243 | if (IsUnaligned(type)) { |
| 215 | Node mask = Immediate(GetUnalignedMask(type)); | 244 | gmem = ExtractUnaligned(gmem, real_address, GetUnalignedMask(type), size); |
| 216 | Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask)); | ||
| 217 | offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3)); | ||
| 218 | |||
| 219 | gmem = Operation(OperationCode::UBitfieldExtract, std::move(gmem), | ||
| 220 | std::move(offset), Immediate(size)); | ||
| 221 | } | 245 | } |
| 222 | 246 | ||
| 223 | SetTemporary(bb, i, gmem); | 247 | SetTemporary(bb, i, gmem); |
| @@ -269,21 +293,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 269 | return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate); | 293 | return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate); |
| 270 | }; | 294 | }; |
| 271 | 295 | ||
| 272 | const auto set_memory = opcode->get().GetId() == OpCode::Id::ST_L | 296 | const bool is_local = opcode->get().GetId() == OpCode::Id::ST_L; |
| 273 | ? &ShaderIR::SetLocalMemory | 297 | const auto set_memory = is_local ? &ShaderIR::SetLocalMemory : &ShaderIR::SetSharedMemory; |
| 274 | : &ShaderIR::SetSharedMemory; | 298 | const auto get_memory = is_local ? &ShaderIR::GetLocalMemory : &ShaderIR::GetSharedMemory; |
| 275 | 299 | ||
| 276 | switch (instr.ldst_sl.type.Value()) { | 300 | switch (instr.ldst_sl.type.Value()) { |
| 277 | case Tegra::Shader::StoreType::Bits128: | 301 | case StoreType::Bits128: |
| 278 | (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3)); | 302 | (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3)); |
| 279 | (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2)); | 303 | (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2)); |
| 280 | [[fallthrough]]; | 304 | [[fallthrough]]; |
| 281 | case Tegra::Shader::StoreType::Bits64: | 305 | case StoreType::Bits64: |
| 282 | (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1)); | 306 | (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1)); |
| 283 | [[fallthrough]]; | 307 | [[fallthrough]]; |
| 284 | case Tegra::Shader::StoreType::Bits32: | 308 | case StoreType::Bits32: |
| 285 | (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); | 309 | (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); |
| 286 | break; | 310 | break; |
| 311 | case StoreType::Signed16: { | ||
| 312 | Node address = GetAddress(0); | ||
| 313 | Node memory = (this->*get_memory)(address); | ||
| 314 | (this->*set_memory)( | ||
| 315 | bb, address, InsertUnaligned(memory, GetRegister(instr.gpr0), address, 0b10, 16)); | ||
| 316 | break; | ||
| 317 | } | ||
| 287 | default: | 318 | default: |
| 288 | UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(), | 319 | UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(), |
| 289 | static_cast<u32>(instr.ldst_sl.type.Value())); | 320 | static_cast<u32>(instr.ldst_sl.type.Value())); |
| @@ -323,18 +354,32 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 323 | Node value = GetRegister(instr.gpr0.Value() + i); | 354 | Node value = GetRegister(instr.gpr0.Value() + i); |
| 324 | 355 | ||
| 325 | if (IsUnaligned(type)) { | 356 | if (IsUnaligned(type)) { |
| 326 | Node mask = Immediate(GetUnalignedMask(type)); | 357 | const u32 mask = GetUnalignedMask(type); |
| 327 | Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask)); | 358 | value = InsertUnaligned(gmem, std::move(value), real_address, mask, size); |
| 328 | offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3)); | ||
| 329 | |||
| 330 | value = Operation(OperationCode::UBitfieldInsert, gmem, std::move(value), offset, | ||
| 331 | Immediate(size)); | ||
| 332 | } | 359 | } |
| 333 | 360 | ||
| 334 | bb.push_back(Operation(OperationCode::Assign, gmem, value)); | 361 | bb.push_back(Operation(OperationCode::Assign, gmem, value)); |
| 335 | } | 362 | } |
| 336 | break; | 363 | break; |
| 337 | } | 364 | } |
| 365 | case OpCode::Id::ATOM: { | ||
| 366 | UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}", | ||
| 367 | static_cast<int>(instr.atom.operation.Value())); | ||
| 368 | UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}", | ||
| 369 | static_cast<int>(instr.atom.type.Value())); | ||
| 370 | |||
| 371 | const auto [real_address, base_address, descriptor] = | ||
| 372 | TrackGlobalMemory(bb, instr, true, true); | ||
| 373 | if (!real_address || !base_address) { | ||
| 374 | // Tracking failed, skip atomic. | ||
| 375 | break; | ||
| 376 | } | ||
| 377 | |||
| 378 | Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor); | ||
| 379 | Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20)); | ||
| 380 | SetRegister(bb, instr.gpr0, std::move(value)); | ||
| 381 | break; | ||
| 382 | } | ||
| 338 | case OpCode::Id::ATOMS: { | 383 | case OpCode::Id::ATOMS: { |
| 339 | UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", | 384 | UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", |
| 340 | static_cast<int>(instr.atoms.operation.Value())); | 385 | static_cast<int>(instr.atoms.operation.Value())); |
| @@ -348,7 +393,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 348 | Node memory = GetSharedMemory(std::move(address)); | 393 | Node memory = GetSharedMemory(std::move(address)); |
| 349 | Node data = GetRegister(instr.gpr20); | 394 | Node data = GetRegister(instr.gpr20); |
| 350 | 395 | ||
| 351 | Node value = Operation(OperationCode::UAtomicAdd, std::move(memory), std::move(data)); | 396 | Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data)); |
| 352 | SetRegister(bb, instr.gpr0, std::move(value)); | 397 | SetRegister(bb, instr.gpr0, std::move(value)); |
| 353 | break; | 398 | break; |
| 354 | } | 399 | } |
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 7321698b2..4944e9d69 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp | |||
| @@ -69,13 +69,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 69 | case OpCode::Id::MOV_SYS: { | 69 | case OpCode::Id::MOV_SYS: { |
| 70 | const Node value = [this, instr] { | 70 | const Node value = [this, instr] { |
| 71 | switch (instr.sys20) { | 71 | switch (instr.sys20) { |
| 72 | case SystemVariable::LaneId: | ||
| 73 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete"); | ||
| 74 | return Immediate(0U); | ||
| 72 | case SystemVariable::InvocationId: | 75 | case SystemVariable::InvocationId: |
| 73 | return Operation(OperationCode::InvocationId); | 76 | return Operation(OperationCode::InvocationId); |
| 74 | case SystemVariable::Ydirection: | 77 | case SystemVariable::Ydirection: |
| 75 | return Operation(OperationCode::YNegate); | 78 | return Operation(OperationCode::YNegate); |
| 76 | case SystemVariable::InvocationInfo: | 79 | case SystemVariable::InvocationInfo: |
| 77 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); | 80 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); |
| 78 | return Immediate(0u); | 81 | return Immediate(0U); |
| 79 | case SystemVariable::Tid: { | 82 | case SystemVariable::Tid: { |
| 80 | Node value = Immediate(0); | 83 | Node value = Immediate(0); |
| 81 | value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9); | 84 | value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9); |
| @@ -188,7 +191,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 188 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", | 191 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", |
| 189 | static_cast<u32>(cc)); | 192 | static_cast<u32>(cc)); |
| 190 | 193 | ||
| 191 | if (disable_flow_stack) { | 194 | if (decompiled) { |
| 192 | break; | 195 | break; |
| 193 | } | 196 | } |
| 194 | 197 | ||
| @@ -200,7 +203,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 200 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; | 203 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; |
| 201 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", | 204 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", |
| 202 | static_cast<u32>(cc)); | 205 | static_cast<u32>(cc)); |
| 203 | if (disable_flow_stack) { | 206 | if (decompiled) { |
| 204 | break; | 207 | break; |
| 205 | } | 208 | } |
| 206 | 209 | ||
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index 0b567e39d..351c8c2f1 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp | |||
| @@ -144,7 +144,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 144 | Node4 values; | 144 | Node4 values; |
| 145 | for (u32 element = 0; element < values.size(); ++element) { | 145 | for (u32 element = 0; element < values.size(); ++element) { |
| 146 | auto coords_copy = coords; | 146 | auto coords_copy = coords; |
| 147 | MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, {}, {}, component, element}; | 147 | MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, |
| 148 | {}, {}, component, element, {}}; | ||
| 148 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); | 149 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); |
| 149 | } | 150 | } |
| 150 | 151 | ||
| @@ -167,9 +168,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 167 | const auto derivate_reg = instr.gpr20.Value(); | 168 | const auto derivate_reg = instr.gpr20.Value(); |
| 168 | const auto texture_type = instr.txd.texture_type.Value(); | 169 | const auto texture_type = instr.txd.texture_type.Value(); |
| 169 | const auto coord_count = GetCoordCount(texture_type); | 170 | const auto coord_count = GetCoordCount(texture_type); |
| 170 | 171 | Node index_var{}; | |
| 171 | const Sampler* sampler = | 172 | const Sampler* sampler = |
| 172 | is_bindless ? GetBindlessSampler(base_reg, {{texture_type, is_array, false}}) | 173 | is_bindless ? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false}}) |
| 173 | : GetSampler(instr.sampler, {{texture_type, is_array, false}}); | 174 | : GetSampler(instr.sampler, {{texture_type, is_array, false}}); |
| 174 | Node4 values; | 175 | Node4 values; |
| 175 | if (sampler == nullptr) { | 176 | if (sampler == nullptr) { |
| @@ -200,7 +201,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 200 | } | 201 | } |
| 201 | 202 | ||
| 202 | for (u32 element = 0; element < values.size(); ++element) { | 203 | for (u32 element = 0; element < values.size(); ++element) { |
| 203 | MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates, {}, {}, {}, element}; | 204 | MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates, |
| 205 | {}, {}, {}, element, index_var}; | ||
| 204 | values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); | 206 | values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); |
| 205 | } | 207 | } |
| 206 | 208 | ||
| @@ -215,8 +217,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 215 | // TODO: The new commits on the texture refactor, change the way samplers work. | 217 | // TODO: The new commits on the texture refactor, change the way samplers work. |
| 216 | // Sadly, not all texture instructions specify the type of texture their sampler | 218 | // Sadly, not all texture instructions specify the type of texture their sampler |
| 217 | // uses. This must be fixed at a later instance. | 219 | // uses. This must be fixed at a later instance. |
| 220 | Node index_var{}; | ||
| 218 | const Sampler* sampler = | 221 | const Sampler* sampler = |
| 219 | is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler); | 222 | is_bindless ? GetBindlessSampler(instr.gpr8, index_var) : GetSampler(instr.sampler); |
| 220 | 223 | ||
| 221 | if (sampler == nullptr) { | 224 | if (sampler == nullptr) { |
| 222 | u32 indexer = 0; | 225 | u32 indexer = 0; |
| @@ -240,7 +243,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 240 | if (!instr.txq.IsComponentEnabled(element)) { | 243 | if (!instr.txq.IsComponentEnabled(element)) { |
| 241 | continue; | 244 | continue; |
| 242 | } | 245 | } |
| 243 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; | 246 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var}; |
| 244 | const Node value = | 247 | const Node value = |
| 245 | Operation(OperationCode::TextureQueryDimensions, meta, | 248 | Operation(OperationCode::TextureQueryDimensions, meta, |
| 246 | GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); | 249 | GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); |
| @@ -266,8 +269,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 266 | 269 | ||
| 267 | auto texture_type = instr.tmml.texture_type.Value(); | 270 | auto texture_type = instr.tmml.texture_type.Value(); |
| 268 | const bool is_array = instr.tmml.array != 0; | 271 | const bool is_array = instr.tmml.array != 0; |
| 272 | Node index_var{}; | ||
| 269 | const Sampler* sampler = | 273 | const Sampler* sampler = |
| 270 | is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler); | 274 | is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler); |
| 271 | 275 | ||
| 272 | if (sampler == nullptr) { | 276 | if (sampler == nullptr) { |
| 273 | u32 indexer = 0; | 277 | u32 indexer = 0; |
| @@ -309,7 +313,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 309 | continue; | 313 | continue; |
| 310 | } | 314 | } |
| 311 | auto params = coords; | 315 | auto params = coords; |
| 312 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; | 316 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var}; |
| 313 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); | 317 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); |
| 314 | SetTemporary(bb, indexer++, value); | 318 | SetTemporary(bb, indexer++, value); |
| 315 | } | 319 | } |
| @@ -383,37 +387,65 @@ const Sampler* ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, | |||
| 383 | // Otherwise create a new mapping for this sampler | 387 | // Otherwise create a new mapping for this sampler |
| 384 | const auto next_index = static_cast<u32>(used_samplers.size()); | 388 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 385 | return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow, | 389 | return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow, |
| 386 | info.is_buffer); | 390 | info.is_buffer, false); |
| 387 | } | 391 | } |
| 388 | 392 | ||
| 389 | const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, | 393 | const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var, |
| 390 | std::optional<SamplerInfo> sampler_info) { | 394 | std::optional<SamplerInfo> sampler_info) { |
| 391 | const Node sampler_register = GetRegister(reg); | 395 | const Node sampler_register = GetRegister(reg); |
| 392 | const auto [base_sampler, buffer, offset] = | 396 | const auto [base_node, tracked_sampler_info] = |
| 393 | TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size())); | 397 | TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size())); |
| 394 | ASSERT(base_sampler != nullptr); | 398 | ASSERT(base_node != nullptr); |
| 395 | if (base_sampler == nullptr) { | 399 | if (base_node == nullptr) { |
| 396 | return nullptr; | 400 | return nullptr; |
| 397 | } | 401 | } |
| 398 | 402 | ||
| 399 | const auto info = GetSamplerInfo(sampler_info, offset, buffer); | 403 | if (const auto bindless_sampler_info = |
| 404 | std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) { | ||
| 405 | const u32 buffer = bindless_sampler_info->GetIndex(); | ||
| 406 | const u32 offset = bindless_sampler_info->GetOffset(); | ||
| 407 | const auto info = GetSamplerInfo(sampler_info, offset, buffer); | ||
| 408 | |||
| 409 | // If this sampler has already been used, return the existing mapping. | ||
| 410 | const auto it = | ||
| 411 | std::find_if(used_samplers.begin(), used_samplers.end(), | ||
| 412 | [buffer = buffer, offset = offset](const Sampler& entry) { | ||
| 413 | return entry.GetBuffer() == buffer && entry.GetOffset() == offset; | ||
| 414 | }); | ||
| 415 | if (it != used_samplers.end()) { | ||
| 416 | ASSERT(it->IsBindless() && it->GetType() == info.type && | ||
| 417 | it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow); | ||
| 418 | return &*it; | ||
| 419 | } | ||
| 400 | 420 | ||
| 401 | // If this sampler has already been used, return the existing mapping. | 421 | // Otherwise create a new mapping for this sampler |
| 402 | const auto it = | 422 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 403 | std::find_if(used_samplers.begin(), used_samplers.end(), | 423 | return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array, |
| 404 | [buffer = buffer, offset = offset](const Sampler& entry) { | 424 | info.is_shadow, info.is_buffer, false); |
| 405 | return entry.GetBuffer() == buffer && entry.GetOffset() == offset; | 425 | } else if (const auto array_sampler_info = |
| 406 | }); | 426 | std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) { |
| 407 | if (it != used_samplers.end()) { | 427 | const u32 base_offset = array_sampler_info->GetBaseOffset() / 4; |
| 408 | ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array && | 428 | index_var = GetCustomVariable(array_sampler_info->GetIndexVar()); |
| 409 | it->IsShadow() == info.is_shadow); | 429 | const auto info = GetSamplerInfo(sampler_info, base_offset); |
| 410 | return &*it; | 430 | |
| 411 | } | 431 | // If this sampler has already been used, return the existing mapping. |
| 432 | const auto it = std::find_if( | ||
| 433 | used_samplers.begin(), used_samplers.end(), | ||
| 434 | [base_offset](const Sampler& entry) { return entry.GetOffset() == base_offset; }); | ||
| 435 | if (it != used_samplers.end()) { | ||
| 436 | ASSERT(!it->IsBindless() && it->GetType() == info.type && | ||
| 437 | it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow && | ||
| 438 | it->IsBuffer() == info.is_buffer && it->IsIndexed()); | ||
| 439 | return &*it; | ||
| 440 | } | ||
| 412 | 441 | ||
| 413 | // Otherwise create a new mapping for this sampler | 442 | uses_indexed_samplers = true; |
| 414 | const auto next_index = static_cast<u32>(used_samplers.size()); | 443 | // Otherwise create a new mapping for this sampler |
| 415 | return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array, | 444 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 416 | info.is_shadow, info.is_buffer); | 445 | return &used_samplers.emplace_back(next_index, base_offset, info.type, info.is_array, |
| 446 | info.is_shadow, info.is_buffer, true); | ||
| 447 | } | ||
| 448 | return nullptr; | ||
| 417 | } | 449 | } |
| 418 | 450 | ||
| 419 | void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { | 451 | void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { |
| @@ -499,8 +531,9 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | |||
| 499 | "This method is not supported."); | 531 | "This method is not supported."); |
| 500 | 532 | ||
| 501 | const SamplerInfo info{texture_type, is_array, is_shadow, false}; | 533 | const SamplerInfo info{texture_type, is_array, is_shadow, false}; |
| 502 | const Sampler* sampler = | 534 | Node index_var{}; |
| 503 | is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info); | 535 | const Sampler* sampler = is_bindless ? GetBindlessSampler(*bindless_reg, index_var, info) |
| 536 | : GetSampler(instr.sampler, info); | ||
| 504 | Node4 values; | 537 | Node4 values; |
| 505 | if (sampler == nullptr) { | 538 | if (sampler == nullptr) { |
| 506 | for (u32 element = 0; element < values.size(); ++element) { | 539 | for (u32 element = 0; element < values.size(); ++element) { |
| @@ -548,7 +581,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | |||
| 548 | 581 | ||
| 549 | for (u32 element = 0; element < values.size(); ++element) { | 582 | for (u32 element = 0; element < values.size(); ++element) { |
| 550 | auto copy_coords = coords; | 583 | auto copy_coords = coords; |
| 551 | MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, lod, {}, element}; | 584 | MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, |
| 585 | lod, {}, element, index_var}; | ||
| 552 | values[element] = Operation(read_method, meta, std::move(copy_coords)); | 586 | values[element] = Operation(read_method, meta, std::move(copy_coords)); |
| 553 | } | 587 | } |
| 554 | 588 | ||
| @@ -596,7 +630,7 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, | |||
| 596 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); | 630 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); |
| 597 | } | 631 | } |
| 598 | 632 | ||
| 599 | Node dc{}; | 633 | Node dc; |
| 600 | if (depth_compare) { | 634 | if (depth_compare) { |
| 601 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod | 635 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod |
| 602 | // or bias are used | 636 | // or bias are used |
| @@ -632,7 +666,7 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, | |||
| 632 | 666 | ||
| 633 | const Node array = is_array ? GetRegister(array_register) : nullptr; | 667 | const Node array = is_array ? GetRegister(array_register) : nullptr; |
| 634 | 668 | ||
| 635 | Node dc{}; | 669 | Node dc; |
| 636 | if (depth_compare) { | 670 | if (depth_compare) { |
| 637 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod | 671 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod |
| 638 | // or bias are used | 672 | // or bias are used |
| @@ -663,7 +697,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 663 | u64 parameter_register = instr.gpr20.Value(); | 697 | u64 parameter_register = instr.gpr20.Value(); |
| 664 | 698 | ||
| 665 | const SamplerInfo info{texture_type, is_array, depth_compare, false}; | 699 | const SamplerInfo info{texture_type, is_array, depth_compare, false}; |
| 666 | const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, info) | 700 | Node index_var{}; |
| 701 | const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, index_var, info) | ||
| 667 | : GetSampler(instr.sampler, info); | 702 | : GetSampler(instr.sampler, info); |
| 668 | Node4 values; | 703 | Node4 values; |
| 669 | if (sampler == nullptr) { | 704 | if (sampler == nullptr) { |
| @@ -692,7 +727,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 692 | for (u32 element = 0; element < values.size(); ++element) { | 727 | for (u32 element = 0; element < values.size(); ++element) { |
| 693 | auto coords_copy = coords; | 728 | auto coords_copy = coords; |
| 694 | MetaTexture meta{ | 729 | MetaTexture meta{ |
| 695 | *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element}; | 730 | *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element, |
| 731 | index_var}; | ||
| 696 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); | 732 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); |
| 697 | } | 733 | } |
| 698 | 734 | ||
| @@ -725,7 +761,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) { | |||
| 725 | Node4 values; | 761 | Node4 values; |
| 726 | for (u32 element = 0; element < values.size(); ++element) { | 762 | for (u32 element = 0; element < values.size(); ++element) { |
| 727 | auto coords_copy = coords; | 763 | auto coords_copy = coords; |
| 728 | MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element}; | 764 | MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element, {}}; |
| 729 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 765 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 730 | } | 766 | } |
| 731 | 767 | ||
| @@ -775,7 +811,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is | |||
| 775 | Node4 values; | 811 | Node4 values; |
| 776 | for (u32 element = 0; element < values.size(); ++element) { | 812 | for (u32 element = 0; element < values.size(); ++element) { |
| 777 | auto coords_copy = coords; | 813 | auto coords_copy = coords; |
| 778 | MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element}; | 814 | MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}}; |
| 779 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 815 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 780 | } | 816 | } |
| 781 | return values; | 817 | return values; |
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 075c7d07c..a0a7b9111 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h | |||
| @@ -162,7 +162,7 @@ enum class OperationCode { | |||
| 162 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void | 162 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void |
| 163 | AtomicImageExchange, /// (MetaImage, int[N] coords) -> void | 163 | AtomicImageExchange, /// (MetaImage, int[N] coords) -> void |
| 164 | 164 | ||
| 165 | UAtomicAdd, /// (smem, uint) -> uint | 165 | AtomicAdd, /// (memory, {u}int) -> {u}int |
| 166 | 166 | ||
| 167 | Branch, /// (uint branch_target) -> void | 167 | Branch, /// (uint branch_target) -> void |
| 168 | BranchIndirect, /// (uint branch_target) -> void | 168 | BranchIndirect, /// (uint branch_target) -> void |
| @@ -212,6 +212,7 @@ enum class MetaStackClass { | |||
| 212 | class OperationNode; | 212 | class OperationNode; |
| 213 | class ConditionalNode; | 213 | class ConditionalNode; |
| 214 | class GprNode; | 214 | class GprNode; |
| 215 | class CustomVarNode; | ||
| 215 | class ImmediateNode; | 216 | class ImmediateNode; |
| 216 | class InternalFlagNode; | 217 | class InternalFlagNode; |
| 217 | class PredicateNode; | 218 | class PredicateNode; |
| @@ -223,26 +224,32 @@ class SmemNode; | |||
| 223 | class GmemNode; | 224 | class GmemNode; |
| 224 | class CommentNode; | 225 | class CommentNode; |
| 225 | 226 | ||
| 226 | using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, | 227 | using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode, |
| 227 | InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode, | 228 | InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode, |
| 228 | LmemNode, SmemNode, GmemNode, CommentNode>; | 229 | LmemNode, SmemNode, GmemNode, CommentNode>; |
| 229 | using Node = std::shared_ptr<NodeData>; | 230 | using Node = std::shared_ptr<NodeData>; |
| 230 | using Node4 = std::array<Node, 4>; | 231 | using Node4 = std::array<Node, 4>; |
| 231 | using NodeBlock = std::vector<Node>; | 232 | using NodeBlock = std::vector<Node>; |
| 232 | 233 | ||
| 234 | class BindlessSamplerNode; | ||
| 235 | class ArraySamplerNode; | ||
| 236 | |||
| 237 | using TrackSamplerData = std::variant<BindlessSamplerNode, ArraySamplerNode>; | ||
| 238 | using TrackSampler = std::shared_ptr<TrackSamplerData>; | ||
| 239 | |||
| 233 | class Sampler { | 240 | class Sampler { |
| 234 | public: | 241 | public: |
| 235 | /// This constructor is for bound samplers | 242 | /// This constructor is for bound samplers |
| 236 | constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type, | 243 | constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type, |
| 237 | bool is_array, bool is_shadow, bool is_buffer) | 244 | bool is_array, bool is_shadow, bool is_buffer, bool is_indexed) |
| 238 | : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, | 245 | : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, |
| 239 | is_buffer{is_buffer} {} | 246 | is_buffer{is_buffer}, is_indexed{is_indexed} {} |
| 240 | 247 | ||
| 241 | /// This constructor is for bindless samplers | 248 | /// This constructor is for bindless samplers |
| 242 | constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, | 249 | constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, |
| 243 | bool is_array, bool is_shadow, bool is_buffer) | 250 | bool is_array, bool is_shadow, bool is_buffer, bool is_indexed) |
| 244 | : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, | 251 | : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, |
| 245 | is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true} {} | 252 | is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {} |
| 246 | 253 | ||
| 247 | constexpr u32 GetIndex() const { | 254 | constexpr u32 GetIndex() const { |
| 248 | return index; | 255 | return index; |
| @@ -276,16 +283,72 @@ public: | |||
| 276 | return is_bindless; | 283 | return is_bindless; |
| 277 | } | 284 | } |
| 278 | 285 | ||
| 286 | constexpr bool IsIndexed() const { | ||
| 287 | return is_indexed; | ||
| 288 | } | ||
| 289 | |||
| 290 | constexpr u32 Size() const { | ||
| 291 | return size; | ||
| 292 | } | ||
| 293 | |||
| 294 | constexpr void SetSize(u32 new_size) { | ||
| 295 | size = new_size; | ||
| 296 | } | ||
| 297 | |||
| 279 | private: | 298 | private: |
| 280 | u32 index{}; ///< Emulated index given for the this sampler. | 299 | u32 index{}; ///< Emulated index given for the this sampler. |
| 281 | u32 offset{}; ///< Offset in the const buffer from where the sampler is being read. | 300 | u32 offset{}; ///< Offset in the const buffer from where the sampler is being read. |
| 282 | u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers). | 301 | u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers). |
| 302 | u32 size{}; ///< Size of the sampler if indexed. | ||
| 283 | 303 | ||
| 284 | Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) | 304 | Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) |
| 285 | bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. | 305 | bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. |
| 286 | bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. | 306 | bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. |
| 287 | bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler. | 307 | bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler. |
| 288 | bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not. | 308 | bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not. |
| 309 | bool is_indexed{}; ///< Whether this sampler is an indexed array of textures. | ||
| 310 | }; | ||
| 311 | |||
| 312 | /// Represents a tracked bindless sampler into a direct const buffer | ||
| 313 | class ArraySamplerNode final { | ||
| 314 | public: | ||
| 315 | explicit ArraySamplerNode(u32 index, u32 base_offset, u32 bindless_var) | ||
| 316 | : index{index}, base_offset{base_offset}, bindless_var{bindless_var} {} | ||
| 317 | |||
| 318 | constexpr u32 GetIndex() const { | ||
| 319 | return index; | ||
| 320 | } | ||
| 321 | |||
| 322 | constexpr u32 GetBaseOffset() const { | ||
| 323 | return base_offset; | ||
| 324 | } | ||
| 325 | |||
| 326 | constexpr u32 GetIndexVar() const { | ||
| 327 | return bindless_var; | ||
| 328 | } | ||
| 329 | |||
| 330 | private: | ||
| 331 | u32 index; | ||
| 332 | u32 base_offset; | ||
| 333 | u32 bindless_var; | ||
| 334 | }; | ||
| 335 | |||
| 336 | /// Represents a tracked bindless sampler into a direct const buffer | ||
| 337 | class BindlessSamplerNode final { | ||
| 338 | public: | ||
| 339 | explicit BindlessSamplerNode(u32 index, u32 offset) : index{index}, offset{offset} {} | ||
| 340 | |||
| 341 | constexpr u32 GetIndex() const { | ||
| 342 | return index; | ||
| 343 | } | ||
| 344 | |||
| 345 | constexpr u32 GetOffset() const { | ||
| 346 | return offset; | ||
| 347 | } | ||
| 348 | |||
| 349 | private: | ||
| 350 | u32 index; | ||
| 351 | u32 offset; | ||
| 289 | }; | 352 | }; |
| 290 | 353 | ||
| 291 | class Image final { | 354 | class Image final { |
| @@ -380,8 +443,9 @@ struct MetaTexture { | |||
| 380 | std::vector<Node> derivates; | 443 | std::vector<Node> derivates; |
| 381 | Node bias; | 444 | Node bias; |
| 382 | Node lod; | 445 | Node lod; |
| 383 | Node component{}; | 446 | Node component; |
| 384 | u32 element{}; | 447 | u32 element{}; |
| 448 | Node index; | ||
| 385 | }; | 449 | }; |
| 386 | 450 | ||
| 387 | struct MetaImage { | 451 | struct MetaImage { |
| @@ -488,6 +552,19 @@ private: | |||
| 488 | Tegra::Shader::Register index{}; | 552 | Tegra::Shader::Register index{}; |
| 489 | }; | 553 | }; |
| 490 | 554 | ||
| 555 | /// A custom variable | ||
| 556 | class CustomVarNode final { | ||
| 557 | public: | ||
| 558 | explicit constexpr CustomVarNode(u32 index) : index{index} {} | ||
| 559 | |||
| 560 | constexpr u32 GetIndex() const { | ||
| 561 | return index; | ||
| 562 | } | ||
| 563 | |||
| 564 | private: | ||
| 565 | u32 index{}; | ||
| 566 | }; | ||
| 567 | |||
| 491 | /// A 32-bits value that represents an immediate value | 568 | /// A 32-bits value that represents an immediate value |
| 492 | class ImmediateNode final { | 569 | class ImmediateNode final { |
| 493 | public: | 570 | public: |
diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h index 0c2aa749b..11231bbea 100644 --- a/src/video_core/shader/node_helper.h +++ b/src/video_core/shader/node_helper.h | |||
| @@ -45,6 +45,12 @@ Node MakeNode(Args&&... args) { | |||
| 45 | return std::make_shared<NodeData>(T(std::forward<Args>(args)...)); | 45 | return std::make_shared<NodeData>(T(std::forward<Args>(args)...)); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | template <typename T, typename... Args> | ||
| 49 | TrackSampler MakeTrackSampler(Args&&... args) { | ||
| 50 | static_assert(std::is_convertible_v<T, TrackSamplerData>); | ||
| 51 | return std::make_shared<TrackSamplerData>(T(std::forward<Args>(args)...)); | ||
| 52 | } | ||
| 53 | |||
| 48 | template <typename... Args> | 54 | template <typename... Args> |
| 49 | Node Operation(OperationCode code, Args&&... args) { | 55 | Node Operation(OperationCode code, Args&&... args) { |
| 50 | if constexpr (sizeof...(args) == 0) { | 56 | if constexpr (sizeof...(args) == 0) { |
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 31eecb3f4..3a5d280a9 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -27,6 +27,7 @@ ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSet | |||
| 27 | ConstBufferLocker& locker) | 27 | ConstBufferLocker& locker) |
| 28 | : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} { | 28 | : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} { |
| 29 | Decode(); | 29 | Decode(); |
| 30 | PostDecode(); | ||
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | ShaderIR::~ShaderIR() = default; | 33 | ShaderIR::~ShaderIR() = default; |
| @@ -38,6 +39,10 @@ Node ShaderIR::GetRegister(Register reg) { | |||
| 38 | return MakeNode<GprNode>(reg); | 39 | return MakeNode<GprNode>(reg); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 42 | Node ShaderIR::GetCustomVariable(u32 id) { | ||
| 43 | return MakeNode<CustomVarNode>(id); | ||
| 44 | } | ||
| 45 | |||
| 41 | Node ShaderIR::GetImmediate19(Instruction instr) { | 46 | Node ShaderIR::GetImmediate19(Instruction instr) { |
| 42 | return Immediate(instr.alu.GetImm20_19()); | 47 | return Immediate(instr.alu.GetImm20_19()); |
| 43 | } | 48 | } |
| @@ -452,4 +457,8 @@ std::size_t ShaderIR::DeclareAmend(Node new_amend) { | |||
| 452 | return id; | 457 | return id; |
| 453 | } | 458 | } |
| 454 | 459 | ||
| 460 | u32 ShaderIR::NewCustomVariable() { | ||
| 461 | return num_custom_variables++; | ||
| 462 | } | ||
| 463 | |||
| 455 | } // namespace VideoCommon::Shader | 464 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index ba1db4c11..b0851c3be 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -180,6 +180,10 @@ public: | |||
| 180 | return amend_code[index]; | 180 | return amend_code[index]; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | u32 GetNumCustomVariables() const { | ||
| 184 | return num_custom_variables; | ||
| 185 | } | ||
| 186 | |||
| 183 | private: | 187 | private: |
| 184 | friend class ASTDecoder; | 188 | friend class ASTDecoder; |
| 185 | 189 | ||
| @@ -191,6 +195,7 @@ private: | |||
| 191 | }; | 195 | }; |
| 192 | 196 | ||
| 193 | void Decode(); | 197 | void Decode(); |
| 198 | void PostDecode(); | ||
| 194 | 199 | ||
| 195 | NodeBlock DecodeRange(u32 begin, u32 end); | 200 | NodeBlock DecodeRange(u32 begin, u32 end); |
| 196 | void DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end); | 201 | void DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end); |
| @@ -235,6 +240,8 @@ private: | |||
| 235 | 240 | ||
| 236 | /// Generates a node for a passed register. | 241 | /// Generates a node for a passed register. |
| 237 | Node GetRegister(Tegra::Shader::Register reg); | 242 | Node GetRegister(Tegra::Shader::Register reg); |
| 243 | /// Generates a node for a custom variable | ||
| 244 | Node GetCustomVariable(u32 id); | ||
| 238 | /// Generates a node representing a 19-bit immediate value | 245 | /// Generates a node representing a 19-bit immediate value |
| 239 | Node GetImmediate19(Tegra::Shader::Instruction instr); | 246 | Node GetImmediate19(Tegra::Shader::Instruction instr); |
| 240 | /// Generates a node representing a 32-bit immediate value | 247 | /// Generates a node representing a 32-bit immediate value |
| @@ -321,7 +328,7 @@ private: | |||
| 321 | std::optional<SamplerInfo> sampler_info = std::nullopt); | 328 | std::optional<SamplerInfo> sampler_info = std::nullopt); |
| 322 | 329 | ||
| 323 | /// Accesses a texture sampler for a bindless texture. | 330 | /// Accesses a texture sampler for a bindless texture. |
| 324 | const Sampler* GetBindlessSampler(Tegra::Shader::Register reg, | 331 | const Sampler* GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var, |
| 325 | std::optional<SamplerInfo> sampler_info = std::nullopt); | 332 | std::optional<SamplerInfo> sampler_info = std::nullopt); |
| 326 | 333 | ||
| 327 | /// Accesses an image. | 334 | /// Accesses an image. |
| @@ -387,6 +394,9 @@ private: | |||
| 387 | 394 | ||
| 388 | std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; | 395 | std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 389 | 396 | ||
| 397 | std::tuple<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code, | ||
| 398 | s64 cursor); | ||
| 399 | |||
| 390 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; | 400 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 391 | 401 | ||
| 392 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, | 402 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, |
| @@ -399,6 +409,8 @@ private: | |||
| 399 | /// Register new amending code and obtain the reference id. | 409 | /// Register new amending code and obtain the reference id. |
| 400 | std::size_t DeclareAmend(Node new_amend); | 410 | std::size_t DeclareAmend(Node new_amend); |
| 401 | 411 | ||
| 412 | u32 NewCustomVariable(); | ||
| 413 | |||
| 402 | const ProgramCode& program_code; | 414 | const ProgramCode& program_code; |
| 403 | const u32 main_offset; | 415 | const u32 main_offset; |
| 404 | const CompilerSettings settings; | 416 | const CompilerSettings settings; |
| @@ -414,6 +426,7 @@ private: | |||
| 414 | NodeBlock global_code; | 426 | NodeBlock global_code; |
| 415 | ASTManager program_manager{true, true}; | 427 | ASTManager program_manager{true, true}; |
| 416 | std::vector<Node> amend_code; | 428 | std::vector<Node> amend_code; |
| 429 | u32 num_custom_variables{}; | ||
| 417 | 430 | ||
| 418 | std::set<u32> used_registers; | 431 | std::set<u32> used_registers; |
| 419 | std::set<Tegra::Shader::Pred> used_predicates; | 432 | std::set<Tegra::Shader::Pred> used_predicates; |
| @@ -431,6 +444,7 @@ private: | |||
| 431 | bool uses_instance_id{}; | 444 | bool uses_instance_id{}; |
| 432 | bool uses_vertex_id{}; | 445 | bool uses_vertex_id{}; |
| 433 | bool uses_warps{}; | 446 | bool uses_warps{}; |
| 447 | bool uses_indexed_samplers{}; | ||
| 434 | 448 | ||
| 435 | Tegra::Shader::Header header; | 449 | Tegra::Shader::Header header; |
| 436 | }; | 450 | }; |
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp index 165c79330..face8c943 100644 --- a/src/video_core/shader/track.cpp +++ b/src/video_core/shader/track.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/shader/node.h" | 10 | #include "video_core/shader/node.h" |
| 11 | #include "video_core/shader/node_helper.h" | ||
| 11 | #include "video_core/shader/shader_ir.h" | 12 | #include "video_core/shader/shader_ir.h" |
| 12 | 13 | ||
| 13 | namespace VideoCommon::Shader { | 14 | namespace VideoCommon::Shader { |
| @@ -35,8 +36,113 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, | |||
| 35 | } | 36 | } |
| 36 | return {}; | 37 | return {}; |
| 37 | } | 38 | } |
| 39 | |||
| 40 | std::optional<std::pair<Node, Node>> DecoupleIndirectRead(const OperationNode& operation) { | ||
| 41 | if (operation.GetCode() != OperationCode::UAdd) { | ||
| 42 | return std::nullopt; | ||
| 43 | } | ||
| 44 | Node gpr; | ||
| 45 | Node offset; | ||
| 46 | ASSERT(operation.GetOperandsCount() == 2); | ||
| 47 | for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) { | ||
| 48 | Node operand = operation[i]; | ||
| 49 | if (std::holds_alternative<ImmediateNode>(*operand)) { | ||
| 50 | offset = operation[i]; | ||
| 51 | } else if (std::holds_alternative<GprNode>(*operand)) { | ||
| 52 | gpr = operation[i]; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | if (offset && gpr) { | ||
| 56 | return std::make_pair(gpr, offset); | ||
| 57 | } | ||
| 58 | return std::nullopt; | ||
| 59 | } | ||
| 60 | |||
| 61 | bool AmendNodeCv(std::size_t amend_index, Node node) { | ||
| 62 | if (const auto operation = std::get_if<OperationNode>(&*node)) { | ||
| 63 | operation->SetAmendIndex(amend_index); | ||
| 64 | return true; | ||
| 65 | } else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { | ||
| 66 | conditional->SetAmendIndex(amend_index); | ||
| 67 | return true; | ||
| 68 | } | ||
| 69 | return false; | ||
| 70 | } | ||
| 71 | |||
| 38 | } // Anonymous namespace | 72 | } // Anonymous namespace |
| 39 | 73 | ||
| 74 | std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code, | ||
| 75 | s64 cursor) { | ||
| 76 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | ||
| 77 | // Constant buffer found, test if it's an immediate | ||
| 78 | const auto offset = cbuf->GetOffset(); | ||
| 79 | if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { | ||
| 80 | auto track = | ||
| 81 | MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue()); | ||
| 82 | return {tracked, track}; | ||
| 83 | } else if (const auto operation = std::get_if<OperationNode>(&*offset)) { | ||
| 84 | auto bound_buffer = locker.ObtainBoundBuffer(); | ||
| 85 | if (!bound_buffer) { | ||
| 86 | return {}; | ||
| 87 | } | ||
| 88 | if (*bound_buffer != cbuf->GetIndex()) { | ||
| 89 | return {}; | ||
| 90 | } | ||
| 91 | auto pair = DecoupleIndirectRead(*operation); | ||
| 92 | if (!pair) { | ||
| 93 | return {}; | ||
| 94 | } | ||
| 95 | auto [gpr, base_offset] = *pair; | ||
| 96 | const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset); | ||
| 97 | auto gpu_driver = locker.AccessGuestDriverProfile(); | ||
| 98 | if (gpu_driver == nullptr) { | ||
| 99 | return {}; | ||
| 100 | } | ||
| 101 | const u32 bindless_cv = NewCustomVariable(); | ||
| 102 | const Node op = Operation(OperationCode::UDiv, NO_PRECISE, gpr, | ||
| 103 | Immediate(gpu_driver->GetTextureHandlerSize())); | ||
| 104 | |||
| 105 | const Node cv_node = GetCustomVariable(bindless_cv); | ||
| 106 | Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op)); | ||
| 107 | const std::size_t amend_index = DeclareAmend(amend_op); | ||
| 108 | AmendNodeCv(amend_index, code[cursor]); | ||
| 109 | // TODO Implement Bindless Index custom variable | ||
| 110 | auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(), | ||
| 111 | offset_inm->GetValue(), bindless_cv); | ||
| 112 | return {tracked, track}; | ||
| 113 | } | ||
| 114 | return {}; | ||
| 115 | } | ||
| 116 | if (const auto gpr = std::get_if<GprNode>(&*tracked)) { | ||
| 117 | if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) { | ||
| 118 | return {}; | ||
| 119 | } | ||
| 120 | // Reduce the cursor in one to avoid infinite loops when the instruction sets the same | ||
| 121 | // register that it uses as operand | ||
| 122 | const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1); | ||
| 123 | if (!source) { | ||
| 124 | return {}; | ||
| 125 | } | ||
| 126 | return TrackBindlessSampler(source, code, new_cursor); | ||
| 127 | } | ||
| 128 | if (const auto operation = std::get_if<OperationNode>(&*tracked)) { | ||
| 129 | for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) { | ||
| 130 | if (auto found = TrackBindlessSampler((*operation)[i - 1], code, cursor); | ||
| 131 | std::get<0>(found)) { | ||
| 132 | // Cbuf found in operand. | ||
| 133 | return found; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | return {}; | ||
| 137 | } | ||
| 138 | if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) { | ||
| 139 | const auto& conditional_code = conditional->GetCode(); | ||
| 140 | return TrackBindlessSampler(tracked, conditional_code, | ||
| 141 | static_cast<s64>(conditional_code.size())); | ||
| 142 | } | ||
| 143 | return {}; | ||
| 144 | } | ||
| 145 | |||
| 40 | std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, | 146 | std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, |
| 41 | s64 cursor) const { | 147 | s64 cursor) const { |
| 42 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | 148 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { |
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 829268b4c..84469b7ba 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp | |||
| @@ -135,7 +135,7 @@ std::vector<CopyParams> SurfaceBaseImpl::BreakDownLayered(const SurfaceParams& i | |||
| 135 | for (u32 level = 0; level < mipmaps; level++) { | 135 | for (u32 level = 0; level < mipmaps; level++) { |
| 136 | const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level); | 136 | const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level); |
| 137 | const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level); | 137 | const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level); |
| 138 | result.emplace_back(width, height, layer, level); | 138 | result.emplace_back(0, 0, layer, 0, 0, layer, level, level, width, height, 1); |
| 139 | } | 139 | } |
| 140 | } | 140 | } |
| 141 | return result; | 141 | return result; |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 8e947394c..a5f81a8a0 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -3,19 +3,32 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include "common/logging/log.h" | ||
| 6 | #include "core/core.h" | 7 | #include "core/core.h" |
| 7 | #include "core/settings.h" | 8 | #include "core/settings.h" |
| 8 | #include "video_core/gpu_asynch.h" | 9 | #include "video_core/gpu_asynch.h" |
| 9 | #include "video_core/gpu_synch.h" | 10 | #include "video_core/gpu_synch.h" |
| 10 | #include "video_core/renderer_base.h" | 11 | #include "video_core/renderer_base.h" |
| 11 | #include "video_core/renderer_opengl/renderer_opengl.h" | 12 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 13 | #ifdef HAS_VULKAN | ||
| 14 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||
| 15 | #endif | ||
| 12 | #include "video_core/video_core.h" | 16 | #include "video_core/video_core.h" |
| 13 | 17 | ||
| 14 | namespace VideoCore { | 18 | namespace VideoCore { |
| 15 | 19 | ||
| 16 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | 20 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, |
| 17 | Core::System& system) { | 21 | Core::System& system) { |
| 18 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); | 22 | switch (Settings::values.renderer_backend) { |
| 23 | case Settings::RendererBackend::OpenGL: | ||
| 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); | ||
| 25 | #ifdef HAS_VULKAN | ||
| 26 | case Settings::RendererBackend::Vulkan: | ||
| 27 | return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); | ||
| 28 | #endif | ||
| 29 | default: | ||
| 30 | return nullptr; | ||
| 31 | } | ||
| 19 | } | 32 | } |
| 20 | 33 | ||
| 21 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { | 34 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { |
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp index 9156ce802..7538389bf 100644 --- a/src/web_service/telemetry_json.cpp +++ b/src/web_service/telemetry_json.cpp | |||
| @@ -117,6 +117,7 @@ bool TelemetryJson::SubmitTestcase() { | |||
| 117 | impl->SerializeSection(Telemetry::FieldType::Session, "Session"); | 117 | impl->SerializeSection(Telemetry::FieldType::Session, "Session"); |
| 118 | impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); | 118 | impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); |
| 119 | impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); | 119 | impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); |
| 120 | impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); | ||
| 120 | 121 | ||
| 121 | auto content = impl->TopSection().dump(); | 122 | auto content = impl->TopSection().dump(); |
| 122 | Client client(impl->host, impl->username, impl->token); | 123 | Client client(impl->host, impl->username, impl->token); |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index a3fb91d29..b841e63fa 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -200,3 +200,8 @@ if (MSVC) | |||
| 200 | copy_yuzu_SDL_deps(yuzu) | 200 | copy_yuzu_SDL_deps(yuzu) |
| 201 | copy_yuzu_unicorn_deps(yuzu) | 201 | copy_yuzu_unicorn_deps(yuzu) |
| 202 | endif() | 202 | endif() |
| 203 | |||
| 204 | if (ENABLE_VULKAN) | ||
| 205 | target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) | ||
| 206 | target_compile_definitions(yuzu PRIVATE HAS_VULKAN) | ||
| 207 | endif() | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7490fb718..55a37fffa 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -2,19 +2,30 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <glad/glad.h> | ||
| 6 | |||
| 5 | #include <QApplication> | 7 | #include <QApplication> |
| 6 | #include <QHBoxLayout> | 8 | #include <QHBoxLayout> |
| 7 | #include <QKeyEvent> | 9 | #include <QKeyEvent> |
| 10 | #include <QMessageBox> | ||
| 8 | #include <QOffscreenSurface> | 11 | #include <QOffscreenSurface> |
| 9 | #include <QOpenGLWindow> | 12 | #include <QOpenGLWindow> |
| 10 | #include <QPainter> | 13 | #include <QPainter> |
| 11 | #include <QScreen> | 14 | #include <QScreen> |
| 15 | #include <QStringList> | ||
| 12 | #include <QWindow> | 16 | #include <QWindow> |
| 17 | #ifdef HAS_VULKAN | ||
| 18 | #include <QVulkanWindow> | ||
| 19 | #endif | ||
| 20 | |||
| 13 | #include <fmt/format.h> | 21 | #include <fmt/format.h> |
| 22 | |||
| 23 | #include "common/assert.h" | ||
| 14 | #include "common/microprofile.h" | 24 | #include "common/microprofile.h" |
| 15 | #include "common/scm_rev.h" | 25 | #include "common/scm_rev.h" |
| 16 | #include "core/core.h" | 26 | #include "core/core.h" |
| 17 | #include "core/frontend/framebuffer_layout.h" | 27 | #include "core/frontend/framebuffer_layout.h" |
| 28 | #include "core/frontend/scope_acquire_window_context.h" | ||
| 18 | #include "core/settings.h" | 29 | #include "core/settings.h" |
| 19 | #include "input_common/keyboard.h" | 30 | #include "input_common/keyboard.h" |
| 20 | #include "input_common/main.h" | 31 | #include "input_common/main.h" |
| @@ -114,19 +125,10 @@ private: | |||
| 114 | QOpenGLContext context; | 125 | QOpenGLContext context; |
| 115 | }; | 126 | }; |
| 116 | 127 | ||
| 117 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | 128 | class GWidgetInternal : public QWindow { |
| 118 | // context. | ||
| 119 | // The corresponding functionality is handled in EmuThread instead | ||
| 120 | class GGLWidgetInternal : public QOpenGLWindow { | ||
| 121 | public: | 129 | public: |
| 122 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) | 130 | GWidgetInternal(GRenderWindow* parent) : parent(parent) {} |
| 123 | : QOpenGLWindow(shared_context), parent(parent) {} | 131 | virtual ~GWidgetInternal() = default; |
| 124 | |||
| 125 | void paintEvent(QPaintEvent* ev) override { | ||
| 126 | if (do_painting) { | ||
| 127 | QPainter painter(this); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | 132 | ||
| 131 | void resizeEvent(QResizeEvent* ev) override { | 133 | void resizeEvent(QResizeEvent* ev) override { |
| 132 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); | 134 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); |
| @@ -182,11 +184,47 @@ public: | |||
| 182 | do_painting = true; | 184 | do_painting = true; |
| 183 | } | 185 | } |
| 184 | 186 | ||
| 187 | std::pair<unsigned, unsigned> GetSize() const { | ||
| 188 | return std::make_pair(width(), height()); | ||
| 189 | } | ||
| 190 | |||
| 191 | protected: | ||
| 192 | bool IsPaintingEnabled() const { | ||
| 193 | return do_painting; | ||
| 194 | } | ||
| 195 | |||
| 185 | private: | 196 | private: |
| 186 | GRenderWindow* parent; | 197 | GRenderWindow* parent; |
| 187 | bool do_painting; | 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 | } | ||
| 188 | }; | 215 | }; |
| 189 | 216 | ||
| 217 | #ifdef HAS_VULKAN | ||
| 218 | class GVKWidgetInternal final : public GWidgetInternal { | ||
| 219 | public: | ||
| 220 | GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { | ||
| 221 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | ||
| 222 | setVulkanInstance(instance); | ||
| 223 | } | ||
| 224 | ~GVKWidgetInternal() override = default; | ||
| 225 | }; | ||
| 226 | #endif | ||
| 227 | |||
| 190 | GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) | 228 | GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) |
| 191 | : QWidget(parent), emu_thread(emu_thread) { | 229 | : QWidget(parent), emu_thread(emu_thread) { |
| 192 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 230 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| @@ -201,9 +239,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) | |||
| 201 | 239 | ||
| 202 | GRenderWindow::~GRenderWindow() { | 240 | GRenderWindow::~GRenderWindow() { |
| 203 | InputCommon::Shutdown(); | 241 | InputCommon::Shutdown(); |
| 242 | |||
| 243 | // Avoid an unordered destruction that generates a segfault | ||
| 244 | delete child; | ||
| 204 | } | 245 | } |
| 205 | 246 | ||
| 206 | void GRenderWindow::moveContext() { | 247 | void GRenderWindow::moveContext() { |
| 248 | if (!context) { | ||
| 249 | return; | ||
| 250 | } | ||
| 207 | DoneCurrent(); | 251 | DoneCurrent(); |
| 208 | 252 | ||
| 209 | // If the thread started running, move the GL Context to the new thread. Otherwise, move it | 253 | // If the thread started running, move the GL Context to the new thread. Otherwise, move it |
| @@ -215,8 +259,9 @@ void GRenderWindow::moveContext() { | |||
| 215 | } | 259 | } |
| 216 | 260 | ||
| 217 | void GRenderWindow::SwapBuffers() { | 261 | void GRenderWindow::SwapBuffers() { |
| 218 | context->swapBuffers(child); | 262 | if (context) { |
| 219 | 263 | context->swapBuffers(child); | |
| 264 | } | ||
| 220 | if (!first_frame) { | 265 | if (!first_frame) { |
| 221 | first_frame = true; | 266 | first_frame = true; |
| 222 | emit FirstFrameDisplayed(); | 267 | emit FirstFrameDisplayed(); |
| @@ -224,15 +269,38 @@ void GRenderWindow::SwapBuffers() { | |||
| 224 | } | 269 | } |
| 225 | 270 | ||
| 226 | void GRenderWindow::MakeCurrent() { | 271 | void GRenderWindow::MakeCurrent() { |
| 227 | context->makeCurrent(child); | 272 | if (context) { |
| 273 | context->makeCurrent(child); | ||
| 274 | } | ||
| 228 | } | 275 | } |
| 229 | 276 | ||
| 230 | void GRenderWindow::DoneCurrent() { | 277 | void GRenderWindow::DoneCurrent() { |
| 231 | context->doneCurrent(); | 278 | if (context) { |
| 279 | context->doneCurrent(); | ||
| 280 | } | ||
| 232 | } | 281 | } |
| 233 | 282 | ||
| 234 | void GRenderWindow::PollEvents() {} | 283 | void GRenderWindow::PollEvents() {} |
| 235 | 284 | ||
| 285 | bool GRenderWindow::IsShown() const { | ||
| 286 | return !isMinimized(); | ||
| 287 | } | ||
| 288 | |||
| 289 | void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 290 | void* surface) const { | ||
| 291 | #ifdef HAS_VULKAN | ||
| 292 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | ||
| 293 | const VkInstance instance_copy = vk_instance->vkInstance(); | ||
| 294 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); | ||
| 295 | |||
| 296 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 297 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | ||
| 298 | std::memcpy(surface, &surface_copy, sizeof(surface_copy)); | ||
| 299 | #else | ||
| 300 | UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); | ||
| 301 | #endif | ||
| 302 | } | ||
| 303 | |||
| 236 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). | 304 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 237 | // | 305 | // |
| 238 | // Older versions get the window size (density independent pixels), | 306 | // Older versions get the window size (density independent pixels), |
| @@ -241,10 +309,9 @@ void GRenderWindow::PollEvents() {} | |||
| 241 | void GRenderWindow::OnFramebufferSizeChanged() { | 309 | void GRenderWindow::OnFramebufferSizeChanged() { |
| 242 | // Screen changes potentially incur a change in screen DPI, hence we should update the | 310 | // Screen changes potentially incur a change in screen DPI, hence we should update the |
| 243 | // framebuffer size | 311 | // framebuffer size |
| 244 | const qreal pixel_ratio = GetWindowPixelRatio(); | 312 | const qreal pixelRatio{GetWindowPixelRatio()}; |
| 245 | const u32 width = child->QPaintDevice::width() * pixel_ratio; | 313 | const auto size{child->GetSize()}; |
| 246 | const u32 height = child->QPaintDevice::height() * pixel_ratio; | 314 | UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); |
| 247 | UpdateCurrentFramebufferLayout(width, height); | ||
| 248 | } | 315 | } |
| 249 | 316 | ||
| 250 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | 317 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { |
| @@ -290,7 +357,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const { | |||
| 290 | } | 357 | } |
| 291 | 358 | ||
| 292 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 359 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { |
| 293 | const qreal pixel_ratio = GetWindowPixelRatio(); | 360 | const qreal pixel_ratio{GetWindowPixelRatio()}; |
| 294 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 361 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 295 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 362 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| 296 | } | 363 | } |
| @@ -356,50 +423,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont | |||
| 356 | return std::make_unique<GGLContext>(context.get()); | 423 | return std::make_unique<GGLContext>(context.get()); |
| 357 | } | 424 | } |
| 358 | 425 | ||
| 359 | void GRenderWindow::InitRenderTarget() { | 426 | bool GRenderWindow::InitRenderTarget() { |
| 360 | shared_context.reset(); | 427 | shared_context.reset(); |
| 361 | context.reset(); | 428 | context.reset(); |
| 362 | 429 | if (child) { | |
| 363 | delete child; | 430 | delete child; |
| 364 | child = nullptr; | 431 | } |
| 365 | 432 | if (container) { | |
| 366 | delete container; | 433 | delete container; |
| 367 | container = nullptr; | 434 | } |
| 368 | 435 | if (layout()) { | |
| 369 | delete layout(); | 436 | delete layout(); |
| 437 | } | ||
| 370 | 438 | ||
| 371 | first_frame = false; | 439 | first_frame = false; |
| 372 | 440 | ||
| 373 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 441 | switch (Settings::values.renderer_backend) { |
| 374 | // WA_DontShowOnScreen, WA_DeleteOnClose | 442 | case Settings::RendererBackend::OpenGL: |
| 375 | QSurfaceFormat fmt; | 443 | if (!InitializeOpenGL()) { |
| 376 | fmt.setVersion(4, 3); | 444 | return false; |
| 377 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | 445 | } |
| 378 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 446 | break; |
| 379 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 447 | case Settings::RendererBackend::Vulkan: |
| 380 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 448 | if (!InitializeVulkan()) { |
| 381 | shared_context = std::make_unique<QOpenGLContext>(); | 449 | return false; |
| 382 | shared_context->setFormat(fmt); | 450 | } |
| 383 | shared_context->create(); | 451 | break; |
| 384 | context = std::make_unique<QOpenGLContext>(); | 452 | } |
| 385 | context->setShareContext(shared_context.get()); | ||
| 386 | context->setFormat(fmt); | ||
| 387 | context->create(); | ||
| 388 | fmt.setSwapInterval(0); | ||
| 389 | 453 | ||
| 390 | child = new GGLWidgetInternal(this, shared_context.get()); | ||
| 391 | container = QWidget::createWindowContainer(child, this); | 454 | container = QWidget::createWindowContainer(child, this); |
| 392 | |||
| 393 | QBoxLayout* layout = new QHBoxLayout(this); | 455 | QBoxLayout* layout = new QHBoxLayout(this); |
| 456 | |||
| 394 | layout->addWidget(container); | 457 | layout->addWidget(container); |
| 395 | layout->setMargin(0); | 458 | layout->setMargin(0); |
| 396 | setLayout(layout); | 459 | setLayout(layout); |
| 397 | 460 | ||
| 398 | // Reset minimum size to avoid unwanted resizes when this function is called for a second time. | 461 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 399 | setMinimumSize(1, 1); | 462 | setMinimumSize(1, 1); |
| 400 | 463 | ||
| 401 | // Show causes the window to actually be created and the OpenGL context as well, but we don't | 464 | // Show causes the window to actually be created and the gl context as well, but we don't want |
| 402 | // want the widget to be shown yet, so immediately hide it. | 465 | // the widget to be shown yet, so immediately hide it. |
| 403 | show(); | 466 | show(); |
| 404 | hide(); | 467 | hide(); |
| 405 | 468 | ||
| @@ -410,9 +473,17 @@ void GRenderWindow::InitRenderTarget() { | |||
| 410 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 473 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 411 | 474 | ||
| 412 | OnFramebufferSizeChanged(); | 475 | OnFramebufferSizeChanged(); |
| 413 | NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height())); | 476 | NotifyClientAreaSizeChanged(child->GetSize()); |
| 414 | 477 | ||
| 415 | BackupGeometry(); | 478 | BackupGeometry(); |
| 479 | |||
| 480 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | ||
| 481 | if (!LoadOpenGL()) { | ||
| 482 | return false; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | return true; | ||
| 416 | } | 487 | } |
| 417 | 488 | ||
| 418 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 489 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| @@ -441,6 +512,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 441 | setMinimumSize(minimal_size.first, minimal_size.second); | 512 | setMinimumSize(minimal_size.first, minimal_size.second); |
| 442 | } | 513 | } |
| 443 | 514 | ||
| 515 | bool GRenderWindow::InitializeOpenGL() { | ||
| 516 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | ||
| 517 | // WA_DontShowOnScreen, WA_DeleteOnClose | ||
| 518 | QSurfaceFormat fmt; | ||
| 519 | fmt.setVersion(4, 3); | ||
| 520 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | ||
| 521 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | ||
| 522 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 523 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 524 | shared_context = std::make_unique<QOpenGLContext>(); | ||
| 525 | shared_context->setFormat(fmt); | ||
| 526 | shared_context->create(); | ||
| 527 | context = std::make_unique<QOpenGLContext>(); | ||
| 528 | context->setShareContext(shared_context.get()); | ||
| 529 | context->setFormat(fmt); | ||
| 530 | context->create(); | ||
| 531 | fmt.setSwapInterval(false); | ||
| 532 | |||
| 533 | child = new GGLWidgetInternal(this, shared_context.get()); | ||
| 534 | return true; | ||
| 535 | } | ||
| 536 | |||
| 537 | bool GRenderWindow::InitializeVulkan() { | ||
| 538 | #ifdef HAS_VULKAN | ||
| 539 | vk_instance = std::make_unique<QVulkanInstance>(); | ||
| 540 | vk_instance->setApiVersion(QVersionNumber(1, 1, 0)); | ||
| 541 | vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); | ||
| 542 | if (Settings::values.renderer_debug) { | ||
| 543 | const auto supported_layers{vk_instance->supportedLayers()}; | ||
| 544 | const bool found = | ||
| 545 | std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) { | ||
| 546 | constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation"; | ||
| 547 | return layer.name == searched_layer; | ||
| 548 | }); | ||
| 549 | if (found) { | ||
| 550 | vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); | ||
| 551 | vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 552 | } | ||
| 553 | } | ||
| 554 | if (!vk_instance->create()) { | ||
| 555 | QMessageBox::critical( | ||
| 556 | this, tr("Error while initializing Vulkan 1.1!"), | ||
| 557 | tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the " | ||
| 558 | "latest graphics drivers.")); | ||
| 559 | return false; | ||
| 560 | } | ||
| 561 | |||
| 562 | child = new GVKWidgetInternal(this, vk_instance.get()); | ||
| 563 | return true; | ||
| 564 | #else | ||
| 565 | QMessageBox::critical(this, tr("Vulkan not available!"), | ||
| 566 | tr("yuzu has not been compiled with Vulkan support.")); | ||
| 567 | return false; | ||
| 568 | #endif | ||
| 569 | } | ||
| 570 | |||
| 571 | bool GRenderWindow::LoadOpenGL() { | ||
| 572 | Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; | ||
| 573 | if (!gladLoadGL()) { | ||
| 574 | 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 " | ||
| 576 | "latest graphics driver.")); | ||
| 577 | return false; | ||
| 578 | } | ||
| 579 | |||
| 580 | QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); | ||
| 581 | if (!unsupported_gl_extensions.empty()) { | ||
| 582 | QMessageBox::critical( | ||
| 583 | this, tr("Error while initializing OpenGL!"), | ||
| 584 | tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you " | ||
| 585 | "have the latest graphics driver.<br><br>Unsupported extensions:<br>") + | ||
| 586 | unsupported_gl_extensions.join(QStringLiteral("<br>"))); | ||
| 587 | return false; | ||
| 588 | } | ||
| 589 | return true; | ||
| 590 | } | ||
| 591 | |||
| 592 | QStringList GRenderWindow::GetUnsupportedGLExtensions() const { | ||
| 593 | QStringList unsupported_ext; | ||
| 594 | |||
| 595 | if (!GLAD_GL_ARB_buffer_storage) | ||
| 596 | unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); | ||
| 597 | if (!GLAD_GL_ARB_direct_state_access) | ||
| 598 | unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); | ||
| 599 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) | ||
| 600 | unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); | ||
| 601 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | ||
| 602 | unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); | ||
| 603 | if (!GLAD_GL_ARB_multi_bind) | ||
| 604 | unsupported_ext.append(QStringLiteral("ARB_multi_bind")); | ||
| 605 | if (!GLAD_GL_ARB_clip_control) | ||
| 606 | unsupported_ext.append(QStringLiteral("ARB_clip_control")); | ||
| 607 | |||
| 608 | // Extensions required to support some texture formats. | ||
| 609 | if (!GLAD_GL_EXT_texture_compression_s3tc) | ||
| 610 | unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); | ||
| 611 | if (!GLAD_GL_ARB_texture_compression_rgtc) | ||
| 612 | unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); | ||
| 613 | if (!GLAD_GL_ARB_depth_buffer_float) | ||
| 614 | unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); | ||
| 615 | |||
| 616 | for (const QString& ext : unsupported_ext) | ||
| 617 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); | ||
| 618 | |||
| 619 | return unsupported_ext; | ||
| 620 | } | ||
| 621 | |||
| 444 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | 622 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { |
| 445 | this->emu_thread = emu_thread; | 623 | this->emu_thread = emu_thread; |
| 446 | child->DisablePainting(); | 624 | child->DisablePainting(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 2fc64895f..71a2fa321 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -7,17 +7,28 @@ | |||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <mutex> | 9 | #include <mutex> |
| 10 | |||
| 10 | #include <QImage> | 11 | #include <QImage> |
| 11 | #include <QThread> | 12 | #include <QThread> |
| 12 | #include <QWidget> | 13 | #include <QWidget> |
| 14 | |||
| 15 | #include "common/thread.h" | ||
| 13 | #include "core/core.h" | 16 | #include "core/core.h" |
| 14 | #include "core/frontend/emu_window.h" | 17 | #include "core/frontend/emu_window.h" |
| 15 | 18 | ||
| 16 | class QKeyEvent; | 19 | class QKeyEvent; |
| 17 | class QScreen; | 20 | class QScreen; |
| 18 | class QTouchEvent; | 21 | class QTouchEvent; |
| 22 | class QStringList; | ||
| 23 | class QSurface; | ||
| 24 | class QOpenGLContext; | ||
| 25 | #ifdef HAS_VULKAN | ||
| 26 | class QVulkanInstance; | ||
| 27 | #endif | ||
| 19 | 28 | ||
| 29 | class GWidgetInternal; | ||
| 20 | class GGLWidgetInternal; | 30 | class GGLWidgetInternal; |
| 31 | class GVKWidgetInternal; | ||
| 21 | class GMainWindow; | 32 | class GMainWindow; |
| 22 | class GRenderWindow; | 33 | class GRenderWindow; |
| 23 | class QSurface; | 34 | class QSurface; |
| @@ -123,6 +134,9 @@ public: | |||
| 123 | void MakeCurrent() override; | 134 | void MakeCurrent() override; |
| 124 | void DoneCurrent() override; | 135 | void DoneCurrent() override; |
| 125 | void PollEvents() override; | 136 | void PollEvents() override; |
| 137 | bool IsShown() const override; | ||
| 138 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 139 | void* surface) const override; | ||
| 126 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 140 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 127 | 141 | ||
| 128 | void ForwardKeyPressEvent(QKeyEvent* event); | 142 | void ForwardKeyPressEvent(QKeyEvent* event); |
| @@ -142,7 +156,7 @@ public: | |||
| 142 | 156 | ||
| 143 | void OnClientAreaResized(u32 width, u32 height); | 157 | void OnClientAreaResized(u32 width, u32 height); |
| 144 | 158 | ||
| 145 | void InitRenderTarget(); | 159 | bool InitRenderTarget(); |
| 146 | 160 | ||
| 147 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 161 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 148 | 162 | ||
| @@ -165,10 +179,13 @@ private: | |||
| 165 | 179 | ||
| 166 | void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; | 180 | void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; |
| 167 | 181 | ||
| 168 | QWidget* container = nullptr; | 182 | bool InitializeOpenGL(); |
| 169 | GGLWidgetInternal* child = nullptr; | 183 | bool InitializeVulkan(); |
| 184 | bool LoadOpenGL(); | ||
| 185 | QStringList GetUnsupportedGLExtensions() const; | ||
| 170 | 186 | ||
| 171 | QByteArray geometry; | 187 | QWidget* container = nullptr; |
| 188 | GWidgetInternal* child = nullptr; | ||
| 172 | 189 | ||
| 173 | EmuThread* emu_thread; | 190 | EmuThread* emu_thread; |
| 174 | // Context that backs the GGLWidgetInternal (and will be used by core to render) | 191 | // Context that backs the GGLWidgetInternal (and will be used by core to render) |
| @@ -177,9 +194,14 @@ private: | |||
| 177 | // current | 194 | // current |
| 178 | std::unique_ptr<QOpenGLContext> shared_context; | 195 | std::unique_ptr<QOpenGLContext> shared_context; |
| 179 | 196 | ||
| 197 | #ifdef HAS_VULKAN | ||
| 198 | std::unique_ptr<QVulkanInstance> vk_instance; | ||
| 199 | #endif | ||
| 200 | |||
| 180 | /// Temporary storage of the screenshot taken | 201 | /// Temporary storage of the screenshot taken |
| 181 | QImage screenshot_image; | 202 | QImage screenshot_image; |
| 182 | 203 | ||
| 204 | QByteArray geometry; | ||
| 183 | bool first_frame = false; | 205 | bool first_frame = false; |
| 184 | 206 | ||
| 185 | protected: | 207 | protected: |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 59918847a..280d81ba9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -624,6 +624,10 @@ void Config::ReadPathValues() { | |||
| 624 | void Config::ReadRendererValues() { | 624 | void Config::ReadRendererValues() { |
| 625 | qt_config->beginGroup(QStringLiteral("Renderer")); | 625 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 626 | 626 | ||
| 627 | Settings::values.renderer_backend = | ||
| 628 | static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); | ||
| 629 | Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); | ||
| 630 | Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); | ||
| 627 | Settings::values.resolution_factor = | 631 | Settings::values.resolution_factor = |
| 628 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); | 632 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); |
| 629 | Settings::values.use_frame_limit = | 633 | Settings::values.use_frame_limit = |
| @@ -1056,6 +1060,9 @@ void Config::SavePathValues() { | |||
| 1056 | void Config::SaveRendererValues() { | 1060 | void Config::SaveRendererValues() { |
| 1057 | qt_config->beginGroup(QStringLiteral("Renderer")); | 1061 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 1058 | 1062 | ||
| 1063 | WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); | ||
| 1064 | WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); | ||
| 1065 | WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); | ||
| 1059 | WriteSetting(QStringLiteral("resolution_factor"), | 1066 | WriteSetting(QStringLiteral("resolution_factor"), |
| 1060 | static_cast<double>(Settings::values.resolution_factor), 1.0); | 1067 | static_cast<double>(Settings::values.resolution_factor), 1.0); |
| 1061 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); | 1068 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 90c1f9459..9631059c7 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() { | |||
| 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); |
| 37 | ui->reporting_services->setChecked(Settings::values.reporting_services); | 37 | ui->reporting_services->setChecked(Settings::values.reporting_services); |
| 38 | ui->quest_flag->setChecked(Settings::values.quest_flag); | 38 | ui->quest_flag->setChecked(Settings::values.quest_flag); |
| 39 | ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||
| 40 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); | ||
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | void ConfigureDebug::ApplyConfiguration() { | 43 | void ConfigureDebug::ApplyConfiguration() { |
| @@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||
| 46 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | 48 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); |
| 47 | Settings::values.reporting_services = ui->reporting_services->isChecked(); | 49 | Settings::values.reporting_services = ui->reporting_services->isChecked(); |
| 48 | Settings::values.quest_flag = ui->quest_flag->isChecked(); | 50 | Settings::values.quest_flag = ui->quest_flag->isChecked(); |
| 51 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); | ||
| 49 | Debugger::ToggleConsole(); | 52 | Debugger::ToggleConsole(); |
| 50 | Log::Filter filter; | 53 | Log::Filter filter; |
| 51 | filter.ParseFilterString(Settings::values.log_filter); | 54 | filter.ParseFilterString(Settings::values.log_filter); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index ce49569bb..e028c4c80 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>400</width> | 9 | <width>400</width> |
| 10 | <height>474</height> | 10 | <height>467</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -103,6 +103,80 @@ | |||
| 103 | </item> | 103 | </item> |
| 104 | </layout> | 104 | </layout> |
| 105 | </item> | 105 | </item> |
| 106 | </layout> | ||
| 107 | </widget> | ||
| 108 | </item> | ||
| 109 | <item> | ||
| 110 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 111 | <property name="title"> | ||
| 112 | <string>Homebrew</string> | ||
| 113 | </property> | ||
| 114 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 115 | <item> | ||
| 116 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 117 | <item> | ||
| 118 | <widget class="QLabel" name="label_3"> | ||
| 119 | <property name="text"> | ||
| 120 | <string>Arguments String</string> | ||
| 121 | </property> | ||
| 122 | </widget> | ||
| 123 | </item> | ||
| 124 | <item> | ||
| 125 | <widget class="QLineEdit" name="homebrew_args_edit"/> | ||
| 126 | </item> | ||
| 127 | </layout> | ||
| 128 | </item> | ||
| 129 | </layout> | ||
| 130 | </widget> | ||
| 131 | </item> | ||
| 132 | <item> | ||
| 133 | <widget class="QGroupBox" name="groupBox_4"> | ||
| 134 | <property name="title"> | ||
| 135 | <string>Graphics</string> | ||
| 136 | </property> | ||
| 137 | <layout class="QVBoxLayout" name="verticalLayout_6"> | ||
| 138 | <item> | ||
| 139 | <widget class="QCheckBox" name="enable_graphics_debugging"> | ||
| 140 | <property name="enabled"> | ||
| 141 | <bool>true</bool> | ||
| 142 | </property> | ||
| 143 | <property name="whatsThis"> | ||
| 144 | <string>When checked, the graphics API enters in a slower debugging mode</string> | ||
| 145 | </property> | ||
| 146 | <property name="text"> | ||
| 147 | <string>Enable Graphics Debugging</string> | ||
| 148 | </property> | ||
| 149 | </widget> | ||
| 150 | </item> | ||
| 151 | </layout> | ||
| 152 | </widget> | ||
| 153 | </item> | ||
| 154 | <item> | ||
| 155 | <widget class="QGroupBox" name="groupBox_5"> | ||
| 156 | <property name="title"> | ||
| 157 | <string>Dump</string> | ||
| 158 | </property> | ||
| 159 | <layout class="QVBoxLayout" name="verticalLayout_6"> | ||
| 160 | <item> | ||
| 161 | <widget class="QCheckBox" name="dump_decompressed_nso"> | ||
| 162 | <property name="whatsThis"> | ||
| 163 | <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> | ||
| 164 | </property> | ||
| 165 | <property name="text"> | ||
| 166 | <string>Dump Decompressed NSOs</string> | ||
| 167 | </property> | ||
| 168 | </widget> | ||
| 169 | </item> | ||
| 170 | <item> | ||
| 171 | <widget class="QCheckBox" name="dump_exefs"> | ||
| 172 | <property name="whatsThis"> | ||
| 173 | <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> | ||
| 174 | </property> | ||
| 175 | <property name="text"> | ||
| 176 | <string>Dump ExeFS</string> | ||
| 177 | </property> | ||
| 178 | </widget> | ||
| 179 | </item> | ||
| 106 | <item> | 180 | <item> |
| 107 | <widget class="QCheckBox" name="reporting_services"> | 181 | <widget class="QCheckBox" name="reporting_services"> |
| 108 | <property name="text"> | 182 | <property name="text"> |
| @@ -129,11 +203,11 @@ | |||
| 129 | </widget> | 203 | </widget> |
| 130 | </item> | 204 | </item> |
| 131 | <item> | 205 | <item> |
| 132 | <widget class="QGroupBox" name="groupBox_5"> | 206 | <widget class="QGroupBox" name="groupBox_6"> |
| 133 | <property name="title"> | 207 | <property name="title"> |
| 134 | <string>Advanced</string> | 208 | <string>Advanced</string> |
| 135 | </property> | 209 | </property> |
| 136 | <layout class="QVBoxLayout" name="verticalLayout"> | 210 | <layout class="QVBoxLayout" name="verticalLayout_7"> |
| 137 | <item> | 211 | <item> |
| 138 | <widget class="QCheckBox" name="quest_flag"> | 212 | <widget class="QCheckBox" name="quest_flag"> |
| 139 | <property name="text"> | 213 | <property name="text"> |
| @@ -145,29 +219,6 @@ | |||
| 145 | </widget> | 219 | </widget> |
| 146 | </item> | 220 | </item> |
| 147 | <item> | 221 | <item> |
| 148 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 149 | <property name="title"> | ||
| 150 | <string>Homebrew</string> | ||
| 151 | </property> | ||
| 152 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 153 | <item> | ||
| 154 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 155 | <item> | ||
| 156 | <widget class="QLabel" name="label_3"> | ||
| 157 | <property name="text"> | ||
| 158 | <string>Arguments String</string> | ||
| 159 | </property> | ||
| 160 | </widget> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <widget class="QLineEdit" name="homebrew_args_edit"/> | ||
| 164 | </item> | ||
| 165 | </layout> | ||
| 166 | </item> | ||
| 167 | </layout> | ||
| 168 | </widget> | ||
| 169 | </item> | ||
| 170 | <item> | ||
| 171 | <spacer name="verticalSpacer"> | 222 | <spacer name="verticalSpacer"> |
| 172 | <property name="orientation"> | 223 | <property name="orientation"> |
| 173 | <enum>Qt::Vertical</enum> | 224 | <enum>Qt::Vertical</enum> |
| @@ -185,6 +236,19 @@ | |||
| 185 | </item> | 236 | </item> |
| 186 | </layout> | 237 | </layout> |
| 187 | </widget> | 238 | </widget> |
| 239 | <tabstops> | ||
| 240 | <tabstop>toggle_gdbstub</tabstop> | ||
| 241 | <tabstop>gdbport_spinbox</tabstop> | ||
| 242 | <tabstop>log_filter_edit</tabstop> | ||
| 243 | <tabstop>toggle_console</tabstop> | ||
| 244 | <tabstop>open_log_button</tabstop> | ||
| 245 | <tabstop>homebrew_args_edit</tabstop> | ||
| 246 | <tabstop>enable_graphics_debugging</tabstop> | ||
| 247 | <tabstop>dump_decompressed_nso</tabstop> | ||
| 248 | <tabstop>dump_exefs</tabstop> | ||
| 249 | <tabstop>reporting_services</tabstop> | ||
| 250 | <tabstop>quest_flag</tabstop> | ||
| 251 | </tabstops> | ||
| 188 | <resources/> | 252 | <resources/> |
| 189 | <connections> | 253 | <connections> |
| 190 | <connection> | 254 | <connection> |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2c9e322c9..f57a24e36 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -3,6 +3,13 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QColorDialog> | 5 | #include <QColorDialog> |
| 6 | #include <QComboBox> | ||
| 7 | #ifdef HAS_VULKAN | ||
| 8 | #include <QVulkanInstance> | ||
| 9 | #endif | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 6 | #include "core/core.h" | 13 | #include "core/core.h" |
| 7 | #include "core/settings.h" | 14 | #include "core/settings.h" |
| 8 | #include "ui_configure_graphics.h" | 15 | #include "ui_configure_graphics.h" |
| @@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) { | |||
| 51 | 58 | ||
| 52 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) | 59 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) |
| 53 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { | 60 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { |
| 61 | vulkan_device = Settings::values.vulkan_device; | ||
| 62 | RetrieveVulkanDevices(); | ||
| 63 | |||
| 54 | ui->setupUi(this); | 64 | ui->setupUi(this); |
| 55 | 65 | ||
| 56 | SetConfiguration(); | 66 | SetConfiguration(); |
| 57 | 67 | ||
| 68 | connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||
| 69 | [this] { UpdateDeviceComboBox(); }); | ||
| 70 | connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, | ||
| 71 | [this](int device) { UpdateDeviceSelection(device); }); | ||
| 72 | |||
| 58 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { | 73 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { |
| 59 | const QColor new_bg_color = QColorDialog::getColor(bg_color); | 74 | const QColor new_bg_color = QColorDialog::getColor(bg_color); |
| 60 | if (!new_bg_color.isValid()) { | 75 | if (!new_bg_color.isValid()) { |
| @@ -64,11 +79,22 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
| 64 | }); | 79 | }); |
| 65 | } | 80 | } |
| 66 | 81 | ||
| 82 | void ConfigureGraphics::UpdateDeviceSelection(int device) { | ||
| 83 | if (device == -1) { | ||
| 84 | return; | ||
| 85 | } | ||
| 86 | if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) { | ||
| 87 | vulkan_device = device; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 67 | ConfigureGraphics::~ConfigureGraphics() = default; | 91 | ConfigureGraphics::~ConfigureGraphics() = default; |
| 68 | 92 | ||
| 69 | void ConfigureGraphics::SetConfiguration() { | 93 | void ConfigureGraphics::SetConfiguration() { |
| 70 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | 94 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); |
| 71 | 95 | ||
| 96 | ui->api->setEnabled(runtime_lock); | ||
| 97 | ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend)); | ||
| 72 | ui->resolution_factor_combobox->setCurrentIndex( | 98 | ui->resolution_factor_combobox->setCurrentIndex( |
| 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | 99 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); |
| 74 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | 100 | ui->use_disk_shader_cache->setEnabled(runtime_lock); |
| @@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 80 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | 106 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); |
| 81 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 107 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 82 | Settings::values.bg_blue)); | 108 | Settings::values.bg_blue)); |
| 109 | UpdateDeviceComboBox(); | ||
| 83 | } | 110 | } |
| 84 | 111 | ||
| 85 | void ConfigureGraphics::ApplyConfiguration() { | 112 | void ConfigureGraphics::ApplyConfiguration() { |
| 113 | Settings::values.renderer_backend = GetCurrentGraphicsBackend(); | ||
| 114 | Settings::values.vulkan_device = vulkan_device; | ||
| 86 | Settings::values.resolution_factor = | 115 | Settings::values.resolution_factor = |
| 87 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | 116 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); |
| 88 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | 117 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); |
| @@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) { | |||
| 116 | const QIcon color_icon(pixmap); | 145 | const QIcon color_icon(pixmap); |
| 117 | ui->bg_button->setIcon(color_icon); | 146 | ui->bg_button->setIcon(color_icon); |
| 118 | } | 147 | } |
| 148 | |||
| 149 | void ConfigureGraphics::UpdateDeviceComboBox() { | ||
| 150 | ui->device->clear(); | ||
| 151 | |||
| 152 | bool enabled = false; | ||
| 153 | switch (GetCurrentGraphicsBackend()) { | ||
| 154 | case Settings::RendererBackend::OpenGL: | ||
| 155 | ui->device->addItem(tr("OpenGL Graphics Device")); | ||
| 156 | enabled = false; | ||
| 157 | break; | ||
| 158 | case Settings::RendererBackend::Vulkan: | ||
| 159 | for (const auto device : vulkan_devices) { | ||
| 160 | ui->device->addItem(device); | ||
| 161 | } | ||
| 162 | ui->device->setCurrentIndex(vulkan_device); | ||
| 163 | enabled = !vulkan_devices.empty(); | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); | ||
| 167 | } | ||
| 168 | |||
| 169 | void ConfigureGraphics::RetrieveVulkanDevices() { | ||
| 170 | #ifdef HAS_VULKAN | ||
| 171 | QVulkanInstance instance; | ||
| 172 | instance.setApiVersion(QVersionNumber(1, 1, 0)); | ||
| 173 | if (!instance.create()) { | ||
| 174 | LOG_INFO(Frontend, "Vulkan 1.1 not available"); | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>( | ||
| 178 | instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))}; | ||
| 179 | if (vkEnumeratePhysicalDevices == nullptr) { | ||
| 180 | LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices"); | ||
| 181 | return; | ||
| 182 | } | ||
| 183 | u32 physical_device_count; | ||
| 184 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) != | ||
| 185 | VK_SUCCESS) { | ||
| 186 | LOG_INFO(Frontend, "Failed to get physical devices count"); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | std::vector<VkPhysicalDevice> physical_devices(physical_device_count); | ||
| 190 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, | ||
| 191 | physical_devices.data()) != VK_SUCCESS) { | ||
| 192 | LOG_INFO(Frontend, "Failed to get physical devices"); | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>( | ||
| 197 | instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))}; | ||
| 198 | if (vkGetPhysicalDeviceProperties == nullptr) { | ||
| 199 | LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties"); | ||
| 200 | return; | ||
| 201 | } | ||
| 202 | for (const auto physical_device : physical_devices) { | ||
| 203 | VkPhysicalDeviceProperties properties; | ||
| 204 | vkGetPhysicalDeviceProperties(physical_device, &properties); | ||
| 205 | vulkan_devices.push_back(QString::fromUtf8(properties.deviceName)); | ||
| 206 | } | ||
| 207 | #endif | ||
| 208 | } | ||
| 209 | |||
| 210 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | ||
| 211 | return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); | ||
| 212 | } | ||
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index fae28d98e..7e0596d9c 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h | |||
| @@ -5,7 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <vector> | ||
| 9 | #include <QString> | ||
| 8 | #include <QWidget> | 10 | #include <QWidget> |
| 11 | #include "core/settings.h" | ||
| 9 | 12 | ||
| 10 | namespace Ui { | 13 | namespace Ui { |
| 11 | class ConfigureGraphics; | 14 | class ConfigureGraphics; |
| @@ -27,7 +30,16 @@ private: | |||
| 27 | void SetConfiguration(); | 30 | void SetConfiguration(); |
| 28 | 31 | ||
| 29 | void UpdateBackgroundColorButton(QColor color); | 32 | void UpdateBackgroundColorButton(QColor color); |
| 33 | void UpdateDeviceComboBox(); | ||
| 34 | void UpdateDeviceSelection(int device); | ||
| 35 | |||
| 36 | void RetrieveVulkanDevices(); | ||
| 37 | |||
| 38 | Settings::RendererBackend GetCurrentGraphicsBackend() const; | ||
| 30 | 39 | ||
| 31 | std::unique_ptr<Ui::ConfigureGraphics> ui; | 40 | std::unique_ptr<Ui::ConfigureGraphics> ui; |
| 32 | QColor bg_color; | 41 | QColor bg_color; |
| 42 | |||
| 43 | std::vector<QString> vulkan_devices; | ||
| 44 | u32 vulkan_device{}; | ||
| 33 | }; | 45 | }; |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 0309ee300..e24372204 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -7,21 +7,69 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>400</width> | 9 | <width>400</width> |
| 10 | <height>300</height> | 10 | <height>321</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| 14 | <string>Form</string> | 14 | <string>Form</string> |
| 15 | </property> | 15 | </property> |
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | 16 | <layout class="QVBoxLayout" name="verticalLayout_1"> |
| 17 | <item> | 17 | <item> |
| 18 | <layout class="QVBoxLayout" name="verticalLayout_3"> | 18 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="groupBox_2"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>API Settings</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 25 | <item> | ||
| 26 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 27 | <item> | ||
| 28 | <widget class="QLabel" name="label_2"> | ||
| 29 | <property name="text"> | ||
| 30 | <string>API:</string> | ||
| 31 | </property> | ||
| 32 | </widget> | ||
| 33 | </item> | ||
| 34 | <item> | ||
| 35 | <widget class="QComboBox" name="api"> | ||
| 36 | <item> | ||
| 37 | <property name="text"> | ||
| 38 | <string notr="true">OpenGL</string> | ||
| 39 | </property> | ||
| 40 | </item> | ||
| 41 | <item> | ||
| 42 | <property name="text"> | ||
| 43 | <string notr="true">Vulkan</string> | ||
| 44 | </property> | ||
| 45 | </item> | ||
| 46 | </widget> | ||
| 47 | </item> | ||
| 48 | </layout> | ||
| 49 | </item> | ||
| 50 | <item> | ||
| 51 | <layout class="QHBoxLayout" name="horizontalLayout_5"> | ||
| 52 | <item> | ||
| 53 | <widget class="QLabel" name="label_3"> | ||
| 54 | <property name="text"> | ||
| 55 | <string>Device:</string> | ||
| 56 | </property> | ||
| 57 | </widget> | ||
| 58 | </item> | ||
| 59 | <item> | ||
| 60 | <widget class="QComboBox" name="device"/> | ||
| 61 | </item> | ||
| 62 | </layout> | ||
| 63 | </item> | ||
| 64 | </layout> | ||
| 65 | </widget> | ||
| 66 | </item> | ||
| 19 | <item> | 67 | <item> |
| 20 | <widget class="QGroupBox" name="groupBox"> | 68 | <widget class="QGroupBox" name="groupBox"> |
| 21 | <property name="title"> | 69 | <property name="title"> |
| 22 | <string>Graphics</string> | 70 | <string>Graphics Settings</string> |
| 23 | </property> | 71 | </property> |
| 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 72 | <layout class="QVBoxLayout" name="verticalLayout_4"> |
| 25 | <item> | 73 | <item> |
| 26 | <widget class="QCheckBox" name="use_disk_shader_cache"> | 74 | <widget class="QCheckBox" name="use_disk_shader_cache"> |
| 27 | <property name="text"> | 75 | <property name="text"> |
| @@ -30,16 +78,16 @@ | |||
| 30 | </widget> | 78 | </widget> |
| 31 | </item> | 79 | </item> |
| 32 | <item> | 80 | <item> |
| 33 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> | 81 | <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> |
| 34 | <property name="text"> | 82 | <property name="text"> |
| 35 | <string>Use accurate GPU emulation (slow)</string> | 83 | <string>Use asynchronous GPU emulation</string> |
| 36 | </property> | 84 | </property> |
| 37 | </widget> | 85 | </widget> |
| 38 | </item> | 86 | </item> |
| 39 | <item> | 87 | <item> |
| 40 | <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> | 88 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> |
| 41 | <property name="text"> | 89 | <property name="text"> |
| 42 | <string>Use asynchronous GPU emulation</string> | 90 | <string>Use accurate GPU emulation (slow)</string> |
| 43 | </property> | 91 | </property> |
| 44 | </widget> | 92 | </widget> |
| 45 | </item> | 93 | </item> |
| @@ -51,11 +99,11 @@ | |||
| 51 | </widget> | 99 | </widget> |
| 52 | </item> | 100 | </item> |
| 53 | <item> | 101 | <item> |
| 54 | <layout class="QHBoxLayout" name="horizontalLayout"> | 102 | <layout class="QHBoxLayout" name="horizontalLayout_2"> |
| 55 | <item> | 103 | <item> |
| 56 | <widget class="QLabel" name="label"> | 104 | <widget class="QLabel" name="label"> |
| 57 | <property name="text"> | 105 | <property name="text"> |
| 58 | <string>Internal Resolution</string> | 106 | <string>Internal Resolution:</string> |
| 59 | </property> | 107 | </property> |
| 60 | </widget> | 108 | </widget> |
| 61 | </item> | 109 | </item> |
| @@ -91,7 +139,7 @@ | |||
| 91 | </layout> | 139 | </layout> |
| 92 | </item> | 140 | </item> |
| 93 | <item> | 141 | <item> |
| 94 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | 142 | <layout class="QHBoxLayout" name="horizontalLayout_3"> |
| 95 | <item> | 143 | <item> |
| 96 | <widget class="QLabel" name="bg_label"> | 144 | <widget class="QLabel" name="bg_label"> |
| 97 | <property name="text"> | 145 | <property name="text"> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 67c9a7c6d..96dec50e2 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -236,6 +236,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 236 | widget->setVisible(false); | 236 | widget->setVisible(false); |
| 237 | 237 | ||
| 238 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; | 238 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; |
| 239 | analog_map_deadzone = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; | ||
| 240 | analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; | ||
| 239 | 241 | ||
| 240 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | 242 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { |
| 241 | auto* const button = button_map[button_id]; | 243 | auto* const button = button_map[button_id]; |
| @@ -326,6 +328,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 326 | InputCommon::Polling::DeviceType::Analog); | 328 | InputCommon::Polling::DeviceType::Analog); |
| 327 | } | 329 | } |
| 328 | }); | 330 | }); |
| 331 | connect(analog_map_deadzone[analog_id], &QSlider::valueChanged, [=] { | ||
| 332 | const float deadzone = analog_map_deadzone[analog_id]->value() / 100.0f; | ||
| 333 | analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1").arg(deadzone)); | ||
| 334 | analogs_param[analog_id].Set("deadzone", deadzone); | ||
| 335 | }); | ||
| 329 | } | 336 | } |
| 330 | 337 | ||
| 331 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); | 338 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); |
| @@ -484,7 +491,7 @@ void ConfigureInputPlayer::ClearAll() { | |||
| 484 | continue; | 491 | continue; |
| 485 | } | 492 | } |
| 486 | 493 | ||
| 487 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | 494 | analogs_param[analog_id].Clear(); |
| 488 | } | 495 | } |
| 489 | } | 496 | } |
| 490 | 497 | ||
| @@ -508,6 +515,23 @@ void ConfigureInputPlayer::UpdateButtonLabels() { | |||
| 508 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | 515 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); |
| 509 | } | 516 | } |
| 510 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); | 517 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); |
| 518 | |||
| 519 | auto& param = analogs_param[analog_id]; | ||
| 520 | auto* const analog_deadzone_slider = analog_map_deadzone[analog_id]; | ||
| 521 | auto* const analog_deadzone_label = analog_map_deadzone_label[analog_id]; | ||
| 522 | |||
| 523 | if (param.Has("engine") && param.Get("engine", "") == "sdl") { | ||
| 524 | if (!param.Has("deadzone")) { | ||
| 525 | param.Set("deadzone", 0.1f); | ||
| 526 | } | ||
| 527 | |||
| 528 | analog_deadzone_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); | ||
| 529 | analog_deadzone_slider->setVisible(true); | ||
| 530 | analog_deadzone_label->setVisible(true); | ||
| 531 | } else { | ||
| 532 | analog_deadzone_slider->setVisible(false); | ||
| 533 | analog_deadzone_label->setVisible(false); | ||
| 534 | } | ||
| 511 | } | 535 | } |
| 512 | } | 536 | } |
| 513 | 537 | ||
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index c66027651..045704e47 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -97,6 +97,8 @@ private: | |||
| 97 | /// Analog inputs are also represented each with a single button, used to configure with an | 97 | /// Analog inputs are also represented each with a single button, used to configure with an |
| 98 | /// actual analog stick | 98 | /// actual analog stick |
| 99 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; | 99 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; |
| 100 | std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone; | ||
| 101 | std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; | ||
| 100 | 102 | ||
| 101 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; | 103 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; |
| 102 | 104 | ||
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 42db020be..1556481d0 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -170,6 +170,44 @@ | |||
| 170 | </item> | 170 | </item> |
| 171 | </layout> | 171 | </layout> |
| 172 | </item> | 172 | </item> |
| 173 | <item row="4" column="0" colspan="2"> | ||
| 174 | <layout class="QVBoxLayout" name="sliderRStickDeadzoneVerticalLayout"> | ||
| 175 | <item> | ||
| 176 | <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout"> | ||
| 177 | <item> | ||
| 178 | <widget class="QLabel" name="labelRStickDeadzone"> | ||
| 179 | <property name="text"> | ||
| 180 | <string>Deadzone: 0</string> | ||
| 181 | </property> | ||
| 182 | <property name="alignment"> | ||
| 183 | <enum>Qt::AlignHCenter</enum> | ||
| 184 | </property> | ||
| 185 | </widget> | ||
| 186 | </item> | ||
| 187 | </layout> | ||
| 188 | </item> | ||
| 189 | <item> | ||
| 190 | <widget class="QSlider" name="sliderRStickDeadzone"> | ||
| 191 | <property name="orientation"> | ||
| 192 | <enum>Qt::Horizontal</enum> | ||
| 193 | </property> | ||
| 194 | </widget> | ||
| 195 | </item> | ||
| 196 | </layout> | ||
| 197 | </item> | ||
| 198 | <item row="5" column="0"> | ||
| 199 | <spacer name="RStick_verticalSpacer"> | ||
| 200 | <property name="orientation"> | ||
| 201 | <enum>Qt::Vertical</enum> | ||
| 202 | </property> | ||
| 203 | <property name="sizeHint" stdset="0"> | ||
| 204 | <size> | ||
| 205 | <width>0</width> | ||
| 206 | <height>0</height> | ||
| 207 | </size> | ||
| 208 | </property> | ||
| 209 | </spacer> | ||
| 210 | </item> | ||
| 173 | </layout> | 211 | </layout> |
| 174 | </widget> | 212 | </widget> |
| 175 | </item> | 213 | </item> |
| @@ -745,6 +783,47 @@ | |||
| 745 | </item> | 783 | </item> |
| 746 | </layout> | 784 | </layout> |
| 747 | </item> | 785 | </item> |
| 786 | <item row="5" column="1" colspan="2"> | ||
| 787 | <layout class="QVBoxLayout" name="sliderLStickDeadzoneVerticalLayout"> | ||
| 788 | <property name="sizeConstraint"> | ||
| 789 | <enum>QLayout::SetDefaultConstraint</enum> | ||
| 790 | </property> | ||
| 791 | <item> | ||
| 792 | <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout"> | ||
| 793 | <item> | ||
| 794 | <widget class="QLabel" name="labelLStickDeadzone"> | ||
| 795 | <property name="text"> | ||
| 796 | <string>Deadzone: 0</string> | ||
| 797 | </property> | ||
| 798 | <property name="alignment"> | ||
| 799 | <enum>Qt::AlignHCenter</enum> | ||
| 800 | </property> | ||
| 801 | </widget> | ||
| 802 | </item> | ||
| 803 | </layout> | ||
| 804 | </item> | ||
| 805 | <item> | ||
| 806 | <widget class="QSlider" name="sliderLStickDeadzone"> | ||
| 807 | <property name="orientation"> | ||
| 808 | <enum>Qt::Horizontal</enum> | ||
| 809 | </property> | ||
| 810 | </widget> | ||
| 811 | </item> | ||
| 812 | </layout> | ||
| 813 | </item> | ||
| 814 | <item row="6" column="1"> | ||
| 815 | <spacer name="LStick_verticalSpacer"> | ||
| 816 | <property name="orientation"> | ||
| 817 | <enum>Qt::Vertical</enum> | ||
| 818 | </property> | ||
| 819 | <property name="sizeHint" stdset="0"> | ||
| 820 | <size> | ||
| 821 | <width>0</width> | ||
| 822 | <height>0</height> | ||
| 823 | </size> | ||
| 824 | </property> | ||
| 825 | </spacer> | ||
| 826 | </item> | ||
| 748 | </layout> | 827 | </layout> |
| 749 | </widget> | 828 | </widget> |
| 750 | </item> | 829 | </item> |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b5dd3e0d6..54ca2dc1d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -454,7 +454,6 @@ void GMainWindow::InitializeWidgets() { | |||
| 454 | // Create status bar | 454 | // Create status bar |
| 455 | message_label = new QLabel(); | 455 | message_label = new QLabel(); |
| 456 | // Configured separately for left alignment | 456 | // Configured separately for left alignment |
| 457 | message_label->setVisible(false); | ||
| 458 | message_label->setFrameStyle(QFrame::NoFrame); | 457 | message_label->setFrameStyle(QFrame::NoFrame); |
| 459 | message_label->setContentsMargins(4, 0, 4, 0); | 458 | message_label->setContentsMargins(4, 0, 4, 0); |
| 460 | message_label->setAlignment(Qt::AlignLeft); | 459 | message_label->setAlignment(Qt::AlignLeft); |
| @@ -476,8 +475,73 @@ void GMainWindow::InitializeWidgets() { | |||
| 476 | label->setVisible(false); | 475 | label->setVisible(false); |
| 477 | label->setFrameStyle(QFrame::NoFrame); | 476 | label->setFrameStyle(QFrame::NoFrame); |
| 478 | label->setContentsMargins(4, 0, 4, 0); | 477 | label->setContentsMargins(4, 0, 4, 0); |
| 479 | statusBar()->addPermanentWidget(label, 0); | 478 | statusBar()->addPermanentWidget(label); |
| 480 | } | 479 | } |
| 480 | |||
| 481 | // Setup Dock button | ||
| 482 | dock_status_button = new QPushButton(); | ||
| 483 | dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||
| 484 | dock_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 485 | connect(dock_status_button, &QPushButton::clicked, [&] { | ||
| 486 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; | ||
| 487 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 488 | OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); | ||
| 489 | }); | ||
| 490 | dock_status_button->setText(tr("DOCK")); | ||
| 491 | dock_status_button->setCheckable(true); | ||
| 492 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 493 | statusBar()->insertPermanentWidget(0, dock_status_button); | ||
| 494 | |||
| 495 | // Setup ASync button | ||
| 496 | async_status_button = new QPushButton(); | ||
| 497 | async_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||
| 498 | async_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 499 | connect(async_status_button, &QPushButton::clicked, [&] { | ||
| 500 | if (emulation_running) { | ||
| 501 | return; | ||
| 502 | } | ||
| 503 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 504 | !Settings::values.use_asynchronous_gpu_emulation; | ||
| 505 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 506 | Settings::Apply(); | ||
| 507 | }); | ||
| 508 | async_status_button->setText(tr("ASYNC")); | ||
| 509 | async_status_button->setCheckable(true); | ||
| 510 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 511 | statusBar()->insertPermanentWidget(0, async_status_button); | ||
| 512 | |||
| 513 | // Setup Renderer API button | ||
| 514 | renderer_status_button = new QPushButton(); | ||
| 515 | renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); | ||
| 516 | renderer_status_button->setCheckable(true); | ||
| 517 | renderer_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 518 | connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) { | ||
| 519 | renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); | ||
| 520 | }); | ||
| 521 | renderer_status_button->toggle(); | ||
| 522 | |||
| 523 | #ifndef HAS_VULKAN | ||
| 524 | renderer_status_button->setChecked(false); | ||
| 525 | renderer_status_button->setCheckable(false); | ||
| 526 | renderer_status_button->setDisabled(true); | ||
| 527 | #else | ||
| 528 | renderer_status_button->setChecked(Settings::values.renderer_backend == | ||
| 529 | Settings::RendererBackend::Vulkan); | ||
| 530 | connect(renderer_status_button, &QPushButton::clicked, [=] { | ||
| 531 | if (emulation_running) { | ||
| 532 | return; | ||
| 533 | } | ||
| 534 | if (renderer_status_button->isChecked()) { | ||
| 535 | Settings::values.renderer_backend = Settings::RendererBackend::Vulkan; | ||
| 536 | } else { | ||
| 537 | Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; | ||
| 538 | } | ||
| 539 | |||
| 540 | Settings::Apply(); | ||
| 541 | }); | ||
| 542 | #endif // HAS_VULKAN | ||
| 543 | statusBar()->insertPermanentWidget(0, renderer_status_button); | ||
| 544 | |||
| 481 | statusBar()->setVisible(true); | 545 | statusBar()->setVisible(true); |
| 482 | setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); | 546 | setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); |
| 483 | } | 547 | } |
| @@ -640,6 +704,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 640 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; | 704 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; |
| 641 | OnDockedModeChanged(!Settings::values.use_docked_mode, | 705 | OnDockedModeChanged(!Settings::values.use_docked_mode, |
| 642 | Settings::values.use_docked_mode); | 706 | Settings::values.use_docked_mode); |
| 707 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 643 | }); | 708 | }); |
| 644 | } | 709 | } |
| 645 | 710 | ||
| @@ -806,70 +871,12 @@ void GMainWindow::AllowOSSleep() { | |||
| 806 | #endif | 871 | #endif |
| 807 | } | 872 | } |
| 808 | 873 | ||
| 809 | QStringList GMainWindow::GetUnsupportedGLExtensions() { | ||
| 810 | QStringList unsupported_ext; | ||
| 811 | |||
| 812 | if (!GLAD_GL_ARB_buffer_storage) { | ||
| 813 | unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); | ||
| 814 | } | ||
| 815 | if (!GLAD_GL_ARB_direct_state_access) { | ||
| 816 | unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); | ||
| 817 | } | ||
| 818 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) { | ||
| 819 | unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); | ||
| 820 | } | ||
| 821 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) { | ||
| 822 | unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); | ||
| 823 | } | ||
| 824 | if (!GLAD_GL_ARB_multi_bind) { | ||
| 825 | unsupported_ext.append(QStringLiteral("ARB_multi_bind")); | ||
| 826 | } | ||
| 827 | if (!GLAD_GL_ARB_clip_control) { | ||
| 828 | unsupported_ext.append(QStringLiteral("ARB_clip_control")); | ||
| 829 | } | ||
| 830 | |||
| 831 | // Extensions required to support some texture formats. | ||
| 832 | if (!GLAD_GL_EXT_texture_compression_s3tc) { | ||
| 833 | unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); | ||
| 834 | } | ||
| 835 | if (!GLAD_GL_ARB_texture_compression_rgtc) { | ||
| 836 | unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); | ||
| 837 | } | ||
| 838 | if (!GLAD_GL_ARB_depth_buffer_float) { | ||
| 839 | unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); | ||
| 840 | } | ||
| 841 | |||
| 842 | for (const QString& ext : unsupported_ext) { | ||
| 843 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); | ||
| 844 | } | ||
| 845 | |||
| 846 | return unsupported_ext; | ||
| 847 | } | ||
| 848 | |||
| 849 | bool GMainWindow::LoadROM(const QString& filename) { | 874 | bool GMainWindow::LoadROM(const QString& filename) { |
| 850 | // Shutdown previous session if the emu thread is still active... | 875 | // Shutdown previous session if the emu thread is still active... |
| 851 | if (emu_thread != nullptr) | 876 | if (emu_thread != nullptr) |
| 852 | ShutdownGame(); | 877 | ShutdownGame(); |
| 853 | 878 | ||
| 854 | render_window->InitRenderTarget(); | 879 | if (!render_window->InitRenderTarget()) { |
| 855 | |||
| 856 | { | ||
| 857 | Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window}; | ||
| 858 | if (!gladLoadGL()) { | ||
| 859 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"), | ||
| 860 | tr("Your GPU may not support OpenGL 4.3, or you do not " | ||
| 861 | "have the latest graphics driver.")); | ||
| 862 | return false; | ||
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); | ||
| 867 | if (!unsupported_gl_extensions.empty()) { | ||
| 868 | QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), | ||
| 869 | tr("Your GPU may not support one or more required OpenGL" | ||
| 870 | "extensions. Please ensure you have the latest graphics " | ||
| 871 | "driver.<br><br>Unsupported extensions:<br>") + | ||
| 872 | unsupported_gl_extensions.join(QStringLiteral("<br>"))); | ||
| 873 | return false; | 880 | return false; |
| 874 | } | 881 | } |
| 875 | 882 | ||
| @@ -980,7 +987,9 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 980 | // Create and start the emulation thread | 987 | // Create and start the emulation thread |
| 981 | emu_thread = std::make_unique<EmuThread>(render_window); | 988 | emu_thread = std::make_unique<EmuThread>(render_window); |
| 982 | emit EmulationStarting(emu_thread.get()); | 989 | emit EmulationStarting(emu_thread.get()); |
| 983 | render_window->moveContext(); | 990 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 991 | render_window->moveContext(); | ||
| 992 | } | ||
| 984 | emu_thread->start(); | 993 | emu_thread->start(); |
| 985 | 994 | ||
| 986 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 995 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| @@ -1000,6 +1009,8 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 1000 | game_list_placeholder->hide(); | 1009 | game_list_placeholder->hide(); |
| 1001 | } | 1010 | } |
| 1002 | status_bar_update_timer.start(2000); | 1011 | status_bar_update_timer.start(2000); |
| 1012 | async_status_button->setDisabled(true); | ||
| 1013 | renderer_status_button->setDisabled(true); | ||
| 1003 | 1014 | ||
| 1004 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 1015 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 1005 | 1016 | ||
| @@ -1065,10 +1076,13 @@ void GMainWindow::ShutdownGame() { | |||
| 1065 | 1076 | ||
| 1066 | // Disable status bar updates | 1077 | // Disable status bar updates |
| 1067 | status_bar_update_timer.stop(); | 1078 | status_bar_update_timer.stop(); |
| 1068 | message_label->setVisible(false); | ||
| 1069 | emu_speed_label->setVisible(false); | 1079 | emu_speed_label->setVisible(false); |
| 1070 | game_fps_label->setVisible(false); | 1080 | game_fps_label->setVisible(false); |
| 1071 | emu_frametime_label->setVisible(false); | 1081 | emu_frametime_label->setVisible(false); |
| 1082 | async_status_button->setEnabled(true); | ||
| 1083 | #ifdef HAS_VULKAN | ||
| 1084 | renderer_status_button->setEnabled(true); | ||
| 1085 | #endif | ||
| 1072 | 1086 | ||
| 1073 | emulation_running = false; | 1087 | emulation_running = false; |
| 1074 | 1088 | ||
| @@ -1836,6 +1850,13 @@ void GMainWindow::OnConfigure() { | |||
| 1836 | } | 1850 | } |
| 1837 | 1851 | ||
| 1838 | config->Save(); | 1852 | config->Save(); |
| 1853 | |||
| 1854 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 1855 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 1856 | #ifdef HAS_VULKAN | ||
| 1857 | renderer_status_button->setChecked(Settings::values.renderer_backend == | ||
| 1858 | Settings::RendererBackend::Vulkan); | ||
| 1859 | #endif | ||
| 1839 | } | 1860 | } |
| 1840 | 1861 | ||
| 1841 | void GMainWindow::OnLoadAmiibo() { | 1862 | void GMainWindow::OnLoadAmiibo() { |
| @@ -2028,7 +2049,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 2028 | if (emu_thread) { | 2049 | if (emu_thread) { |
| 2029 | emu_thread->SetRunning(true); | 2050 | emu_thread->SetRunning(true); |
| 2030 | message_label->setText(status_message); | 2051 | message_label->setText(status_message); |
| 2031 | message_label->setVisible(true); | ||
| 2032 | } | 2052 | } |
| 2033 | } | 2053 | } |
| 2034 | } | 2054 | } |
| @@ -2195,6 +2215,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 2195 | QWidget::closeEvent(event); | 2215 | QWidget::closeEvent(event); |
| 2196 | } | 2216 | } |
| 2197 | 2217 | ||
| 2218 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2219 | if (render_window) { | ||
| 2220 | render_window->ForwardKeyPressEvent(event); | ||
| 2221 | } | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2225 | if (render_window) { | ||
| 2226 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2227 | } | ||
| 2228 | } | ||
| 2229 | |||
| 2198 | static bool IsSingleFileDropEvent(QDropEvent* event) { | 2230 | static bool IsSingleFileDropEvent(QDropEvent* event) { |
| 2199 | const QMimeData* mimeData = event->mimeData(); | 2231 | const QMimeData* mimeData = event->mimeData(); |
| 2200 | return mimeData->hasUrls() && mimeData->urls().length() == 1; | 2232 | return mimeData->hasUrls() && mimeData->urls().length() == 1; |
| @@ -2227,18 +2259,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | |||
| 2227 | event->acceptProposedAction(); | 2259 | event->acceptProposedAction(); |
| 2228 | } | 2260 | } |
| 2229 | 2261 | ||
| 2230 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2231 | if (render_window) { | ||
| 2232 | render_window->ForwardKeyPressEvent(event); | ||
| 2233 | } | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2237 | if (render_window) { | ||
| 2238 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2239 | } | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | bool GMainWindow::ConfirmChangeGame() { | 2262 | bool GMainWindow::ConfirmChangeGame() { |
| 2243 | if (emu_thread == nullptr) | 2263 | if (emu_thread == nullptr) |
| 2244 | return true; | 2264 | return true; |
| @@ -2290,8 +2310,16 @@ void GMainWindow::UpdateUITheme() { | |||
| 2290 | QStringList theme_paths(default_theme_paths); | 2310 | QStringList theme_paths(default_theme_paths); |
| 2291 | 2311 | ||
| 2292 | if (is_default_theme || current_theme.isEmpty()) { | 2312 | if (is_default_theme || current_theme.isEmpty()) { |
| 2293 | qApp->setStyleSheet({}); | 2313 | const QString theme_uri(QStringLiteral(":default/style.qss")); |
| 2294 | setStyleSheet({}); | 2314 | QFile f(theme_uri); |
| 2315 | if (f.open(QFile::ReadOnly | QFile::Text)) { | ||
| 2316 | QTextStream ts(&f); | ||
| 2317 | qApp->setStyleSheet(ts.readAll()); | ||
| 2318 | setStyleSheet(ts.readAll()); | ||
| 2319 | } else { | ||
| 2320 | qApp->setStyleSheet({}); | ||
| 2321 | setStyleSheet({}); | ||
| 2322 | } | ||
| 2295 | theme_paths.append(default_icons); | 2323 | theme_paths.append(default_icons); |
| 2296 | QIcon::setThemeName(default_icons); | 2324 | QIcon::setThemeName(default_icons); |
| 2297 | } else { | 2325 | } else { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index a56f9a981..8eba2172c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -27,6 +27,7 @@ class LoadingScreen; | |||
| 27 | class MicroProfileDialog; | 27 | class MicroProfileDialog; |
| 28 | class ProfilerWidget; | 28 | class ProfilerWidget; |
| 29 | class QLabel; | 29 | class QLabel; |
| 30 | class QPushButton; | ||
| 30 | class WaitTreeWidget; | 31 | class WaitTreeWidget; |
| 31 | enum class GameListOpenTarget; | 32 | enum class GameListOpenTarget; |
| 32 | class GameListPlaceholder; | 33 | class GameListPlaceholder; |
| @@ -130,7 +131,6 @@ private: | |||
| 130 | void PreventOSSleep(); | 131 | void PreventOSSleep(); |
| 131 | void AllowOSSleep(); | 132 | void AllowOSSleep(); |
| 132 | 133 | ||
| 133 | QStringList GetUnsupportedGLExtensions(); | ||
| 134 | bool LoadROM(const QString& filename); | 134 | bool LoadROM(const QString& filename); |
| 135 | void BootGame(const QString& filename); | 135 | void BootGame(const QString& filename); |
| 136 | void ShutdownGame(); | 136 | void ShutdownGame(); |
| @@ -229,6 +229,9 @@ private: | |||
| 229 | QLabel* emu_speed_label = nullptr; | 229 | QLabel* emu_speed_label = nullptr; |
| 230 | QLabel* game_fps_label = nullptr; | 230 | QLabel* game_fps_label = nullptr; |
| 231 | QLabel* emu_frametime_label = nullptr; | 231 | QLabel* emu_frametime_label = nullptr; |
| 232 | QPushButton* async_status_button = nullptr; | ||
| 233 | QPushButton* renderer_status_button = nullptr; | ||
| 234 | QPushButton* dock_status_button = nullptr; | ||
| 232 | QTimer status_bar_update_timer; | 235 | QTimer status_bar_update_timer; |
| 233 | 236 | ||
| 234 | std::unique_ptr<Config> config; | 237 | std::unique_ptr<Config> config; |
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index b5f06ab9e..a15719a0f 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt | |||
| @@ -8,11 +8,22 @@ add_executable(yuzu-cmd | |||
| 8 | emu_window/emu_window_sdl2_gl.h | 8 | emu_window/emu_window_sdl2_gl.h |
| 9 | emu_window/emu_window_sdl2.cpp | 9 | emu_window/emu_window_sdl2.cpp |
| 10 | emu_window/emu_window_sdl2.h | 10 | emu_window/emu_window_sdl2.h |
| 11 | emu_window/emu_window_sdl2_gl.cpp | ||
| 12 | emu_window/emu_window_sdl2_gl.h | ||
| 11 | resource.h | 13 | resource.h |
| 12 | yuzu.cpp | 14 | yuzu.cpp |
| 13 | yuzu.rc | 15 | yuzu.rc |
| 14 | ) | 16 | ) |
| 15 | 17 | ||
| 18 | if (ENABLE_VULKAN) | ||
| 19 | target_sources(yuzu-cmd PRIVATE | ||
| 20 | emu_window/emu_window_sdl2_vk.cpp | ||
| 21 | emu_window/emu_window_sdl2_vk.h) | ||
| 22 | |||
| 23 | target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) | ||
| 24 | target_compile_definitions(yuzu-cmd PRIVATE HAS_VULKAN) | ||
| 25 | endif() | ||
| 26 | |||
| 16 | create_target_directory_groups(yuzu-cmd) | 27 | create_target_directory_groups(yuzu-cmd) |
| 17 | 28 | ||
| 18 | target_link_libraries(yuzu-cmd PRIVATE common core input_common) | 29 | target_link_libraries(yuzu-cmd PRIVATE common core input_common) |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 161583b54..b01a36023 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -371,6 +371,12 @@ void Config::ReadValues() { | |||
| 371 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); | 371 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); |
| 372 | 372 | ||
| 373 | // Renderer | 373 | // Renderer |
| 374 | const int renderer_backend = sdl2_config->GetInteger( | ||
| 375 | "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); | ||
| 376 | Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend); | ||
| 377 | Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); | ||
| 378 | Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0); | ||
| 379 | |||
| 374 | Settings::values.resolution_factor = | 380 | Settings::values.resolution_factor = |
| 375 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); | 381 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); |
| 376 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | 382 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index e829f8695..00fd88279 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -98,6 +98,17 @@ udp_pad_index= | |||
| 98 | use_multi_core= | 98 | use_multi_core= |
| 99 | 99 | ||
| 100 | [Renderer] | 100 | [Renderer] |
| 101 | # Which backend API to use. | ||
| 102 | # 0 (default): OpenGL, 1: Vulkan | ||
| 103 | backend = | ||
| 104 | |||
| 105 | # Enable graphics API debugging mode. | ||
| 106 | # 0 (default): Disabled, 1: Enabled | ||
| 107 | debug = | ||
| 108 | |||
| 109 | # Which Vulkan physical device to use (defaults to 0) | ||
| 110 | vulkan_device = | ||
| 111 | |||
| 101 | # Whether to use software or hardware rendering. | 112 | # Whether to use software or hardware rendering. |
| 102 | # 0: Software, 1 (default): Hardware | 113 | # 0: Software, 1 (default): Hardware |
| 103 | use_hw_renderer = | 114 | use_hw_renderer = |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index b1c512db1..e96139885 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -89,6 +89,10 @@ bool EmuWindow_SDL2::IsOpen() const { | |||
| 89 | return is_open; | 89 | return is_open; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | bool EmuWindow_SDL2::IsShown() const { | ||
| 93 | return is_shown; | ||
| 94 | } | ||
| 95 | |||
| 92 | void EmuWindow_SDL2::OnResize() { | 96 | void EmuWindow_SDL2::OnResize() { |
| 93 | int width, height; | 97 | int width, height; |
| 94 | SDL_GetWindowSize(render_window, &width, &height); | 98 | SDL_GetWindowSize(render_window, &width, &height); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index eaa971f77..b38f56661 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -21,6 +21,9 @@ public: | |||
| 21 | /// Whether the window is still open, and a close request hasn't yet been sent | 21 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 22 | bool IsOpen() const; | 22 | bool IsOpen() const; |
| 23 | 23 | ||
| 24 | /// Returns if window is shown (not minimized) | ||
| 25 | bool IsShown() const override; | ||
| 26 | |||
| 24 | protected: | 27 | protected: |
| 25 | /// Called by PollEvents when a key is pressed or released. | 28 | /// Called by PollEvents when a key is pressed or released. |
| 26 | void OnKeyEvent(int key, u8 state); | 29 | void OnKeyEvent(int key, u8 state); |
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 6fde694a2..7ffa0ac09 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <SDL.h> | 9 | #include <SDL.h> |
| 10 | #include <fmt/format.h> | 10 | #include <fmt/format.h> |
| 11 | #include <glad/glad.h> | 11 | #include <glad/glad.h> |
| 12 | #include "common/assert.h" | ||
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/scm_rev.h" | 14 | #include "common/scm_rev.h" |
| 14 | #include "common/string_util.h" | 15 | #include "common/string_util.h" |
| @@ -151,6 +152,12 @@ void EmuWindow_SDL2_GL::DoneCurrent() { | |||
| 151 | SDL_GL_MakeCurrent(render_window, nullptr); | 152 | SDL_GL_MakeCurrent(render_window, nullptr); |
| 152 | } | 153 | } |
| 153 | 154 | ||
| 155 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 156 | void* surface) const { | ||
| 157 | // Should not have been called from OpenGL | ||
| 158 | UNREACHABLE(); | ||
| 159 | } | ||
| 160 | |||
| 154 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | 161 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { |
| 155 | return std::make_unique<SDLGLContext>(); | 162 | return std::make_unique<SDLGLContext>(); |
| 156 | } | 163 | } |
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 630deba93..c753085a8 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -22,6 +22,10 @@ public: | |||
| 22 | /// Releases the GL context from the caller thread | 22 | /// Releases the GL context from the caller thread |
| 23 | void DoneCurrent() override; | 23 | void DoneCurrent() override; |
| 24 | 24 | ||
| 25 | /// Ignored in OpenGL | ||
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 27 | void* surface) const override; | ||
| 28 | |||
| 25 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 29 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 26 | 30 | ||
| 27 | private: | 31 | private: |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp new file mode 100644 index 000000000..a203f0da9 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <string> | ||
| 7 | #include <vector> | ||
| 8 | #include <SDL.h> | ||
| 9 | #include <SDL_vulkan.h> | ||
| 10 | #include <fmt/format.h> | ||
| 11 | #include <vulkan/vulkan.h> | ||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/logging/log.h" | ||
| 14 | #include "common/scm_rev.h" | ||
| 15 | #include "core/settings.h" | ||
| 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" | ||
| 17 | |||
| 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | ||
| 19 | if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { | ||
| 20 | LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); | ||
| 21 | exit(EXIT_FAILURE); | ||
| 22 | } | ||
| 23 | |||
| 24 | vkGetInstanceProcAddr = | ||
| 25 | reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr()); | ||
| 26 | if (vkGetInstanceProcAddr == nullptr) { | ||
| 27 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 28 | exit(EXIT_FAILURE); | ||
| 29 | } | ||
| 30 | |||
| 31 | const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, | ||
| 32 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 33 | render_window = | ||
| 34 | SDL_CreateWindow(window_title.c_str(), | ||
| 35 | SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 36 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 37 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 38 | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN); | ||
| 39 | |||
| 40 | const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr); | ||
| 41 | |||
| 42 | u32 extra_ext_count{}; | ||
| 43 | if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) { | ||
| 44 | LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}", | ||
| 45 | SDL_GetError()); | ||
| 46 | exit(1); | ||
| 47 | } | ||
| 48 | |||
| 49 | auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count); | ||
| 50 | if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) { | ||
| 51 | LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError()); | ||
| 52 | exit(1); | ||
| 53 | } | ||
| 54 | std::vector<const char*> enabled_extensions; | ||
| 55 | enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(), | ||
| 56 | extra_ext_names.get() + extra_ext_count); | ||
| 57 | |||
| 58 | std::vector<const char*> enabled_layers; | ||
| 59 | if (use_standard_layers) { | ||
| 60 | enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 61 | enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation"); | ||
| 62 | } | ||
| 63 | |||
| 64 | VkApplicationInfo app_info{}; | ||
| 65 | app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | ||
| 66 | app_info.apiVersion = VK_API_VERSION_1_1; | ||
| 67 | app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 68 | app_info.pApplicationName = "yuzu-emu"; | ||
| 69 | app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 70 | app_info.pEngineName = "yuzu-emu"; | ||
| 71 | |||
| 72 | VkInstanceCreateInfo instance_ci{}; | ||
| 73 | instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | ||
| 74 | instance_ci.pApplicationInfo = &app_info; | ||
| 75 | instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size()); | ||
| 76 | instance_ci.ppEnabledExtensionNames = enabled_extensions.data(); | ||
| 77 | if (Settings::values.renderer_debug) { | ||
| 78 | instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size()); | ||
| 79 | instance_ci.ppEnabledLayerNames = enabled_layers.data(); | ||
| 80 | } | ||
| 81 | |||
| 82 | const auto vkCreateInstance = | ||
| 83 | reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance")); | ||
| 84 | if (vkCreateInstance == nullptr || | ||
| 85 | vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) { | ||
| 86 | LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!"); | ||
| 87 | exit(EXIT_FAILURE); | ||
| 88 | } | ||
| 89 | |||
| 90 | vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>( | ||
| 91 | vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance")); | ||
| 92 | if (vkDestroyInstance == nullptr) { | ||
| 93 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 94 | exit(EXIT_FAILURE); | ||
| 95 | } | ||
| 96 | |||
| 97 | if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) { | ||
| 98 | LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError()); | ||
| 99 | exit(EXIT_FAILURE); | ||
| 100 | } | ||
| 101 | |||
| 102 | OnResize(); | ||
| 103 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||
| 104 | SDL_PumpEvents(); | ||
| 105 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name, | ||
| 106 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 107 | } | ||
| 108 | |||
| 109 | EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { | ||
| 110 | vkDestroyInstance(vk_instance, nullptr); | ||
| 111 | } | ||
| 112 | |||
| 113 | void EmuWindow_SDL2_VK::SwapBuffers() {} | ||
| 114 | |||
| 115 | void EmuWindow_SDL2_VK::MakeCurrent() { | ||
| 116 | // Unused on Vulkan | ||
| 117 | } | ||
| 118 | |||
| 119 | void EmuWindow_SDL2_VK::DoneCurrent() { | ||
| 120 | // Unused on Vulkan | ||
| 121 | } | ||
| 122 | |||
| 123 | void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 124 | void* surface) const { | ||
| 125 | const auto instance_proc_addr = vkGetInstanceProcAddr; | ||
| 126 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 127 | std::memcpy(instance, &vk_instance, sizeof(vk_instance)); | ||
| 128 | std::memcpy(surface, &vk_surface, sizeof(vk_surface)); | ||
| 129 | } | ||
| 130 | |||
| 131 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { | ||
| 132 | return nullptr; | ||
| 133 | } | ||
| 134 | |||
| 135 | bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const { | ||
| 136 | if (!Settings::values.renderer_debug) { | ||
| 137 | return false; | ||
| 138 | } | ||
| 139 | |||
| 140 | const auto vkEnumerateInstanceLayerProperties = | ||
| 141 | reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>( | ||
| 142 | vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties")); | ||
| 143 | if (vkEnumerateInstanceLayerProperties == nullptr) { | ||
| 144 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 145 | return false; | ||
| 146 | } | ||
| 147 | |||
| 148 | u32 available_layers_count{}; | ||
| 149 | if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) { | ||
| 150 | LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); | ||
| 151 | return false; | ||
| 152 | } | ||
| 153 | std::vector<VkLayerProperties> layers(available_layers_count); | ||
| 154 | if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) { | ||
| 155 | LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); | ||
| 156 | return false; | ||
| 157 | } | ||
| 158 | |||
| 159 | return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { | ||
| 160 | return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); | ||
| 161 | }) != layers.end(); | ||
| 162 | } | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h new file mode 100644 index 000000000..2a7c06a24 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2018 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 <vulkan/vulkan.h> | ||
| 8 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | ||
| 10 | |||
| 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | ||
| 12 | public: | ||
| 13 | explicit EmuWindow_SDL2_VK(bool fullscreen); | ||
| 14 | ~EmuWindow_SDL2_VK(); | ||
| 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; | ||
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | ||
| 24 | |||
| 25 | /// Retrieves Vulkan specific handlers from the window | ||
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 27 | void* surface) const override; | ||
| 28 | |||
| 29 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const; | ||
| 33 | |||
| 34 | VkInstance vk_instance{}; | ||
| 35 | VkSurfaceKHR vk_surface{}; | ||
| 36 | |||
| 37 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; | ||
| 38 | PFN_vkDestroyInstance vkDestroyInstance{}; | ||
| 39 | }; | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 3ee088a91..325795321 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -32,6 +32,9 @@ | |||
| 32 | #include "yuzu_cmd/config.h" | 32 | #include "yuzu_cmd/config.h" |
| 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 34 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | 34 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" |
| 35 | #ifdef HAS_VULKAN | ||
| 36 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" | ||
| 37 | #endif | ||
| 35 | 38 | ||
| 36 | #include "core/file_sys/registered_cache.h" | 39 | #include "core/file_sys/registered_cache.h" |
| 37 | 40 | ||
| @@ -174,7 +177,20 @@ int main(int argc, char** argv) { | |||
| 174 | Settings::values.use_gdbstub = use_gdbstub; | 177 | Settings::values.use_gdbstub = use_gdbstub; |
| 175 | Settings::Apply(); | 178 | Settings::Apply(); |
| 176 | 179 | ||
| 177 | std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)}; | 180 | std::unique_ptr<EmuWindow_SDL2> emu_window; |
| 181 | switch (Settings::values.renderer_backend) { | ||
| 182 | case Settings::RendererBackend::OpenGL: | ||
| 183 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen); | ||
| 184 | break; | ||
| 185 | case Settings::RendererBackend::Vulkan: | ||
| 186 | #ifdef HAS_VULKAN | ||
| 187 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen); | ||
| 188 | break; | ||
| 189 | #else | ||
| 190 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); | ||
| 191 | return 1; | ||
| 192 | #endif | ||
| 193 | } | ||
| 178 | 194 | ||
| 179 | if (!Settings::values.use_multi_core) { | 195 | if (!Settings::values.use_multi_core) { |
| 180 | // Single core mode must acquire OpenGL context for entire emulation session | 196 | // Single core mode must acquire OpenGL context for entire emulation session |
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 e7fe8decf..f2cc4a797 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | |||
| @@ -5,10 +5,15 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstdlib> | 6 | #include <cstdlib> |
| 7 | #include <string> | 7 | #include <string> |
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 8 | #define SDL_MAIN_HANDLED | 11 | #define SDL_MAIN_HANDLED |
| 9 | #include <SDL.h> | 12 | #include <SDL.h> |
| 10 | #include <fmt/format.h> | 13 | |
| 11 | #include <glad/glad.h> | 14 | #include <glad/glad.h> |
| 15 | |||
| 16 | #include "common/assert.h" | ||
| 12 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| 13 | #include "common/scm_rev.h" | 18 | #include "common/scm_rev.h" |
| 14 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| @@ -120,3 +125,11 @@ void EmuWindow_SDL2_Hide::MakeCurrent() { | |||
| 120 | void EmuWindow_SDL2_Hide::DoneCurrent() { | 125 | void EmuWindow_SDL2_Hide::DoneCurrent() { |
| 121 | SDL_GL_MakeCurrent(render_window, nullptr); | 126 | SDL_GL_MakeCurrent(render_window, nullptr); |
| 122 | } | 127 | } |
| 128 | |||
| 129 | bool EmuWindow_SDL2_Hide::IsShown() const { | ||
| 130 | return false; | ||
| 131 | } | ||
| 132 | |||
| 133 | void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { | ||
| 134 | UNREACHABLE(); | ||
| 135 | } | ||
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 1a8953c75..c7fccc002 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -25,6 +25,13 @@ public: | |||
| 25 | /// Releases the GL context from the caller thread | 25 | /// Releases the GL context from the caller thread |
| 26 | void DoneCurrent() override; | 26 | void DoneCurrent() override; |
| 27 | 27 | ||
| 28 | /// Whether the screen is being shown or not. | ||
| 29 | bool IsShown() const override; | ||
| 30 | |||
| 31 | /// Retrieves Vulkan specific handlers from the window | ||
| 32 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 33 | void* surface) const override; | ||
| 34 | |||
| 28 | /// Whether the window is still open, and a close request hasn't yet been sent | 35 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 29 | bool IsOpen() const; | 36 | bool IsOpen() const; |
| 30 | 37 | ||