diff options
Diffstat (limited to 'src')
33 files changed, 629 insertions, 153 deletions
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h new file mode 100644 index 000000000..e83064c7f --- /dev/null +++ b/src/common/bounded_threadsafe_queue.h | |||
| @@ -0,0 +1,180 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se> | ||
| 2 | // SPDX-License-Identifier: MIT | ||
| 3 | #pragma once | ||
| 4 | #ifdef _MSC_VER | ||
| 5 | #pragma warning(push) | ||
| 6 | #pragma warning(disable : 4324) | ||
| 7 | #endif | ||
| 8 | |||
| 9 | #include <atomic> | ||
| 10 | #include <bit> | ||
| 11 | #include <condition_variable> | ||
| 12 | #include <memory> | ||
| 13 | #include <mutex> | ||
| 14 | #include <new> | ||
| 15 | #include <stdexcept> | ||
| 16 | #include <stop_token> | ||
| 17 | #include <type_traits> | ||
| 18 | #include <utility> | ||
| 19 | |||
| 20 | namespace Common { | ||
| 21 | namespace mpsc { | ||
| 22 | #if defined(__cpp_lib_hardware_interference_size) | ||
| 23 | constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size; | ||
| 24 | #else | ||
| 25 | constexpr size_t hardware_interference_size = 64; | ||
| 26 | #endif | ||
| 27 | |||
| 28 | template <typename T> | ||
| 29 | using AlignedAllocator = std::allocator<T>; | ||
| 30 | |||
| 31 | template <typename T> | ||
| 32 | struct Slot { | ||
| 33 | ~Slot() noexcept { | ||
| 34 | if (turn.test()) { | ||
| 35 | destroy(); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | template <typename... Args> | ||
| 40 | void construct(Args&&... args) noexcept { | ||
| 41 | static_assert(std::is_nothrow_constructible_v<T, Args&&...>, | ||
| 42 | "T must be nothrow constructible with Args&&..."); | ||
| 43 | std::construct_at(reinterpret_cast<T*>(&storage), std::forward<Args>(args)...); | ||
| 44 | } | ||
| 45 | |||
| 46 | void destroy() noexcept { | ||
| 47 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); | ||
| 48 | std::destroy_at(reinterpret_cast<T*>(&storage)); | ||
| 49 | } | ||
| 50 | |||
| 51 | T&& move() noexcept { | ||
| 52 | return reinterpret_cast<T&&>(storage); | ||
| 53 | } | ||
| 54 | |||
| 55 | // Align to avoid false sharing between adjacent slots | ||
| 56 | alignas(hardware_interference_size) std::atomic_flag turn{}; | ||
| 57 | struct aligned_store { | ||
| 58 | struct type { | ||
| 59 | alignas(T) unsigned char data[sizeof(T)]; | ||
| 60 | }; | ||
| 61 | }; | ||
| 62 | typename aligned_store::type storage; | ||
| 63 | }; | ||
| 64 | |||
| 65 | template <typename T, typename Allocator = AlignedAllocator<Slot<T>>> | ||
| 66 | class Queue { | ||
| 67 | public: | ||
| 68 | explicit Queue(const size_t capacity, const Allocator& allocator = Allocator()) | ||
| 69 | : allocator_(allocator) { | ||
| 70 | if (capacity < 1) { | ||
| 71 | throw std::invalid_argument("capacity < 1"); | ||
| 72 | } | ||
| 73 | // Ensure that the queue length is an integer power of 2 | ||
| 74 | // This is so that idx(i) can be a simple i & mask_ insted of i % capacity | ||
| 75 | // https://github.com/rigtorp/MPMCQueue/pull/36 | ||
| 76 | if (!std::has_single_bit(capacity)) { | ||
| 77 | throw std::invalid_argument("capacity must be an integer power of 2"); | ||
| 78 | } | ||
| 79 | |||
| 80 | mask_ = capacity - 1; | ||
| 81 | |||
| 82 | // Allocate one extra slot to prevent false sharing on the last slot | ||
| 83 | slots_ = allocator_.allocate(mask_ + 2); | ||
| 84 | // Allocators are not required to honor alignment for over-aligned types | ||
| 85 | // (see http://eel.is/c++draft/allocator.requirements#10) so we verify | ||
| 86 | // alignment here | ||
| 87 | if (reinterpret_cast<uintptr_t>(slots_) % alignof(Slot<T>) != 0) { | ||
| 88 | allocator_.deallocate(slots_, mask_ + 2); | ||
| 89 | throw std::bad_alloc(); | ||
| 90 | } | ||
| 91 | for (size_t i = 0; i < mask_ + 1; ++i) { | ||
| 92 | std::construct_at(&slots_[i]); | ||
| 93 | } | ||
| 94 | static_assert(alignof(Slot<T>) == hardware_interference_size, | ||
| 95 | "Slot must be aligned to cache line boundary to prevent false sharing"); | ||
| 96 | static_assert(sizeof(Slot<T>) % hardware_interference_size == 0, | ||
| 97 | "Slot size must be a multiple of cache line size to prevent " | ||
| 98 | "false sharing between adjacent slots"); | ||
| 99 | static_assert(sizeof(Queue) % hardware_interference_size == 0, | ||
| 100 | "Queue size must be a multiple of cache line size to " | ||
| 101 | "prevent false sharing between adjacent queues"); | ||
| 102 | } | ||
| 103 | |||
| 104 | ~Queue() noexcept { | ||
| 105 | for (size_t i = 0; i < mask_ + 1; ++i) { | ||
| 106 | slots_[i].~Slot(); | ||
| 107 | } | ||
| 108 | allocator_.deallocate(slots_, mask_ + 2); | ||
| 109 | } | ||
| 110 | |||
| 111 | // non-copyable and non-movable | ||
| 112 | Queue(const Queue&) = delete; | ||
| 113 | Queue& operator=(const Queue&) = delete; | ||
| 114 | |||
| 115 | void Push(const T& v) noexcept { | ||
| 116 | static_assert(std::is_nothrow_copy_constructible_v<T>, | ||
| 117 | "T must be nothrow copy constructible"); | ||
| 118 | emplace(v); | ||
| 119 | } | ||
| 120 | |||
| 121 | template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>> | ||
| 122 | void Push(P&& v) noexcept { | ||
| 123 | emplace(std::forward<P>(v)); | ||
| 124 | } | ||
| 125 | |||
| 126 | void Pop(T& v, std::stop_token stop) noexcept { | ||
| 127 | auto const tail = tail_.fetch_add(1); | ||
| 128 | auto& slot = slots_[idx(tail)]; | ||
| 129 | if (false == slot.turn.test()) { | ||
| 130 | std::unique_lock lock{cv_mutex}; | ||
| 131 | cv.wait(lock, stop, [&slot] { return slot.turn.test(); }); | ||
| 132 | } | ||
| 133 | v = slot.move(); | ||
| 134 | slot.destroy(); | ||
| 135 | slot.turn.clear(); | ||
| 136 | slot.turn.notify_one(); | ||
| 137 | } | ||
| 138 | |||
| 139 | private: | ||
| 140 | template <typename... Args> | ||
| 141 | void emplace(Args&&... args) noexcept { | ||
| 142 | static_assert(std::is_nothrow_constructible_v<T, Args&&...>, | ||
| 143 | "T must be nothrow constructible with Args&&..."); | ||
| 144 | auto const head = head_.fetch_add(1); | ||
| 145 | auto& slot = slots_[idx(head)]; | ||
| 146 | slot.turn.wait(true); | ||
| 147 | slot.construct(std::forward<Args>(args)...); | ||
| 148 | slot.turn.test_and_set(); | ||
| 149 | cv.notify_one(); | ||
| 150 | } | ||
| 151 | |||
| 152 | constexpr size_t idx(size_t i) const noexcept { | ||
| 153 | return i & mask_; | ||
| 154 | } | ||
| 155 | |||
| 156 | std::conditional_t<true, std::condition_variable_any, std::condition_variable> cv; | ||
| 157 | std::mutex cv_mutex; | ||
| 158 | size_t mask_; | ||
| 159 | Slot<T>* slots_; | ||
| 160 | [[no_unique_address]] Allocator allocator_; | ||
| 161 | |||
| 162 | // Align to avoid false sharing between head_ and tail_ | ||
| 163 | alignas(hardware_interference_size) std::atomic<size_t> head_{0}; | ||
| 164 | alignas(hardware_interference_size) std::atomic<size_t> tail_{0}; | ||
| 165 | |||
| 166 | static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>, | ||
| 167 | "T must be nothrow copy or move assignable"); | ||
| 168 | |||
| 169 | static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible"); | ||
| 170 | }; | ||
| 171 | } // namespace mpsc | ||
| 172 | |||
| 173 | template <typename T, typename Allocator = mpsc::AlignedAllocator<mpsc::Slot<T>>> | ||
| 174 | using MPSCQueue = mpsc::Queue<T, Allocator>; | ||
| 175 | |||
| 176 | } // namespace Common | ||
| 177 | |||
| 178 | #ifdef _MSC_VER | ||
| 179 | #pragma warning(pop) | ||
| 180 | #endif | ||
diff --git a/src/common/settings.h b/src/common/settings.h index a7bbfb0da..a507744a2 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -496,7 +496,7 @@ struct Values { | |||
| 496 | 496 | ||
| 497 | // Renderer | 497 | // Renderer |
| 498 | RangedSetting<RendererBackend> renderer_backend{ | 498 | RangedSetting<RendererBackend> renderer_backend{ |
| 499 | RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; | 499 | RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; |
| 500 | BasicSetting<bool> renderer_debug{false, "debug"}; | 500 | BasicSetting<bool> renderer_debug{false, "debug"}; |
| 501 | BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; | 501 | BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; |
| 502 | BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; | 502 | BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; |
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 1d7f9a775..edf991d71 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp | |||
| @@ -110,7 +110,7 @@ private: | |||
| 110 | connection_thread = std::jthread([&, port](std::stop_token stop_token) { | 110 | connection_thread = std::jthread([&, port](std::stop_token stop_token) { |
| 111 | try { | 111 | try { |
| 112 | // Initialize the listening socket and accept a new client. | 112 | // Initialize the listening socket and accept a new client. |
| 113 | tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port}; | 113 | tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; |
| 114 | tcp::acceptor acceptor{io_context, endpoint}; | 114 | tcp::acceptor acceptor{io_context, endpoint}; |
| 115 | 115 | ||
| 116 | acceptor.async_accept(client_socket, [](const auto&) {}); | 116 | acceptor.async_accept(client_socket, [](const auto&) {}); |
diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h index 4d039a9f7..2540d6456 100644 --- a/src/core/debugger/gdbstub_arch.h +++ b/src/core/debugger/gdbstub_arch.h | |||
| @@ -15,6 +15,7 @@ namespace Core { | |||
| 15 | 15 | ||
| 16 | class GDBStubArch { | 16 | class GDBStubArch { |
| 17 | public: | 17 | public: |
| 18 | virtual ~GDBStubArch() = default; | ||
| 18 | virtual std::string GetTargetXML() const = 0; | 19 | virtual std::string GetTargetXML() const = 0; |
| 19 | virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0; | 20 | virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0; |
| 20 | virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0; | 21 | virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0; |
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index fd3cbfd94..4ae40ec8e 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -27,23 +27,18 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr | |||
| 27 | auto& page_table = m_owner->PageTable(); | 27 | auto& page_table = m_owner->PageTable(); |
| 28 | 28 | ||
| 29 | // Construct the page group. | 29 | // Construct the page group. |
| 30 | m_page_group = | 30 | m_page_group = {}; |
| 31 | KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize)); | ||
| 32 | 31 | ||
| 33 | // Lock the memory. | 32 | // Lock the memory. |
| 34 | R_TRY(page_table.LockForCodeMemory(addr, size)) | 33 | R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size)) |
| 35 | 34 | ||
| 36 | // Clear the memory. | 35 | // Clear the memory. |
| 37 | // | 36 | for (const auto& block : m_page_group.Nodes()) { |
| 38 | // FIXME: this ends up clobbering address ranges outside the scope of the mapping within | 37 | std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); |
| 39 | // guest memory, and is not specifically required if the guest program is correctly | 38 | } |
| 40 | // written, so disable until this is further investigated. | ||
| 41 | // | ||
| 42 | // for (const auto& block : m_page_group.Nodes()) { | ||
| 43 | // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); | ||
| 44 | // } | ||
| 45 | 39 | ||
| 46 | // Set remaining tracking members. | 40 | // Set remaining tracking members. |
| 41 | m_owner->Open(); | ||
| 47 | m_address = addr; | 42 | m_address = addr; |
| 48 | m_is_initialized = true; | 43 | m_is_initialized = true; |
| 49 | m_is_owner_mapped = false; | 44 | m_is_owner_mapped = false; |
| @@ -57,8 +52,14 @@ void KCodeMemory::Finalize() { | |||
| 57 | // Unlock. | 52 | // Unlock. |
| 58 | if (!m_is_mapped && !m_is_owner_mapped) { | 53 | if (!m_is_mapped && !m_is_owner_mapped) { |
| 59 | const size_t size = m_page_group.GetNumPages() * PageSize; | 54 | const size_t size = m_page_group.GetNumPages() * PageSize; |
| 60 | m_owner->PageTable().UnlockForCodeMemory(m_address, size); | 55 | m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group); |
| 61 | } | 56 | } |
| 57 | |||
| 58 | // Close the page group. | ||
| 59 | m_page_group = {}; | ||
| 60 | |||
| 61 | // Close our reference to our owner. | ||
| 62 | m_owner->Close(); | ||
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | ResultCode KCodeMemory::Map(VAddr address, size_t size) { | 65 | ResultCode KCodeMemory::Map(VAddr address, size_t size) { |
| @@ -118,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis | |||
| 118 | k_perm = KMemoryPermission::UserReadExecute; | 119 | k_perm = KMemoryPermission::UserReadExecute; |
| 119 | break; | 120 | break; |
| 120 | default: | 121 | default: |
| 121 | break; | 122 | // Already validated by ControlCodeMemory svc |
| 123 | UNREACHABLE(); | ||
| 122 | } | 124 | } |
| 123 | 125 | ||
| 124 | // Map the memory. | 126 | // Map the memory. |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index b38ef333b..68867a2bb 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -542,6 +542,95 @@ ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num | |||
| 542 | return ResultSuccess; | 542 | return ResultSuccess; |
| 543 | } | 543 | } |
| 544 | 544 | ||
| 545 | bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size_t num_pages) { | ||
| 546 | ASSERT(this->IsLockedByCurrentThread()); | ||
| 547 | |||
| 548 | const size_t size = num_pages * PageSize; | ||
| 549 | const auto& pg = pg_ll.Nodes(); | ||
| 550 | const auto& memory_layout = system.Kernel().MemoryLayout(); | ||
| 551 | |||
| 552 | // Empty groups are necessarily invalid. | ||
| 553 | if (pg.empty()) { | ||
| 554 | return false; | ||
| 555 | } | ||
| 556 | |||
| 557 | // We're going to validate that the group we'd expect is the group we see. | ||
| 558 | auto cur_it = pg.begin(); | ||
| 559 | PAddr cur_block_address = cur_it->GetAddress(); | ||
| 560 | size_t cur_block_pages = cur_it->GetNumPages(); | ||
| 561 | |||
| 562 | auto UpdateCurrentIterator = [&]() { | ||
| 563 | if (cur_block_pages == 0) { | ||
| 564 | if ((++cur_it) == pg.end()) { | ||
| 565 | return false; | ||
| 566 | } | ||
| 567 | |||
| 568 | cur_block_address = cur_it->GetAddress(); | ||
| 569 | cur_block_pages = cur_it->GetNumPages(); | ||
| 570 | } | ||
| 571 | return true; | ||
| 572 | }; | ||
| 573 | |||
| 574 | // Begin traversal. | ||
| 575 | Common::PageTable::TraversalContext context; | ||
| 576 | Common::PageTable::TraversalEntry next_entry; | ||
| 577 | if (!page_table_impl.BeginTraversal(next_entry, context, addr)) { | ||
| 578 | return false; | ||
| 579 | } | ||
| 580 | |||
| 581 | // Prepare tracking variables. | ||
| 582 | PAddr cur_addr = next_entry.phys_addr; | ||
| 583 | size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); | ||
| 584 | size_t tot_size = cur_size; | ||
| 585 | |||
| 586 | // Iterate, comparing expected to actual. | ||
| 587 | while (tot_size < size) { | ||
| 588 | if (!page_table_impl.ContinueTraversal(next_entry, context)) { | ||
| 589 | return false; | ||
| 590 | } | ||
| 591 | |||
| 592 | if (next_entry.phys_addr != (cur_addr + cur_size)) { | ||
| 593 | const size_t cur_pages = cur_size / PageSize; | ||
| 594 | |||
| 595 | if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { | ||
| 596 | return false; | ||
| 597 | } | ||
| 598 | |||
| 599 | if (!UpdateCurrentIterator()) { | ||
| 600 | return false; | ||
| 601 | } | ||
| 602 | |||
| 603 | if (cur_block_address != cur_addr || cur_block_pages < cur_pages) { | ||
| 604 | return false; | ||
| 605 | } | ||
| 606 | |||
| 607 | cur_block_address += cur_size; | ||
| 608 | cur_block_pages -= cur_pages; | ||
| 609 | cur_addr = next_entry.phys_addr; | ||
| 610 | cur_size = next_entry.block_size; | ||
| 611 | } else { | ||
| 612 | cur_size += next_entry.block_size; | ||
| 613 | } | ||
| 614 | |||
| 615 | tot_size += next_entry.block_size; | ||
| 616 | } | ||
| 617 | |||
| 618 | // Ensure we compare the right amount for the last block. | ||
| 619 | if (tot_size > size) { | ||
| 620 | cur_size -= (tot_size - size); | ||
| 621 | } | ||
| 622 | |||
| 623 | if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) { | ||
| 624 | return false; | ||
| 625 | } | ||
| 626 | |||
| 627 | if (!UpdateCurrentIterator()) { | ||
| 628 | return false; | ||
| 629 | } | ||
| 630 | |||
| 631 | return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); | ||
| 632 | } | ||
| 633 | |||
| 545 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | 634 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, |
| 546 | KPageTable& src_page_table, VAddr src_addr) { | 635 | KPageTable& src_page_table, VAddr src_addr) { |
| 547 | KScopedLightLock lk(general_lock); | 636 | KScopedLightLock lk(general_lock); |
| @@ -1687,22 +1776,22 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 1687 | return ResultSuccess; | 1776 | return ResultSuccess; |
| 1688 | } | 1777 | } |
| 1689 | 1778 | ||
| 1690 | ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { | 1779 | ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size) { |
| 1691 | return this->LockMemoryAndOpen( | 1780 | return this->LockMemoryAndOpen( |
| 1692 | nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | 1781 | out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, |
| 1693 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, | 1782 | KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, |
| 1694 | KMemoryAttribute::All, KMemoryAttribute::None, | 1783 | KMemoryAttribute::None, |
| 1695 | static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | | 1784 | static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | |
| 1696 | KMemoryPermission::KernelReadWrite), | 1785 | KMemoryPermission::KernelReadWrite), |
| 1697 | KMemoryAttribute::Locked); | 1786 | KMemoryAttribute::Locked); |
| 1698 | } | 1787 | } |
| 1699 | 1788 | ||
| 1700 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { | 1789 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, |
| 1701 | return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory, | 1790 | const KPageLinkedList& pg) { |
| 1702 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, | 1791 | return this->UnlockMemory( |
| 1703 | KMemoryPermission::None, KMemoryAttribute::All, | 1792 | addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, |
| 1704 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | 1793 | KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, |
| 1705 | KMemoryAttribute::Locked, nullptr); | 1794 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg); |
| 1706 | } | 1795 | } |
| 1707 | 1796 | ||
| 1708 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { | 1797 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { |
| @@ -2125,7 +2214,7 @@ ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_ | |||
| 2125 | 2214 | ||
| 2126 | // Check the page group. | 2215 | // Check the page group. |
| 2127 | if (pg != nullptr) { | 2216 | if (pg != nullptr) { |
| 2128 | UNIMPLEMENTED_MSG("PageGroup support is unimplemented!"); | 2217 | R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion); |
| 2129 | } | 2218 | } |
| 2130 | 2219 | ||
| 2131 | // Decide on new perm and attr. | 2220 | // Decide on new perm and attr. |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 52a93ce86..6312eb682 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -72,8 +72,8 @@ public: | |||
| 72 | KMemoryPermission perm, PAddr map_addr = 0); | 72 | KMemoryPermission perm, PAddr map_addr = 0); |
| 73 | ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); | 73 | ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); |
| 74 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); | 74 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); |
| 75 | ResultCode LockForCodeMemory(VAddr addr, std::size_t size); | 75 | ResultCode LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size); |
| 76 | ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); | 76 | ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageLinkedList& pg); |
| 77 | ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages, | 77 | ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages, |
| 78 | KMemoryState state_mask, KMemoryState state, | 78 | KMemoryState state_mask, KMemoryState state, |
| 79 | KMemoryPermission perm_mask, KMemoryPermission perm, | 79 | KMemoryPermission perm_mask, KMemoryPermission perm, |
| @@ -178,6 +178,7 @@ private: | |||
| 178 | const KPageLinkedList* pg); | 178 | const KPageLinkedList* pg); |
| 179 | 179 | ||
| 180 | ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages); | 180 | ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages); |
| 181 | bool IsValidPageGroup(const KPageLinkedList& pg, VAddr addr, size_t num_pages); | ||
| 181 | 182 | ||
| 182 | bool IsLockedByCurrentThread() const { | 183 | bool IsLockedByCurrentThread() const { |
| 183 | return general_lock.IsLockedByCurrentThread(); | 184 | return general_lock.IsLockedByCurrentThread(); |
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 3eae1ae35..32e0708ba 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp | |||
| @@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | last_update_timestamp = shared_memory->gesture_lifo.timestamp; | 63 | last_update_timestamp = shared_memory->gesture_lifo.timestamp; |
| 64 | UpdateGestureSharedMemory(gesture, time_difference); | ||
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | void Controller_Gesture::ReadTouchInput() { | 67 | void Controller_Gesture::ReadTouchInput() { |
| @@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, | |||
| 94 | return false; | 95 | return false; |
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, | 98 | void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, |
| 98 | GestureProperties& gesture, | ||
| 99 | f32 time_difference) { | 99 | f32 time_difference) { |
| 100 | GestureType type = GestureType::Idle; | 100 | GestureType type = GestureType::Idle; |
| 101 | GestureAttribute attributes{}; | 101 | GestureAttribute attributes{}; |
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index c62a341bf..0d6099ea0 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h | |||
| @@ -107,8 +107,7 @@ private: | |||
| 107 | bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); | 107 | bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); |
| 108 | 108 | ||
| 109 | // Updates the shared memory to the next state | 109 | // Updates the shared memory to the next state |
| 110 | void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture, | 110 | void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); |
| 111 | f32 time_difference); | ||
| 112 | 111 | ||
| 113 | // Initializes new gesture | 112 | // Initializes new gesture |
| 114 | void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); | 113 | void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); |
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index b79a73132..8479dc6d2 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -31,7 +31,8 @@ static void RunThread(std::stop_token stop_token, Core::System& system, | |||
| 31 | VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); | 31 | VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); |
| 32 | 32 | ||
| 33 | while (!stop_token.stop_requested()) { | 33 | while (!stop_token.stop_requested()) { |
| 34 | CommandDataContainer next = state.queue.PopWait(stop_token); | 34 | CommandDataContainer next; |
| 35 | state.queue.Pop(next, stop_token); | ||
| 35 | if (stop_token.stop_requested()) { | 36 | if (stop_token.stop_requested()) { |
| 36 | break; | 37 | break; |
| 37 | } | 38 | } |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 71cd35756..ad9fd5eff 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include <thread> | 10 | #include <thread> |
| 11 | #include <variant> | 11 | #include <variant> |
| 12 | 12 | ||
| 13 | #include "common/threadsafe_queue.h" | 13 | #include "common/bounded_threadsafe_queue.h" |
| 14 | #include "video_core/framebuffer_config.h" | 14 | #include "video_core/framebuffer_config.h" |
| 15 | 15 | ||
| 16 | namespace Tegra { | 16 | namespace Tegra { |
| @@ -96,9 +96,9 @@ struct CommandDataContainer { | |||
| 96 | 96 | ||
| 97 | /// Struct used to synchronize the GPU thread | 97 | /// Struct used to synchronize the GPU thread |
| 98 | struct SynchState final { | 98 | struct SynchState final { |
| 99 | using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>; | 99 | using CommandQueue = Common::MPSCQueue<CommandDataContainer>; |
| 100 | std::mutex write_lock; | 100 | std::mutex write_lock; |
| 101 | CommandQueue queue; | 101 | CommandQueue queue{512}; // size must be 2^n |
| 102 | u64 last_fence{}; | 102 | u64 last_fence{}; |
| 103 | std::atomic<u64> signaled_fence{}; | 103 | std::atomic<u64> signaled_fence{}; |
| 104 | std::condition_variable_any cv; | 104 | std::condition_variable_any cv; |
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp index a5dd33fb2..4eb3913ee 100644 --- a/src/video_core/vulkan_common/vulkan_library.cpp +++ b/src/video_core/vulkan_common/vulkan_library.cpp | |||
| @@ -5,11 +5,13 @@ | |||
| 5 | 5 | ||
| 6 | #include "common/dynamic_library.h" | 6 | #include "common/dynamic_library.h" |
| 7 | #include "common/fs/path_util.h" | 7 | #include "common/fs/path_util.h" |
| 8 | #include "common/logging/log.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_library.h" | 9 | #include "video_core/vulkan_common/vulkan_library.h" |
| 9 | 10 | ||
| 10 | namespace Vulkan { | 11 | namespace Vulkan { |
| 11 | 12 | ||
| 12 | Common::DynamicLibrary OpenLibrary() { | 13 | Common::DynamicLibrary OpenLibrary() { |
| 14 | LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library"); | ||
| 13 | Common::DynamicLibrary library; | 15 | Common::DynamicLibrary library; |
| 14 | #ifdef __APPLE__ | 16 | #ifdef __APPLE__ |
| 15 | // Check if a path to a specific Vulkan library has been specified. | 17 | // Check if a path to a specific Vulkan library has been specified. |
| @@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() { | |||
| 22 | } | 24 | } |
| 23 | #else | 25 | #else |
| 24 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); | 26 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); |
| 27 | LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename); | ||
| 25 | if (!library.Open(filename.c_str())) { | 28 | if (!library.Open(filename.c_str())) { |
| 26 | // Android devices may not have libvulkan.so.1, only libvulkan.so. | 29 | // Android devices may not have libvulkan.so.1, only libvulkan.so. |
| 27 | filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); | 30 | filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); |
| 31 | LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename); | ||
| 28 | void(library.Open(filename.c_str())); | 32 | void(library.Open(filename.c_str())); |
| 29 | } | 33 | } |
| 30 | #endif | 34 | #endif |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 07df9675d..242867a4f 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -30,6 +30,8 @@ add_executable(yuzu | |||
| 30 | applets/qt_web_browser_scripts.h | 30 | applets/qt_web_browser_scripts.h |
| 31 | bootmanager.cpp | 31 | bootmanager.cpp |
| 32 | bootmanager.h | 32 | bootmanager.h |
| 33 | check_vulkan.cpp | ||
| 34 | check_vulkan.h | ||
| 33 | compatdb.ui | 35 | compatdb.ui |
| 34 | compatibility_list.cpp | 36 | compatibility_list.cpp |
| 35 | compatibility_list.h | 37 | compatibility_list.h |
| @@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION) | |||
| 187 | # Update source TS file if enabled | 189 | # Update source TS file if enabled |
| 188 | if (GENERATE_QT_TRANSLATION) | 190 | if (GENERATE_QT_TRANSLATION) |
| 189 | get_target_property(SRCS yuzu SOURCES) | 191 | get_target_property(SRCS yuzu SOURCES) |
| 190 | qt5_create_translation(QM_FILES | 192 | qt_create_translation(QM_FILES |
| 191 | ${SRCS} | 193 | ${SRCS} |
| 192 | ${UIS} | 194 | ${UIS} |
| 193 | ${YUZU_QT_LANGUAGES}/en.ts | 195 | ${YUZU_QT_LANGUAGES}/en.ts |
| @@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION) | |||
| 203 | list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) | 205 | list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) |
| 204 | 206 | ||
| 205 | # Compile TS files to QM files | 207 | # Compile TS files to QM files |
| 206 | qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) | 208 | qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) |
| 207 | 209 | ||
| 208 | # Build a QRC file from the QM file list | 210 | # Build a QRC file from the QM file list |
| 209 | set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) | 211 | set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) |
| @@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION) | |||
| 215 | file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") | 217 | file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") |
| 216 | 218 | ||
| 217 | # Add the QRC file to package in all QM files | 219 | # Add the QRC file to package in all QM files |
| 218 | qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) | 220 | qt_add_resources(LANGUAGES ${LANGUAGES_QRC}) |
| 219 | else() | 221 | else() |
| 220 | set(LANGUAGES) | 222 | set(LANGUAGES) |
| 221 | endif() | 223 | endif() |
| @@ -236,8 +238,13 @@ if (APPLE) | |||
| 236 | set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) | 238 | set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) |
| 237 | elseif(WIN32) | 239 | elseif(WIN32) |
| 238 | # compile as a win32 gui application instead of a console application | 240 | # compile as a win32 gui application instead of a console application |
| 239 | target_link_libraries(yuzu PRIVATE Qt5::WinMain) | 241 | if (QT_VERSION VERSION_GREATER 6) |
| 242 | target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate) | ||
| 243 | else() | ||
| 244 | target_link_libraries(yuzu PRIVATE Qt5::WinMain) | ||
| 245 | endif() | ||
| 240 | if(MSVC) | 246 | if(MSVC) |
| 247 | target_link_libraries(yuzu PRIVATE version.lib) | ||
| 241 | set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") | 248 | set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") |
| 242 | elseif(MINGW) | 249 | elseif(MINGW) |
| 243 | set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows") | 250 | set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows") |
| @@ -247,7 +254,7 @@ endif() | |||
| 247 | create_target_directory_groups(yuzu) | 254 | create_target_directory_groups(yuzu) |
| 248 | 255 | ||
| 249 | target_link_libraries(yuzu PRIVATE common core input_common video_core) | 256 | target_link_libraries(yuzu PRIVATE common core input_common video_core) |
| 250 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) | 257 | target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets) |
| 251 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | 258 | target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) |
| 252 | 259 | ||
| 253 | target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) | 260 | target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) |
| @@ -255,7 +262,7 @@ if (NOT WIN32) | |||
| 255 | target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) | 262 | target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) |
| 256 | endif() | 263 | endif() |
| 257 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") | 264 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") |
| 258 | target_link_libraries(yuzu PRIVATE Qt5::DBus) | 265 | target_link_libraries(yuzu PRIVATE Qt::DBus) |
| 259 | endif() | 266 | endif() |
| 260 | 267 | ||
| 261 | target_compile_definitions(yuzu PRIVATE | 268 | target_compile_definitions(yuzu PRIVATE |
| @@ -291,7 +298,7 @@ if (USE_DISCORD_PRESENCE) | |||
| 291 | endif() | 298 | endif() |
| 292 | 299 | ||
| 293 | if (YUZU_USE_QT_WEB_ENGINE) | 300 | if (YUZU_USE_QT_WEB_ENGINE) |
| 294 | target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) | 301 | target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets) |
| 295 | target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) | 302 | target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) |
| 296 | endif () | 303 | endif () |
| 297 | 304 | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index aae2de2f8..bde465485 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -752,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 752 | input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y); | 752 | input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y); |
| 753 | 753 | ||
| 754 | if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { | 754 | if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { |
| 755 | QCursor::setPos(mapToGlobal({center_x, center_y})); | 755 | QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); |
| 756 | } | 756 | } |
| 757 | 757 | ||
| 758 | emit MouseActivity(); | 758 | emit MouseActivity(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 87c559e7a..d01538039 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <mutex> | 10 | #include <mutex> |
| 11 | 11 | ||
| 12 | #include <QImage> | 12 | #include <QImage> |
| 13 | #include <QStringList> | ||
| 13 | #include <QThread> | 14 | #include <QThread> |
| 14 | #include <QTouchEvent> | 15 | #include <QTouchEvent> |
| 15 | #include <QWidget> | 16 | #include <QWidget> |
| @@ -20,7 +21,6 @@ | |||
| 20 | class GRenderWindow; | 21 | class GRenderWindow; |
| 21 | class GMainWindow; | 22 | class GMainWindow; |
| 22 | class QKeyEvent; | 23 | class QKeyEvent; |
| 23 | class QStringList; | ||
| 24 | 24 | ||
| 25 | namespace Core { | 25 | namespace Core { |
| 26 | enum class SystemResultStatus : u32; | 26 | enum class SystemResultStatus : u32; |
diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/check_vulkan.cpp new file mode 100644 index 000000000..e6d66ab34 --- /dev/null +++ b/src/yuzu/check_vulkan.cpp | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 5 | |||
| 6 | #include <filesystem> | ||
| 7 | #include <fstream> | ||
| 8 | #include "common/fs/fs.h" | ||
| 9 | #include "common/fs/path_util.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "video_core/vulkan_common/vulkan_instance.h" | ||
| 12 | #include "video_core/vulkan_common/vulkan_library.h" | ||
| 13 | #include "yuzu/check_vulkan.h" | ||
| 14 | #include "yuzu/uisettings.h" | ||
| 15 | |||
| 16 | constexpr char TEMP_FILE_NAME[] = "vulkan_check"; | ||
| 17 | |||
| 18 | bool CheckVulkan() { | ||
| 19 | if (UISettings::values.has_broken_vulkan) { | ||
| 20 | return true; | ||
| 21 | } | ||
| 22 | |||
| 23 | LOG_DEBUG(Frontend, "Checking presence of Vulkan"); | ||
| 24 | |||
| 25 | const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir); | ||
| 26 | const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME; | ||
| 27 | |||
| 28 | if (std::filesystem::exists(temp_file_loc)) { | ||
| 29 | LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization"); | ||
| 30 | |||
| 31 | UISettings::values.has_broken_vulkan = true; | ||
| 32 | std::filesystem::remove(temp_file_loc); | ||
| 33 | return false; | ||
| 34 | } | ||
| 35 | |||
| 36 | std::ofstream temp_file_handle(temp_file_loc); | ||
| 37 | temp_file_handle.close(); | ||
| 38 | |||
| 39 | try { | ||
| 40 | Vulkan::vk::InstanceDispatch dld; | ||
| 41 | const Common::DynamicLibrary library = Vulkan::OpenLibrary(); | ||
| 42 | const Vulkan::vk::Instance instance = | ||
| 43 | Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); | ||
| 44 | |||
| 45 | } catch (const Vulkan::vk::Exception& exception) { | ||
| 46 | LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what()); | ||
| 47 | // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the | ||
| 48 | // application, not when we can handle it. | ||
| 49 | } | ||
| 50 | |||
| 51 | std::filesystem::remove(temp_file_loc); | ||
| 52 | return true; | ||
| 53 | } | ||
diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/check_vulkan.h new file mode 100644 index 000000000..e4ea93582 --- /dev/null +++ b/src/yuzu/check_vulkan.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | bool CheckVulkan(); | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 583e9df24..9df4752be 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -71,28 +71,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{ | |||
| 71 | // UISetting::values.shortcuts, which is alphabetically ordered. | 71 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 72 | // clang-format off | 72 | // clang-format off |
| 73 | const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ | 73 | const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ |
| 74 | {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, | 74 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, |
| 75 | {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, | 75 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, |
| 76 | {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, | 76 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, |
| 77 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, | 77 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, |
| 78 | {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, | 78 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, |
| 79 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, | 79 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, |
| 80 | {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, | 80 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, |
| 81 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, | 81 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, |
| 82 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, | 82 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 83 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, | 83 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, |
| 84 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, | 84 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, |
| 85 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, | 85 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, |
| 86 | {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, | 86 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, |
| 87 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, | 87 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 88 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, | 88 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 89 | {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 89 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 90 | {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 90 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 91 | {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 91 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 92 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, | 92 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 93 | {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, | 93 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, |
| 94 | {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 94 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 95 | {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, | 95 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 96 | }}; | 96 | }}; |
| 97 | // clang-format on | 97 | // clang-format on |
| 98 | 98 | ||
| @@ -682,6 +682,12 @@ void Config::ReadRendererValues() { | |||
| 682 | ReadGlobalSetting(Settings::values.bg_green); | 682 | ReadGlobalSetting(Settings::values.bg_green); |
| 683 | ReadGlobalSetting(Settings::values.bg_blue); | 683 | ReadGlobalSetting(Settings::values.bg_blue); |
| 684 | 684 | ||
| 685 | if (!global && UISettings::values.has_broken_vulkan && | ||
| 686 | Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan && | ||
| 687 | !Settings::values.renderer_backend.UsingGlobal()) { | ||
| 688 | Settings::values.renderer_backend.SetGlobal(true); | ||
| 689 | } | ||
| 690 | |||
| 685 | if (global) { | 691 | if (global) { |
| 686 | ReadBasicSetting(Settings::values.renderer_debug); | 692 | ReadBasicSetting(Settings::values.renderer_debug); |
| 687 | ReadBasicSetting(Settings::values.renderer_shader_feedback); | 693 | ReadBasicSetting(Settings::values.renderer_shader_feedback); |
| @@ -801,6 +807,7 @@ void Config::ReadUIValues() { | |||
| 801 | ReadBasicSetting(UISettings::values.pause_when_in_background); | 807 | ReadBasicSetting(UISettings::values.pause_when_in_background); |
| 802 | ReadBasicSetting(UISettings::values.mute_when_in_background); | 808 | ReadBasicSetting(UISettings::values.mute_when_in_background); |
| 803 | ReadBasicSetting(UISettings::values.hide_mouse); | 809 | ReadBasicSetting(UISettings::values.hide_mouse); |
| 810 | ReadBasicSetting(UISettings::values.has_broken_vulkan); | ||
| 804 | ReadBasicSetting(UISettings::values.disable_web_applet); | 811 | ReadBasicSetting(UISettings::values.disable_web_applet); |
| 805 | 812 | ||
| 806 | qt_config->endGroup(); | 813 | qt_config->endGroup(); |
| @@ -1348,6 +1355,7 @@ void Config::SaveUIValues() { | |||
| 1348 | WriteBasicSetting(UISettings::values.pause_when_in_background); | 1355 | WriteBasicSetting(UISettings::values.pause_when_in_background); |
| 1349 | WriteBasicSetting(UISettings::values.mute_when_in_background); | 1356 | WriteBasicSetting(UISettings::values.mute_when_in_background); |
| 1350 | WriteBasicSetting(UISettings::values.hide_mouse); | 1357 | WriteBasicSetting(UISettings::values.hide_mouse); |
| 1358 | WriteBasicSetting(UISettings::values.has_broken_vulkan); | ||
| 1351 | WriteBasicSetting(UISettings::values.disable_web_applet); | 1359 | WriteBasicSetting(UISettings::values.disable_web_applet); |
| 1352 | 1360 | ||
| 1353 | qt_config->endGroup(); | 1361 | qt_config->endGroup(); |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2f1435b10..85f34dc35 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "video_core/vulkan_common/vulkan_library.h" | 17 | #include "video_core/vulkan_common/vulkan_library.h" |
| 18 | #include "yuzu/configuration/configuration_shared.h" | 18 | #include "yuzu/configuration/configuration_shared.h" |
| 19 | #include "yuzu/configuration/configure_graphics.h" | 19 | #include "yuzu/configuration/configure_graphics.h" |
| 20 | #include "yuzu/uisettings.h" | ||
| 20 | 21 | ||
| 21 | ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) | 22 | ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) |
| 22 | : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { | 23 | : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { |
| @@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren | |||
| 57 | UpdateBackgroundColorButton(new_bg_color); | 58 | UpdateBackgroundColorButton(new_bg_color); |
| 58 | }); | 59 | }); |
| 59 | 60 | ||
| 61 | connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] { | ||
| 62 | UISettings::values.has_broken_vulkan = false; | ||
| 63 | |||
| 64 | if (RetrieveVulkanDevices()) { | ||
| 65 | ui->api->setEnabled(true); | ||
| 66 | ui->button_check_vulkan->hide(); | ||
| 67 | |||
| 68 | for (const auto& device : vulkan_devices) { | ||
| 69 | ui->device->addItem(device); | ||
| 70 | } | ||
| 71 | } else { | ||
| 72 | UISettings::values.has_broken_vulkan = true; | ||
| 73 | } | ||
| 74 | }); | ||
| 75 | |||
| 76 | ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue()); | ||
| 77 | ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue()); | ||
| 78 | |||
| 60 | ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); | 79 | ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); |
| 61 | ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); | 80 | ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); |
| 62 | } | 81 | } |
| @@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() { | |||
| 296 | vulkan_device = Settings::values.vulkan_device.GetValue(true); | 315 | vulkan_device = Settings::values.vulkan_device.GetValue(true); |
| 297 | shader_backend = Settings::values.shader_backend.GetValue(true); | 316 | shader_backend = Settings::values.shader_backend.GetValue(true); |
| 298 | ui->device_widget->setEnabled(false); | 317 | ui->device_widget->setEnabled(false); |
| 299 | ui->backend_widget->setEnabled(false); | 318 | ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue()); |
| 300 | } else { | 319 | } else { |
| 301 | vulkan_device = Settings::values.vulkan_device.GetValue(); | 320 | vulkan_device = Settings::values.vulkan_device.GetValue(); |
| 302 | shader_backend = Settings::values.shader_backend.GetValue(); | 321 | shader_backend = Settings::values.shader_backend.GetValue(); |
| @@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() { | |||
| 318 | } | 337 | } |
| 319 | } | 338 | } |
| 320 | 339 | ||
| 321 | void ConfigureGraphics::RetrieveVulkanDevices() try { | 340 | bool ConfigureGraphics::RetrieveVulkanDevices() try { |
| 341 | if (UISettings::values.has_broken_vulkan) { | ||
| 342 | return false; | ||
| 343 | } | ||
| 344 | |||
| 322 | using namespace Vulkan; | 345 | using namespace Vulkan; |
| 323 | 346 | ||
| 324 | vk::InstanceDispatch dld; | 347 | vk::InstanceDispatch dld; |
| @@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try { | |||
| 333 | vulkan_devices.push_back(QString::fromStdString(name)); | 356 | vulkan_devices.push_back(QString::fromStdString(name)); |
| 334 | } | 357 | } |
| 335 | 358 | ||
| 359 | return true; | ||
| 336 | } catch (const Vulkan::vk::Exception& exception) { | 360 | } catch (const Vulkan::vk::Exception& exception) { |
| 337 | LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); | 361 | LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); |
| 362 | return false; | ||
| 338 | } | 363 | } |
| 339 | 364 | ||
| 340 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | 365 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { |
| @@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 415 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); | 440 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); |
| 416 | ConfigurationShared::InsertGlobalItem( | 441 | ConfigurationShared::InsertGlobalItem( |
| 417 | ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); | 442 | ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); |
| 443 | |||
| 444 | if (UISettings::values.has_broken_vulkan) { | ||
| 445 | ui->backend_widget->setEnabled(true); | ||
| 446 | ConfigurationShared::SetColoredComboBox( | ||
| 447 | ui->backend, ui->backend_widget, | ||
| 448 | static_cast<int>(Settings::values.shader_backend.GetValue(true))); | ||
| 449 | } | ||
| 418 | } | 450 | } |
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 1b101c940..8438f0187 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h | |||
| @@ -41,7 +41,7 @@ private: | |||
| 41 | void UpdateDeviceSelection(int device); | 41 | void UpdateDeviceSelection(int device); |
| 42 | void UpdateShaderBackendSelection(int backend); | 42 | void UpdateShaderBackendSelection(int backend); |
| 43 | 43 | ||
| 44 | void RetrieveVulkanDevices(); | 44 | bool RetrieveVulkanDevices(); |
| 45 | 45 | ||
| 46 | void SetupPerGameUI(); | 46 | void SetupPerGameUI(); |
| 47 | 47 | ||
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 74f0e0b79..2f94c94bc 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>437</width> | 9 | <width>471</width> |
| 10 | <height>482</height> | 10 | <height>759</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -171,11 +171,11 @@ | |||
| 171 | </widget> | 171 | </widget> |
| 172 | </item> | 172 | </item> |
| 173 | <item> | 173 | <item> |
| 174 | <widget class="QCheckBox" name="accelerate_astc"> | 174 | <widget class="QCheckBox" name="accelerate_astc"> |
| 175 | <property name="text"> | 175 | <property name="text"> |
| 176 | <string>Accelerate ASTC texture decoding</string> | 176 | <string>Accelerate ASTC texture decoding</string> |
| 177 | </property> | 177 | </property> |
| 178 | </widget> | 178 | </widget> |
| 179 | </item> | 179 | </item> |
| 180 | <item> | 180 | <item> |
| 181 | <widget class="QWidget" name="nvdec_emulation_widget" native="true"> | 181 | <widget class="QWidget" name="nvdec_emulation_widget" native="true"> |
| @@ -438,43 +438,43 @@ | |||
| 438 | </widget> | 438 | </widget> |
| 439 | </item> | 439 | </item> |
| 440 | <item> | 440 | <item> |
| 441 | <widget class="QWidget" name="anti_aliasing_layout" native="true"> | 441 | <widget class="QWidget" name="anti_aliasing_layout" native="true"> |
| 442 | <layout class="QHBoxLayout" name="horizontalLayout_7"> | 442 | <layout class="QHBoxLayout" name="horizontalLayout_7"> |
| 443 | <property name="leftMargin"> | 443 | <property name="leftMargin"> |
| 444 | <number>0</number> | 444 | <number>0</number> |
| 445 | </property> | 445 | </property> |
| 446 | <property name="topMargin"> | 446 | <property name="topMargin"> |
| 447 | <number>0</number> | 447 | <number>0</number> |
| 448 | </property> | 448 | </property> |
| 449 | <property name="rightMargin"> | 449 | <property name="rightMargin"> |
| 450 | <number>0</number> | 450 | <number>0</number> |
| 451 | </property> | ||
| 452 | <property name="bottomMargin"> | ||
| 453 | <number>0</number> | ||
| 454 | </property> | ||
| 455 | <item> | ||
| 456 | <widget class="QLabel" name="anti_aliasing_label"> | ||
| 457 | <property name="text"> | ||
| 458 | <string>Anti-Aliasing Method:</string> | ||
| 459 | </property> | ||
| 460 | </widget> | ||
| 461 | </item> | ||
| 462 | <item> | ||
| 463 | <widget class="QComboBox" name="anti_aliasing_combobox"> | ||
| 464 | <item> | ||
| 465 | <property name="text"> | ||
| 466 | <string>None</string> | ||
| 451 | </property> | 467 | </property> |
| 452 | <property name="bottomMargin"> | 468 | </item> |
| 453 | <number>0</number> | 469 | <item> |
| 470 | <property name="text"> | ||
| 471 | <string>FXAA</string> | ||
| 454 | </property> | 472 | </property> |
| 455 | <item> | 473 | </item> |
| 456 | <widget class="QLabel" name="anti_aliasing_label"> | 474 | </widget> |
| 457 | <property name="text"> | 475 | </item> |
| 458 | <string>Anti-Aliasing Method:</string> | 476 | </layout> |
| 459 | </property> | 477 | </widget> |
| 460 | </widget> | ||
| 461 | </item> | ||
| 462 | <item> | ||
| 463 | <widget class="QComboBox" name="anti_aliasing_combobox"> | ||
| 464 | <item> | ||
| 465 | <property name="text"> | ||
| 466 | <string>None</string> | ||
| 467 | </property> | ||
| 468 | </item> | ||
| 469 | <item> | ||
| 470 | <property name="text"> | ||
| 471 | <string>FXAA</string> | ||
| 472 | </property> | ||
| 473 | </item> | ||
| 474 | </widget> | ||
| 475 | </item> | ||
| 476 | </layout> | ||
| 477 | </widget> | ||
| 478 | </item> | 478 | </item> |
| 479 | <item> | 479 | <item> |
| 480 | <widget class="QWidget" name="bg_layout" native="true"> | 480 | <widget class="QWidget" name="bg_layout" native="true"> |
| @@ -574,6 +574,13 @@ | |||
| 574 | </property> | 574 | </property> |
| 575 | </spacer> | 575 | </spacer> |
| 576 | </item> | 576 | </item> |
| 577 | <item> | ||
| 578 | <widget class="QPushButton" name="button_check_vulkan"> | ||
| 579 | <property name="text"> | ||
| 580 | <string>Check for Working Vulkan</string> | ||
| 581 | </property> | ||
| 582 | </widget> | ||
| 583 | </item> | ||
| 577 | </layout> | 584 | </layout> |
| 578 | </widget> | 585 | </widget> |
| 579 | <resources/> | 586 | <resources/> |
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 6679e9c53..edf0893c4 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp | |||
| @@ -61,14 +61,18 @@ ConfigureHotkeys::~ConfigureHotkeys() = default; | |||
| 61 | 61 | ||
| 62 | void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { | 62 | void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { |
| 63 | for (const auto& group : registry.hotkey_groups) { | 63 | for (const auto& group : registry.hotkey_groups) { |
| 64 | auto* parent_item = new QStandardItem(group.first); | 64 | auto* parent_item = |
| 65 | new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first))); | ||
| 65 | parent_item->setEditable(false); | 66 | parent_item->setEditable(false); |
| 67 | parent_item->setData(group.first); | ||
| 66 | for (const auto& hotkey : group.second) { | 68 | for (const auto& hotkey : group.second) { |
| 67 | auto* action = new QStandardItem(hotkey.first); | 69 | auto* action = |
| 70 | new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first))); | ||
| 68 | auto* keyseq = | 71 | auto* keyseq = |
| 69 | new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); | 72 | new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); |
| 70 | auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); | 73 | auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); |
| 71 | action->setEditable(false); | 74 | action->setEditable(false); |
| 75 | action->setData(hotkey.first); | ||
| 72 | keyseq->setEditable(false); | 76 | keyseq->setEditable(false); |
| 73 | controller_keyseq->setEditable(false); | 77 | controller_keyseq->setEditable(false); |
| 74 | parent_item->appendRow({action, keyseq, controller_keyseq}); | 78 | parent_item->appendRow({action, keyseq, controller_keyseq}); |
| @@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() { | |||
| 93 | ui->retranslateUi(this); | 97 | ui->retranslateUi(this); |
| 94 | 98 | ||
| 95 | model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); | 99 | model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); |
| 100 | for (int key_id = 0; key_id < model->rowCount(); key_id++) { | ||
| 101 | QStandardItem* parent = model->item(key_id, 0); | ||
| 102 | parent->setText( | ||
| 103 | QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString()))); | ||
| 104 | for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { | ||
| 105 | QStandardItem* action = parent->child(key_column_id, name_column); | ||
| 106 | action->setText( | ||
| 107 | QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString()))); | ||
| 108 | } | ||
| 109 | } | ||
| 96 | } | 110 | } |
| 97 | 111 | ||
| 98 | void ConfigureHotkeys::Configure(QModelIndex index) { | 112 | void ConfigureHotkeys::Configure(QModelIndex index) { |
| @@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { | |||
| 273 | const QStandardItem* controller_keyseq = | 287 | const QStandardItem* controller_keyseq = |
| 274 | parent->child(key_column_id, controller_column); | 288 | parent->child(key_column_id, controller_column); |
| 275 | for (auto& [group, sub_actions] : registry.hotkey_groups) { | 289 | for (auto& [group, sub_actions] : registry.hotkey_groups) { |
| 276 | if (group != parent->text()) | 290 | if (group != parent->data()) |
| 277 | continue; | 291 | continue; |
| 278 | for (auto& [action_name, hotkey] : sub_actions) { | 292 | for (auto& [action_name, hotkey] : sub_actions) { |
| 279 | if (action_name != action->text()) | 293 | if (action_name != action->data()) |
| 280 | continue; | 294 | continue; |
| 281 | hotkey.keyseq = QKeySequence(keyseq->text()); | 295 | hotkey.keyseq = QKeySequence(keyseq->text()); |
| 282 | hotkey.controller_keyseq = controller_keyseq->text(); | 296 | hotkey.controller_keyseq = controller_keyseq->text(); |
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index 27559c37b..c313b0919 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp | |||
| @@ -151,6 +151,8 @@ void ConfigureMotionTouch::ConnectEvents() { | |||
| 151 | &ConfigureMotionTouch::OnConfigureTouchCalibration); | 151 | &ConfigureMotionTouch::OnConfigureTouchCalibration); |
| 152 | connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, | 152 | connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, |
| 153 | &ConfigureMotionTouch::OnConfigureTouchFromButton); | 153 | &ConfigureMotionTouch::OnConfigureTouchFromButton); |
| 154 | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, | ||
| 155 | &ConfigureMotionTouch::ApplyConfiguration); | ||
| 154 | connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { | 156 | connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { |
| 155 | if (CanCloseDialog()) { | 157 | if (CanCloseDialog()) { |
| 156 | reject(); | 158 | reject(); |
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui index c75a84ae4..0237fae54 100644 --- a/src/yuzu/configuration/configure_motion_touch.ui +++ b/src/yuzu/configuration/configure_motion_touch.ui | |||
| @@ -293,22 +293,5 @@ | |||
| 293 | </layout> | 293 | </layout> |
| 294 | </widget> | 294 | </widget> |
| 295 | <resources/> | 295 | <resources/> |
| 296 | <connections> | 296 | <connections/> |
| 297 | <connection> | ||
| 298 | <sender>buttonBox</sender> | ||
| 299 | <signal>accepted()</signal> | ||
| 300 | <receiver>ConfigureMotionTouch</receiver> | ||
| 301 | <slot>ApplyConfiguration()</slot> | ||
| 302 | <hints> | ||
| 303 | <hint type="sourcelabel"> | ||
| 304 | <x>20</x> | ||
| 305 | <y>20</y> | ||
| 306 | </hint> | ||
| 307 | <hint type="destinationlabel"> | ||
| 308 | <x>20</x> | ||
| 309 | <y>20</y> | ||
| 310 | </hint> | ||
| 311 | </hints> | ||
| 312 | </connection> | ||
| 313 | </connections> | ||
| 314 | </ui> | 297 | </ui> |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 19aa589f9..ecebb0fb7 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -130,8 +130,7 @@ void ConfigureSystem::ApplyConfiguration() { | |||
| 130 | // Guard if during game and set to game-specific value | 130 | // Guard if during game and set to game-specific value |
| 131 | if (Settings::values.rng_seed.UsingGlobal()) { | 131 | if (Settings::values.rng_seed.UsingGlobal()) { |
| 132 | if (ui->rng_seed_checkbox->isChecked()) { | 132 | if (ui->rng_seed_checkbox->isChecked()) { |
| 133 | Settings::values.rng_seed.SetValue( | 133 | Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16)); |
| 134 | ui->rng_seed_edit->text().toULongLong(nullptr, 16)); | ||
| 135 | } else { | 134 | } else { |
| 136 | Settings::values.rng_seed.SetValue(std::nullopt); | 135 | Settings::values.rng_seed.SetValue(std::nullopt); |
| 137 | } | 136 | } |
| @@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() { | |||
| 142 | case ConfigurationShared::CheckState::Off: | 141 | case ConfigurationShared::CheckState::Off: |
| 143 | Settings::values.rng_seed.SetGlobal(false); | 142 | Settings::values.rng_seed.SetGlobal(false); |
| 144 | if (ui->rng_seed_checkbox->isChecked()) { | 143 | if (ui->rng_seed_checkbox->isChecked()) { |
| 145 | Settings::values.rng_seed.SetValue( | 144 | Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16)); |
| 146 | ui->rng_seed_edit->text().toULongLong(nullptr, 16)); | ||
| 147 | } else { | 145 | } else { |
| 148 | Settings::values.rng_seed.SetValue(std::nullopt); | 146 | Settings::values.rng_seed.SetValue(std::nullopt); |
| 149 | } | 147 | } |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 4a6d74a7e..6321afc83 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -483,7 +483,7 @@ void GameList::DonePopulating(const QStringList& watch_list) { | |||
| 483 | // Also artificially caps the watcher to a certain number of directories | 483 | // Also artificially caps the watcher to a certain number of directories |
| 484 | constexpr int LIMIT_WATCH_DIRECTORIES = 5000; | 484 | constexpr int LIMIT_WATCH_DIRECTORIES = 5000; |
| 485 | constexpr int SLICE_SIZE = 25; | 485 | constexpr int SLICE_SIZE = 25; |
| 486 | int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); | 486 | int len = std::min(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES); |
| 487 | for (int i = 0; i < len; i += SLICE_SIZE) { | 487 | for (int i = 0; i < len; i += SLICE_SIZE) { |
| 488 | watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); | 488 | watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); |
| 489 | QCoreApplication::processEvents(); | 489 | QCoreApplication::processEvents(); |
| @@ -870,7 +870,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} | |||
| 870 | layout->setAlignment(Qt::AlignCenter); | 870 | layout->setAlignment(Qt::AlignCenter); |
| 871 | image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); | 871 | image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); |
| 872 | 872 | ||
| 873 | text->setText(tr("Double-click to add a new folder to the game list")); | 873 | RetranslateUI(); |
| 874 | QFont font = text->font(); | 874 | QFont font = text->font(); |
| 875 | font.setPointSize(20); | 875 | font.setPointSize(20); |
| 876 | text->setFont(font); | 876 | text->setFont(font); |
| @@ -891,3 +891,15 @@ void GameListPlaceholder::onUpdateThemedIcons() { | |||
| 891 | void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { | 891 | void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { |
| 892 | emit GameListPlaceholder::AddDirectory(); | 892 | emit GameListPlaceholder::AddDirectory(); |
| 893 | } | 893 | } |
| 894 | |||
| 895 | void GameListPlaceholder::changeEvent(QEvent* event) { | ||
| 896 | if (event->type() == QEvent::LanguageChange) { | ||
| 897 | RetranslateUI(); | ||
| 898 | } | ||
| 899 | |||
| 900 | QWidget::changeEvent(event); | ||
| 901 | } | ||
| 902 | |||
| 903 | void GameListPlaceholder::RetranslateUI() { | ||
| 904 | text->setText(tr("Double-click to add a new folder to the game list")); | ||
| 905 | } | ||
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index d19dbe4b0..464da98ad 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -166,6 +166,9 @@ protected: | |||
| 166 | void mouseDoubleClickEvent(QMouseEvent* event) override; | 166 | void mouseDoubleClickEvent(QMouseEvent* event) override; |
| 167 | 167 | ||
| 168 | private: | 168 | private: |
| 169 | void changeEvent(QEvent* event) override; | ||
| 170 | void RetranslateUI(); | ||
| 171 | |||
| 169 | QVBoxLayout* layout = nullptr; | 172 | QVBoxLayout* layout = nullptr; |
| 170 | QLabel* image = nullptr; | 173 | QLabel* image = nullptr; |
| 171 | QLabel* text = nullptr; | 174 | QLabel* text = nullptr; |
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index edfb946a8..e273744fd 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp | |||
| @@ -183,7 +183,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size | |||
| 183 | 183 | ||
| 184 | void LoadingScreen::paintEvent(QPaintEvent* event) { | 184 | void LoadingScreen::paintEvent(QPaintEvent* event) { |
| 185 | QStyleOption opt; | 185 | QStyleOption opt; |
| 186 | opt.init(this); | 186 | opt.initFrom(this); |
| 187 | QPainter p(this); | 187 | QPainter p(this); |
| 188 | style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); | 188 | style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); |
| 189 | QWidget::paintEvent(event); | 189 | QWidget::paintEvent(event); |
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h index 7c960ee72..17045595d 100644 --- a/src/yuzu/loading_screen.h +++ b/src/yuzu/loading_screen.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <QString> | 8 | #include <QString> |
| 9 | #include <QWidget> | 9 | #include <QWidget> |
| 10 | #include <QtGlobal> | ||
| 10 | 11 | ||
| 11 | #if !QT_CONFIG(movie) | 12 | #if !QT_CONFIG(movie) |
| 12 | #define YUZU_QT_MOVIE_MISSING 1 | 13 | #define YUZU_QT_MOVIE_MISSING 1 |
| @@ -88,4 +89,6 @@ private: | |||
| 88 | std::size_t slow_shader_first_value = 0; | 89 | std::size_t slow_shader_first_value = 0; |
| 89 | }; | 90 | }; |
| 90 | 91 | ||
| 92 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
| 91 | Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); | 93 | Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); |
| 94 | #endif | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c9288b4fe..33886e50e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -52,7 +52,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 52 | #define QT_NO_OPENGL | 52 | #define QT_NO_OPENGL |
| 53 | #include <QClipboard> | 53 | #include <QClipboard> |
| 54 | #include <QDesktopServices> | 54 | #include <QDesktopServices> |
| 55 | #include <QDesktopWidget> | ||
| 56 | #include <QFile> | 55 | #include <QFile> |
| 57 | #include <QFileDialog> | 56 | #include <QFileDialog> |
| 58 | #include <QInputDialog> | 57 | #include <QInputDialog> |
| @@ -60,6 +59,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 60 | #include <QProgressBar> | 59 | #include <QProgressBar> |
| 61 | #include <QProgressDialog> | 60 | #include <QProgressDialog> |
| 62 | #include <QPushButton> | 61 | #include <QPushButton> |
| 62 | #include <QScreen> | ||
| 63 | #include <QShortcut> | 63 | #include <QShortcut> |
| 64 | #include <QStatusBar> | 64 | #include <QStatusBar> |
| 65 | #include <QString> | 65 | #include <QString> |
| @@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 115 | #include "video_core/shader_notify.h" | 115 | #include "video_core/shader_notify.h" |
| 116 | #include "yuzu/about_dialog.h" | 116 | #include "yuzu/about_dialog.h" |
| 117 | #include "yuzu/bootmanager.h" | 117 | #include "yuzu/bootmanager.h" |
| 118 | #include "yuzu/check_vulkan.h" | ||
| 118 | #include "yuzu/compatdb.h" | 119 | #include "yuzu/compatdb.h" |
| 119 | #include "yuzu/compatibility_list.h" | 120 | #include "yuzu/compatibility_list.h" |
| 120 | #include "yuzu/configuration/config.h" | 121 | #include "yuzu/configuration/config.h" |
| @@ -198,6 +199,34 @@ static void RemoveCachedContents() { | |||
| 198 | Common::FS::RemoveDirRecursively(offline_system_data); | 199 | Common::FS::RemoveDirRecursively(offline_system_data); |
| 199 | } | 200 | } |
| 200 | 201 | ||
| 202 | static void LogRuntimes() { | ||
| 203 | #ifdef _MSC_VER | ||
| 204 | // It is possible that the name of the dll will change. | ||
| 205 | // vcruntime140.dll is for 2015 and onwards | ||
| 206 | constexpr char runtime_dll_name[] = "vcruntime140.dll"; | ||
| 207 | UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr); | ||
| 208 | bool runtime_version_inspection_worked = false; | ||
| 209 | if (sz > 0) { | ||
| 210 | std::vector<u8> buf(sz); | ||
| 211 | if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) { | ||
| 212 | VS_FIXEDFILEINFO* pvi; | ||
| 213 | sz = sizeof(VS_FIXEDFILEINFO); | ||
| 214 | if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) { | ||
| 215 | if (pvi->dwSignature == VS_FFI_SIGNATURE) { | ||
| 216 | runtime_version_inspection_worked = true; | ||
| 217 | LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER, | ||
| 218 | pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF, | ||
| 219 | pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | if (!runtime_version_inspection_worked) { | ||
| 225 | LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name); | ||
| 226 | } | ||
| 227 | #endif | ||
| 228 | } | ||
| 229 | |||
| 201 | static QString PrettyProductName() { | 230 | static QString PrettyProductName() { |
| 202 | #ifdef _WIN32 | 231 | #ifdef _WIN32 |
| 203 | // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2 | 232 | // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2 |
| @@ -268,6 +297,7 @@ GMainWindow::GMainWindow() | |||
| 268 | const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; | 297 | const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; |
| 269 | 298 | ||
| 270 | LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); | 299 | LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); |
| 300 | LogRuntimes(); | ||
| 271 | #ifdef ARCHITECTURE_x86_64 | 301 | #ifdef ARCHITECTURE_x86_64 |
| 272 | const auto& caps = Common::GetCPUCaps(); | 302 | const auto& caps = Common::GetCPUCaps(); |
| 273 | std::string cpu_string = caps.cpu_string; | 303 | std::string cpu_string = caps.cpu_string; |
| @@ -322,6 +352,23 @@ GMainWindow::GMainWindow() | |||
| 322 | 352 | ||
| 323 | MigrateConfigFiles(); | 353 | MigrateConfigFiles(); |
| 324 | 354 | ||
| 355 | if (!CheckVulkan()) { | ||
| 356 | config->Save(); | ||
| 357 | |||
| 358 | QMessageBox::warning( | ||
| 359 | this, tr("Broken Vulkan Installation Detected"), | ||
| 360 | tr("Vulkan initialization failed on the previous boot.<br><br>Click <a " | ||
| 361 | "href='https://yuzu-emu.org/wiki/faq/" | ||
| 362 | "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for " | ||
| 363 | "instructions to fix the issue</a>.")); | ||
| 364 | } | ||
| 365 | if (UISettings::values.has_broken_vulkan) { | ||
| 366 | Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; | ||
| 367 | |||
| 368 | renderer_status_button->setDisabled(true); | ||
| 369 | renderer_status_button->setChecked(false); | ||
| 370 | } | ||
| 371 | |||
| 325 | #if defined(HAVE_SDL2) && !defined(_WIN32) | 372 | #if defined(HAVE_SDL2) && !defined(_WIN32) |
| 326 | SDL_InitSubSystem(SDL_INIT_VIDEO); | 373 | SDL_InitSubSystem(SDL_INIT_VIDEO); |
| 327 | // SDL disables the screen saver by default, and setting the hint | 374 | // SDL disables the screen saver by default, and setting the hint |
| @@ -852,12 +899,11 @@ void GMainWindow::InitializeWidgets() { | |||
| 852 | 899 | ||
| 853 | // Setup Dock button | 900 | // Setup Dock button |
| 854 | dock_status_button = new QPushButton(); | 901 | dock_status_button = new QPushButton(); |
| 855 | dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | 902 | dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton")); |
| 856 | dock_status_button->setFocusPolicy(Qt::NoFocus); | 903 | dock_status_button->setFocusPolicy(Qt::NoFocus); |
| 857 | connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); | 904 | connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); |
| 858 | dock_status_button->setText(tr("DOCK")); | ||
| 859 | dock_status_button->setCheckable(true); | 905 | dock_status_button->setCheckable(true); |
| 860 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | 906 | UpdateDockedButton(); |
| 861 | statusBar()->insertPermanentWidget(0, dock_status_button); | 907 | statusBar()->insertPermanentWidget(0, dock_status_button); |
| 862 | 908 | ||
| 863 | gpu_accuracy_button = new QPushButton(); | 909 | gpu_accuracy_button = new QPushButton(); |
| @@ -1027,7 +1073,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 1027 | 1073 | ||
| 1028 | void GMainWindow::SetDefaultUIGeometry() { | 1074 | void GMainWindow::SetDefaultUIGeometry() { |
| 1029 | // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half | 1075 | // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half |
| 1030 | const QRect screenRect = QApplication::desktop()->screenGeometry(this); | 1076 | const QRect screenRect = QGuiApplication::primaryScreen()->geometry(); |
| 1031 | 1077 | ||
| 1032 | const int w = screenRect.width() * 2 / 3; | 1078 | const int w = screenRect.width() * 2 / 3; |
| 1033 | const int h = screenRect.height() * 2 / 3; | 1079 | const int h = screenRect.height() * 2 / 3; |
| @@ -1593,7 +1639,7 @@ void GMainWindow::ShutdownGame() { | |||
| 1593 | emu_speed_label->setVisible(false); | 1639 | emu_speed_label->setVisible(false); |
| 1594 | game_fps_label->setVisible(false); | 1640 | game_fps_label->setVisible(false); |
| 1595 | emu_frametime_label->setVisible(false); | 1641 | emu_frametime_label->setVisible(false); |
| 1596 | renderer_status_button->setEnabled(true); | 1642 | renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); |
| 1597 | 1643 | ||
| 1598 | game_path.clear(); | 1644 | game_path.clear(); |
| 1599 | 1645 | ||
| @@ -1613,7 +1659,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) { | |||
| 1613 | 1659 | ||
| 1614 | void GMainWindow::UpdateRecentFiles() { | 1660 | void GMainWindow::UpdateRecentFiles() { |
| 1615 | const int num_recent_files = | 1661 | const int num_recent_files = |
| 1616 | std::min(UISettings::values.recent_files.size(), max_recent_files_item); | 1662 | std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item); |
| 1617 | 1663 | ||
| 1618 | for (int i = 0; i < num_recent_files; i++) { | 1664 | for (int i = 0; i < num_recent_files; i++) { |
| 1619 | const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( | 1665 | const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( |
| @@ -2611,6 +2657,18 @@ void GMainWindow::ToggleFullscreen() { | |||
| 2611 | } | 2657 | } |
| 2612 | } | 2658 | } |
| 2613 | 2659 | ||
| 2660 | // We're going to return the screen that the given window has the most pixels on | ||
| 2661 | static QScreen* GuessCurrentScreen(QWidget* window) { | ||
| 2662 | const QList<QScreen*> screens = QGuiApplication::screens(); | ||
| 2663 | return *std::max_element( | ||
| 2664 | screens.cbegin(), screens.cend(), [window](const QScreen* left, const QScreen* right) { | ||
| 2665 | const QSize left_size = left->geometry().intersected(window->geometry()).size(); | ||
| 2666 | const QSize right_size = right->geometry().intersected(window->geometry()).size(); | ||
| 2667 | return (left_size.height() * left_size.width()) < | ||
| 2668 | (right_size.height() * right_size.width()); | ||
| 2669 | }); | ||
| 2670 | } | ||
| 2671 | |||
| 2614 | void GMainWindow::ShowFullscreen() { | 2672 | void GMainWindow::ShowFullscreen() { |
| 2615 | const auto show_fullscreen = [](QWidget* window) { | 2673 | const auto show_fullscreen = [](QWidget* window) { |
| 2616 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 2674 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { |
| @@ -2619,7 +2677,7 @@ void GMainWindow::ShowFullscreen() { | |||
| 2619 | } | 2677 | } |
| 2620 | window->hide(); | 2678 | window->hide(); |
| 2621 | window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint); | 2679 | window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint); |
| 2622 | const auto screen_geometry = QApplication::desktop()->screenGeometry(window); | 2680 | const auto screen_geometry = GuessCurrentScreen(window)->geometry(); |
| 2623 | window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), | 2681 | window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), |
| 2624 | screen_geometry.height() + 1); | 2682 | screen_geometry.height() + 1); |
| 2625 | window->raise(); | 2683 | window->raise(); |
| @@ -2803,6 +2861,10 @@ void GMainWindow::OnConfigure() { | |||
| 2803 | mouse_hide_timer.start(); | 2861 | mouse_hide_timer.start(); |
| 2804 | } | 2862 | } |
| 2805 | 2863 | ||
| 2864 | if (!UISettings::values.has_broken_vulkan) { | ||
| 2865 | renderer_status_button->setEnabled(!emulation_running); | ||
| 2866 | } | ||
| 2867 | |||
| 2806 | UpdateStatusButtons(); | 2868 | UpdateStatusButtons(); |
| 2807 | controller_dialog->refreshConfiguration(); | 2869 | controller_dialog->refreshConfiguration(); |
| 2808 | } | 2870 | } |
| @@ -2888,7 +2950,7 @@ void GMainWindow::OnToggleDockedMode() { | |||
| 2888 | } | 2950 | } |
| 2889 | 2951 | ||
| 2890 | Settings::values.use_docked_mode.SetValue(!is_docked); | 2952 | Settings::values.use_docked_mode.SetValue(!is_docked); |
| 2891 | dock_status_button->setChecked(!is_docked); | 2953 | UpdateDockedButton(); |
| 2892 | OnDockedModeChanged(is_docked, !is_docked, *system); | 2954 | OnDockedModeChanged(is_docked, !is_docked, *system); |
| 2893 | } | 2955 | } |
| 2894 | 2956 | ||
| @@ -3254,6 +3316,12 @@ void GMainWindow::UpdateGPUAccuracyButton() { | |||
| 3254 | } | 3316 | } |
| 3255 | } | 3317 | } |
| 3256 | 3318 | ||
| 3319 | void GMainWindow::UpdateDockedButton() { | ||
| 3320 | const bool is_docked = Settings::values.use_docked_mode.GetValue(); | ||
| 3321 | dock_status_button->setChecked(is_docked); | ||
| 3322 | dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD")); | ||
| 3323 | } | ||
| 3324 | |||
| 3257 | void GMainWindow::UpdateFilterText() { | 3325 | void GMainWindow::UpdateFilterText() { |
| 3258 | const auto filter = Settings::values.scaling_filter.GetValue(); | 3326 | const auto filter = Settings::values.scaling_filter.GetValue(); |
| 3259 | switch (filter) { | 3327 | switch (filter) { |
| @@ -3297,10 +3365,10 @@ void GMainWindow::UpdateAAText() { | |||
| 3297 | } | 3365 | } |
| 3298 | 3366 | ||
| 3299 | void GMainWindow::UpdateStatusButtons() { | 3367 | void GMainWindow::UpdateStatusButtons() { |
| 3300 | dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); | ||
| 3301 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == | 3368 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == |
| 3302 | Settings::RendererBackend::Vulkan); | 3369 | Settings::RendererBackend::Vulkan); |
| 3303 | UpdateGPUAccuracyButton(); | 3370 | UpdateGPUAccuracyButton(); |
| 3371 | UpdateDockedButton(); | ||
| 3304 | UpdateFilterText(); | 3372 | UpdateFilterText(); |
| 3305 | UpdateAAText(); | 3373 | UpdateAAText(); |
| 3306 | } | 3374 | } |
| @@ -3351,7 +3419,7 @@ void GMainWindow::CenterMouseCursor() { | |||
| 3351 | const int center_x = render_window->width() / 2; | 3419 | const int center_x = render_window->width() / 2; |
| 3352 | const int center_y = render_window->height() / 2; | 3420 | const int center_y = render_window->height() / 2; |
| 3353 | 3421 | ||
| 3354 | QCursor::setPos(mapToGlobal({center_x, center_y})); | 3422 | QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); |
| 3355 | } | 3423 | } |
| 3356 | 3424 | ||
| 3357 | void GMainWindow::OnMouseActivity() { | 3425 | void GMainWindow::OnMouseActivity() { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index b399e9b01..600647015 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -320,6 +320,7 @@ private: | |||
| 320 | void MigrateConfigFiles(); | 320 | void MigrateConfigFiles(); |
| 321 | void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, | 321 | void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, |
| 322 | std::string_view gpu_vendor = {}); | 322 | std::string_view gpu_vendor = {}); |
| 323 | void UpdateDockedButton(); | ||
| 323 | void UpdateFilterText(); | 324 | void UpdateFilterText(); |
| 324 | void UpdateAAText(); | 325 | void UpdateAAText(); |
| 325 | void UpdateStatusBar(); | 326 | void UpdateStatusBar(); |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 15ba9ea17..c64d87ace 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -77,6 +77,8 @@ struct Values { | |||
| 77 | Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; | 77 | Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; |
| 78 | Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; | 78 | Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; |
| 79 | Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; | 79 | Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; |
| 80 | // Set when Vulkan is known to crash the application | ||
| 81 | Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; | ||
| 80 | 82 | ||
| 81 | Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; | 83 | Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; |
| 82 | 84 | ||
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index d5281863f..a3b8432f5 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -218,7 +218,7 @@ cpuopt_unsafe_ignore_global_monitor = | |||
| 218 | 218 | ||
| 219 | [Renderer] | 219 | [Renderer] |
| 220 | # Which backend API to use. | 220 | # Which backend API to use. |
| 221 | # 0 (default): OpenGL, 1: Vulkan | 221 | # 0: OpenGL, 1 (default): Vulkan |
| 222 | backend = | 222 | backend = |
| 223 | 223 | ||
| 224 | # Enable graphics API debugging mode. | 224 | # Enable graphics API debugging mode. |