diff options
Diffstat (limited to 'src')
79 files changed, 1785 insertions, 542 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp index c53d122be..0eb0c0dca 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -268,7 +268,9 @@ struct System::Impl { is_powered_on = false; exit_lock = false; - gpu_core->WaitIdle(); + if (gpu_core) { + gpu_core->WaitIdle(); + } // Shutdown emulation session renderer.reset(); 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: return nullptr; } + /// Returns if window is shown (not minimized) + virtual bool IsShown() const = 0; + + /// Retrieves Vulkan specific handlers from the window + virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const = 0; + /** * Signal that a touch pressed event has occurred (e.g. mouse click pressed) * @param framebuffer_x Framebuffer x-coordinate that was pressed diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 2db28dcf0..ab05788d7 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -284,13 +284,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { std::vector<u8> buffer; - const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; + const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && + BufferDescriptorA()[buffer_index].Size()}; auto& memory = Core::System::GetInstance().Memory(); if (is_buffer_a) { + ASSERT_MSG(BufferDescriptorA().size() > buffer_index, + "BufferDescriptorA invalid buffer_index {}", buffer_index); buffer.resize(BufferDescriptorA()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); } else { + ASSERT_MSG(BufferDescriptorX().size() > buffer_index, + "BufferDescriptorX invalid buffer_index {}", buffer_index); buffer.resize(BufferDescriptorX()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); } @@ -305,7 +310,8 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, return 0; } - const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; + const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && + BufferDescriptorB()[buffer_index].Size()}; const std::size_t buffer_size{GetWriteBufferSize(buffer_index)}; if (size > buffer_size) { LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, @@ -315,8 +321,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, auto& memory = Core::System::GetInstance().Memory(); if (is_buffer_b) { + ASSERT_MSG(BufferDescriptorB().size() > buffer_index, + "BufferDescriptorB invalid buffer_index {}", buffer_index); + ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, + "BufferDescriptorB buffer_index {} is not large enough", buffer_index); memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); } else { + ASSERT_MSG(BufferDescriptorC().size() > buffer_index, + "BufferDescriptorC invalid buffer_index {}", buffer_index); + ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, + "BufferDescriptorC buffer_index {} is not large enough", buffer_index); memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); } @@ -324,15 +338,35 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, } std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { - const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; - return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() - : BufferDescriptorX()[buffer_index].Size(); + const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && + BufferDescriptorA()[buffer_index].Size()}; + if (is_buffer_a) { + ASSERT_MSG(BufferDescriptorA().size() > buffer_index, + "BufferDescriptorA invalid buffer_index {}", buffer_index); + ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0, + "BufferDescriptorA buffer_index {} is empty", buffer_index); + return BufferDescriptorA()[buffer_index].Size(); + } else { + ASSERT_MSG(BufferDescriptorX().size() > buffer_index, + "BufferDescriptorX invalid buffer_index {}", buffer_index); + ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0, + "BufferDescriptorX buffer_index {} is empty", buffer_index); + return BufferDescriptorX()[buffer_index].Size(); + } } std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { - const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; - return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() - : BufferDescriptorC()[buffer_index].Size(); + const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && + BufferDescriptorB()[buffer_index].Size()}; + if (is_buffer_b) { + ASSERT_MSG(BufferDescriptorB().size() > buffer_index, + "BufferDescriptorB invalid buffer_index {}", buffer_index); + return BufferDescriptorB()[buffer_index].Size(); + } else { + ASSERT_MSG(BufferDescriptorC().size() > buffer_index, + "BufferDescriptorC invalid buffer_index {}", buffer_index); + return BufferDescriptorC()[buffer_index].Size(); + } } std::string HLERequestContext::Description() const { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 1d99bf7a2..9cae5c73d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1863,10 +1863,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd } auto& kernel = system.Kernel(); - auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); + auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms); + + if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) { + return reserve_result; + } auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); - const auto result = handle_table.Create(std::move(transfer_mem_handle)); + const auto result{handle_table.Create(std::move(transfer_mem_handle))}; if (result.Failed()) { return result.Code(); } diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index f0e73f57b..f2d3f8b49 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp @@ -8,15 +8,23 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/transfer_memory.h" #include "core/hle/result.h" +#include "core/memory.h" namespace Kernel { -TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} -TransferMemory::~TransferMemory() = default; +TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory) + : Object{kernel}, memory{memory} {} -std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, - u64 size, MemoryPermission permissions) { - std::shared_ptr<TransferMemory> transfer_memory{std::make_shared<TransferMemory>(kernel)}; +TransferMemory::~TransferMemory() { + // Release memory region when transfer memory is destroyed + Reset(); +} + +std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory, + VAddr base_address, u64 size, + MemoryPermission permissions) { + std::shared_ptr<TransferMemory> transfer_memory{ + std::make_shared<TransferMemory>(kernel, memory)}; transfer_memory->base_address = base_address; transfer_memory->memory_size = size; @@ -27,7 +35,7 @@ std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr } const u8* TransferMemory::GetPointer() const { - return backing_block.get()->data(); + return memory.GetPointer(base_address); } u64 TransferMemory::GetSize() const { @@ -62,6 +70,52 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p return RESULT_SUCCESS; } +ResultCode TransferMemory::Reserve() { + auto& vm_manager{owner_process->VMManager()}; + const auto check_range_result{vm_manager.CheckRangeState( + base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, + MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All, + VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, + MemoryAttribute::IpcAndDeviceMapped)}; + + if (check_range_result.Failed()) { + return check_range_result.Code(); + } + + auto [state_, permissions_, attribute] = *check_range_result; + + if (const auto result{vm_manager.ReprotectRange( + base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))}; + result.IsError()) { + return result; + } + + return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, + attribute | MemoryAttribute::Locked); +} + +ResultCode TransferMemory::Reset() { + auto& vm_manager{owner_process->VMManager()}; + if (const auto result{vm_manager.CheckRangeState( + base_address, memory_size, + MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, + MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None, + VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, + MemoryAttribute::IpcAndDeviceMapped)}; + result.Failed()) { + return result.Code(); + } + + if (const auto result{ + vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)}; + result.IsError()) { + return result; + } + + return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, + MemoryAttribute::None); +} + ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { if (memory_size != size) { return ERR_INVALID_SIZE; diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index 0a6e15d18..6e388536a 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h @@ -11,6 +11,10 @@ union ResultCode; +namespace Memory { +class Memory; +} + namespace Kernel { class KernelCore; @@ -26,12 +30,13 @@ enum class MemoryPermission : u32; /// class TransferMemory final : public Object { public: - explicit TransferMemory(KernelCore& kernel); + explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory); ~TransferMemory() override; static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; - static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size, + static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory, + VAddr base_address, u64 size, MemoryPermission permissions); TransferMemory(const TransferMemory&) = delete; @@ -80,6 +85,14 @@ public: /// ResultCode UnmapMemory(VAddr address, u64 size); + /// Reserves the region to be used for the transfer memory, called after the transfer memory is + /// created. + ResultCode Reserve(); + + /// Resets the region previously used for the transfer memory, called after the transfer memory + /// is closed. + ResultCode Reset(); + private: /// Memory block backing this instance. std::shared_ptr<PhysicalMemory> backing_block; @@ -98,6 +111,8 @@ private: /// Whether or not this transfer memory instance has mapped memory. bool is_mapped = false; + + Memory::Memory& memory; }; } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 0b3500fce..024c22901 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -544,7 +544,8 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const { ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, MemoryAttribute attribute) { - constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped; + constexpr auto ignore_mask = + MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked; constexpr auto attribute_mask = ~ignore_mask; const auto result = CheckRangeState( diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 850a7ebc3..90b4b006a 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -98,6 +98,8 @@ enum class MemoryAttribute : u32 { DeviceMapped = 4, /// Uncached memory Uncached = 8, + + IpcAndDeviceMapped = LockedForIPC | DeviceMapped, }; constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { @@ -654,6 +656,35 @@ public: /// is scheduled. Common::PageTable page_table{Memory::PAGE_BITS}; + using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; + + /// Checks if an address range adheres to the specified states provided. + /// + /// @param address The starting address of the address range. + /// @param size The size of the address range. + /// @param state_mask The memory state mask. + /// @param state The state to compare the individual VMA states against, + /// which is done in the form of: (vma.state & state_mask) != state. + /// @param permission_mask The memory permissions mask. + /// @param permissions The permission to compare the individual VMA permissions against, + /// which is done in the form of: + /// (vma.permission & permission_mask) != permission. + /// @param attribute_mask The memory attribute mask. + /// @param attribute The memory attributes to compare the individual VMA attributes + /// against, which is done in the form of: + /// (vma.attributes & attribute_mask) != attribute. + /// @param ignore_mask The memory attributes to ignore during the check. + /// + /// @returns If successful, returns a tuple containing the memory attributes + /// (with ignored bits specified by ignore_mask unset), memory permissions, and + /// memory state across the memory range. + /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. + /// + CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, + VMAPermission permission_mask, VMAPermission permissions, + MemoryAttribute attribute_mask, MemoryAttribute attribute, + MemoryAttribute ignore_mask) const; + private: using VMAIter = VMAMap::iterator; @@ -707,35 +738,6 @@ private: /// Clears out the page table void ClearPageTable(); - using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; - - /// Checks if an address range adheres to the specified states provided. - /// - /// @param address The starting address of the address range. - /// @param size The size of the address range. - /// @param state_mask The memory state mask. - /// @param state The state to compare the individual VMA states against, - /// which is done in the form of: (vma.state & state_mask) != state. - /// @param permission_mask The memory permissions mask. - /// @param permissions The permission to compare the individual VMA permissions against, - /// which is done in the form of: - /// (vma.permission & permission_mask) != permission. - /// @param attribute_mask The memory attribute mask. - /// @param attribute The memory attributes to compare the individual VMA attributes - /// against, which is done in the form of: - /// (vma.attributes & attribute_mask) != attribute. - /// @param ignore_mask The memory attributes to ignore during the check. - /// - /// @returns If successful, returns a tuple containing the memory attributes - /// (with ignored bits specified by ignore_mask unset), memory permissions, and - /// memory state across the memory range. - /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. - /// - CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, - VMAPermission permission_mask, VMAPermission permissions, - MemoryAttribute attribute_mask, MemoryAttribute attribute, - MemoryAttribute ignore_mask) const; - /// Gets the amount of memory currently mapped (state != Unmapped) in a range. ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index a0c806e8f..1838260fd 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -50,17 +50,8 @@ std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const { if (ShouldWait(thread.get())) continue; - // A thread is ready to run if it's either in ThreadStatus::WaitSynch - // and the rest of the objects it is waiting on are ready. - bool ready_to_run = true; - if (thread_status == ThreadStatus::WaitSynch) { - ready_to_run = thread->AllWaitObjectsReady(); - } - - if (ready_to_run) { - candidate = thread.get(); - candidate_priority = thread->GetPriority(); - } + candidate = thread.get(); + candidate_priority = thread->GetPriority(); } return SharedFrom(candidate); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 95aa5d23d..cc978713b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -709,8 +709,34 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { apm_sys->SetCpuBoostMode(ctx); } -IStorage::IStorage(std::vector<u8> buffer) - : ServiceFramework("IStorage"), buffer(std::move(buffer)) { +IStorageImpl::~IStorageImpl() = default; + +class StorageDataImpl final : public IStorageImpl { +public: + explicit StorageDataImpl(std::vector<u8>&& buffer) : buffer{std::move(buffer)} {} + + std::vector<u8>& GetData() override { + return buffer; + } + + const std::vector<u8>& GetData() const override { + return buffer; + } + + std::size_t GetSize() const override { + return buffer.size(); + } + +private: + std::vector<u8> buffer; +}; + +IStorage::IStorage(std::vector<u8>&& buffer) + : ServiceFramework("IStorage"), impl{std::make_shared<StorageDataImpl>(std::move(buffer))} { + Register(); +} + +void IStorage::Register() { // clang-format off static const FunctionInfo functions[] = { {0, &IStorage::Open, "Open"}, @@ -723,8 +749,13 @@ IStorage::IStorage(std::vector<u8> buffer) IStorage::~IStorage() = default; -const std::vector<u8>& IStorage::GetData() const { - return buffer; +void IStorage::Open(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorageAccessor>(*this); } void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { @@ -816,7 +847,7 @@ private: LOG_DEBUG(Service_AM, "called"); IPC::RequestParser rp{ctx}; - applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); + applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -825,26 +856,25 @@ private: void PopOutData(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - const auto storage = applet->GetBroker().PopNormalDataToGame(); if (storage == nullptr) { LOG_ERROR(Service_AM, "storage is a nullptr. There is no data in the current normal channel"); - + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NO_DATA_IN_CHANNEL); return; } + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IStorage>(std::move(*storage)); + rb.PushIpcInterface<IStorage>(std::move(storage)); } void PushInteractiveInData(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); IPC::RequestParser rp{ctx}; - applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); + applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); ASSERT(applet->IsInitialized()); applet->ExecuteInteractive(); @@ -857,19 +887,18 @@ private: void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - const auto storage = applet->GetBroker().PopInteractiveDataToGame(); if (storage == nullptr) { LOG_ERROR(Service_AM, "storage is a nullptr. There is no data in the current interactive channel"); - + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NO_DATA_IN_CHANNEL); return; } + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IStorage>(std::move(*storage)); + rb.PushIpcInterface<IStorage>(std::move(storage)); } void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { @@ -891,15 +920,6 @@ private: std::shared_ptr<Applets::Applet> applet; }; -void IStorage::Open(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IStorageAccessor>(*this); -} - IStorageAccessor::IStorageAccessor(IStorage& storage) : ServiceFramework("IStorageAccessor"), backing(storage) { // clang-format off @@ -921,7 +941,7 @@ void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u64>(backing.buffer.size())); + rb.Push(static_cast<u64>(backing.GetSize())); } void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { @@ -932,17 +952,17 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); - if (data.size() > backing.buffer.size() - offset) { + if (data.size() > backing.GetSize() - offset) { LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", - backing.buffer.size(), data.size(), offset); + backing.GetSize(), data.size(), offset); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_SIZE_OUT_OF_BOUNDS); return; } - std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); + std::memcpy(backing.GetData().data() + offset, data.data(), data.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -956,16 +976,16 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); - if (size > backing.buffer.size() - offset) { + if (size > backing.GetSize() - offset) { LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", - backing.buffer.size(), size, offset); + backing.GetSize(), size, offset); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_SIZE_OUT_OF_BOUNDS); return; } - ctx.WriteBuffer(backing.buffer.data() + offset, size); + ctx.WriteBuffer(backing.GetData().data() + offset, size); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -1031,7 +1051,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex rp.SetCurrentOffset(3); const auto handle{rp.Pop<Kernel::Handle>()}; - const auto transfer_mem = + auto transfer_mem = system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); if (transfer_mem == nullptr) { @@ -1047,7 +1067,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); + rb.PushIpcInterface<IStorage>(std::move(memory)); } IApplicationFunctions::IApplicationFunctions(Core::System& system_) @@ -1189,13 +1209,11 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { u64 build_id{}; std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); - const auto data = - backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id}); - + auto data = backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id}); if (data.has_value()) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<AM::IStorage>(*data); + rb.PushIpcInterface<IStorage>(std::move(*data)); launch_popped_application_specific = true; return; } @@ -1218,7 +1236,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); std::memcpy(buffer.data(), ¶ms, buffer.size()); - rb.PushIpcInterface<AM::IStorage>(buffer); + rb.PushIpcInterface<IStorage>(std::move(buffer)); launch_popped_account_preselect = true; return; } diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 448817be9..0b9a4332d 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -12,7 +12,8 @@ namespace Kernel { class KernelCore; -} +class TransferMemory; +} // namespace Kernel namespace Service::NVFlinger { class NVFlinger; @@ -188,19 +189,36 @@ private: std::shared_ptr<AppletMessageQueue> msg_queue; }; +class IStorageImpl { +public: + virtual ~IStorageImpl(); + virtual std::vector<u8>& GetData() = 0; + virtual const std::vector<u8>& GetData() const = 0; + virtual std::size_t GetSize() const = 0; +}; + class IStorage final : public ServiceFramework<IStorage> { public: - explicit IStorage(std::vector<u8> buffer); + explicit IStorage(std::vector<u8>&& buffer); ~IStorage() override; - const std::vector<u8>& GetData() const; + std::vector<u8>& GetData() { + return impl->GetData(); + } + + const std::vector<u8>& GetData() const { + return impl->GetData(); + } + + std::size_t GetSize() const { + return impl->GetSize(); + } private: + void Register(); void Open(Kernel::HLERequestContext& ctx); - std::vector<u8> buffer; - - friend class IStorageAccessor; + std::shared_ptr<IStorageImpl> impl; }; class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 92f995f8f..c3261f3e6 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -50,16 +50,17 @@ AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() co return {std::move(out_normal), std::move(out_interactive)}; } -std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { +std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { if (out_channel.empty()) return nullptr; auto out = std::move(out_channel.front()); out_channel.pop_front(); + pop_out_data_event.writable->Clear(); return out; } -std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { +std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { if (in_channel.empty()) return nullptr; @@ -68,16 +69,17 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { return out; } -std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { +std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { if (out_interactive_channel.empty()) return nullptr; auto out = std::move(out_interactive_channel.front()); out_interactive_channel.pop_front(); + pop_interactive_out_data_event.writable->Clear(); return out; } -std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { +std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { if (in_interactive_channel.empty()) return nullptr; @@ -86,21 +88,21 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { return out; } -void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { - in_channel.push_back(std::make_unique<IStorage>(storage)); +void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage) { + in_channel.emplace_back(std::move(storage)); } -void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { - out_channel.push_back(std::make_unique<IStorage>(storage)); +void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) { + out_channel.emplace_back(std::move(storage)); pop_out_data_event.writable->Signal(); } -void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { - in_interactive_channel.push_back(std::make_unique<IStorage>(storage)); +void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) { + in_interactive_channel.emplace_back(std::move(storage)); } -void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { - out_interactive_channel.push_back(std::make_unique<IStorage>(storage)); +void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) { + out_interactive_channel.emplace_back(std::move(storage)); pop_interactive_out_data_event.writable->Signal(); } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 16e61fc6f..e75be86a2 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -72,17 +72,17 @@ public: // Retrieves but does not pop the data sent to applet. RawChannelData PeekDataToAppletForDebug() const; - std::unique_ptr<IStorage> PopNormalDataToGame(); - std::unique_ptr<IStorage> PopNormalDataToApplet(); + std::shared_ptr<IStorage> PopNormalDataToGame(); + std::shared_ptr<IStorage> PopNormalDataToApplet(); - std::unique_ptr<IStorage> PopInteractiveDataToGame(); - std::unique_ptr<IStorage> PopInteractiveDataToApplet(); + std::shared_ptr<IStorage> PopInteractiveDataToGame(); + std::shared_ptr<IStorage> PopInteractiveDataToApplet(); - void PushNormalDataFromGame(IStorage storage); - void PushNormalDataFromApplet(IStorage storage); + void PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage); + void PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage); - void PushInteractiveDataFromGame(IStorage storage); - void PushInteractiveDataFromApplet(IStorage storage); + void PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage); + void PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage); void SignalStateChanged() const; @@ -94,16 +94,16 @@ private: // Queues are named from applet's perspective // PopNormalDataToApplet and PushNormalDataFromGame - std::deque<std::unique_ptr<IStorage>> in_channel; + std::deque<std::shared_ptr<IStorage>> in_channel; // PopNormalDataToGame and PushNormalDataFromApplet - std::deque<std::unique_ptr<IStorage>> out_channel; + std::deque<std::shared_ptr<IStorage>> out_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame - std::deque<std::unique_ptr<IStorage>> in_interactive_channel; + std::deque<std::shared_ptr<IStorage>> in_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet - std::deque<std::unique_ptr<IStorage>> out_interactive_channel; + std::deque<std::shared_ptr<IStorage>> out_interactive_channel; Kernel::EventPair state_changed_event; diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index eab0d42c9..f12fd7f89 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp @@ -186,7 +186,7 @@ void Error::Execute() { void Error::DisplayCompleted() { complete = true; - broker.PushNormalDataFromApplet(IStorage{{}}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{})); broker.SignalStateChanged(); } diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index 328438a1d..104501ac5 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp @@ -20,7 +20,7 @@ namespace Service::AM::Applets { constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { - std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet(); + std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet(); for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { const auto data = storage->GetData(); LOG_INFO(Service_AM, @@ -148,7 +148,7 @@ void Auth::AuthFinished(bool successful) { std::vector<u8> out(sizeof(Return)); std::memcpy(out.data(), &return_, sizeof(Return)); - broker.PushNormalDataFromApplet(IStorage{out}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out))); broker.SignalStateChanged(); } @@ -198,7 +198,7 @@ void PhotoViewer::Execute() { } void PhotoViewer::ViewFinished() { - broker.PushNormalDataFromApplet(IStorage{{}}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{})); broker.SignalStateChanged(); } @@ -234,8 +234,8 @@ void StubApplet::ExecuteInteractive() { LOG_WARNING(Service_AM, "called (STUBBED)"); LogCurrentStorage(broker, "ExecuteInteractive"); - broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); - broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); broker.SignalStateChanged(); } @@ -243,8 +243,8 @@ void StubApplet::Execute() { LOG_WARNING(Service_AM, "called (STUBBED)"); LogCurrentStorage(broker, "Execute"); - broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); - broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); broker.SignalStateChanged(); } diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 3eba696ca..70cc23552 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -50,7 +50,7 @@ void ProfileSelect::ExecuteInteractive() { void ProfileSelect::Execute() { if (complete) { - broker.PushNormalDataFromApplet(IStorage{final_data}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data))); return; } @@ -71,7 +71,7 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { final_data = std::vector<u8>(sizeof(UserSelectionOutput)); std::memcpy(final_data.data(), &output, final_data.size()); - broker.PushNormalDataFromApplet(IStorage{final_data}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data))); broker.SignalStateChanged(); } diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 748559cd0..54e63c138 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -102,7 +102,8 @@ void SoftwareKeyboard::ExecuteInteractive() { void SoftwareKeyboard::Execute() { if (complete) { - broker.PushNormalDataFromApplet(IStorage{final_data}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data))); + broker.SignalStateChanged(); return; } @@ -119,7 +120,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); if (config.utf_8) { - const u64 size = text->size() + 8; + const u64 size = text->size() + sizeof(u64); const auto new_text = Common::UTF16ToUTF8(*text); std::memcpy(output_sub.data(), &size, sizeof(u64)); @@ -130,7 +131,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { std::memcpy(output_main.data() + 4, new_text.data(), std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); } else { - const u64 size = text->size() * 2 + 8; + const u64 size = text->size() * 2 + sizeof(u64); std::memcpy(output_sub.data(), &size, sizeof(u64)); std::memcpy(output_sub.data() + 8, text->data(), std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); @@ -144,15 +145,15 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { final_data = output_main; if (complete) { - broker.PushNormalDataFromApplet(IStorage{output_main}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main))); broker.SignalStateChanged(); } else { - broker.PushInteractiveDataFromApplet(IStorage{output_sub}); + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::move(output_sub))); } } else { output_main[0] = 1; complete = true; - broker.PushNormalDataFromApplet(IStorage{output_main}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main))); broker.SignalStateChanged(); } } diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 5546ef6e8..12443c910 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -284,7 +284,7 @@ void WebBrowser::Finalize() { std::vector<u8> data(sizeof(WebCommonReturnValue)); std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue)); - broker.PushNormalDataFromApplet(IStorage{data}); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); broker.SignalStateChanged(); if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 55d62fc5e..e6811d5b5 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -420,7 +420,7 @@ public: return; } - IFile file(result.Unwrap()); + auto file = std::make_shared<IFile>(result.Unwrap()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -445,7 +445,7 @@ public: return; } - IDirectory directory(result.Unwrap()); + auto directory = std::make_shared<IDirectory>(result.Unwrap()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -794,8 +794,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) { void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); - IFileSystem filesystem(fsc.OpenSDMC().Unwrap(), - SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); + auto filesystem = std::make_shared<IFileSystem>( + fsc.OpenSDMC().Unwrap(), SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -846,7 +846,8 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { id = FileSys::StorageId::NandSystem; } - IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); + auto filesystem = + std::make_shared<IFileSystem>(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -898,7 +899,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { return; } - IStorage storage(std::move(romfs.Unwrap())); + auto storage = std::make_shared<IStorage>(std::move(romfs.Unwrap())); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -937,7 +938,8 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { FileSys::PatchManager pm{title_id}; - IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); + auto storage = std::make_shared<IStorage>( + pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 5eb26caf8..8f1be0e48 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -50,16 +50,16 @@ private: IPC::RequestParser rp{ctx}; const auto process_id = rp.PopRaw<u64>(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = ctx.ReadBuffer(1); + std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; + if (Type == Core::Reporter::PlayReportType::New) { + data.emplace_back(ctx.ReadBuffer(1)); + } - LOG_DEBUG(Service_PREPO, - "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", - static_cast<u8>(Type), process_id, data1.size(), data2.size()); + LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}", + static_cast<u8>(Type), process_id, data[0].size()); const auto& reporter{system.GetReporter()}; - reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, - process_id); + reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -70,19 +70,19 @@ private: IPC::RequestParser rp{ctx}; const auto user_id = rp.PopRaw<u128>(); const auto process_id = rp.PopRaw<u64>(); - - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = ctx.ReadBuffer(1); + std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; + if (Type == Core::Reporter::PlayReportType::New) { + data.emplace_back(ctx.ReadBuffer(1)); + } LOG_DEBUG( Service_PREPO, - "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, " - "data2_size={:016X}", - static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size()); + "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}", + static_cast<u8>(Type), user_id[1], user_id[0], process_id, data[0].size()); const auto& reporter{system.GetReporter()}; - reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, - process_id, user_id); + reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id, + user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); 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 { S1TB = 0x10000000000ULL, }; +enum class RendererBackend { + OpenGL = 0, + Vulkan = 1, +}; + struct Values { // System bool use_docked_mode; @@ -419,6 +424,10 @@ struct Values { SDMCSize sdmc_size; // Renderer + RendererBackend renderer_backend; + bool renderer_debug; + int vulkan_device; + float resolution_factor; bool use_frame_limit; 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() { return telemetry_id; } +static const char* TranslateRenderer(Settings::RendererBackend backend) { + switch (backend) { + case Settings::RendererBackend::OpenGL: + return "OpenGL"; + case Settings::RendererBackend::Vulkan: + return "Vulkan"; + } + return "Unknown"; +} + u64 GetTelemetryId() { u64 telemetry_id{}; const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + @@ -169,7 +179,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { AddField(field_type, "Audio_SinkId", Settings::values.sink_id); AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); - AddField(field_type, "Renderer_Backend", "OpenGL"); + AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend)); AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); 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() { Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); motion_emu.reset(); sdl.reset(); + udp.reset(); } Keyboard* GetKeyboard() { 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 @@ #include "input_common/udp/client.h" #include "input_common/udp/protocol.h" -using boost::asio::ip::address_v4; using boost::asio::ip::udp; namespace InputCommon::CemuhookUDP { @@ -31,10 +30,10 @@ public: explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, SocketCallback callback) - : client_id(client_id), timer(io_service), - send_endpoint(udp::endpoint(address_v4::from_string(host), port)), - socket(io_service, udp::endpoint(udp::v4(), 0)), pad_index(pad_index), - callback(std::move(callback)) {} + : callback(std::move(callback)), timer(io_service), + socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), + pad_index(pad_index), + send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {} void Stop() { io_service.stop(); @@ -126,7 +125,7 @@ static void SocketLoop(Socket* socket) { Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, u8 pad_index, u32 client_id) - : status(status) { + : status(std::move(status)) { StartCommunication(host, port, pad_index, client_id); } @@ -207,7 +206,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie Common::Event success_event; SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, [&](Response::PadData data) { success_event.Set(); }}; - Socket socket{host, port, pad_index, client_id, callback}; + Socket socket{host, port, pad_index, client_id, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; bool result = success_event.WaitFor(std::chrono::seconds(8)); socket.Stop(); @@ -267,7 +266,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( complete_event.Set(); } }}; - Socket socket{host, port, pad_index, client_id, callback}; + Socket socket{host, port, pad_index, client_id, std::move(callback)}; std::thread worker_thread{SocketLoop, &socket}; complete_event.Wait(); 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 @@ #include <string> #include <thread> #include <tuple> -#include <vector> #include "common/common_types.h" #include "common/thread.h" #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 @@ #include <array> #include <optional> #include <type_traits> -#include <vector> #include <boost/crc.hpp> #include "common/bit_field.h" #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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/log.h" +#include <mutex> +#include <tuple> + #include "common/param_package.h" #include "core/frontend/input.h" #include "core/settings.h" @@ -14,7 +16,7 @@ namespace InputCommon::CemuhookUDP { class UDPTouchDevice final : public Input::TouchDevice { public: explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} - std::tuple<float, float, bool> GetStatus() const { + std::tuple<float, float, bool> GetStatus() const override { std::lock_guard guard(status->update_mutex); return status->touch_status; } @@ -26,7 +28,7 @@ private: class UDPMotionDevice final : public Input::MotionDevice { public: explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} - std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const { + std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { std::lock_guard guard(status->update_mutex); return status->motion_status; } 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <memory> -#include <unordered_map> -#include "input_common/main.h" -#include "input_common/udp/client.h" namespace InputCommon::CemuhookUDP { -class UDPTouchDevice; -class UDPMotionDevice; +class Client; class State { public: diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 04a25da4f..db9332d00 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -156,6 +156,7 @@ if (ENABLE_VULKAN) renderer_vulkan/maxwell_to_vk.cpp renderer_vulkan/maxwell_to_vk.h renderer_vulkan/renderer_vulkan.h + renderer_vulkan/renderer_vulkan.cpp renderer_vulkan/vk_blit_screen.cpp renderer_vulkan/vk_blit_screen.h 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: void TickFrame() { ++epoch; while (!pending_destruction.empty()) { - if (pending_destruction.front()->GetEpoch() + 1 > epoch) { + // Delay at least 4 frames before destruction. + // This is due to triple buffering happening on some drivers. + static constexpr u64 epochs_to_destroy = 5; + if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) { break; } pending_destruction.pop_front(); diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index f443ec0fe..402869fde 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -624,6 +624,19 @@ enum class ShuffleOperation : u64 { Bfly = 3, // shuffleXorNV }; +enum class ShfType : u64 { + Bits32 = 0, + U64 = 2, + S64 = 3, +}; + +enum class ShfXmode : u64 { + None = 0, + HI = 1, + X = 2, + XHI = 3, +}; + union Instruction { constexpr Instruction& operator=(const Instruction& instr) { value = instr.value; @@ -776,6 +789,13 @@ union Instruction { } shr; union { + BitField<37, 2, ShfType> type; + BitField<48, 2, ShfXmode> xmode; + BitField<50, 1, u64> wrap; + BitField<20, 6, u64> immediate; + } shf; + + union { BitField<39, 5, u64> shift_amount; BitField<48, 1, u64> negate_b; BitField<49, 1, u64> negate_a; @@ -1124,6 +1144,11 @@ union Instruction { } fset; union { + BitField<47, 1, u64> ftz; + BitField<48, 4, PredCondition> cond; + } fcmp; + + union { BitField<49, 1, u64> bf; BitField<35, 3, PredCondition> cond; BitField<50, 1, u64> ftz; @@ -1703,6 +1728,7 @@ public: BFE_C, BFE_R, BFE_IMM, + BFI_RC, BFI_IMM_R, BRA, BRX, @@ -1800,6 +1826,7 @@ public: ICMP_R, ICMP_CR, ICMP_IMM, + FCMP_R, MUFU, // Multi-Function Operator RRO_C, // Range Reduction Operator RRO_R, @@ -2104,6 +2131,7 @@ private: INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), + INST("010110111010----", Id::FCMP_R, Type::Arithmetic, "FCMP_R"), INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), @@ -2128,6 +2156,7 @@ private: INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), + INST("0101001111110---", Id::BFI_RC, Type::Bfi, "BFI_RC"), INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index b9c5c41a2..062ca83b8 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -23,7 +23,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) : system{system}, renderer{renderer}, is_async{is_async} { auto& rasterizer{renderer.Rasterizer()}; - memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); + memory_manager = std::make_unique<Tegra::MemoryManager>(system); dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 08dc96bb3..882e2d9c7 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -86,7 +86,7 @@ struct CommandDataContainer { struct SynchState final { std::atomic_bool is_running{true}; - using CommandQueue = Common::SPSCQueue<CommandDataContainer>; + using CommandQueue = Common::MPSCQueue<CommandDataContainer>; CommandQueue queue; u64 last_fence{}; std::atomic<u64> signaled_fence{}; diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 11848fbce..f1d50be3e 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -9,13 +9,12 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" +#include "video_core/gpu.h" #include "video_core/memory_manager.h" -#include "video_core/rasterizer_interface.h" namespace Tegra { -MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) - : rasterizer{rasterizer}, system{system} { +MemoryManager::MemoryManager(Core::System& system) : system{system} { std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); std::fill(page_table.attributes.begin(), page_table.attributes.end(), Common::PageType::Unmapped); @@ -84,7 +83,8 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { const auto cpu_addr = GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); - rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size); + system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size); + UnmapRange(gpu_addr, aligned_size); ASSERT(system.CurrentProcess() ->VMManager() @@ -242,7 +242,7 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s switch (page_table.attributes[page_index]) { case Common::PageType::Memory: { const u8* src_ptr{page_table.pointers[page_index] + page_offset}; - rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); + system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount); std::memcpy(dest_buffer, src_ptr, copy_amount); break; } @@ -292,7 +292,7 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const switch (page_table.attributes[page_index]) { case Common::PageType::Memory: { u8* dest_ptr{page_table.pointers[page_index] + page_offset}; - rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); + system.GPU().InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); std::memcpy(dest_ptr, src_buffer, copy_amount); break; } @@ -340,7 +340,7 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std:: switch (page_table.attributes[page_index]) { case Common::PageType::Memory: { const u8* src_ptr{page_table.pointers[page_index] + page_offset}; - rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); + system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount); WriteBlock(dest_addr, src_ptr, copy_amount); break; } diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index aea010087..393447eb4 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -10,10 +10,6 @@ #include "common/common_types.h" #include "common/page_table.h" -namespace VideoCore { -class RasterizerInterface; -} - namespace Core { class System; } @@ -51,7 +47,7 @@ struct VirtualMemoryArea { class MemoryManager final { public: - explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); + explicit MemoryManager(Core::System& system); ~MemoryManager(); GPUVAddr AllocateSpace(u64 size, u64 align); @@ -176,7 +172,6 @@ private: Common::PageTable page_table{page_bits}; VMAMap vma_map; - VideoCore::RasterizerInterface& rasterizer; Core::System& system; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 362942e09..46a7433ea 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -248,9 +248,6 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) { } GLintptr RasterizerOpenGL::SetupIndexBuffer() { - if (accelerate_draw != AccelDraw::Indexed) { - return 0; - } MICROPROFILE_SCOPE(OpenGL_Index); const auto& regs = system.GPU().Maxwell3D().regs; const std::size_t size = CalculateIndexBufferSize(); @@ -546,7 +543,8 @@ void RasterizerOpenGL::Clear() { } } -void RasterizerOpenGL::DrawPrelude() { +void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { + MICROPROFILE_SCOPE(OpenGL_Drawing); auto& gpu = system.GPU().Maxwell3D(); SyncRasterizeEnable(state); @@ -567,9 +565,6 @@ void RasterizerOpenGL::DrawPrelude() { buffer_cache.Acquire(); - // Draw the vertex batch - const bool is_indexed = accelerate_draw == AccelDraw::Indexed; - std::size_t buffer_size = CalculateVertexArraysSize(); // Add space for index buffer @@ -596,7 +591,11 @@ void RasterizerOpenGL::DrawPrelude() { // Upload vertex and index data. SetupVertexBuffer(vao); SetupVertexInstances(vao); - index_buffer_offset = SetupIndexBuffer(); + + GLintptr index_buffer_offset; + if (is_indexed) { + index_buffer_offset = SetupIndexBuffer(); + } // Prepare packed bindings. bind_ubo_pushbuffer.Setup(); @@ -630,6 +629,7 @@ void RasterizerOpenGL::DrawPrelude() { // As all cached buffers are invalidated, we need to recheck their state. gpu.dirty.ResetVertexArrays(); } + gpu.dirty.memory_general = false; shader_program_manager->ApplyTo(state); state.Apply(); @@ -637,106 +637,33 @@ void RasterizerOpenGL::DrawPrelude() { if (texture_cache.TextureBarrier()) { glTextureBarrier(); } -} - -struct DrawParams { - bool is_indexed{}; - bool is_instanced{}; - GLenum primitive_mode{}; - GLint count{}; - GLint base_vertex{}; - - // Indexed settings - GLenum index_format{}; - GLintptr index_buffer_offset{}; - - // Instanced setting - GLint num_instances{}; - GLint base_instance{}; - - void DispatchDraw() { - if (is_indexed) { - const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); - if (is_instanced) { - glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, - index_buffer_ptr, num_instances, - base_vertex, base_instance); - } else { - glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, - base_vertex); - } - } else { - if (is_instanced) { - glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances, - base_instance); - } else { - glDrawArrays(primitive_mode, base_vertex, count); - } - } - } -}; - -bool RasterizerOpenGL::DrawBatch(bool is_indexed) { - accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; - MICROPROFILE_SCOPE(OpenGL_Drawing); - - DrawPrelude(); - - auto& maxwell3d = system.GPU().Maxwell3D(); - const auto& regs = maxwell3d.regs; - const auto current_instance = maxwell3d.state.current_instance; - DrawParams draw_call{}; - draw_call.is_indexed = is_indexed; - draw_call.num_instances = static_cast<GLint>(1); - draw_call.base_instance = static_cast<GLint>(current_instance); - draw_call.is_instanced = current_instance > 0; - draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); - if (draw_call.is_indexed) { - draw_call.count = static_cast<GLint>(regs.index_array.count); - draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); - draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); - draw_call.index_buffer_offset = index_buffer_offset; + const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); + const GLsizei num_instances = + static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1); + if (is_indexed) { + const GLenum index_format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format); + const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base); + const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count); + glDrawElementsInstancedBaseVertexBaseInstance( + primitive_mode, num_vertices, index_format, + reinterpret_cast<const void*>(index_buffer_offset), num_instances, base_vertex, + base_instance); } else { - draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); - draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); + const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first); + const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count); + glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, num_instances, + base_instance); } - draw_call.DispatchDraw(); +} - maxwell3d.dirty.memory_general = false; - accelerate_draw = AccelDraw::Disabled; +bool RasterizerOpenGL::DrawBatch(bool is_indexed) { + Draw(is_indexed, false); return true; } bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { - accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; - - MICROPROFILE_SCOPE(OpenGL_Drawing); - - DrawPrelude(); - - auto& maxwell3d = system.GPU().Maxwell3D(); - const auto& regs = maxwell3d.regs; - const auto& draw_setup = maxwell3d.mme_draw; - DrawParams draw_call{}; - draw_call.is_indexed = is_indexed; - draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count); - draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance); - draw_call.is_instanced = draw_setup.instance_count > 1; - draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); - if (draw_call.is_indexed) { - draw_call.count = static_cast<GLint>(regs.index_array.count); - draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); - draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); - draw_call.index_buffer_offset = index_buffer_offset; - } else { - draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); - draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); - } - draw_call.DispatchDraw(); - - maxwell3d.dirty.memory_general = false; - accelerate_draw = AccelDraw::Disabled; + Draw(is_indexed, true); return true; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 6a27cf497..0501f3828 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -103,7 +103,7 @@ private: std::size_t size); /// Syncs all the state, shaders, render targets and textures setting before a draw call. - void DrawPrelude(); + void Draw(bool is_indexed, bool is_instanced); /// Configures the current textures to use for the draw command. void SetupDrawTextures(std::size_t stage_index, const Shader& shader); @@ -220,12 +220,7 @@ private: GLintptr SetupIndexBuffer(); - GLintptr index_buffer_offset; - void SetupShaders(GLenum primitive_mode); - - enum class AccelDraw { Disabled, Arrays, Indexed }; - AccelDraw accelerate_draw = AccelDraw::Disabled; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index ea4f35663..7ed505628 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -47,8 +47,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { case Maxwell::VertexAttribute::Size::Size_10_10_10_2: return GL_UNSIGNED_INT_2_10_10_10_REV; default: - LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); - UNREACHABLE(); + LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); return {}; } case Maxwell::VertexAttribute::Type::SignedInt: @@ -72,8 +71,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { case Maxwell::VertexAttribute::Size::Size_10_10_10_2: return GL_INT_2_10_10_10_REV; default: - LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); - UNREACHABLE(); + LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); return {}; } case Maxwell::VertexAttribute::Type::Float: @@ -89,13 +87,19 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { case Maxwell::VertexAttribute::Size::Size_32_32_32_32: return GL_FLOAT; default: - LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); - UNREACHABLE(); + LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); + return {}; + } + case Maxwell::VertexAttribute::Type::UnsignedScaled: + switch (attrib.size) { + case Maxwell::VertexAttribute::Size::Size_8_8: + return GL_UNSIGNED_BYTE; + default: + LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); return {}; } default: - LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); - UNREACHABLE(); + LOG_ERROR(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); return {}; } } 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 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include <optional> +#include <vector> + +#include <fmt/format.h> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/telemetry.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/frontend/emu_window.h" +#include "core/memory.h" +#include "core/perf_stats.h" +#include "core/settings.h" +#include "core/telemetry_session.h" +#include "video_core/gpu.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/renderer_vulkan/vk_blit_screen.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_memory_manager.h" +#include "video_core/renderer_vulkan/vk_rasterizer.h" +#include "video_core/renderer_vulkan/vk_resource_manager.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_swapchain.h" + +namespace Vulkan { + +namespace { + +VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, + VkDebugUtilsMessageTypeFlagsEXT type, + const VkDebugUtilsMessengerCallbackDataEXT* data, + [[maybe_unused]] void* user_data) { + const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_}; + const char* message{data->pMessage}; + + if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) { + LOG_CRITICAL(Render_Vulkan, "{}", message); + } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) { + LOG_WARNING(Render_Vulkan, "{}", message); + } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) { + LOG_INFO(Render_Vulkan, "{}", message); + } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) { + LOG_DEBUG(Render_Vulkan, "{}", message); + } + return VK_FALSE; +} + +std::string GetReadableVersion(u32 version) { + return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), + VK_VERSION_PATCH(version)); +} + +std::string GetDriverVersion(const VKDevice& device) { + // Extracted from + // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 + const u32 version = device.GetDriverVersion(); + + if (device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) { + const u32 major = (version >> 22) & 0x3ff; + const u32 minor = (version >> 14) & 0x0ff; + const u32 secondary = (version >> 6) & 0x0ff; + const u32 tertiary = version & 0x003f; + return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary); + } + if (device.GetDriverID() == vk::DriverIdKHR::eIntelProprietaryWindows) { + const u32 major = version >> 14; + const u32 minor = version & 0x3fff; + return fmt::format("{}.{}", major, minor); + } + + return GetReadableVersion(version); +} + +std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) { + std::sort(std::begin(available_extensions), std::end(available_extensions)); + + static constexpr std::size_t AverageExtensionSize = 64; + std::string separated_extensions; + separated_extensions.reserve(available_extensions.size() * AverageExtensionSize); + + const auto end = std::end(available_extensions); + for (auto extension = std::begin(available_extensions); extension != end; ++extension) { + if (const bool is_last = extension + 1 == end; is_last) { + separated_extensions += *extension; + } else { + separated_extensions += fmt::format("{},", *extension); + } + } + return separated_extensions; +} + +} // Anonymous namespace + +RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system) + : RendererBase(window), system{system} {} + +RendererVulkan::~RendererVulkan() { + ShutDown(); +} + +void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { + const auto& layout = render_window.GetFramebufferLayout(); + if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) { + const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; + const bool use_accelerated = + rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); + const bool is_srgb = use_accelerated && screen_info.is_srgb; + if (swapchain->HasFramebufferChanged(layout) || swapchain->GetSrgbState() != is_srgb) { + swapchain->Create(layout.width, layout.height, is_srgb); + blit_screen->Recreate(); + } + + scheduler->WaitWorker(); + + swapchain->AcquireNextImage(); + const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated); + + scheduler->Flush(false, render_semaphore); + + if (swapchain->Present(render_semaphore, fence)) { + blit_screen->Recreate(); + } + + render_window.SwapBuffers(); + rasterizer->TickFrame(); + } + + render_window.PollEvents(); +} + +bool RendererVulkan::Init() { + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; + render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); + const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr); + + std::optional<vk::DebugUtilsMessengerEXT> callback; + if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) { + callback = CreateDebugCallback(dldi); + if (!callback) { + return false; + } + } + + if (!PickDevices(dldi)) { + if (callback) { + instance.destroy(*callback, nullptr, dldi); + } + return false; + } + debug_callback = UniqueDebugUtilsMessengerEXT( + *callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>( + instance, nullptr, device->GetDispatchLoader())); + + Report(); + + memory_manager = std::make_unique<VKMemoryManager>(*device); + + resource_manager = std::make_unique<VKResourceManager>(*device); + + const auto& framebuffer = render_window.GetFramebufferLayout(); + swapchain = std::make_unique<VKSwapchain>(surface, *device); + swapchain->Create(framebuffer.width, framebuffer.height, false); + + scheduler = std::make_unique<VKScheduler>(*device, *resource_manager); + + rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device, + *resource_manager, *memory_manager, *scheduler); + + blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device, + *resource_manager, *memory_manager, *swapchain, + *scheduler, screen_info); + + return true; +} + +void RendererVulkan::ShutDown() { + if (!device) { + return; + } + const auto dev = device->GetLogical(); + const auto& dld = device->GetDispatchLoader(); + if (dev && dld.vkDeviceWaitIdle) { + dev.waitIdle(dld); + } + + rasterizer.reset(); + blit_screen.reset(); + scheduler.reset(); + swapchain.reset(); + memory_manager.reset(); + resource_manager.reset(); + device.reset(); +} + +std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback( + const vk::DispatchLoaderDynamic& dldi) { + const vk::DebugUtilsMessengerCreateInfoEXT callback_ci( + {}, + vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose, + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | + vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | + vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, + &DebugCallback, nullptr); + vk::DebugUtilsMessengerEXT callback; + if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) != + vk::Result::eSuccess) { + LOG_ERROR(Render_Vulkan, "Failed to create debug callback"); + return {}; + } + return callback; +} + +bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) { + const auto devices = instance.enumeratePhysicalDevices(dldi); + + // TODO(Rodrigo): Choose device from config file + const s32 device_index = Settings::values.vulkan_device; + if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) { + LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); + return false; + } + const vk::PhysicalDevice physical_device = devices[device_index]; + + if (!VKDevice::IsSuitable(dldi, physical_device, surface)) { + return false; + } + + device = std::make_unique<VKDevice>(dldi, physical_device, surface); + return device->Create(dldi, instance); +} + +void RendererVulkan::Report() const { + const std::string vendor_name{device->GetVendorName()}; + const std::string model_name{device->GetModelName()}; + const std::string driver_version = GetDriverVersion(*device); + const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version); + + const std::string api_version = GetReadableVersion(device->GetApiVersion()); + + const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions()); + + LOG_INFO(Render_Vulkan, "Driver: {}", driver_name); + LOG_INFO(Render_Vulkan, "Device: {}", model_name); + LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); + + auto& telemetry_session = system.TelemetrySession(); + constexpr auto field = Telemetry::FieldType::UserSystem; + telemetry_session.AddField(field, "GPU_Vendor", vendor_name); + telemetry_session.AddField(field, "GPU_Model", model_name); + telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name); + telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version); + telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); +} + +} // 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 VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); - Test(extension, nv_device_diagnostic_checkpoints, - VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); + if (Settings::values.renderer_debug) { + Test(extension, nv_device_diagnostic_checkpoints, + VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); + } } if (khr_shader_float16_int8) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d2c6b1189..aada38702 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -571,7 +571,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); } if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { - texceptions.set(rt); + texceptions[rt] = true; } } @@ -579,7 +579,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { zeta_attachment = texture_cache.GetDepthBufferSurface(true); } if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { - texceptions.set(ZETA_TEXCEPTION_INDEX); + texceptions[ZETA_TEXCEPTION_INDEX] = true; } texture_cache.GuardRenderTargets(false); @@ -1122,11 +1122,12 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions) for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) { const auto& rendertarget = regs.rt[rt]; - if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) + if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) { continue; + } renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{ static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format), - texceptions.test(rt)}); + texceptions[rt]}); } renderpass_params.has_zeta = regs.zeta_enable; 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: void DetachSegment(ASTNode start, ASTNode end); void Remove(ASTNode node); - ASTNode first{}; - ASTNode last{}; + ASTNode first; + ASTNode last; }; class ASTProgram { @@ -299,9 +299,9 @@ private: friend class ASTZipper; ASTData data; - ASTNode parent{}; - ASTNode next{}; - ASTNode previous{}; + ASTNode parent; + ASTNode next; + ASTNode previous; ASTZipper* manager{}; }; 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) { Node op_a = GetRegister(instr.gpr8); - Node op_b = [&]() -> Node { + Node op_b = [&] { if (instr.is_b_imm) { return GetImmediate19(instr); } else if (instr.is_b_gpr) { @@ -141,6 +141,15 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { SetRegister(bb, instr.gpr0, value); break; } + case OpCode::Id::FCMP_R: { + UNIMPLEMENTED_IF(instr.fcmp.ftz == 0); + Node op_c = GetRegister(instr.gpr39); + Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f)); + SetRegister( + bb, instr.gpr0, + Operation(OperationCode::Select, std::move(comp), std::move(op_a), std::move(op_b))); + break; + } case OpCode::Id::RRO_C: case OpCode::Id::RRO_R: 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 const Node one = Immediate(1); const Node two = Immediate(2); - Node value{}; + Node value; for (u32 i = 0; i < lop_iterations; ++i) { const Node shift_amount = Immediate(i); diff --git a/src/video_core/shader/decode/bfi.cpp b/src/video_core/shader/decode/bfi.cpp index 8be1119df..f992bbe2a 100644 --- a/src/video_core/shader/decode/bfi.cpp +++ b/src/video_core/shader/decode/bfi.cpp @@ -17,10 +17,13 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); - const auto [base, packed_shift] = [&]() -> std::tuple<Node, Node> { + const auto [packed_shift, base] = [&]() -> std::pair<Node, Node> { switch (opcode->get().GetId()) { + case OpCode::Id::BFI_RC: + return {GetRegister(instr.gpr39), + GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)}; case OpCode::Id::BFI_IMM_R: - return {GetRegister(instr.gpr39), Immediate(instr.alu.GetSignedImm20_20())}; + return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)}; default: UNREACHABLE(); return {Immediate(0), Immediate(0)}; 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) { case OpCode::Id::MOV_SYS: { const Node value = [this, instr] { switch (instr.sys20) { + case SystemVariable::LaneId: + LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete"); + return Immediate(0U); case SystemVariable::InvocationId: return Operation(OperationCode::InvocationId); case SystemVariable::Ydirection: return Operation(OperationCode::YNegate); case SystemVariable::InvocationInfo: LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); - return Immediate(0u); + return Immediate(0U); case SystemVariable::Tid: { Node value = Immediate(0); value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9); @@ -188,7 +191,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", static_cast<u32>(cc)); - if (disable_flow_stack) { + if (decompiled) { break; } @@ -200,7 +203,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", static_cast<u32>(cc)); - if (disable_flow_stack) { + if (decompiled) { break; } diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp index d419e9c45..3b391d3e6 100644 --- a/src/video_core/shader/decode/shift.cpp +++ b/src/video_core/shader/decode/shift.cpp @@ -10,8 +10,80 @@ namespace VideoCommon::Shader { +using std::move; using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; +using Tegra::Shader::ShfType; +using Tegra::Shader::ShfXmode; + +namespace { + +Node IsFull(Node shift) { + return Operation(OperationCode::LogicalIEqual, move(shift), Immediate(32)); +} + +Node Shift(OperationCode opcode, Node value, Node shift) { + Node is_full = Operation(OperationCode::LogicalIEqual, shift, Immediate(32)); + Node shifted = Operation(opcode, move(value), shift); + return Operation(OperationCode::Select, IsFull(move(shift)), Immediate(0), move(shifted)); +} + +Node ClampShift(Node shift, s32 size = 32) { + shift = Operation(OperationCode::IMax, move(shift), Immediate(0)); + return Operation(OperationCode::IMin, move(shift), Immediate(size)); +} + +Node WrapShift(Node shift, s32 size = 32) { + return Operation(OperationCode::UBitwiseAnd, move(shift), Immediate(size - 1)); +} + +Node ShiftRight(Node low, Node high, Node shift, Node low_shift, ShfType type) { + // These values are used when the shift value is less than 32 + Node less_low = Shift(OperationCode::ILogicalShiftRight, low, shift); + Node less_high = Shift(OperationCode::ILogicalShiftLeft, high, low_shift); + Node less = Operation(OperationCode::IBitwiseOr, move(less_high), move(less_low)); + + if (type == ShfType::Bits32) { + // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits + return Operation(OperationCode::Select, IsFull(move(shift)), move(high), move(less)); + } + + // And these when it's larger than or 32 + const bool is_signed = type == ShfType::S64; + const auto opcode = SignedToUnsignedCode(OperationCode::IArithmeticShiftRight, is_signed); + Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32)); + Node greater = Shift(opcode, high, move(reduced)); + + Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32)); + Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0)); + + Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater)); + return Operation(OperationCode::Select, move(is_zero), move(high), move(value)); +} + +Node ShiftLeft(Node low, Node high, Node shift, Node low_shift, ShfType type) { + // These values are used when the shift value is less than 32 + Node less_low = Operation(OperationCode::ILogicalShiftRight, low, low_shift); + Node less_high = Operation(OperationCode::ILogicalShiftLeft, high, shift); + Node less = Operation(OperationCode::IBitwiseOr, move(less_low), move(less_high)); + + if (type == ShfType::Bits32) { + // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits + return Operation(OperationCode::Select, IsFull(move(shift)), move(low), move(less)); + } + + // And these when it's larger than or 32 + Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32)); + Node greater = Shift(OperationCode::ILogicalShiftLeft, move(low), move(reduced)); + + Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32)); + Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0)); + + Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater)); + return Operation(OperationCode::Select, move(is_zero), move(high), move(value)); +} + +} // Anonymous namespace u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; @@ -28,29 +100,48 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) { } }(); - switch (opcode->get().GetId()) { + switch (const auto opid = opcode->get().GetId(); opid) { case OpCode::Id::SHR_C: case OpCode::Id::SHR_R: case OpCode::Id::SHR_IMM: { - if (instr.shr.wrap) { - op_b = Operation(OperationCode::UBitwiseAnd, std::move(op_b), Immediate(0x1f)); - } else { - op_b = Operation(OperationCode::IMax, std::move(op_b), Immediate(0)); - op_b = Operation(OperationCode::IMin, std::move(op_b), Immediate(31)); - } + op_b = instr.shr.wrap ? WrapShift(move(op_b)) : ClampShift(move(op_b)); Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed, - std::move(op_a), std::move(op_b)); + move(op_a), move(op_b)); SetInternalFlagsFromInteger(bb, value, instr.generates_cc); - SetRegister(bb, instr.gpr0, std::move(value)); + SetRegister(bb, instr.gpr0, move(value)); break; } case OpCode::Id::SHL_C: case OpCode::Id::SHL_R: case OpCode::Id::SHL_IMM: { - const Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b); + Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b); SetInternalFlagsFromInteger(bb, value, instr.generates_cc); - SetRegister(bb, instr.gpr0, value); + SetRegister(bb, instr.gpr0, move(value)); + break; + } + case OpCode::Id::SHF_RIGHT_R: + case OpCode::Id::SHF_RIGHT_IMM: + case OpCode::Id::SHF_LEFT_R: + case OpCode::Id::SHF_LEFT_IMM: { + UNIMPLEMENTED_IF(instr.generates_cc); + UNIMPLEMENTED_IF_MSG(instr.shf.xmode != ShfXmode::None, "xmode={}", + static_cast<int>(instr.shf.xmode.Value())); + + if (instr.is_b_imm) { + op_b = Immediate(static_cast<u32>(instr.shf.immediate)); + } + const s32 size = instr.shf.type == ShfType::Bits32 ? 32 : 64; + Node shift = instr.shf.wrap ? WrapShift(move(op_b), size) : ClampShift(move(op_b), size); + + Node negated_shift = Operation(OperationCode::INegate, shift); + Node low_shift = Operation(OperationCode::IAdd, move(negated_shift), Immediate(32)); + + const bool is_right = opid == OpCode::Id::SHF_RIGHT_R || opid == OpCode::Id::SHF_RIGHT_IMM; + Node value = (is_right ? ShiftRight : ShiftLeft)( + move(op_a), GetRegister(instr.gpr39), move(shift), move(low_shift), instr.shf.type); + + SetRegister(bb, instr.gpr0, move(value)); break; } default: diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index d980535b1..351c8c2f1 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp @@ -630,7 +630,7 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); } - Node dc{}; + Node dc; if (depth_compare) { // Depth is always stored in the register signaled by gpr20 or in the next register if lod // or bias are used @@ -666,7 +666,7 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, const Node array = is_array ? GetRegister(array_register) : nullptr; - Node dc{}; + Node dc; if (depth_compare) { // Depth is always stored in the register signaled by gpr20 or in the next register if lod // or bias are used diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 5f83403db..a0a7b9111 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -443,9 +443,9 @@ struct MetaTexture { std::vector<Node> derivates; Node bias; Node lod; - Node component{}; + Node component; u32 element{}; - Node index{}; + Node index; }; struct MetaImage { diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp index ea39bca54..face8c943 100644 --- a/src/video_core/shader/track.cpp +++ b/src/video_core/shader/track.cpp @@ -41,8 +41,8 @@ std::optional<std::pair<Node, Node>> DecoupleIndirectRead(const OperationNode& o if (operation.GetCode() != OperationCode::UAdd) { return std::nullopt; } - Node gpr{}; - Node offset{}; + Node gpr; + Node offset; ASSERT(operation.GetOperandsCount() == 2); for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) { Node operand = operation[i]; 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 @@ // Refer to the license.txt file included. #include <memory> +#include "common/logging/log.h" #include "core/core.h" #include "core/settings.h" #include "video_core/gpu_asynch.h" #include "video_core/gpu_synch.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/renderer_opengl.h" +#ifdef HAS_VULKAN +#include "video_core/renderer_vulkan/renderer_vulkan.h" +#endif #include "video_core/video_core.h" namespace VideoCore { std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, Core::System& system) { - return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); + switch (Settings::values.renderer_backend) { + case Settings::RendererBackend::OpenGL: + return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); +#ifdef HAS_VULKAN + case Settings::RendererBackend::Vulkan: + return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); +#endif + default: + return nullptr; + } } 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() { impl->SerializeSection(Telemetry::FieldType::Session, "Session"); impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); + impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); auto content = impl->TopSection().dump(); 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) copy_yuzu_SDL_deps(yuzu) copy_yuzu_unicorn_deps(yuzu) endif() + +if (ENABLE_VULKAN) + target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) + target_compile_definitions(yuzu PRIVATE HAS_VULKAN) +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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <glad/glad.h> + #include <QApplication> #include <QHBoxLayout> #include <QKeyEvent> +#include <QMessageBox> #include <QOffscreenSurface> #include <QOpenGLWindow> #include <QPainter> #include <QScreen> +#include <QStringList> #include <QWindow> +#ifdef HAS_VULKAN +#include <QVulkanWindow> +#endif + #include <fmt/format.h> + +#include "common/assert.h" #include "common/microprofile.h" #include "common/scm_rev.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" +#include "core/frontend/scope_acquire_window_context.h" #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" @@ -114,19 +125,10 @@ private: QOpenGLContext context; }; -// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL -// context. -// The corresponding functionality is handled in EmuThread instead -class GGLWidgetInternal : public QOpenGLWindow { +class GWidgetInternal : public QWindow { public: - GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) - : QOpenGLWindow(shared_context), parent(parent) {} - - void paintEvent(QPaintEvent* ev) override { - if (do_painting) { - QPainter painter(this); - } - } + GWidgetInternal(GRenderWindow* parent) : parent(parent) {} + virtual ~GWidgetInternal() = default; void resizeEvent(QResizeEvent* ev) override { parent->OnClientAreaResized(ev->size().width(), ev->size().height()); @@ -182,11 +184,47 @@ public: do_painting = true; } + std::pair<unsigned, unsigned> GetSize() const { + return std::make_pair(width(), height()); + } + +protected: + bool IsPaintingEnabled() const { + return do_painting; + } + private: GRenderWindow* parent; - bool do_painting; + bool do_painting = false; +}; + +// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL +// context. +// The corresponding functionality is handled in EmuThread instead +class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow { +public: + GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) + : GWidgetInternal(parent), QOpenGLWindow(shared_context) {} + ~GGLWidgetInternal() override = default; + + void paintEvent(QPaintEvent* ev) override { + if (IsPaintingEnabled()) { + QPainter painter(this); + } + } }; +#ifdef HAS_VULKAN +class GVKWidgetInternal final : public GWidgetInternal { +public: + GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { + setSurfaceType(QSurface::SurfaceType::VulkanSurface); + setVulkanInstance(instance); + } + ~GVKWidgetInternal() override = default; +}; +#endif + GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) : QWidget(parent), emu_thread(emu_thread) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") @@ -201,9 +239,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) GRenderWindow::~GRenderWindow() { InputCommon::Shutdown(); + + // Avoid an unordered destruction that generates a segfault + delete child; } void GRenderWindow::moveContext() { + if (!context) { + return; + } DoneCurrent(); // If the thread started running, move the GL Context to the new thread. Otherwise, move it @@ -215,8 +259,9 @@ void GRenderWindow::moveContext() { } void GRenderWindow::SwapBuffers() { - context->swapBuffers(child); - + if (context) { + context->swapBuffers(child); + } if (!first_frame) { first_frame = true; emit FirstFrameDisplayed(); @@ -224,15 +269,38 @@ void GRenderWindow::SwapBuffers() { } void GRenderWindow::MakeCurrent() { - context->makeCurrent(child); + if (context) { + context->makeCurrent(child); + } } void GRenderWindow::DoneCurrent() { - context->doneCurrent(); + if (context) { + context->doneCurrent(); + } } void GRenderWindow::PollEvents() {} +bool GRenderWindow::IsShown() const { + return !isMinimized(); +} + +void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const { +#ifdef HAS_VULKAN + const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); + const VkInstance instance_copy = vk_instance->vkInstance(); + const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); + + std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); + std::memcpy(instance, &instance_copy, sizeof(instance_copy)); + std::memcpy(surface, &surface_copy, sizeof(surface_copy)); +#else + UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); +#endif +} + // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). // // Older versions get the window size (density independent pixels), @@ -241,10 +309,9 @@ void GRenderWindow::PollEvents() {} void GRenderWindow::OnFramebufferSizeChanged() { // Screen changes potentially incur a change in screen DPI, hence we should update the // framebuffer size - const qreal pixel_ratio = GetWindowPixelRatio(); - const u32 width = child->QPaintDevice::width() * pixel_ratio; - const u32 height = child->QPaintDevice::height() * pixel_ratio; - UpdateCurrentFramebufferLayout(width, height); + const qreal pixelRatio{GetWindowPixelRatio()}; + const auto size{child->GetSize()}; + UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); } void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { @@ -290,7 +357,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const { } std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { - const qreal pixel_ratio = GetWindowPixelRatio(); + const qreal pixel_ratio{GetWindowPixelRatio()}; return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; } @@ -356,50 +423,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont return std::make_unique<GGLContext>(context.get()); } -void GRenderWindow::InitRenderTarget() { +bool GRenderWindow::InitRenderTarget() { shared_context.reset(); context.reset(); - - delete child; - child = nullptr; - - delete container; - container = nullptr; - - delete layout(); + if (child) { + delete child; + } + if (container) { + delete container; + } + if (layout()) { + delete layout(); + } first_frame = false; - // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, - // WA_DontShowOnScreen, WA_DeleteOnClose - QSurfaceFormat fmt; - fmt.setVersion(4, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); - fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); - // TODO: expose a setting for buffer value (ie default/single/double/triple) - fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); - shared_context = std::make_unique<QOpenGLContext>(); - shared_context->setFormat(fmt); - shared_context->create(); - context = std::make_unique<QOpenGLContext>(); - context->setShareContext(shared_context.get()); - context->setFormat(fmt); - context->create(); - fmt.setSwapInterval(0); + switch (Settings::values.renderer_backend) { + case Settings::RendererBackend::OpenGL: + if (!InitializeOpenGL()) { + return false; + } + break; + case Settings::RendererBackend::Vulkan: + if (!InitializeVulkan()) { + return false; + } + break; + } - child = new GGLWidgetInternal(this, shared_context.get()); container = QWidget::createWindowContainer(child, this); - QBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(container); layout->setMargin(0); setLayout(layout); - // Reset minimum size to avoid unwanted resizes when this function is called for a second time. + // Reset minimum required size to avoid resizing issues on the main window after restarting. setMinimumSize(1, 1); - // Show causes the window to actually be created and the OpenGL context as well, but we don't - // want the widget to be shown yet, so immediately hide it. + // Show causes the window to actually be created and the gl context as well, but we don't want + // the widget to be shown yet, so immediately hide it. show(); hide(); @@ -410,9 +473,17 @@ void GRenderWindow::InitRenderTarget() { OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); OnFramebufferSizeChanged(); - NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height())); + NotifyClientAreaSizeChanged(child->GetSize()); BackupGeometry(); + + if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { + if (!LoadOpenGL()) { + return false; + } + } + + return true; } void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { @@ -441,6 +512,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal setMinimumSize(minimal_size.first, minimal_size.second); } +bool GRenderWindow::InitializeOpenGL() { + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, + // WA_DontShowOnScreen, WA_DeleteOnClose + QSurfaceFormat fmt; + fmt.setVersion(4, 3); + fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); + // TODO: expose a setting for buffer value (ie default/single/double/triple) + fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); + shared_context = std::make_unique<QOpenGLContext>(); + shared_context->setFormat(fmt); + shared_context->create(); + context = std::make_unique<QOpenGLContext>(); + context->setShareContext(shared_context.get()); + context->setFormat(fmt); + context->create(); + fmt.setSwapInterval(false); + + child = new GGLWidgetInternal(this, shared_context.get()); + return true; +} + +bool GRenderWindow::InitializeVulkan() { +#ifdef HAS_VULKAN + vk_instance = std::make_unique<QVulkanInstance>(); + vk_instance->setApiVersion(QVersionNumber(1, 1, 0)); + vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); + if (Settings::values.renderer_debug) { + const auto supported_layers{vk_instance->supportedLayers()}; + const bool found = + std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) { + constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation"; + return layer.name == searched_layer; + }); + if (found) { + vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); + vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + } + if (!vk_instance->create()) { + QMessageBox::critical( + this, tr("Error while initializing Vulkan 1.1!"), + tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the " + "latest graphics drivers.")); + return false; + } + + child = new GVKWidgetInternal(this, vk_instance.get()); + return true; +#else + QMessageBox::critical(this, tr("Vulkan not available!"), + tr("yuzu has not been compiled with Vulkan support.")); + return false; +#endif +} + +bool GRenderWindow::LoadOpenGL() { + Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; + if (!gladLoadGL()) { + QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), + tr("Your GPU may not support OpenGL 4.3, or you do not have the " + "latest graphics driver.")); + return false; + } + + QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); + if (!unsupported_gl_extensions.empty()) { + QMessageBox::critical( + this, tr("Error while initializing OpenGL!"), + tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you " + "have the latest graphics driver.<br><br>Unsupported extensions:<br>") + + unsupported_gl_extensions.join(QStringLiteral("<br>"))); + return false; + } + return true; +} + +QStringList GRenderWindow::GetUnsupportedGLExtensions() const { + QStringList unsupported_ext; + + if (!GLAD_GL_ARB_buffer_storage) + unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); + if (!GLAD_GL_ARB_direct_state_access) + unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); + if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) + unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); + if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) + unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); + if (!GLAD_GL_ARB_multi_bind) + unsupported_ext.append(QStringLiteral("ARB_multi_bind")); + if (!GLAD_GL_ARB_clip_control) + unsupported_ext.append(QStringLiteral("ARB_clip_control")); + + // Extensions required to support some texture formats. + if (!GLAD_GL_EXT_texture_compression_s3tc) + unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); + if (!GLAD_GL_ARB_texture_compression_rgtc) + unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); + if (!GLAD_GL_ARB_depth_buffer_float) + unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); + + for (const QString& ext : unsupported_ext) + LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); + + return unsupported_ext; +} + void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { this->emu_thread = emu_thread; 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 @@ #include <atomic> #include <condition_variable> #include <mutex> + #include <QImage> #include <QThread> #include <QWidget> + +#include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" class QKeyEvent; class QScreen; class QTouchEvent; +class QStringList; +class QSurface; +class QOpenGLContext; +#ifdef HAS_VULKAN +class QVulkanInstance; +#endif +class GWidgetInternal; class GGLWidgetInternal; +class GVKWidgetInternal; class GMainWindow; class GRenderWindow; class QSurface; @@ -123,6 +134,9 @@ public: void MakeCurrent() override; void DoneCurrent() override; void PollEvents() override; + bool IsShown() const override; + void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const override; std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; void ForwardKeyPressEvent(QKeyEvent* event); @@ -142,7 +156,7 @@ public: void OnClientAreaResized(u32 width, u32 height); - void InitRenderTarget(); + bool InitRenderTarget(); void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); @@ -165,10 +179,13 @@ private: void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; - QWidget* container = nullptr; - GGLWidgetInternal* child = nullptr; + bool InitializeOpenGL(); + bool InitializeVulkan(); + bool LoadOpenGL(); + QStringList GetUnsupportedGLExtensions() const; - QByteArray geometry; + QWidget* container = nullptr; + GWidgetInternal* child = nullptr; EmuThread* emu_thread; // Context that backs the GGLWidgetInternal (and will be used by core to render) @@ -177,9 +194,14 @@ private: // current std::unique_ptr<QOpenGLContext> shared_context; +#ifdef HAS_VULKAN + std::unique_ptr<QVulkanInstance> vk_instance; +#endif + /// Temporary storage of the screenshot taken QImage screenshot_image; + QByteArray geometry; bool first_frame = false; protected: diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 59918847a..cd94693c1 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -624,6 +624,10 @@ void Config::ReadPathValues() { void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); + Settings::values.renderer_backend = + static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); + Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); + Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); Settings::values.resolution_factor = ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); Settings::values.use_frame_limit = @@ -738,7 +742,6 @@ void Config::ReadUIValues() { void Config::ReadUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); - UISettings::values.show_unknown = ReadSetting(QStringLiteral("show_unknown"), true).toBool(); UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool(); UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt(); UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt(); @@ -1056,6 +1059,9 @@ void Config::SavePathValues() { void Config::SaveRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); + WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); + WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); + WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); WriteSetting(QStringLiteral("resolution_factor"), static_cast<double>(Settings::values.resolution_factor), 1.0); WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); @@ -1152,7 +1158,6 @@ void Config::SaveUIValues() { void Config::SaveUIGamelistValues() { qt_config->beginGroup(QStringLiteral("UIGameList")); - WriteSetting(QStringLiteral("show_unknown"), UISettings::values.show_unknown, true); WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true); WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64); WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); 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() { ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); ui->reporting_services->setChecked(Settings::values.reporting_services); ui->quest_flag->setChecked(Settings::values.quest_flag); + ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); } void ConfigureDebug::ApplyConfiguration() { @@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); + Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); Debugger::ToggleConsole(); Log::Filter filter; 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 @@ <x>0</x> <y>0</y> <width>400</width> - <height>474</height> + <height>467</height> </rect> </property> <property name="windowTitle"> @@ -103,6 +103,80 @@ </item> </layout> </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Homebrew</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Arguments String</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="homebrew_args_edit"/> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Graphics</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QCheckBox" name="enable_graphics_debugging"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="whatsThis"> + <string>When checked, the graphics API enters in a slower debugging mode</string> + </property> + <property name="text"> + <string>Enable Graphics Debugging</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>Dump</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QCheckBox" name="dump_decompressed_nso"> + <property name="whatsThis"> + <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> + </property> + <property name="text"> + <string>Dump Decompressed NSOs</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="dump_exefs"> + <property name="whatsThis"> + <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> + </property> + <property name="text"> + <string>Dump ExeFS</string> + </property> + </widget> + </item> <item> <widget class="QCheckBox" name="reporting_services"> <property name="text"> @@ -129,11 +203,11 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_5"> + <widget class="QGroupBox" name="groupBox_6"> <property name="title"> <string>Advanced</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_7"> <item> <widget class="QCheckBox" name="quest_flag"> <property name="text"> @@ -145,29 +219,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Homebrew</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <item> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Arguments String</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="homebrew_args_edit"/> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -185,6 +236,19 @@ </item> </layout> </widget> + <tabstops> + <tabstop>toggle_gdbstub</tabstop> + <tabstop>gdbport_spinbox</tabstop> + <tabstop>log_filter_edit</tabstop> + <tabstop>toggle_console</tabstop> + <tabstop>open_log_button</tabstop> + <tabstop>homebrew_args_edit</tabstop> + <tabstop>enable_graphics_debugging</tabstop> + <tabstop>dump_decompressed_nso</tabstop> + <tabstop>dump_exefs</tabstop> + <tabstop>reporting_services</tabstop> + <tabstop>quest_flag</tabstop> + </tabstops> <resources/> <connections> <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 @@ // Refer to the license.txt file included. #include <QColorDialog> +#include <QComboBox> +#ifdef HAS_VULKAN +#include <QVulkanInstance> +#endif + +#include "common/common_types.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/settings.h" #include "ui_configure_graphics.h" @@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) { ConfigureGraphics::ConfigureGraphics(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureGraphics) { + vulkan_device = Settings::values.vulkan_device; + RetrieveVulkanDevices(); + ui->setupUi(this); SetConfiguration(); + connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, + [this] { UpdateDeviceComboBox(); }); + connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, + [this](int device) { UpdateDeviceSelection(device); }); + connect(ui->bg_button, &QPushButton::clicked, this, [this] { const QColor new_bg_color = QColorDialog::getColor(bg_color); if (!new_bg_color.isValid()) { @@ -64,11 +79,22 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) }); } +void ConfigureGraphics::UpdateDeviceSelection(int device) { + if (device == -1) { + return; + } + if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) { + vulkan_device = device; + } +} + ConfigureGraphics::~ConfigureGraphics() = default; void ConfigureGraphics::SetConfiguration() { const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); + ui->api->setEnabled(runtime_lock); + ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend)); ui->resolution_factor_combobox->setCurrentIndex( static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); ui->use_disk_shader_cache->setEnabled(runtime_lock); @@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() { ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue)); + UpdateDeviceComboBox(); } void ConfigureGraphics::ApplyConfiguration() { + Settings::values.renderer_backend = GetCurrentGraphicsBackend(); + Settings::values.vulkan_device = vulkan_device; Settings::values.resolution_factor = ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); @@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) { const QIcon color_icon(pixmap); ui->bg_button->setIcon(color_icon); } + +void ConfigureGraphics::UpdateDeviceComboBox() { + ui->device->clear(); + + bool enabled = false; + switch (GetCurrentGraphicsBackend()) { + case Settings::RendererBackend::OpenGL: + ui->device->addItem(tr("OpenGL Graphics Device")); + enabled = false; + break; + case Settings::RendererBackend::Vulkan: + for (const auto device : vulkan_devices) { + ui->device->addItem(device); + } + ui->device->setCurrentIndex(vulkan_device); + enabled = !vulkan_devices.empty(); + break; + } + ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); +} + +void ConfigureGraphics::RetrieveVulkanDevices() { +#ifdef HAS_VULKAN + QVulkanInstance instance; + instance.setApiVersion(QVersionNumber(1, 1, 0)); + if (!instance.create()) { + LOG_INFO(Frontend, "Vulkan 1.1 not available"); + return; + } + const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>( + instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))}; + if (vkEnumeratePhysicalDevices == nullptr) { + LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices"); + return; + } + u32 physical_device_count; + if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) != + VK_SUCCESS) { + LOG_INFO(Frontend, "Failed to get physical devices count"); + return; + } + std::vector<VkPhysicalDevice> physical_devices(physical_device_count); + if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, + physical_devices.data()) != VK_SUCCESS) { + LOG_INFO(Frontend, "Failed to get physical devices"); + return; + } + + const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>( + instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))}; + if (vkGetPhysicalDeviceProperties == nullptr) { + LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties"); + return; + } + for (const auto physical_device : physical_devices) { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physical_device, &properties); + vulkan_devices.push_back(QString::fromUtf8(properties.deviceName)); + } +#endif +} + +Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { + return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); +} 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 @@ #pragma once #include <memory> +#include <vector> +#include <QString> #include <QWidget> +#include "core/settings.h" namespace Ui { class ConfigureGraphics; @@ -27,7 +30,16 @@ private: void SetConfiguration(); void UpdateBackgroundColorButton(QColor color); + void UpdateDeviceComboBox(); + void UpdateDeviceSelection(int device); + + void RetrieveVulkanDevices(); + + Settings::RendererBackend GetCurrentGraphicsBackend() const; std::unique_ptr<Ui::ConfigureGraphics> ui; QColor bg_color; + + std::vector<QString> vulkan_devices; + u32 vulkan_device{}; }; 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 @@ <x>0</x> <y>0</y> <width>400</width> - <height>300</height> + <height>321</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_1"> <item> - <layout class="QVBoxLayout" name="verticalLayout_3"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>API Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>API:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="api"> + <item> + <property name="text"> + <string notr="true">OpenGL</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">Vulkan</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Device:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="device"/> + </item> + </layout> + </item> + </layout> + </widget> + </item> <item> <widget class="QGroupBox" name="groupBox"> <property name="title"> - <string>Graphics</string> + <string>Graphics Settings</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QVBoxLayout" name="verticalLayout_4"> <item> <widget class="QCheckBox" name="use_disk_shader_cache"> <property name="text"> @@ -30,16 +78,16 @@ </widget> </item> <item> - <widget class="QCheckBox" name="use_accurate_gpu_emulation"> + <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> <property name="text"> - <string>Use accurate GPU emulation (slow)</string> + <string>Use asynchronous GPU emulation</string> </property> </widget> </item> <item> - <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> + <widget class="QCheckBox" name="use_accurate_gpu_emulation"> <property name="text"> - <string>Use asynchronous GPU emulation</string> + <string>Use accurate GPU emulation (slow)</string> </property> </widget> </item> @@ -51,11 +99,11 @@ </widget> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QLabel" name="label"> <property name="text"> - <string>Internal Resolution</string> + <string>Internal Resolution:</string> </property> </widget> </item> @@ -91,7 +139,7 @@ </layout> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_6"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QLabel" name="bg_label"> <property name="text"> diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index c4a84cc67..94424ee44 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -40,7 +40,6 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur SetConfiguration(); // Force game list reload if any of the relevant settings are changed. - connect(ui->show_unknown, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate); connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ConfigureUi::RequestGameListUpdate); connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, @@ -60,7 +59,6 @@ ConfigureUi::~ConfigureUi() = default; void ConfigureUi::ApplyConfiguration() { UISettings::values.theme = ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); - UISettings::values.show_unknown = ui->show_unknown->isChecked(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); @@ -74,7 +72,6 @@ void ConfigureUi::RequestGameListUpdate() { void ConfigureUi::SetConfiguration() { ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); - ui->show_unknown->setChecked(UISettings::values.show_unknown); ui->show_add_ons->setChecked(UISettings::values.show_add_ons); ui->icon_size_combobox->setCurrentIndex( ui->icon_size_combobox->findData(UISettings::values.icon_size)); diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index aa36bd112..bd5c5d3c2 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui @@ -52,13 +52,6 @@ <item> <layout class="QVBoxLayout" name="GeneralVerticalLayout"> <item> - <widget class="QCheckBox" name="show_unknown"> - <property name="text"> - <string>Show files with type 'Unknown'</string> - </property> - </widget> - </item> - <item> <widget class="QCheckBox" name="show_add_ons"> <property name="text"> <string>Show Add-Ons Column</string> diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 4c81ef12b..da2c27aa2 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -298,8 +298,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa } const auto file_type = loader->GetFileType(); - if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) && - !UISettings::values.show_unknown) { + if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { return true; } 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() { // Create status bar message_label = new QLabel(); // Configured separately for left alignment - message_label->setVisible(false); message_label->setFrameStyle(QFrame::NoFrame); message_label->setContentsMargins(4, 0, 4, 0); message_label->setAlignment(Qt::AlignLeft); @@ -476,8 +475,73 @@ void GMainWindow::InitializeWidgets() { label->setVisible(false); label->setFrameStyle(QFrame::NoFrame); label->setContentsMargins(4, 0, 4, 0); - statusBar()->addPermanentWidget(label, 0); + statusBar()->addPermanentWidget(label); } + + // Setup Dock button + dock_status_button = new QPushButton(); + dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + dock_status_button->setFocusPolicy(Qt::NoFocus); + connect(dock_status_button, &QPushButton::clicked, [&] { + Settings::values.use_docked_mode = !Settings::values.use_docked_mode; + dock_status_button->setChecked(Settings::values.use_docked_mode); + OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); + }); + dock_status_button->setText(tr("DOCK")); + dock_status_button->setCheckable(true); + dock_status_button->setChecked(Settings::values.use_docked_mode); + statusBar()->insertPermanentWidget(0, dock_status_button); + + // Setup ASync button + async_status_button = new QPushButton(); + async_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + async_status_button->setFocusPolicy(Qt::NoFocus); + connect(async_status_button, &QPushButton::clicked, [&] { + if (emulation_running) { + return; + } + Settings::values.use_asynchronous_gpu_emulation = + !Settings::values.use_asynchronous_gpu_emulation; + async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); + Settings::Apply(); + }); + async_status_button->setText(tr("ASYNC")); + async_status_button->setCheckable(true); + async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); + statusBar()->insertPermanentWidget(0, async_status_button); + + // Setup Renderer API button + renderer_status_button = new QPushButton(); + renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); + renderer_status_button->setCheckable(true); + renderer_status_button->setFocusPolicy(Qt::NoFocus); + connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) { + renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); + }); + renderer_status_button->toggle(); + +#ifndef HAS_VULKAN + renderer_status_button->setChecked(false); + renderer_status_button->setCheckable(false); + renderer_status_button->setDisabled(true); +#else + renderer_status_button->setChecked(Settings::values.renderer_backend == + Settings::RendererBackend::Vulkan); + connect(renderer_status_button, &QPushButton::clicked, [=] { + if (emulation_running) { + return; + } + if (renderer_status_button->isChecked()) { + Settings::values.renderer_backend = Settings::RendererBackend::Vulkan; + } else { + Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; + } + + Settings::Apply(); + }); +#endif // HAS_VULKAN + statusBar()->insertPermanentWidget(0, renderer_status_button); + statusBar()->setVisible(true); setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); } @@ -640,6 +704,7 @@ void GMainWindow::InitializeHotkeys() { Settings::values.use_docked_mode = !Settings::values.use_docked_mode; OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); + dock_status_button->setChecked(Settings::values.use_docked_mode); }); } @@ -806,70 +871,12 @@ void GMainWindow::AllowOSSleep() { #endif } -QStringList GMainWindow::GetUnsupportedGLExtensions() { - QStringList unsupported_ext; - - if (!GLAD_GL_ARB_buffer_storage) { - unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); - } - if (!GLAD_GL_ARB_direct_state_access) { - unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); - } - if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) { - unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); - } - if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) { - unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); - } - if (!GLAD_GL_ARB_multi_bind) { - unsupported_ext.append(QStringLiteral("ARB_multi_bind")); - } - if (!GLAD_GL_ARB_clip_control) { - unsupported_ext.append(QStringLiteral("ARB_clip_control")); - } - - // Extensions required to support some texture formats. - if (!GLAD_GL_EXT_texture_compression_s3tc) { - unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); - } - if (!GLAD_GL_ARB_texture_compression_rgtc) { - unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); - } - if (!GLAD_GL_ARB_depth_buffer_float) { - unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); - } - - for (const QString& ext : unsupported_ext) { - LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); - } - - return unsupported_ext; -} - bool GMainWindow::LoadROM(const QString& filename) { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) ShutdownGame(); - render_window->InitRenderTarget(); - - { - Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window}; - if (!gladLoadGL()) { - QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"), - tr("Your GPU may not support OpenGL 4.3, or you do not " - "have the latest graphics driver.")); - return false; - } - } - - const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); - if (!unsupported_gl_extensions.empty()) { - QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), - tr("Your GPU may not support one or more required OpenGL" - "extensions. Please ensure you have the latest graphics " - "driver.<br><br>Unsupported extensions:<br>") + - unsupported_gl_extensions.join(QStringLiteral("<br>"))); + if (!render_window->InitRenderTarget()) { return false; } @@ -980,7 +987,9 @@ void GMainWindow::BootGame(const QString& filename) { // Create and start the emulation thread emu_thread = std::make_unique<EmuThread>(render_window); emit EmulationStarting(emu_thread.get()); - render_window->moveContext(); + if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { + render_window->moveContext(); + } emu_thread->start(); connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); @@ -1000,6 +1009,8 @@ void GMainWindow::BootGame(const QString& filename) { game_list_placeholder->hide(); } status_bar_update_timer.start(2000); + async_status_button->setDisabled(true); + renderer_status_button->setDisabled(true); const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); @@ -1065,10 +1076,13 @@ void GMainWindow::ShutdownGame() { // Disable status bar updates status_bar_update_timer.stop(); - message_label->setVisible(false); emu_speed_label->setVisible(false); game_fps_label->setVisible(false); emu_frametime_label->setVisible(false); + async_status_button->setEnabled(true); +#ifdef HAS_VULKAN + renderer_status_button->setEnabled(true); +#endif emulation_running = false; @@ -1836,6 +1850,13 @@ void GMainWindow::OnConfigure() { } config->Save(); + + dock_status_button->setChecked(Settings::values.use_docked_mode); + async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); +#ifdef HAS_VULKAN + renderer_status_button->setChecked(Settings::values.renderer_backend == + Settings::RendererBackend::Vulkan); +#endif } void GMainWindow::OnLoadAmiibo() { @@ -2028,7 +2049,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det if (emu_thread) { emu_thread->SetRunning(true); message_label->setText(status_message); - message_label->setVisible(true); } } } @@ -2195,6 +2215,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } +void GMainWindow::keyPressEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyPressEvent(event); + } +} + +void GMainWindow::keyReleaseEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyReleaseEvent(event); + } +} + static bool IsSingleFileDropEvent(QDropEvent* event) { const QMimeData* mimeData = event->mimeData(); return mimeData->hasUrls() && mimeData->urls().length() == 1; @@ -2227,18 +2259,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { event->acceptProposedAction(); } -void GMainWindow::keyPressEvent(QKeyEvent* event) { - if (render_window) { - render_window->ForwardKeyPressEvent(event); - } -} - -void GMainWindow::keyReleaseEvent(QKeyEvent* event) { - if (render_window) { - render_window->ForwardKeyReleaseEvent(event); - } -} - bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; @@ -2290,8 +2310,16 @@ void GMainWindow::UpdateUITheme() { QStringList theme_paths(default_theme_paths); if (is_default_theme || current_theme.isEmpty()) { - qApp->setStyleSheet({}); - setStyleSheet({}); + const QString theme_uri(QStringLiteral(":default/style.qss")); + QFile f(theme_uri); + if (f.open(QFile::ReadOnly | QFile::Text)) { + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + setStyleSheet(ts.readAll()); + } else { + qApp->setStyleSheet({}); + setStyleSheet({}); + } theme_paths.append(default_icons); QIcon::setThemeName(default_icons); } 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; class MicroProfileDialog; class ProfilerWidget; class QLabel; +class QPushButton; class WaitTreeWidget; enum class GameListOpenTarget; class GameListPlaceholder; @@ -130,7 +131,6 @@ private: void PreventOSSleep(); void AllowOSSleep(); - QStringList GetUnsupportedGLExtensions(); bool LoadROM(const QString& filename); void BootGame(const QString& filename); void ShutdownGame(); @@ -229,6 +229,9 @@ private: QLabel* emu_speed_label = nullptr; QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; + QPushButton* async_status_button = nullptr; + QPushButton* renderer_status_button = nullptr; + QPushButton* dock_status_button = nullptr; QTimer status_bar_update_timer; std::unique_ptr<Config> config; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index bc7725a01..a675ecf4d 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -89,7 +89,6 @@ struct Values { int profile_index; // Game List - bool show_unknown; bool show_add_ons; uint32_t icon_size; uint8_t row_1_text_id; 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 emu_window/emu_window_sdl2_gl.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h + emu_window/emu_window_sdl2_gl.cpp + emu_window/emu_window_sdl2_gl.h resource.h yuzu.cpp yuzu.rc ) +if (ENABLE_VULKAN) + target_sources(yuzu-cmd PRIVATE + emu_window/emu_window_sdl2_vk.cpp + emu_window/emu_window_sdl2_vk.h) + + target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) + target_compile_definitions(yuzu-cmd PRIVATE HAS_VULKAN) +endif() + create_target_directory_groups(yuzu-cmd) 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() { Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); // Renderer + const int renderer_backend = sdl2_config->GetInteger( + "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); + Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend); + Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); + Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0); + Settings::values.resolution_factor = static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); 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= use_multi_core= [Renderer] +# Which backend API to use. +# 0 (default): OpenGL, 1: Vulkan +backend = + +# Enable graphics API debugging mode. +# 0 (default): Disabled, 1: Enabled +debug = + +# Which Vulkan physical device to use (defaults to 0) +vulkan_device = + # Whether to use software or hardware rendering. # 0: Software, 1 (default): Hardware 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 { return is_open; } +bool EmuWindow_SDL2::IsShown() const { + return is_shown; +} + void EmuWindow_SDL2::OnResize() { int width, height; 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: /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; + /// Returns if window is shown (not minimized) + bool IsShown() const override; + protected: /// Called by PollEvents when a key is pressed or released. 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 @@ #include <SDL.h> #include <fmt/format.h> #include <glad/glad.h> +#include "common/assert.h" #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/string_util.h" @@ -151,6 +152,12 @@ void EmuWindow_SDL2_GL::DoneCurrent() { SDL_GL_MakeCurrent(render_window, nullptr); } +void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const { + // Should not have been called from OpenGL + UNREACHABLE(); +} + std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { return std::make_unique<SDLGLContext>(); } 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: /// Releases the GL context from the caller thread void DoneCurrent() override; + /// Ignored in OpenGL + void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const override; + std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 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 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <string> +#include <vector> +#include <SDL.h> +#include <SDL_vulkan.h> +#include <fmt/format.h> +#include <vulkan/vulkan.h> +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "core/settings.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" + +EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { + if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { + LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); + exit(EXIT_FAILURE); + } + + vkGetInstanceProcAddr = + reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr()); + if (vkGetInstanceProcAddr == nullptr) { + LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); + exit(EXIT_FAILURE); + } + + const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); + render_window = + SDL_CreateWindow(window_title.c_str(), + SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN); + + const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr); + + u32 extra_ext_count{}; + if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) { + LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}", + SDL_GetError()); + exit(1); + } + + auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count); + if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) { + LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError()); + exit(1); + } + std::vector<const char*> enabled_extensions; + enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(), + extra_ext_names.get() + extra_ext_count); + + std::vector<const char*> enabled_layers; + if (use_standard_layers) { + enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation"); + } + + VkApplicationInfo app_info{}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.apiVersion = VK_API_VERSION_1_1; + app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); + app_info.pApplicationName = "yuzu-emu"; + app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); + app_info.pEngineName = "yuzu-emu"; + + VkInstanceCreateInfo instance_ci{}; + instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_ci.pApplicationInfo = &app_info; + instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size()); + instance_ci.ppEnabledExtensionNames = enabled_extensions.data(); + if (Settings::values.renderer_debug) { + instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size()); + instance_ci.ppEnabledLayerNames = enabled_layers.data(); + } + + const auto vkCreateInstance = + reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance")); + if (vkCreateInstance == nullptr || + vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) { + LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!"); + exit(EXIT_FAILURE); + } + + vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>( + vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance")); + if (vkDestroyInstance == nullptr) { + LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); + exit(EXIT_FAILURE); + } + + if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) { + LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError()); + exit(EXIT_FAILURE); + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); + LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); +} + +EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { + vkDestroyInstance(vk_instance, nullptr); +} + +void EmuWindow_SDL2_VK::SwapBuffers() {} + +void EmuWindow_SDL2_VK::MakeCurrent() { + // Unused on Vulkan +} + +void EmuWindow_SDL2_VK::DoneCurrent() { + // Unused on Vulkan +} + +void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const { + const auto instance_proc_addr = vkGetInstanceProcAddr; + std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); + std::memcpy(instance, &vk_instance, sizeof(vk_instance)); + std::memcpy(surface, &vk_surface, sizeof(vk_surface)); +} + +std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { + return nullptr; +} + +bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const { + if (!Settings::values.renderer_debug) { + return false; + } + + const auto vkEnumerateInstanceLayerProperties = + reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>( + vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties")); + if (vkEnumerateInstanceLayerProperties == nullptr) { + LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); + return false; + } + + u32 available_layers_count{}; + if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) { + LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); + return false; + } + std::vector<VkLayerProperties> layers(available_layers_count); + if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) { + LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); + return false; + } + + return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { + return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); + }) != layers.end(); +} 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 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vulkan/vulkan.h> +#include "core/frontend/emu_window.h" +#include "yuzu_cmd/emu_window/emu_window_sdl2.h" + +class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { +public: + explicit EmuWindow_SDL2_VK(bool fullscreen); + ~EmuWindow_SDL2_VK(); + + /// Swap buffers to display the next frame + void SwapBuffers() override; + + /// Makes the graphics context current for the caller thread + void MakeCurrent() override; + + /// Releases the GL context from the caller thread + void DoneCurrent() override; + + /// Retrieves Vulkan specific handlers from the window + void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const override; + + std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; + +private: + bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const; + + VkInstance vk_instance{}; + VkSurfaceKHR vk_surface{}; + + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; + PFN_vkDestroyInstance vkDestroyInstance{}; +}; 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 @@ #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" +#ifdef HAS_VULKAN +#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" +#endif #include "core/file_sys/registered_cache.h" @@ -174,7 +177,20 @@ int main(int argc, char** argv) { Settings::values.use_gdbstub = use_gdbstub; Settings::Apply(); - std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)}; + std::unique_ptr<EmuWindow_SDL2> emu_window; + switch (Settings::values.renderer_backend) { + case Settings::RendererBackend::OpenGL: + emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen); + break; + case Settings::RendererBackend::Vulkan: +#ifdef HAS_VULKAN + emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen); + break; +#else + LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); + return 1; +#endif + } if (!Settings::values.use_multi_core) { // 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 @@ #include <algorithm> #include <cstdlib> #include <string> + +#include <fmt/format.h> + #define SDL_MAIN_HANDLED #include <SDL.h> -#include <fmt/format.h> + #include <glad/glad.h> + +#include "common/assert.h" #include "common/logging/log.h" #include "common/scm_rev.h" #include "core/settings.h" @@ -120,3 +125,11 @@ void EmuWindow_SDL2_Hide::MakeCurrent() { void EmuWindow_SDL2_Hide::DoneCurrent() { SDL_GL_MakeCurrent(render_window, nullptr); } + +bool EmuWindow_SDL2_Hide::IsShown() const { + return false; +} + +void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { + UNREACHABLE(); +} 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: /// Releases the GL context from the caller thread void DoneCurrent() override; + /// Whether the screen is being shown or not. + bool IsShown() const override; + + /// Retrieves Vulkan specific handlers from the window + void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, + void* surface) const override; + /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; |