diff options
Diffstat (limited to 'src')
117 files changed, 2530 insertions, 337 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0913be72c..3a57356ab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -54,8 +54,10 @@ else() | |||
| 54 | add_compile_options( | 54 | add_compile_options( |
| 55 | -Wall | 55 | -Wall |
| 56 | -Werror=implicit-fallthrough | 56 | -Werror=implicit-fallthrough |
| 57 | -Werror=missing-declarations | ||
| 57 | -Werror=reorder | 58 | -Werror=reorder |
| 58 | -Wextra | 59 | -Wextra |
| 60 | -Wmissing-declarations | ||
| 59 | -Wno-attributes | 61 | -Wno-attributes |
| 60 | -Wno-unused-parameter | 62 | -Wno-unused-parameter |
| 61 | ) | 63 | ) |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index fd2bbbd99..26ae6c7fc 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -180,7 +180,7 @@ public: | |||
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | constexpr void Assign(const T& value) { | 182 | constexpr void Assign(const T& value) { |
| 183 | storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value); | 183 | storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | constexpr T Value() const { | 186 | constexpr T Value() const { |
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index d64302f2e..7ed71ac3a 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp | |||
| @@ -202,8 +202,8 @@ static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector< | |||
| 202 | return out; | 202 | return out; |
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, | 205 | static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, |
| 206 | const std::string& name) { | 206 | const std::string& name) { |
| 207 | const auto upper = Common::ToUpper(name); | 207 | const auto upper = Common::ToUpper(name); |
| 208 | 208 | ||
| 209 | for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) { | 209 | for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) { |
| @@ -345,8 +345,7 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con | |||
| 345 | return package2.at(static_cast<size_t>(type)); | 345 | return package2.at(static_cast<size_t>(type)); |
| 346 | } | 346 | } |
| 347 | 347 | ||
| 348 | bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { | 348 | static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { |
| 349 | |||
| 350 | const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end()); | 349 | const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end()); |
| 351 | Package2Header temp = header; | 350 | Package2Header temp = header; |
| 352 | AESCipher<Key128> cipher(key, Mode::CTR); | 351 | AESCipher<Key128> cipher(key, Mode::CTR); |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 2f15635c5..70c0f8b80 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -1389,10 +1389,9 @@ void SendTrap(Kernel::Thread* thread, int trap) { | |||
| 1389 | return; | 1389 | return; |
| 1390 | } | 1390 | } |
| 1391 | 1391 | ||
| 1392 | if (!halt_loop || current_thread == thread) { | 1392 | current_thread = thread; |
| 1393 | current_thread = thread; | 1393 | SendSignal(thread, trap); |
| 1394 | SendSignal(thread, trap); | 1394 | |
| 1395 | } | ||
| 1396 | halt_loop = true; | 1395 | halt_loop = true; |
| 1397 | send_trap = false; | 1396 | send_trap = false; |
| 1398 | } | 1397 | } |
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h index e11043b60..9db1f7b39 100644 --- a/src/core/hle/kernel/memory/memory_block.h +++ b/src/core/hle/kernel/memory/memory_block.h | |||
| @@ -17,7 +17,7 @@ namespace Kernel::Memory { | |||
| 17 | 17 | ||
| 18 | enum class MemoryState : u32 { | 18 | enum class MemoryState : u32 { |
| 19 | None = 0, | 19 | None = 0, |
| 20 | Mask = 0xFFFFFFFF, // TODO(bunnei): This should probable be 0xFF | 20 | Mask = 0xFF, |
| 21 | All = ~None, | 21 | All = ~None, |
| 22 | 22 | ||
| 23 | FlagCanReprotect = (1 << 8), | 23 | FlagCanReprotect = (1 << 8), |
| @@ -253,6 +253,23 @@ public: | |||
| 253 | }; | 253 | }; |
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | void ShareToDevice(MemoryPermission /*new_perm*/) { | ||
| 257 | ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared || | ||
| 258 | device_use_count == 0); | ||
| 259 | attribute |= MemoryAttribute::DeviceShared; | ||
| 260 | const u16 new_use_count{++device_use_count}; | ||
| 261 | ASSERT(new_use_count > 0); | ||
| 262 | } | ||
| 263 | |||
| 264 | void UnshareToDevice(MemoryPermission /*new_perm*/) { | ||
| 265 | ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared); | ||
| 266 | const u16 prev_use_count{device_use_count--}; | ||
| 267 | ASSERT(prev_use_count > 0); | ||
| 268 | if (prev_use_count == 1) { | ||
| 269 | attribute &= ~MemoryAttribute::DeviceShared; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 256 | private: | 273 | private: |
| 257 | constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const { | 274 | constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const { |
| 258 | constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask | | 275 | constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask | |
| @@ -287,9 +304,9 @@ private: | |||
| 287 | state = new_state; | 304 | state = new_state; |
| 288 | perm = new_perm; | 305 | perm = new_perm; |
| 289 | 306 | ||
| 290 | // TODO(bunnei): Is this right? | ||
| 291 | attribute = static_cast<MemoryAttribute>( | 307 | attribute = static_cast<MemoryAttribute>( |
| 292 | new_attribute /*| (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))*/); | 308 | new_attribute | |
| 309 | (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))); | ||
| 293 | } | 310 | } |
| 294 | 311 | ||
| 295 | constexpr MemoryBlock Split(VAddr split_addr) { | 312 | constexpr MemoryBlock Split(VAddr split_addr) { |
diff --git a/src/core/hle/kernel/memory/memory_block_manager.cpp b/src/core/hle/kernel/memory/memory_block_manager.cpp index 1ebc126c0..900395c37 100644 --- a/src/core/hle/kernel/memory/memory_block_manager.cpp +++ b/src/core/hle/kernel/memory/memory_block_manager.cpp | |||
| @@ -143,6 +143,42 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState s | |||
| 143 | } | 143 | } |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, | ||
| 147 | MemoryPermission perm) { | ||
| 148 | const std::size_t prev_count{memory_block_tree.size()}; | ||
| 149 | const VAddr end_addr{addr + num_pages * PageSize}; | ||
| 150 | iterator node{memory_block_tree.begin()}; | ||
| 151 | |||
| 152 | while (node != memory_block_tree.end()) { | ||
| 153 | MemoryBlock* block{&(*node)}; | ||
| 154 | iterator next_node{std::next(node)}; | ||
| 155 | const VAddr cur_addr{block->GetAddress()}; | ||
| 156 | const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; | ||
| 157 | |||
| 158 | if (addr < cur_end_addr && cur_addr < end_addr) { | ||
| 159 | iterator new_node{node}; | ||
| 160 | |||
| 161 | if (addr > cur_addr) { | ||
| 162 | memory_block_tree.insert(node, block->Split(addr)); | ||
| 163 | } | ||
| 164 | |||
| 165 | if (end_addr < cur_end_addr) { | ||
| 166 | new_node = memory_block_tree.insert(node, block->Split(end_addr)); | ||
| 167 | } | ||
| 168 | |||
| 169 | lock_func(new_node, perm); | ||
| 170 | |||
| 171 | MergeAdjacent(new_node, next_node); | ||
| 172 | } | ||
| 173 | |||
| 174 | if (cur_end_addr - 1 >= end_addr - 1) { | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | |||
| 178 | node = next_node; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 146 | void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { | 182 | void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { |
| 147 | const_iterator it{FindIterator(start)}; | 183 | const_iterator it{FindIterator(start)}; |
| 148 | MemoryInfo info{}; | 184 | MemoryInfo info{}; |
diff --git a/src/core/hle/kernel/memory/memory_block_manager.h b/src/core/hle/kernel/memory/memory_block_manager.h index 0f2270f0f..9451b5df6 100644 --- a/src/core/hle/kernel/memory/memory_block_manager.h +++ b/src/core/hle/kernel/memory/memory_block_manager.h | |||
| @@ -45,6 +45,9 @@ public: | |||
| 45 | MemoryPermission perm = MemoryPermission::None, | 45 | MemoryPermission perm = MemoryPermission::None, |
| 46 | MemoryAttribute attribute = MemoryAttribute::None); | 46 | MemoryAttribute attribute = MemoryAttribute::None); |
| 47 | 47 | ||
| 48 | using LockFunc = std::function<void(iterator, MemoryPermission)>; | ||
| 49 | void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm); | ||
| 50 | |||
| 48 | using IterateFunc = std::function<void(const MemoryInfo&)>; | 51 | using IterateFunc = std::function<void(const MemoryInfo&)>; |
| 49 | void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); | 52 | void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); |
| 50 | 53 | ||
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index 091e52ca4..3281611f8 100644 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp | |||
| @@ -840,6 +840,50 @@ ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, s | |||
| 840 | return MakeResult<VAddr>(addr); | 840 | return MakeResult<VAddr>(addr); |
| 841 | } | 841 | } |
| 842 | 842 | ||
| 843 | ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { | ||
| 844 | std::lock_guard lock{page_table_lock}; | ||
| 845 | |||
| 846 | MemoryPermission perm{}; | ||
| 847 | if (const ResultCode result{CheckMemoryState( | ||
| 848 | nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute, | ||
| 849 | MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None, | ||
| 850 | MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None, | ||
| 851 | MemoryAttribute::DeviceSharedAndUncached)}; | ||
| 852 | result.IsError()) { | ||
| 853 | return result; | ||
| 854 | } | ||
| 855 | |||
| 856 | block_manager->UpdateLock(addr, size / PageSize, | ||
| 857 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { | ||
| 858 | block->ShareToDevice(perm); | ||
| 859 | }, | ||
| 860 | perm); | ||
| 861 | |||
| 862 | return RESULT_SUCCESS; | ||
| 863 | } | ||
| 864 | |||
| 865 | ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { | ||
| 866 | std::lock_guard lock{page_table_lock}; | ||
| 867 | |||
| 868 | MemoryPermission perm{}; | ||
| 869 | if (const ResultCode result{CheckMemoryState( | ||
| 870 | nullptr, &perm, nullptr, addr, size, MemoryState::FlagCanChangeAttribute, | ||
| 871 | MemoryState::FlagCanChangeAttribute, MemoryPermission::None, MemoryPermission::None, | ||
| 872 | MemoryAttribute::LockedAndIpcLocked, MemoryAttribute::None, | ||
| 873 | MemoryAttribute::DeviceSharedAndUncached)}; | ||
| 874 | result.IsError()) { | ||
| 875 | return result; | ||
| 876 | } | ||
| 877 | |||
| 878 | block_manager->UpdateLock(addr, size / PageSize, | ||
| 879 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { | ||
| 880 | block->UnshareToDevice(perm); | ||
| 881 | }, | ||
| 882 | perm); | ||
| 883 | |||
| 884 | return RESULT_SUCCESS; | ||
| 885 | } | ||
| 886 | |||
| 843 | ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) { | 887 | ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) { |
| 844 | block_manager = std::make_unique<MemoryBlockManager>(start, end); | 888 | block_manager = std::make_unique<MemoryBlockManager>(start, end); |
| 845 | 889 | ||
diff --git a/src/core/hle/kernel/memory/page_table.h b/src/core/hle/kernel/memory/page_table.h index 80384ab0f..a867aa050 100644 --- a/src/core/hle/kernel/memory/page_table.h +++ b/src/core/hle/kernel/memory/page_table.h | |||
| @@ -53,6 +53,8 @@ public: | |||
| 53 | bool is_map_only, VAddr region_start, | 53 | bool is_map_only, VAddr region_start, |
| 54 | std::size_t region_num_pages, MemoryState state, | 54 | std::size_t region_num_pages, MemoryState state, |
| 55 | MemoryPermission perm, PAddr map_addr = 0); | 55 | MemoryPermission perm, PAddr map_addr = 0); |
| 56 | ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); | ||
| 57 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); | ||
| 56 | 58 | ||
| 57 | Common::PageTable& PageTableImpl() { | 59 | Common::PageTable& PageTableImpl() { |
| 58 | return page_table_impl; | 60 | return page_table_impl; |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index c67696757..0cd467110 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -36,22 +36,22 @@ std::shared_ptr<SharedMemory> SharedMemory::Create( | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size, | 38 | ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size, |
| 39 | Memory::MemoryPermission permission) { | 39 | Memory::MemoryPermission permissions) { |
| 40 | const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize}; | 40 | const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize}; |
| 41 | 41 | ||
| 42 | if (page_list.GetNumPages() != page_count) { | 42 | if (page_list.GetNumPages() != page_count) { |
| 43 | UNIMPLEMENTED_MSG("Page count does not match"); | 43 | UNIMPLEMENTED_MSG("Page count does not match"); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | Memory::MemoryPermission expected = | 46 | const Memory::MemoryPermission expected = |
| 47 | &target_process == owner_process ? owner_permission : user_permission; | 47 | &target_process == owner_process ? owner_permission : user_permission; |
| 48 | 48 | ||
| 49 | if (permission != expected) { | 49 | if (permissions != expected) { |
| 50 | UNIMPLEMENTED_MSG("Permission does not match"); | 50 | UNIMPLEMENTED_MSG("Permission does not match"); |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared, | 53 | return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared, |
| 54 | permission); | 54 | permissions); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | } // namespace Kernel | 57 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index cd16d6412..0ef87235c 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -51,7 +51,7 @@ public: | |||
| 51 | * @param permissions Memory block map permissions (specified by SVC field) | 51 | * @param permissions Memory block map permissions (specified by SVC field) |
| 52 | */ | 52 | */ |
| 53 | ResultCode Map(Process& target_process, VAddr address, std::size_t size, | 53 | ResultCode Map(Process& target_process, VAddr address, std::size_t size, |
| 54 | Memory::MemoryPermission permission); | 54 | Memory::MemoryPermission permissions); |
| 55 | 55 | ||
| 56 | /** | 56 | /** |
| 57 | * Gets a pointer to the shared memory block | 57 | * Gets a pointer to the shared memory block |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4134acf65..25b4a23b4 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -55,9 +55,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) { | |||
| 55 | return address + size > address; | 55 | return address + size > address; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | // 8 GiB | ||
| 59 | constexpr u64 MAIN_MEMORY_SIZE = 0x200000000; | ||
| 60 | |||
| 61 | // Helper function that performs the common sanity checks for svcMapMemory | 58 | // Helper function that performs the common sanity checks for svcMapMemory |
| 62 | // and svcUnmapMemory. This is doable, as both functions perform their sanitizing | 59 | // and svcUnmapMemory. This is doable, as both functions perform their sanitizing |
| 63 | // in the same order. | 60 | // in the same order. |
| @@ -1229,6 +1226,142 @@ static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address, | |||
| 1229 | return QueryMemory(system, memory_info_address, page_info_address, query_address); | 1226 | return QueryMemory(system, memory_info_address, page_info_address, query_address); |
| 1230 | } | 1227 | } |
| 1231 | 1228 | ||
| 1229 | static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||
| 1230 | u64 src_address, u64 size) { | ||
| 1231 | LOG_DEBUG(Kernel_SVC, | ||
| 1232 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " | ||
| 1233 | "src_address=0x{:016X}, size=0x{:016X}", | ||
| 1234 | process_handle, dst_address, src_address, size); | ||
| 1235 | |||
| 1236 | if (!Common::Is4KBAligned(src_address)) { | ||
| 1237 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 1238 | src_address); | ||
| 1239 | return ERR_INVALID_ADDRESS; | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 1243 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 1244 | dst_address); | ||
| 1245 | return ERR_INVALID_ADDRESS; | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | if (size == 0 || !Common::Is4KBAligned(size)) { | ||
| 1249 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); | ||
| 1250 | return ERR_INVALID_SIZE; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 1254 | LOG_ERROR(Kernel_SVC, | ||
| 1255 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 1256 | "size=0x{:016X}).", | ||
| 1257 | dst_address, size); | ||
| 1258 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | if (!IsValidAddressRange(src_address, size)) { | ||
| 1262 | LOG_ERROR(Kernel_SVC, | ||
| 1263 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 1264 | "size=0x{:016X}).", | ||
| 1265 | src_address, size); | ||
| 1266 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1270 | auto process = handle_table.Get<Process>(process_handle); | ||
| 1271 | if (!process) { | ||
| 1272 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 1273 | process_handle); | ||
| 1274 | return ERR_INVALID_HANDLE; | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | auto& page_table = process->PageTable(); | ||
| 1278 | if (!page_table.IsInsideAddressSpace(src_address, size)) { | ||
| 1279 | LOG_ERROR(Kernel_SVC, | ||
| 1280 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 1281 | "size=0x{:016X}).", | ||
| 1282 | src_address, size); | ||
| 1283 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | if (!page_table.IsInsideASLRRegion(dst_address, size)) { | ||
| 1287 | LOG_ERROR(Kernel_SVC, | ||
| 1288 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 1289 | "size=0x{:016X}).", | ||
| 1290 | dst_address, size); | ||
| 1291 | return ERR_INVALID_MEMORY_RANGE; | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | return page_table.MapProcessCodeMemory(dst_address, src_address, size); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, | ||
| 1298 | u64 dst_address, u64 src_address, u64 size) { | ||
| 1299 | LOG_DEBUG(Kernel_SVC, | ||
| 1300 | "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " | ||
| 1301 | "size=0x{:016X}", | ||
| 1302 | process_handle, dst_address, src_address, size); | ||
| 1303 | |||
| 1304 | if (!Common::Is4KBAligned(dst_address)) { | ||
| 1305 | LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||
| 1306 | dst_address); | ||
| 1307 | return ERR_INVALID_ADDRESS; | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | if (!Common::Is4KBAligned(src_address)) { | ||
| 1311 | LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||
| 1312 | src_address); | ||
| 1313 | return ERR_INVALID_ADDRESS; | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | if (size == 0 || Common::Is4KBAligned(size)) { | ||
| 1317 | LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); | ||
| 1318 | return ERR_INVALID_SIZE; | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | if (!IsValidAddressRange(dst_address, size)) { | ||
| 1322 | LOG_ERROR(Kernel_SVC, | ||
| 1323 | "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||
| 1324 | "size=0x{:016X}).", | ||
| 1325 | dst_address, size); | ||
| 1326 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | if (!IsValidAddressRange(src_address, size)) { | ||
| 1330 | LOG_ERROR(Kernel_SVC, | ||
| 1331 | "Source address range overflows the address space (src_address=0x{:016X}, " | ||
| 1332 | "size=0x{:016X}).", | ||
| 1333 | src_address, size); | ||
| 1334 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||
| 1338 | auto process = handle_table.Get<Process>(process_handle); | ||
| 1339 | if (!process) { | ||
| 1340 | LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||
| 1341 | process_handle); | ||
| 1342 | return ERR_INVALID_HANDLE; | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | auto& page_table = process->PageTable(); | ||
| 1346 | if (!page_table.IsInsideAddressSpace(src_address, size)) { | ||
| 1347 | LOG_ERROR(Kernel_SVC, | ||
| 1348 | "Source address range is not within the address space (src_address=0x{:016X}, " | ||
| 1349 | "size=0x{:016X}).", | ||
| 1350 | src_address, size); | ||
| 1351 | return ERR_INVALID_ADDRESS_STATE; | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | if (!page_table.IsInsideASLRRegion(dst_address, size)) { | ||
| 1355 | LOG_ERROR(Kernel_SVC, | ||
| 1356 | "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||
| 1357 | "size=0x{:016X}).", | ||
| 1358 | dst_address, size); | ||
| 1359 | return ERR_INVALID_MEMORY_RANGE; | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | return page_table.UnmapProcessCodeMemory(dst_address, src_address, size); | ||
| 1363 | } | ||
| 1364 | |||
| 1232 | /// Exits the current process | 1365 | /// Exits the current process |
| 1233 | static void ExitProcess(Core::System& system) { | 1366 | static void ExitProcess(Core::System& system) { |
| 1234 | auto* current_process = system.Kernel().CurrentProcess(); | 1367 | auto* current_process = system.Kernel().CurrentProcess(); |
| @@ -2256,8 +2389,8 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2256 | {0x74, nullptr, "MapProcessMemory"}, | 2389 | {0x74, nullptr, "MapProcessMemory"}, |
| 2257 | {0x75, nullptr, "UnmapProcessMemory"}, | 2390 | {0x75, nullptr, "UnmapProcessMemory"}, |
| 2258 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, | 2391 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, |
| 2259 | {0x77, nullptr, "MapProcessCodeMemory"}, | 2392 | {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, |
| 2260 | {0x78, nullptr, "UnmapProcessCodeMemory"}, | 2393 | {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, |
| 2261 | {0x79, nullptr, "CreateProcess"}, | 2394 | {0x79, nullptr, "CreateProcess"}, |
| 2262 | {0x7A, nullptr, "StartProcess"}, | 2395 | {0x7A, nullptr, "StartProcess"}, |
| 2263 | {0x7B, nullptr, "TerminateProcess"}, | 2396 | {0x7B, nullptr, "TerminateProcess"}, |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 4c0451c01..a919750a6 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -150,8 +150,7 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, | |||
| 150 | context.pc = entry_point; | 150 | context.pc = entry_point; |
| 151 | context.sp = stack_top; | 151 | context.sp = stack_top; |
| 152 | // TODO(merry): Perform a hardware test to determine the below value. | 152 | // TODO(merry): Perform a hardware test to determine the below value. |
| 153 | // AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero | 153 | context.fpcr = 0; |
| 154 | context.fpcr = 0x03C00000; | ||
| 155 | } | 154 | } |
| 156 | 155 | ||
| 157 | ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, | 156 | ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, |
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index b941c260b..ae88deda5 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp | |||
| @@ -33,8 +33,10 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 33 | {111, nullptr, "ClearSaveDataThumbnail"}, | 33 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 34 | {112, nullptr, "LoadSaveDataThumbnail"}, | 34 | {112, nullptr, "LoadSaveDataThumbnail"}, |
| 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, |
| 36 | {120, nullptr, "ListOpenUsersInApplication"}, | ||
| 36 | {130, nullptr, "ActivateOpenContextRetention"}, | 37 | {130, nullptr, "ActivateOpenContextRetention"}, |
| 37 | {140, nullptr, "ListQualifiedUsers"}, | 38 | {140, nullptr, "ListQualifiedUsers"}, |
| 39 | {150, nullptr, "AuthenticateApplicationAsync"}, | ||
| 38 | {190, nullptr, "GetUserLastOpenedApplication"}, | 40 | {190, nullptr, "GetUserLastOpenedApplication"}, |
| 39 | {191, nullptr, "ActivateOpenContextHolder"}, | 41 | {191, nullptr, "ActivateOpenContextHolder"}, |
| 40 | {200, nullptr, "BeginUserRegistration"}, | 42 | {200, nullptr, "BeginUserRegistration"}, |
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 858e91dde..2b9c11928 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp | |||
| @@ -35,6 +35,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, | 35 | {113, nullptr, "GetSaveDataThumbnailExistence"}, |
| 36 | {130, nullptr, "ActivateOpenContextRetention"}, | 36 | {130, nullptr, "ActivateOpenContextRetention"}, |
| 37 | {140, nullptr, "ListQualifiedUsers"}, | 37 | {140, nullptr, "ListQualifiedUsers"}, |
| 38 | {150, nullptr, "AuthenticateApplicationAsync"}, | ||
| 38 | {190, nullptr, "GetUserLastOpenedApplication"}, | 39 | {190, nullptr, "GetUserLastOpenedApplication"}, |
| 39 | {191, nullptr, "ActivateOpenContextHolder"}, | 40 | {191, nullptr, "ActivateOpenContextHolder"}, |
| 40 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, | 41 | {997, nullptr, "DebugInvalidateTokenCacheForUser"}, |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3ece2cf3c..bee4a9d3f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -235,6 +235,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { | |||
| 235 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, | 235 | {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, |
| 236 | {40, nullptr, "GetAppletResourceUsageInfo"}, | 236 | {40, nullptr, "GetAppletResourceUsageInfo"}, |
| 237 | {100, nullptr, "SetCpuBoostModeForApplet"}, | 237 | {100, nullptr, "SetCpuBoostModeForApplet"}, |
| 238 | {101, nullptr, "CancelCpuBoostModeForApplet"}, | ||
| 238 | {110, nullptr, "PushToAppletBoundChannelForDebug"}, | 239 | {110, nullptr, "PushToAppletBoundChannelForDebug"}, |
| 239 | {111, nullptr, "TryPopFromAppletBoundChannelForDebug"}, | 240 | {111, nullptr, "TryPopFromAppletBoundChannelForDebug"}, |
| 240 | {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"}, | 241 | {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"}, |
| @@ -277,6 +278,8 @@ ISelfController::ISelfController(Core::System& system, | |||
| 277 | {41, nullptr, "IsSystemBufferSharingEnabled"}, | 278 | {41, nullptr, "IsSystemBufferSharingEnabled"}, |
| 278 | {42, nullptr, "GetSystemSharedLayerHandle"}, | 279 | {42, nullptr, "GetSystemSharedLayerHandle"}, |
| 279 | {43, nullptr, "GetSystemSharedBufferHandle"}, | 280 | {43, nullptr, "GetSystemSharedBufferHandle"}, |
| 281 | {44, nullptr, "CreateManagedDisplaySeparableLayer"}, | ||
| 282 | {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, | ||
| 280 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, | 283 | {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, |
| 281 | {51, nullptr, "ApproveToDisplay"}, | 284 | {51, nullptr, "ApproveToDisplay"}, |
| 282 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, | 285 | {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, |
| @@ -623,11 +626,15 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system, | |||
| 623 | {64, nullptr, "SetTvPowerStateMatchingMode"}, | 626 | {64, nullptr, "SetTvPowerStateMatchingMode"}, |
| 624 | {65, nullptr, "GetApplicationIdByContentActionName"}, | 627 | {65, nullptr, "GetApplicationIdByContentActionName"}, |
| 625 | {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, | 628 | {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, |
| 629 | {67, nullptr, "CancelCpuBoostMode"}, | ||
| 626 | {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, | 630 | {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, |
| 627 | {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, | 631 | {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, |
| 628 | {91, nullptr, "GetCurrentPerformanceConfiguration"}, | 632 | {91, nullptr, "GetCurrentPerformanceConfiguration"}, |
| 633 | {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, | ||
| 629 | {200, nullptr, "GetOperationModeSystemInfo"}, | 634 | {200, nullptr, "GetOperationModeSystemInfo"}, |
| 630 | {300, nullptr, "GetSettingsPlatformRegion"}, | 635 | {300, nullptr, "GetSettingsPlatformRegion"}, |
| 636 | {400, nullptr, "ActivateMigrationService"}, | ||
| 637 | {401, nullptr, "DeactivateMigrationService"}, | ||
| 631 | }; | 638 | }; |
| 632 | // clang-format on | 639 | // clang-format on |
| 633 | 640 | ||
| @@ -835,6 +842,7 @@ public: | |||
| 835 | {25, nullptr, "Terminate"}, | 842 | {25, nullptr, "Terminate"}, |
| 836 | {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, | 843 | {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, |
| 837 | {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, | 844 | {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, |
| 845 | {60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"}, | ||
| 838 | {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, | 846 | {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, |
| 839 | {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, | 847 | {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, |
| 840 | {102, nullptr, "PushExtraStorage"}, | 848 | {102, nullptr, "PushExtraStorage"}, |
| @@ -1139,6 +1147,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1139 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, | 1147 | {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, |
| 1140 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, | 1148 | {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, |
| 1141 | {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, | 1149 | {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, |
| 1150 | {34, nullptr, "SelectApplicationLicense"}, | ||
| 1142 | {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, | 1151 | {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, |
| 1143 | {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, | 1152 | {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, |
| 1144 | {60, nullptr, "SetMediaPlaybackStateForApplication"}, | 1153 | {60, nullptr, "SetMediaPlaybackStateForApplication"}, |
| @@ -1148,6 +1157,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1148 | {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, | 1157 | {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, |
| 1149 | {70, nullptr, "RequestToShutdown"}, | 1158 | {70, nullptr, "RequestToShutdown"}, |
| 1150 | {71, nullptr, "RequestToReboot"}, | 1159 | {71, nullptr, "RequestToReboot"}, |
| 1160 | {72, nullptr, "RequestToSleep"}, | ||
| 1151 | {80, nullptr, "ExitAndRequestToShowThanksMessage"}, | 1161 | {80, nullptr, "ExitAndRequestToShowThanksMessage"}, |
| 1152 | {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, | 1162 | {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, |
| 1153 | {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"}, | 1163 | {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"}, |
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index 9e08e5346..6ddb547fb 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp | |||
| @@ -39,6 +39,8 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} { | |||
| 39 | {25, nullptr, "GetAudioVolumeDataForPlayReport"}, | 39 | {25, nullptr, "GetAudioVolumeDataForPlayReport"}, |
| 40 | {26, nullptr, "UpdateHeadphoneSettings"}, | 40 | {26, nullptr, "UpdateHeadphoneSettings"}, |
| 41 | {27, nullptr, "SetVolumeMappingTableForDev"}, | 41 | {27, nullptr, "SetVolumeMappingTableForDev"}, |
| 42 | {28, nullptr, "GetAudioOutputChannelCountForPlayReport"}, | ||
| 43 | {29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"}, | ||
| 42 | }; | 44 | }; |
| 43 | // clang-format on | 45 | // clang-format on |
| 44 | 46 | ||
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index f589864ee..5febe8fc1 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/hle/service/bcat/backend/boxcat.h" | 18 | #include "core/hle/service/bcat/backend/boxcat.h" |
| 19 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| 20 | 20 | ||
| 21 | namespace Service::BCAT { | ||
| 21 | namespace { | 22 | namespace { |
| 22 | 23 | ||
| 23 | // Prevents conflicts with windows macro called CreateFile | 24 | // Prevents conflicts with windows macro called CreateFile |
| @@ -30,10 +31,6 @@ bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) { | |||
| 30 | return dir->DeleteFile(name); | 31 | return dir->DeleteFile(name); |
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | } // Anonymous namespace | ||
| 34 | |||
| 35 | namespace Service::BCAT { | ||
| 36 | |||
| 37 | constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; | 34 | constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; |
| 38 | 35 | ||
| 39 | constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; | 36 | constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; |
| @@ -90,8 +87,6 @@ constexpr u32 PORT = 443; | |||
| 90 | constexpr u32 TIMEOUT_SECONDS = 30; | 87 | constexpr u32 TIMEOUT_SECONDS = 30; |
| 91 | [[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB | 88 | [[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB |
| 92 | 89 | ||
| 93 | namespace { | ||
| 94 | |||
| 95 | std::string GetBINFilePath(u64 title_id) { | 90 | std::string GetBINFilePath(u64 title_id) { |
| 96 | return fmt::format("{}bcat/{:016X}/launchparam.bin", | 91 | return fmt::format("{}bcat/{:016X}/launchparam.bin", |
| 97 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); | 92 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); |
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 7ada67130..34aba7a27 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -141,6 +141,7 @@ public: | |||
| 141 | {20301, nullptr, "RequestSuspendDeliveryTask"}, | 141 | {20301, nullptr, "RequestSuspendDeliveryTask"}, |
| 142 | {20400, nullptr, "RegisterSystemApplicationDeliveryTask"}, | 142 | {20400, nullptr, "RegisterSystemApplicationDeliveryTask"}, |
| 143 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, | 143 | {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, |
| 144 | {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, | ||
| 144 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, | 145 | {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, |
| 145 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, | 146 | {30200, nullptr, "RegisterBackgroundDeliveryTask"}, |
| 146 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, | 147 | {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, |
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 86f36915a..f8e9df4b1 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "core/crypto/key_manager.h" | 5 | #include "core/crypto/key_manager.h" |
| 6 | #include "core/hle/ipc_helpers.h" | 6 | #include "core/hle/ipc_helpers.h" |
| 7 | #include "core/hle/service/es/es.h" | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | 9 | ||
| 9 | namespace Service::ES { | 10 | namespace Service::ES { |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 61045c75c..6b9b4f3b9 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -697,12 +697,14 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) | |||
| 697 | {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, | 697 | {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, |
| 698 | {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, | 698 | {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, |
| 699 | {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, | 699 | {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, |
| 700 | {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, | ||
| 700 | {80, nullptr, "OpenSaveDataMetaFile"}, | 701 | {80, nullptr, "OpenSaveDataMetaFile"}, |
| 701 | {81, nullptr, "OpenSaveDataTransferManager"}, | 702 | {81, nullptr, "OpenSaveDataTransferManager"}, |
| 702 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, | 703 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, |
| 703 | {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"}, | 704 | {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"}, |
| 704 | {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"}, | 705 | {84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"}, |
| 705 | {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"}, | 706 | {85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"}, |
| 707 | {86, nullptr, "OpenSaveDataMover"}, | ||
| 706 | {100, nullptr, "OpenImageDirectoryFileSystem"}, | 708 | {100, nullptr, "OpenImageDirectoryFileSystem"}, |
| 707 | {110, nullptr, "OpenContentStorageFileSystem"}, | 709 | {110, nullptr, "OpenContentStorageFileSystem"}, |
| 708 | {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, | 710 | {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, |
| @@ -762,9 +764,11 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) | |||
| 762 | {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"}, | 764 | {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"}, |
| 763 | {1012, nullptr, "GetFsStackUsage"}, | 765 | {1012, nullptr, "GetFsStackUsage"}, |
| 764 | {1013, nullptr, "UnsetSaveDataRootPath"}, | 766 | {1013, nullptr, "UnsetSaveDataRootPath"}, |
| 767 | {1014, nullptr, "OutputMultiProgramTagAccessLog"}, | ||
| 765 | {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, | 768 | {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, |
| 766 | {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, | 769 | {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, |
| 767 | {1200, nullptr, "OpenMultiCommitManager"}, | 770 | {1200, nullptr, "OpenMultiCommitManager"}, |
| 771 | {1300, nullptr, "OpenBisWiper"}, | ||
| 768 | }; | 772 | }; |
| 769 | // clang-format on | 773 | // clang-format on |
| 770 | RegisterHandlers(functions); | 774 | RegisterHandlers(functions); |
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 7938b4b80..68f259b70 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp | |||
| @@ -96,6 +96,7 @@ public: | |||
| 96 | {30830, nullptr, "ClearPlayLog"}, | 96 | {30830, nullptr, "ClearPlayLog"}, |
| 97 | {30900, nullptr, "SendFriendInvitation"}, | 97 | {30900, nullptr, "SendFriendInvitation"}, |
| 98 | {30910, nullptr, "ReadFriendInvitation"}, | 98 | {30910, nullptr, "ReadFriendInvitation"}, |
| 99 | {30911, nullptr, "ReadAllFriendInvitations"}, | ||
| 99 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, | 100 | {49900, nullptr, "DeleteNetworkServiceAccountCache"}, |
| 100 | }; | 101 | }; |
| 101 | // clang-format on | 102 | // clang-format on |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d6031a987..5559587e3 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -233,7 +233,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 233 | {302, nullptr, "StopConsoleSixAxisSensor"}, | 233 | {302, nullptr, "StopConsoleSixAxisSensor"}, |
| 234 | {303, nullptr, "ActivateSevenSixAxisSensor"}, | 234 | {303, nullptr, "ActivateSevenSixAxisSensor"}, |
| 235 | {304, nullptr, "StartSevenSixAxisSensor"}, | 235 | {304, nullptr, "StartSevenSixAxisSensor"}, |
| 236 | {305, nullptr, "StopSevenSixAxisSensor"}, | 236 | {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, |
| 237 | {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, | 237 | {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, |
| 238 | {307, nullptr, "FinalizeSevenSixAxisSensor"}, | 238 | {307, nullptr, "FinalizeSevenSixAxisSensor"}, |
| 239 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, | 239 | {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, |
| @@ -282,6 +282,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 282 | {1001, nullptr, "GetNpadCommunicationMode"}, | 282 | {1001, nullptr, "GetNpadCommunicationMode"}, |
| 283 | {1002, nullptr, "SetTouchScreenConfiguration"}, | 283 | {1002, nullptr, "SetTouchScreenConfiguration"}, |
| 284 | {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, | 284 | {1003, nullptr, "IsFirmwareUpdateNeededForNotification"}, |
| 285 | {2000, nullptr, "ActivateDigitizer"}, | ||
| 285 | }; | 286 | }; |
| 286 | // clang-format on | 287 | // clang-format on |
| 287 | 288 | ||
| @@ -852,6 +853,17 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { | |||
| 852 | rb.Push(RESULT_SUCCESS); | 853 | rb.Push(RESULT_SUCCESS); |
| 853 | } | 854 | } |
| 854 | 855 | ||
| 856 | void Hid::StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 857 | IPC::RequestParser rp{ctx}; | ||
| 858 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 859 | |||
| 860 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||
| 861 | applet_resource_user_id); | ||
| 862 | |||
| 863 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 864 | rb.Push(RESULT_SUCCESS); | ||
| 865 | } | ||
| 866 | |||
| 855 | void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { | 867 | void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { |
| 856 | LOG_WARNING(Service_HID, "(STUBBED) called"); | 868 | LOG_WARNING(Service_HID, "(STUBBED) called"); |
| 857 | 869 | ||
| @@ -870,6 +882,7 @@ public: | |||
| 870 | {10, nullptr, "DeactivateTouchScreen"}, | 882 | {10, nullptr, "DeactivateTouchScreen"}, |
| 871 | {11, nullptr, "SetTouchScreenAutoPilotState"}, | 883 | {11, nullptr, "SetTouchScreenAutoPilotState"}, |
| 872 | {12, nullptr, "UnsetTouchScreenAutoPilotState"}, | 884 | {12, nullptr, "UnsetTouchScreenAutoPilotState"}, |
| 885 | {13, nullptr, "GetTouchScreenConfiguration"}, | ||
| 873 | {20, nullptr, "DeactivateMouse"}, | 886 | {20, nullptr, "DeactivateMouse"}, |
| 874 | {21, nullptr, "SetMouseAutoPilotState"}, | 887 | {21, nullptr, "SetMouseAutoPilotState"}, |
| 875 | {22, nullptr, "UnsetMouseAutoPilotState"}, | 888 | {22, nullptr, "UnsetMouseAutoPilotState"}, |
| @@ -879,7 +892,9 @@ public: | |||
| 879 | {50, nullptr, "DeactivateXpad"}, | 892 | {50, nullptr, "DeactivateXpad"}, |
| 880 | {51, nullptr, "SetXpadAutoPilotState"}, | 893 | {51, nullptr, "SetXpadAutoPilotState"}, |
| 881 | {52, nullptr, "UnsetXpadAutoPilotState"}, | 894 | {52, nullptr, "UnsetXpadAutoPilotState"}, |
| 882 | {60, nullptr, "DeactivateJoyXpad"}, | 895 | {60, nullptr, "ClearNpadSystemCommonPolicy"}, |
| 896 | {61, nullptr, "DeactivateNpad"}, | ||
| 897 | {62, nullptr, "ForceDisconnectNpad"}, | ||
| 883 | {91, nullptr, "DeactivateGesture"}, | 898 | {91, nullptr, "DeactivateGesture"}, |
| 884 | {110, nullptr, "DeactivateHomeButton"}, | 899 | {110, nullptr, "DeactivateHomeButton"}, |
| 885 | {111, nullptr, "SetHomeButtonAutoPilotState"}, | 900 | {111, nullptr, "SetHomeButtonAutoPilotState"}, |
| @@ -899,6 +914,15 @@ public: | |||
| 899 | {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, | 914 | {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, |
| 900 | {142, nullptr, "DeactivateSevenSixAxisSensor"}, | 915 | {142, nullptr, "DeactivateSevenSixAxisSensor"}, |
| 901 | {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, | 916 | {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, |
| 917 | {144, nullptr, "GetAccelerometerFsr"}, | ||
| 918 | {145, nullptr, "SetAccelerometerFsr"}, | ||
| 919 | {146, nullptr, "GetAccelerometerOdr"}, | ||
| 920 | {147, nullptr, "SetAccelerometerOdr"}, | ||
| 921 | {148, nullptr, "GetGyroscopeFsr"}, | ||
| 922 | {149, nullptr, "SetGyroscopeFsr"}, | ||
| 923 | {150, nullptr, "GetGyroscopeOdr"}, | ||
| 924 | {151, nullptr, "SetGyroscopeOdr"}, | ||
| 925 | {152, nullptr, "GetWhoAmI"}, | ||
| 902 | {201, nullptr, "ActivateFirmwareUpdate"}, | 926 | {201, nullptr, "ActivateFirmwareUpdate"}, |
| 903 | {202, nullptr, "DeactivateFirmwareUpdate"}, | 927 | {202, nullptr, "DeactivateFirmwareUpdate"}, |
| 904 | {203, nullptr, "StartFirmwareUpdate"}, | 928 | {203, nullptr, "StartFirmwareUpdate"}, |
| @@ -927,6 +951,17 @@ public: | |||
| 927 | {233, nullptr, "ClearPairingInfo"}, | 951 | {233, nullptr, "ClearPairingInfo"}, |
| 928 | {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, | 952 | {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, |
| 929 | {235, nullptr, "EnableAnalogStickPower"}, | 953 | {235, nullptr, "EnableAnalogStickPower"}, |
| 954 | {236, nullptr, "RequestKuinaUartClockCal"}, | ||
| 955 | {237, nullptr, "GetKuinaUartClockCal"}, | ||
| 956 | {238, nullptr, "SetKuinaUartClockTrim"}, | ||
| 957 | {239, nullptr, "KuinaLoopbackTest"}, | ||
| 958 | {240, nullptr, "RequestBatteryVoltage"}, | ||
| 959 | {241, nullptr, "GetBatteryVoltage"}, | ||
| 960 | {242, nullptr, "GetUniquePadPowerInfo"}, | ||
| 961 | {243, nullptr, "RebootUniquePad"}, | ||
| 962 | {244, nullptr, "RequestKuinaFirmwareVersion"}, | ||
| 963 | {245, nullptr, "GetKuinaFirmwareVersion"}, | ||
| 964 | {246, nullptr, "GetVidPid"}, | ||
| 930 | {301, nullptr, "GetAbstractedPadHandles"}, | 965 | {301, nullptr, "GetAbstractedPadHandles"}, |
| 931 | {302, nullptr, "GetAbstractedPadState"}, | 966 | {302, nullptr, "GetAbstractedPadState"}, |
| 932 | {303, nullptr, "GetAbstractedPadsState"}, | 967 | {303, nullptr, "GetAbstractedPadsState"}, |
| @@ -945,6 +980,17 @@ public: | |||
| 945 | {350, nullptr, "AddRegisteredDevice"}, | 980 | {350, nullptr, "AddRegisteredDevice"}, |
| 946 | {400, nullptr, "DisableExternalMcuOnNxDevice"}, | 981 | {400, nullptr, "DisableExternalMcuOnNxDevice"}, |
| 947 | {401, nullptr, "DisableRailDeviceFiltering"}, | 982 | {401, nullptr, "DisableRailDeviceFiltering"}, |
| 983 | {402, nullptr, "EnableWiredPairing"}, | ||
| 984 | {403, nullptr, "EnableShipmentModeAutoClear"}, | ||
| 985 | {500, nullptr, "SetFactoryInt"}, | ||
| 986 | {501, nullptr, "IsFactoryBootEnabled"}, | ||
| 987 | {550, nullptr, "SetAnalogStickModelDataTemporarily"}, | ||
| 988 | {551, nullptr, "GetAnalogStickModelData"}, | ||
| 989 | {552, nullptr, "ResetAnalogStickModelData"}, | ||
| 990 | {600, nullptr, "ConvertPadState"}, | ||
| 991 | {2000, nullptr, "DeactivateDigitizer"}, | ||
| 992 | {2001, nullptr, "SetDigitizerAutoPilotState"}, | ||
| 993 | {2002, nullptr, "UnsetDigitizerAutoPilotState"}, | ||
| 948 | }; | 994 | }; |
| 949 | // clang-format on | 995 | // clang-format on |
| 950 | 996 | ||
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 039c38b58..23552efb1 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -128,6 +128,7 @@ private: | |||
| 128 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); | 128 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 129 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); | 129 | void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); |
| 130 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); | 130 | void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); |
| 131 | void StopSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | ||
| 131 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); | 132 | void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 132 | 133 | ||
| 133 | std::shared_ptr<IAppletResource> applet_resource; | 134 | std::shared_ptr<IAppletResource> applet_resource; |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 0cde7a557..6ad3be1b3 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -116,6 +116,7 @@ public: | |||
| 116 | {1, nullptr, "GetProgramInfo"}, | 116 | {1, nullptr, "GetProgramInfo"}, |
| 117 | {2, nullptr, "RegisterTitle"}, | 117 | {2, nullptr, "RegisterTitle"}, |
| 118 | {3, nullptr, "UnregisterTitle"}, | 118 | {3, nullptr, "UnregisterTitle"}, |
| 119 | {4, nullptr, "SetEnabledProgramVerification"}, | ||
| 119 | }; | 120 | }; |
| 120 | // clang-format on | 121 | // clang-format on |
| 121 | 122 | ||
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp index 89e283ca5..ec9aae04a 100644 --- a/src/core/hle/service/ncm/ncm.cpp +++ b/src/core/hle/service/ncm/ncm.cpp | |||
| @@ -122,6 +122,7 @@ public: | |||
| 122 | {11, nullptr, "ActivateContentMetaDatabase"}, | 122 | {11, nullptr, "ActivateContentMetaDatabase"}, |
| 123 | {12, nullptr, "InactivateContentMetaDatabase"}, | 123 | {12, nullptr, "InactivateContentMetaDatabase"}, |
| 124 | {13, nullptr, "InvalidateRightsIdCache"}, | 124 | {13, nullptr, "InvalidateRightsIdCache"}, |
| 125 | {14, nullptr, "GetMemoryReport"}, | ||
| 125 | }; | 126 | }; |
| 126 | // clang-format on | 127 | // clang-format on |
| 127 | 128 | ||
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index aa171473b..f38d01084 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp | |||
| @@ -48,6 +48,8 @@ public: | |||
| 48 | {151, nullptr, "GetStateWithHandover"}, | 48 | {151, nullptr, "GetStateWithHandover"}, |
| 49 | {152, nullptr, "GetStateChangeEventWithHandover"}, | 49 | {152, nullptr, "GetStateChangeEventWithHandover"}, |
| 50 | {153, nullptr, "GetDropEventWithHandover"}, | 50 | {153, nullptr, "GetDropEventWithHandover"}, |
| 51 | {161, nullptr, "GetRequestChangeStateCancelEvent"}, | ||
| 52 | {162, nullptr, "RequestChangeStateForceTimedWithCancelEvent"}, | ||
| 51 | {201, nullptr, "RequestChangeStateForceTimed"}, | 53 | {201, nullptr, "RequestChangeStateForceTimed"}, |
| 52 | {202, nullptr, "RequestChangeStateForceAsync"}, | 54 | {202, nullptr, "RequestChangeStateForceAsync"}, |
| 53 | }; | 55 | }; |
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index fdab3cf78..8fb88990e 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -110,6 +110,10 @@ IApplicationManagerInterface::IApplicationManagerInterface() | |||
| 110 | {100, nullptr, "ResetToFactorySettings"}, | 110 | {100, nullptr, "ResetToFactorySettings"}, |
| 111 | {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, | 111 | {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, |
| 112 | {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, | 112 | {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, |
| 113 | {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"}, | ||
| 114 | {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"}, | ||
| 115 | {105, nullptr, "RequestResetToFactorySettingsSecurely"}, | ||
| 116 | {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"}, | ||
| 113 | {200, nullptr, "CalculateUserSaveDataStatistics"}, | 117 | {200, nullptr, "CalculateUserSaveDataStatistics"}, |
| 114 | {201, nullptr, "DeleteUserSaveDataAll"}, | 118 | {201, nullptr, "DeleteUserSaveDataAll"}, |
| 115 | {210, nullptr, "DeleteUserSystemSaveData"}, | 119 | {210, nullptr, "DeleteUserSystemSaveData"}, |
| @@ -191,6 +195,9 @@ IApplicationManagerInterface::IApplicationManagerInterface() | |||
| 191 | {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, | 195 | {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, |
| 192 | {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, | 196 | {1308, nullptr, "DeleteApplicationCompletelyForDebug"}, |
| 193 | {1309, nullptr, "CleanupUnavailableAddOnContents"}, | 197 | {1309, nullptr, "CleanupUnavailableAddOnContents"}, |
| 198 | {1310, nullptr, "RequestMoveApplicationEntity"}, | ||
| 199 | {1311, nullptr, "EstimateSizeToMove"}, | ||
| 200 | {1312, nullptr, "HasMovableEntity"}, | ||
| 194 | {1400, nullptr, "PrepareShutdown"}, | 201 | {1400, nullptr, "PrepareShutdown"}, |
| 195 | {1500, nullptr, "FormatSdCard"}, | 202 | {1500, nullptr, "FormatSdCard"}, |
| 196 | {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, | 203 | {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, |
| @@ -241,7 +248,7 @@ IApplicationManagerInterface::IApplicationManagerInterface() | |||
| 241 | {2153, nullptr, "DeactivateRightsEnvironment"}, | 248 | {2153, nullptr, "DeactivateRightsEnvironment"}, |
| 242 | {2154, nullptr, "ForceActivateRightsContextForExit"}, | 249 | {2154, nullptr, "ForceActivateRightsContextForExit"}, |
| 243 | {2155, nullptr, "UpdateRightsEnvironmentStatus"}, | 250 | {2155, nullptr, "UpdateRightsEnvironmentStatus"}, |
| 244 | {2156, nullptr, "CreateRightsEnvironmentForPreomia"}, | 251 | {2156, nullptr, "CreateRightsEnvironmentForMicroApplication"}, |
| 245 | {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, | 252 | {2160, nullptr, "AddTargetApplicationToRightsEnvironment"}, |
| 246 | {2161, nullptr, "SetUsersToRightsEnvironment"}, | 253 | {2161, nullptr, "SetUsersToRightsEnvironment"}, |
| 247 | {2170, nullptr, "GetRightsEnvironmentStatus"}, | 254 | {2170, nullptr, "GetRightsEnvironmentStatus"}, |
| @@ -258,6 +265,7 @@ IApplicationManagerInterface::IApplicationManagerInterface() | |||
| 258 | {2350, nullptr, "PerformAutoUpdateByApplicationId"}, | 265 | {2350, nullptr, "PerformAutoUpdateByApplicationId"}, |
| 259 | {2351, nullptr, "RequestNoDownloadRightsErrorResolution"}, | 266 | {2351, nullptr, "RequestNoDownloadRightsErrorResolution"}, |
| 260 | {2352, nullptr, "RequestResolveNoDownloadRightsError"}, | 267 | {2352, nullptr, "RequestResolveNoDownloadRightsError"}, |
| 268 | {2353, nullptr, "GetApplicationDownloadTaskInfo"}, | ||
| 261 | {2400, nullptr, "GetPromotionInfo"}, | 269 | {2400, nullptr, "GetPromotionInfo"}, |
| 262 | {2401, nullptr, "CountPromotionInfo"}, | 270 | {2401, nullptr, "CountPromotionInfo"}, |
| 263 | {2402, nullptr, "ListPromotionInfo"}, | 271 | {2402, nullptr, "ListPromotionInfo"}, |
| @@ -266,9 +274,12 @@ IApplicationManagerInterface::IApplicationManagerInterface() | |||
| 266 | {2500, nullptr, "ConfirmAvailableTime"}, | 274 | {2500, nullptr, "ConfirmAvailableTime"}, |
| 267 | {2510, nullptr, "CreateApplicationResource"}, | 275 | {2510, nullptr, "CreateApplicationResource"}, |
| 268 | {2511, nullptr, "GetApplicationResource"}, | 276 | {2511, nullptr, "GetApplicationResource"}, |
| 269 | {2513, nullptr, "LaunchPreomia"}, | 277 | {2513, nullptr, "LaunchMicroApplication"}, |
| 270 | {2514, nullptr, "ClearTaskOfAsyncTaskManager"}, | 278 | {2514, nullptr, "ClearTaskOfAsyncTaskManager"}, |
| 279 | {2515, nullptr, "CleanupAllPlaceHolderAndFragmentsIfNoTask"}, | ||
| 280 | {2516, nullptr, "EnsureApplicationCertificate"}, | ||
| 271 | {2800, nullptr, "GetApplicationIdOfPreomia"}, | 281 | {2800, nullptr, "GetApplicationIdOfPreomia"}, |
| 282 | {9999, nullptr, "GetApplicationCertificate"}, | ||
| 272 | }; | 283 | }; |
| 273 | // clang-format on | 284 | // clang-format on |
| 274 | 285 | ||
| @@ -505,6 +516,10 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface() | |||
| 505 | {100, nullptr, "ResetToFactorySettings"}, | 516 | {100, nullptr, "ResetToFactorySettings"}, |
| 506 | {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, | 517 | {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, |
| 507 | {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, | 518 | {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, |
| 519 | {103, nullptr, "ResetToFactorySettingsWithPlatformRegion"}, | ||
| 520 | {104, nullptr, "ResetToFactorySettingsWithPlatformRegionAuthentication"}, | ||
| 521 | {105, nullptr, "RequestResetToFactorySettingsSecurely"}, | ||
| 522 | {106, nullptr, "RequestResetToFactorySettingsWithPlatformRegionAuthenticationSecurely"}, | ||
| 508 | }; | 523 | }; |
| 509 | // clang-format on | 524 | // clang-format on |
| 510 | 525 | ||
| @@ -553,6 +568,9 @@ public: | |||
| 553 | {10, nullptr, "TerminateApplication2"}, | 568 | {10, nullptr, "TerminateApplication2"}, |
| 554 | {11, nullptr, "GetRunningApplicationProcessId"}, | 569 | {11, nullptr, "GetRunningApplicationProcessId"}, |
| 555 | {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"}, | 570 | {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"}, |
| 571 | {13, nullptr, "CreateApplicationResourceForDevelop"}, | ||
| 572 | {14, nullptr, "IsPreomiaForDevelop"}, | ||
| 573 | {15, nullptr, "GetApplicationProgramIdFromHost"}, | ||
| 556 | }; | 574 | }; |
| 557 | // clang-format on | 575 | // clang-format on |
| 558 | 576 | ||
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index ab1746d28..6efdf1606 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp | |||
| @@ -164,6 +164,7 @@ PL_U::PL_U(Core::System& system) | |||
| 164 | {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, | 164 | {6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"}, |
| 165 | {100, nullptr, "RequestApplicationFunctionAuthorization"}, | 165 | {100, nullptr, "RequestApplicationFunctionAuthorization"}, |
| 166 | {101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"}, | 166 | {101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"}, |
| 167 | {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"}, | ||
| 167 | {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, | 168 | {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"}, |
| 168 | {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, | 169 | {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"}, |
| 169 | }; | 170 | }; |
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index c75b4ee34..caf14ed61 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp | |||
| @@ -31,6 +31,8 @@ public: | |||
| 31 | {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, | 31 | {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, |
| 32 | {1015, nullptr, "ConfirmPlayableApplicationVideo"}, | 32 | {1015, nullptr, "ConfirmPlayableApplicationVideo"}, |
| 33 | {1016, nullptr, "ConfirmShowNewsPermission"}, | 33 | {1016, nullptr, "ConfirmShowNewsPermission"}, |
| 34 | {1017, nullptr, "EndFreeCommunication"}, | ||
| 35 | {1018, nullptr, "IsFreeCommunicationAvailable"}, | ||
| 34 | {1031, nullptr, "IsRestrictionEnabled"}, | 36 | {1031, nullptr, "IsRestrictionEnabled"}, |
| 35 | {1032, nullptr, "GetSafetyLevel"}, | 37 | {1032, nullptr, "GetSafetyLevel"}, |
| 36 | {1033, nullptr, "SetSafetyLevel"}, | 38 | {1033, nullptr, "SetSafetyLevel"}, |
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 8f1be0e48..14309c679 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -21,8 +21,10 @@ public: | |||
| 21 | static const FunctionInfo functions[] = { | 21 | static const FunctionInfo functions[] = { |
| 22 | {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"}, | 22 | {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"}, |
| 23 | {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"}, | 23 | {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"}, |
| 24 | {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"}, | 24 | {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old2>, "SaveReportOld2"}, |
| 25 | {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"}, | 25 | {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old2>, "SaveReportWithUserOld2"}, |
| 26 | {10104, nullptr, "SaveReport"}, | ||
| 27 | {10105, nullptr, "SaveReportWithUser"}, | ||
| 26 | {10200, nullptr, "RequestImmediateTransmission"}, | 28 | {10200, nullptr, "RequestImmediateTransmission"}, |
| 27 | {10300, nullptr, "GetTransmissionStatus"}, | 29 | {10300, nullptr, "GetTransmissionStatus"}, |
| 28 | {10400, nullptr, "GetSystemSessionId"}, | 30 | {10400, nullptr, "GetSystemSessionId"}, |
| @@ -35,8 +37,10 @@ public: | |||
| 35 | {30400, nullptr, "GetStatistics"}, | 37 | {30400, nullptr, "GetStatistics"}, |
| 36 | {30401, nullptr, "GetThroughputHistory"}, | 38 | {30401, nullptr, "GetThroughputHistory"}, |
| 37 | {30500, nullptr, "GetLastUploadError"}, | 39 | {30500, nullptr, "GetLastUploadError"}, |
| 40 | {30600, nullptr, "GetApplicationUploadSummary"}, | ||
| 38 | {40100, nullptr, "IsUserAgreementCheckEnabled"}, | 41 | {40100, nullptr, "IsUserAgreementCheckEnabled"}, |
| 39 | {40101, nullptr, "SetUserAgreementCheckEnabled"}, | 42 | {40101, nullptr, "SetUserAgreementCheckEnabled"}, |
| 43 | {50100, nullptr, "ReadAllApplicationReportFiles"}, | ||
| 40 | {90100, nullptr, "ReadAllReportFiles"}, | 44 | {90100, nullptr, "ReadAllReportFiles"}, |
| 41 | }; | 45 | }; |
| 42 | // clang-format on | 46 | // clang-format on |
| @@ -51,7 +55,7 @@ private: | |||
| 51 | const auto process_id = rp.PopRaw<u64>(); | 55 | const auto process_id = rp.PopRaw<u64>(); |
| 52 | 56 | ||
| 53 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; | 57 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; |
| 54 | if (Type == Core::Reporter::PlayReportType::New) { | 58 | if constexpr (Type == Core::Reporter::PlayReportType::Old2) { |
| 55 | data.emplace_back(ctx.ReadBuffer(1)); | 59 | data.emplace_back(ctx.ReadBuffer(1)); |
| 56 | } | 60 | } |
| 57 | 61 | ||
| @@ -71,7 +75,7 @@ private: | |||
| 71 | const auto user_id = rp.PopRaw<u128>(); | 75 | const auto user_id = rp.PopRaw<u128>(); |
| 72 | const auto process_id = rp.PopRaw<u64>(); | 76 | const auto process_id = rp.PopRaw<u64>(); |
| 73 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; | 77 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; |
| 74 | if (Type == Core::Reporter::PlayReportType::New) { | 78 | if constexpr (Type == Core::Reporter::PlayReportType::Old2) { |
| 75 | data.emplace_back(ctx.ReadBuffer(1)); | 79 | data.emplace_back(ctx.ReadBuffer(1)); |
| 76 | } | 80 | } |
| 77 | 81 | ||
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index 1398a4a48..3fbfecc9e 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp | |||
| @@ -50,6 +50,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") { | |||
| 50 | {39, nullptr, "GetConsoleSixAxisSensorModuleType"}, | 50 | {39, nullptr, "GetConsoleSixAxisSensorModuleType"}, |
| 51 | {40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"}, | 51 | {40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"}, |
| 52 | {41, nullptr, "GetBatteryVersion"}, | 52 | {41, nullptr, "GetBatteryVersion"}, |
| 53 | {42, nullptr, "GetDeviceId"}, | ||
| 54 | {43, nullptr, "GetConsoleSixAxisSensorMountType"}, | ||
| 53 | }; | 55 | }; |
| 54 | // clang-format on | 56 | // clang-format on |
| 55 | 57 | ||
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index b7c9ea74b..8bd4c7e79 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp | |||
| @@ -288,6 +288,18 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") { | |||
| 288 | {186, nullptr, "GetMemoryUsageRateFlag"}, | 288 | {186, nullptr, "GetMemoryUsageRateFlag"}, |
| 289 | {187, nullptr, "GetTouchScreenMode"}, | 289 | {187, nullptr, "GetTouchScreenMode"}, |
| 290 | {188, nullptr, "SetTouchScreenMode"}, | 290 | {188, nullptr, "SetTouchScreenMode"}, |
| 291 | {189, nullptr, "GetButtonConfigSettingsFull"}, | ||
| 292 | {190, nullptr, "SetButtonConfigSettingsFull"}, | ||
| 293 | {191, nullptr, "GetButtonConfigSettingsEmbedded"}, | ||
| 294 | {192, nullptr, "SetButtonConfigSettingsEmbedded"}, | ||
| 295 | {193, nullptr, "GetButtonConfigSettingsLeft"}, | ||
| 296 | {194, nullptr, "SetButtonConfigSettingsLeft"}, | ||
| 297 | {195, nullptr, "GetButtonConfigSettingsRight"}, | ||
| 298 | {196, nullptr, "SetButtonConfigSettingsRight"}, | ||
| 299 | {197, nullptr, "GetButtonConfigRegisteredSettingsEmbedded"}, | ||
| 300 | {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"}, | ||
| 301 | {199, nullptr, "GetButtonConfigRegisteredSettings"}, | ||
| 302 | {200, nullptr, "SetButtonConfigRegisteredSettings"}, | ||
| 291 | }; | 303 | }; |
| 292 | // clang-format on | 304 | // clang-format on |
| 293 | 305 | ||
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index f67fab2f9..8d4952c0e 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -148,6 +148,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 148 | {30, nullptr, "SendMMsg"}, | 148 | {30, nullptr, "SendMMsg"}, |
| 149 | {31, nullptr, "EventFd"}, | 149 | {31, nullptr, "EventFd"}, |
| 150 | {32, nullptr, "RegisterResourceStatisticsName"}, | 150 | {32, nullptr, "RegisterResourceStatisticsName"}, |
| 151 | {33, nullptr, "Initialize2"}, | ||
| 151 | }; | 152 | }; |
| 152 | // clang-format on | 153 | // clang-format on |
| 153 | 154 | ||
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index e722886de..67f1bbcf3 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -20,8 +20,8 @@ namespace Service::Time { | |||
| 20 | 20 | ||
| 21 | class ISystemClock final : public ServiceFramework<ISystemClock> { | 21 | class ISystemClock final : public ServiceFramework<ISystemClock> { |
| 22 | public: | 22 | public: |
| 23 | ISystemClock(Clock::SystemClockCore& clock_core) | 23 | explicit ISystemClock(Clock::SystemClockCore& clock_core, Core::System& system) |
| 24 | : ServiceFramework("ISystemClock"), clock_core{clock_core} { | 24 | : ServiceFramework("ISystemClock"), clock_core{clock_core}, system{system} { |
| 25 | // clang-format off | 25 | // clang-format off |
| 26 | static const FunctionInfo functions[] = { | 26 | static const FunctionInfo functions[] = { |
| 27 | {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, | 27 | {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, |
| @@ -46,9 +46,8 @@ private: | |||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | s64 posix_time{}; | 48 | s64 posix_time{}; |
| 49 | if (const ResultCode result{ | 49 | if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)}; |
| 50 | clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)}; | 50 | result.IsError()) { |
| 51 | result != RESULT_SUCCESS) { | ||
| 52 | IPC::ResponseBuilder rb{ctx, 2}; | 51 | IPC::ResponseBuilder rb{ctx, 2}; |
| 53 | rb.Push(result); | 52 | rb.Push(result); |
| 54 | return; | 53 | return; |
| @@ -69,9 +68,8 @@ private: | |||
| 69 | } | 68 | } |
| 70 | 69 | ||
| 71 | Clock::SystemClockContext system_clock_context{}; | 70 | Clock::SystemClockContext system_clock_context{}; |
| 72 | if (const ResultCode result{ | 71 | if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)}; |
| 73 | clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)}; | 72 | result.IsError()) { |
| 74 | result != RESULT_SUCCESS) { | ||
| 75 | IPC::ResponseBuilder rb{ctx, 2}; | 73 | IPC::ResponseBuilder rb{ctx, 2}; |
| 76 | rb.Push(result); | 74 | rb.Push(result); |
| 77 | return; | 75 | return; |
| @@ -83,12 +81,13 @@ private: | |||
| 83 | } | 81 | } |
| 84 | 82 | ||
| 85 | Clock::SystemClockCore& clock_core; | 83 | Clock::SystemClockCore& clock_core; |
| 84 | Core::System& system; | ||
| 86 | }; | 85 | }; |
| 87 | 86 | ||
| 88 | class ISteadyClock final : public ServiceFramework<ISteadyClock> { | 87 | class ISteadyClock final : public ServiceFramework<ISteadyClock> { |
| 89 | public: | 88 | public: |
| 90 | ISteadyClock(Clock::SteadyClockCore& clock_core) | 89 | explicit ISteadyClock(Clock::SteadyClockCore& clock_core, Core::System& system) |
| 91 | : ServiceFramework("ISteadyClock"), clock_core{clock_core} { | 90 | : ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} { |
| 92 | static const FunctionInfo functions[] = { | 91 | static const FunctionInfo functions[] = { |
| 93 | {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, | 92 | {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, |
| 94 | }; | 93 | }; |
| @@ -105,14 +104,14 @@ private: | |||
| 105 | return; | 104 | return; |
| 106 | } | 105 | } |
| 107 | 106 | ||
| 108 | const Clock::SteadyClockTimePoint time_point{ | 107 | const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)}; |
| 109 | clock_core.GetCurrentTimePoint(Core::System::GetInstance())}; | ||
| 110 | IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2}; | 108 | IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2}; |
| 111 | rb.Push(RESULT_SUCCESS); | 109 | rb.Push(RESULT_SUCCESS); |
| 112 | rb.PushRaw(time_point); | 110 | rb.PushRaw(time_point); |
| 113 | } | 111 | } |
| 114 | 112 | ||
| 115 | Clock::SteadyClockCore& clock_core; | 113 | Clock::SteadyClockCore& clock_core; |
| 114 | Core::System& system; | ||
| 116 | }; | 115 | }; |
| 117 | 116 | ||
| 118 | ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( | 117 | ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( |
| @@ -134,7 +133,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( | |||
| 134 | } | 133 | } |
| 135 | 134 | ||
| 136 | const auto current_time_point{ | 135 | const auto current_time_point{ |
| 137 | time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())}; | 136 | time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; |
| 138 | if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime( | 137 | if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime( |
| 139 | clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)}; | 138 | clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)}; |
| 140 | result != RESULT_SUCCESS) { | 139 | result != RESULT_SUCCESS) { |
| @@ -176,21 +175,24 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct | |||
| 176 | LOG_DEBUG(Service_Time, "called"); | 175 | LOG_DEBUG(Service_Time, "called"); |
| 177 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 176 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 178 | rb.Push(RESULT_SUCCESS); | 177 | rb.Push(RESULT_SUCCESS); |
| 179 | rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore()); | 178 | rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore(), |
| 179 | system); | ||
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { | 182 | void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { |
| 183 | LOG_DEBUG(Service_Time, "called"); | 183 | LOG_DEBUG(Service_Time, "called"); |
| 184 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 184 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 185 | rb.Push(RESULT_SUCCESS); | 185 | rb.Push(RESULT_SUCCESS); |
| 186 | rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore()); | 186 | rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore(), |
| 187 | system); | ||
| 187 | } | 188 | } |
| 188 | 189 | ||
| 189 | void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { | 190 | void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { |
| 190 | LOG_DEBUG(Service_Time, "called"); | 191 | LOG_DEBUG(Service_Time, "called"); |
| 191 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 192 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 192 | rb.Push(RESULT_SUCCESS); | 193 | rb.Push(RESULT_SUCCESS); |
| 193 | rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore()); | 194 | rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore(), |
| 195 | system); | ||
| 194 | } | 196 | } |
| 195 | 197 | ||
| 196 | void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { | 198 | void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { |
| @@ -204,7 +206,8 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c | |||
| 204 | LOG_DEBUG(Service_Time, "called"); | 206 | LOG_DEBUG(Service_Time, "called"); |
| 205 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 207 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 206 | rb.Push(RESULT_SUCCESS); | 208 | rb.Push(RESULT_SUCCESS); |
| 207 | rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore()); | 209 | rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore(), |
| 210 | system); | ||
| 208 | } | 211 | } |
| 209 | 212 | ||
| 210 | void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient( | 213 | void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient( |
| @@ -228,8 +231,7 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe | |||
| 228 | 231 | ||
| 229 | IPC::RequestParser rp{ctx}; | 232 | IPC::RequestParser rp{ctx}; |
| 230 | const auto context{rp.PopRaw<Clock::SystemClockContext>()}; | 233 | const auto context{rp.PopRaw<Clock::SystemClockContext>()}; |
| 231 | const auto current_time_point{ | 234 | const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; |
| 232 | steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())}; | ||
| 233 | 235 | ||
| 234 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { | 236 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { |
| 235 | const auto ticks{Clock::TimeSpanType::FromTicks( | 237 | const auto ticks{Clock::TimeSpanType::FromTicks( |
| @@ -255,8 +257,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | |||
| 255 | Clock::SystemClockContext user_context{}; | 257 | Clock::SystemClockContext user_context{}; |
| 256 | if (const ResultCode result{ | 258 | if (const ResultCode result{ |
| 257 | module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext( | 259 | module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext( |
| 258 | Core::System::GetInstance(), user_context)}; | 260 | system, user_context)}; |
| 259 | result != RESULT_SUCCESS) { | 261 | result.IsError()) { |
| 260 | IPC::ResponseBuilder rb{ctx, 2}; | 262 | IPC::ResponseBuilder rb{ctx, 2}; |
| 261 | rb.Push(result); | 263 | rb.Push(result); |
| 262 | return; | 264 | return; |
| @@ -264,8 +266,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | |||
| 264 | Clock::SystemClockContext network_context{}; | 266 | Clock::SystemClockContext network_context{}; |
| 265 | if (const ResultCode result{ | 267 | if (const ResultCode result{ |
| 266 | module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( | 268 | module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( |
| 267 | Core::System::GetInstance(), network_context)}; | 269 | system, network_context)}; |
| 268 | result != RESULT_SUCCESS) { | 270 | result.IsError()) { |
| 269 | IPC::ResponseBuilder rb{ctx, 2}; | 271 | IPC::ResponseBuilder rb{ctx, 2}; |
| 270 | rb.Push(result); | 272 | rb.Push(result); |
| 271 | return; | 273 | return; |
| @@ -274,7 +276,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | |||
| 274 | Clock::ClockSnapshot clock_snapshot{}; | 276 | Clock::ClockSnapshot clock_snapshot{}; |
| 275 | if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( | 277 | if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal( |
| 276 | &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; | 278 | &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; |
| 277 | result != RESULT_SUCCESS) { | 279 | result.IsError()) { |
| 278 | IPC::ResponseBuilder rb{ctx, 2}; | 280 | IPC::ResponseBuilder rb{ctx, 2}; |
| 279 | rb.Push(result); | 281 | rb.Push(result); |
| 280 | return; | 282 | return; |
diff --git a/src/core/reporter.h b/src/core/reporter.h index 380941b1b..86d760cf0 100644 --- a/src/core/reporter.h +++ b/src/core/reporter.h | |||
| @@ -56,6 +56,7 @@ public: | |||
| 56 | 56 | ||
| 57 | enum class PlayReportType { | 57 | enum class PlayReportType { |
| 58 | Old, | 58 | Old, |
| 59 | Old2, | ||
| 59 | New, | 60 | New, |
| 60 | System, | 61 | System, |
| 61 | }; | 62 | }; |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index c1282cb80..cd6c257f5 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -92,7 +92,7 @@ void LogSettings() { | |||
| 92 | LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); | 92 | LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); |
| 93 | LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); | 93 | LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); |
| 94 | LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); | 94 | LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); |
| 95 | LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); | 95 | LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy); |
| 96 | LogSetting("Renderer_UseAsynchronousGpuEmulation", | 96 | LogSetting("Renderer_UseAsynchronousGpuEmulation", |
| 97 | Settings::values.use_asynchronous_gpu_emulation); | 97 | Settings::values.use_asynchronous_gpu_emulation); |
| 98 | LogSetting("Renderer_UseVsync", Settings::values.use_vsync); | 98 | LogSetting("Renderer_UseVsync", Settings::values.use_vsync); |
| @@ -109,4 +109,12 @@ void LogSettings() { | |||
| 109 | LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); | 109 | LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local); |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | bool IsGPULevelExtreme() { | ||
| 113 | return values.gpu_accuracy == GPUAccuracy::Extreme; | ||
| 114 | } | ||
| 115 | |||
| 116 | bool IsGPULevelHigh() { | ||
| 117 | return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High; | ||
| 118 | } | ||
| 119 | |||
| 112 | } // namespace Settings | 120 | } // namespace Settings |
diff --git a/src/core/settings.h b/src/core/settings.h index c73d1c596..163900f0b 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -376,6 +376,12 @@ enum class RendererBackend { | |||
| 376 | Vulkan = 1, | 376 | Vulkan = 1, |
| 377 | }; | 377 | }; |
| 378 | 378 | ||
| 379 | enum class GPUAccuracy : u32 { | ||
| 380 | Normal = 0, | ||
| 381 | High = 1, | ||
| 382 | Extreme = 2, | ||
| 383 | }; | ||
| 384 | |||
| 379 | struct Values { | 385 | struct Values { |
| 380 | // System | 386 | // System |
| 381 | bool use_docked_mode; | 387 | bool use_docked_mode; |
| @@ -436,10 +442,11 @@ struct Values { | |||
| 436 | bool use_frame_limit; | 442 | bool use_frame_limit; |
| 437 | u16 frame_limit; | 443 | u16 frame_limit; |
| 438 | bool use_disk_shader_cache; | 444 | bool use_disk_shader_cache; |
| 439 | bool use_accurate_gpu_emulation; | 445 | GPUAccuracy gpu_accuracy; |
| 440 | bool use_asynchronous_gpu_emulation; | 446 | bool use_asynchronous_gpu_emulation; |
| 441 | bool use_vsync; | 447 | bool use_vsync; |
| 442 | bool force_30fps_mode; | 448 | bool force_30fps_mode; |
| 449 | bool use_fast_gpu_time; | ||
| 443 | 450 | ||
| 444 | float bg_red; | 451 | float bg_red; |
| 445 | float bg_green; | 452 | float bg_green; |
| @@ -480,6 +487,9 @@ struct Values { | |||
| 480 | std::map<u64, std::vector<std::string>> disabled_addons; | 487 | std::map<u64, std::vector<std::string>> disabled_addons; |
| 481 | } extern values; | 488 | } extern values; |
| 482 | 489 | ||
| 490 | bool IsGPULevelExtreme(); | ||
| 491 | bool IsGPULevelHigh(); | ||
| 492 | |||
| 483 | void Apply(); | 493 | void Apply(); |
| 484 | void LogSettings(); | 494 | void LogSettings(); |
| 485 | } // namespace Settings | 495 | } // namespace Settings |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index fd5a3ee9f..1c3b03a1c 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -56,6 +56,18 @@ static const char* TranslateRenderer(Settings::RendererBackend backend) { | |||
| 56 | return "Unknown"; | 56 | return "Unknown"; |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) { | ||
| 60 | switch (backend) { | ||
| 61 | case Settings::GPUAccuracy::Normal: | ||
| 62 | return "Normal"; | ||
| 63 | case Settings::GPUAccuracy::High: | ||
| 64 | return "High"; | ||
| 65 | case Settings::GPUAccuracy::Extreme: | ||
| 66 | return "Extreme"; | ||
| 67 | } | ||
| 68 | return "Unknown"; | ||
| 69 | } | ||
| 70 | |||
| 59 | u64 GetTelemetryId() { | 71 | u64 GetTelemetryId() { |
| 60 | u64 telemetry_id{}; | 72 | u64 telemetry_id{}; |
| 61 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + | 73 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + |
| @@ -184,8 +196,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | |||
| 184 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); | 196 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); |
| 185 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); | 197 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); |
| 186 | AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); | 198 | AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); |
| 187 | AddField(field_type, "Renderer_UseAccurateGpuEmulation", | 199 | AddField(field_type, "Renderer_GPUAccuracyLevel", |
| 188 | Settings::values.use_accurate_gpu_emulation); | 200 | TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy)); |
| 189 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", | 201 | AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", |
| 190 | Settings::values.use_asynchronous_gpu_emulation); | 202 | Settings::values.use_asynchronous_gpu_emulation); |
| 191 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); | 203 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync); |
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 1e3940801..ff2d11cc8 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -14,13 +14,14 @@ | |||
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | 16 | ||
| 17 | namespace { | ||
| 17 | // Numbers are chosen randomly to make sure the correct one is given. | 18 | // Numbers are chosen randomly to make sure the correct one is given. |
| 18 | static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; | 19 | constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; |
| 19 | static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals | 20 | constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals |
| 20 | 21 | ||
| 21 | static std::bitset<CB_IDS.size()> callbacks_ran_flags; | 22 | std::bitset<CB_IDS.size()> callbacks_ran_flags; |
| 22 | static u64 expected_callback = 0; | 23 | u64 expected_callback = 0; |
| 23 | static s64 lateness = 0; | 24 | s64 lateness = 0; |
| 24 | 25 | ||
| 25 | template <unsigned int IDX> | 26 | template <unsigned int IDX> |
| 26 | void CallbackTemplate(u64 userdata, s64 cycles_late) { | 27 | void CallbackTemplate(u64 userdata, s64 cycles_late) { |
| @@ -31,7 +32,7 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) { | |||
| 31 | REQUIRE(lateness == cycles_late); | 32 | REQUIRE(lateness == cycles_late); |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | static u64 callbacks_done = 0; | 35 | u64 callbacks_done = 0; |
| 35 | 36 | ||
| 36 | void EmptyCallback(u64 userdata, s64 cycles_late) { | 37 | void EmptyCallback(u64 userdata, s64 cycles_late) { |
| 37 | ++callbacks_done; | 38 | ++callbacks_done; |
| @@ -48,8 +49,8 @@ struct ScopeInit final { | |||
| 48 | Core::Timing::CoreTiming core_timing; | 49 | Core::Timing::CoreTiming core_timing; |
| 49 | }; | 50 | }; |
| 50 | 51 | ||
| 51 | static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, | 52 | void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0, |
| 52 | int expected_lateness = 0, int cpu_downcount = 0) { | 53 | int expected_lateness = 0, int cpu_downcount = 0) { |
| 53 | callbacks_ran_flags = 0; | 54 | callbacks_ran_flags = 0; |
| 54 | expected_callback = CB_IDS[idx]; | 55 | expected_callback = CB_IDS[idx]; |
| 55 | lateness = expected_lateness; | 56 | lateness = expected_lateness; |
| @@ -62,6 +63,7 @@ static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 | |||
| 62 | 63 | ||
| 63 | REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); | 64 | REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); |
| 64 | } | 65 | } |
| 66 | } // Anonymous namespace | ||
| 65 | 67 | ||
| 66 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | 68 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { |
| 67 | ScopeInit guard; | 69 | ScopeInit guard; |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 258d58eba..8ede4ba9b 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -23,6 +23,7 @@ add_library(video_core STATIC | |||
| 23 | engines/shader_bytecode.h | 23 | engines/shader_bytecode.h |
| 24 | engines/shader_header.h | 24 | engines/shader_header.h |
| 25 | engines/shader_type.h | 25 | engines/shader_type.h |
| 26 | fence_manager.h | ||
| 26 | gpu.cpp | 27 | gpu.cpp |
| 27 | gpu.h | 28 | gpu.h |
| 28 | gpu_asynch.cpp | 29 | gpu_asynch.cpp |
| @@ -51,6 +52,8 @@ add_library(video_core STATIC | |||
| 51 | renderer_opengl/gl_buffer_cache.h | 52 | renderer_opengl/gl_buffer_cache.h |
| 52 | renderer_opengl/gl_device.cpp | 53 | renderer_opengl/gl_device.cpp |
| 53 | renderer_opengl/gl_device.h | 54 | renderer_opengl/gl_device.h |
| 55 | renderer_opengl/gl_fence_manager.cpp | ||
| 56 | renderer_opengl/gl_fence_manager.h | ||
| 54 | renderer_opengl/gl_framebuffer_cache.cpp | 57 | renderer_opengl/gl_framebuffer_cache.cpp |
| 55 | renderer_opengl/gl_framebuffer_cache.h | 58 | renderer_opengl/gl_framebuffer_cache.h |
| 56 | renderer_opengl/gl_rasterizer.cpp | 59 | renderer_opengl/gl_rasterizer.cpp |
| @@ -160,6 +163,8 @@ if (ENABLE_VULKAN) | |||
| 160 | renderer_vulkan/fixed_pipeline_state.h | 163 | renderer_vulkan/fixed_pipeline_state.h |
| 161 | renderer_vulkan/maxwell_to_vk.cpp | 164 | renderer_vulkan/maxwell_to_vk.cpp |
| 162 | renderer_vulkan/maxwell_to_vk.h | 165 | renderer_vulkan/maxwell_to_vk.h |
| 166 | renderer_vulkan/nsight_aftermath_tracker.cpp | ||
| 167 | renderer_vulkan/nsight_aftermath_tracker.h | ||
| 163 | renderer_vulkan/renderer_vulkan.h | 168 | renderer_vulkan/renderer_vulkan.h |
| 164 | renderer_vulkan/renderer_vulkan.cpp | 169 | renderer_vulkan/renderer_vulkan.cpp |
| 165 | renderer_vulkan/vk_blit_screen.cpp | 170 | renderer_vulkan/vk_blit_screen.cpp |
| @@ -174,6 +179,8 @@ if (ENABLE_VULKAN) | |||
| 174 | renderer_vulkan/vk_descriptor_pool.h | 179 | renderer_vulkan/vk_descriptor_pool.h |
| 175 | renderer_vulkan/vk_device.cpp | 180 | renderer_vulkan/vk_device.cpp |
| 176 | renderer_vulkan/vk_device.h | 181 | renderer_vulkan/vk_device.h |
| 182 | renderer_vulkan/vk_fence_manager.cpp | ||
| 183 | renderer_vulkan/vk_fence_manager.h | ||
| 177 | renderer_vulkan/vk_graphics_pipeline.cpp | 184 | renderer_vulkan/vk_graphics_pipeline.cpp |
| 178 | renderer_vulkan/vk_graphics_pipeline.h | 185 | renderer_vulkan/vk_graphics_pipeline.h |
| 179 | renderer_vulkan/vk_image.cpp | 186 | renderer_vulkan/vk_image.cpp |
| @@ -213,19 +220,30 @@ if (ENABLE_VULKAN) | |||
| 213 | renderer_vulkan/wrapper.cpp | 220 | renderer_vulkan/wrapper.cpp |
| 214 | renderer_vulkan/wrapper.h | 221 | renderer_vulkan/wrapper.h |
| 215 | ) | 222 | ) |
| 216 | |||
| 217 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) | ||
| 218 | target_compile_definitions(video_core PRIVATE HAS_VULKAN) | ||
| 219 | endif() | 223 | endif() |
| 220 | 224 | ||
| 221 | create_target_directory_groups(video_core) | 225 | create_target_directory_groups(video_core) |
| 222 | 226 | ||
| 223 | target_link_libraries(video_core PUBLIC common core) | 227 | target_link_libraries(video_core PUBLIC common core) |
| 224 | target_link_libraries(video_core PRIVATE glad) | 228 | target_link_libraries(video_core PRIVATE glad) |
| 229 | |||
| 225 | if (ENABLE_VULKAN) | 230 | if (ENABLE_VULKAN) |
| 231 | target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) | ||
| 232 | target_compile_definitions(video_core PRIVATE HAS_VULKAN) | ||
| 226 | target_link_libraries(video_core PRIVATE sirit) | 233 | target_link_libraries(video_core PRIVATE sirit) |
| 227 | endif() | 234 | endif() |
| 228 | 235 | ||
| 236 | if (ENABLE_NSIGHT_AFTERMATH) | ||
| 237 | if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) | ||
| 238 | message(ERROR "Environment variable NSIGHT_AFTERMATH_SDK has to be provided") | ||
| 239 | endif() | ||
| 240 | if (NOT WIN32) | ||
| 241 | message(ERROR "Nsight Aftermath doesn't support non-Windows platforms") | ||
| 242 | endif() | ||
| 243 | target_compile_definitions(video_core PRIVATE HAS_NSIGHT_AFTERMATH) | ||
| 244 | target_include_directories(video_core PRIVATE "$ENV{NSIGHT_AFTERMATH_SDK}/include") | ||
| 245 | endif() | ||
| 246 | |||
| 229 | if (MSVC) | 247 | if (MSVC) |
| 230 | target_compile_options(video_core PRIVATE /we4267) | 248 | target_compile_options(video_core PRIVATE /we4267) |
| 231 | else() | 249 | else() |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 83e7a1cde..510f11089 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <list> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <mutex> | 10 | #include <mutex> |
| 10 | #include <unordered_map> | 11 | #include <unordered_map> |
| @@ -18,8 +19,10 @@ | |||
| 18 | 19 | ||
| 19 | #include "common/alignment.h" | 20 | #include "common/alignment.h" |
| 20 | #include "common/common_types.h" | 21 | #include "common/common_types.h" |
| 22 | #include "common/logging/log.h" | ||
| 21 | #include "core/core.h" | 23 | #include "core/core.h" |
| 22 | #include "core/memory.h" | 24 | #include "core/memory.h" |
| 25 | #include "core/settings.h" | ||
| 23 | #include "video_core/buffer_cache/buffer_block.h" | 26 | #include "video_core/buffer_cache/buffer_block.h" |
| 24 | #include "video_core/buffer_cache/map_interval.h" | 27 | #include "video_core/buffer_cache/map_interval.h" |
| 25 | #include "video_core/memory_manager.h" | 28 | #include "video_core/memory_manager.h" |
| @@ -79,6 +82,9 @@ public: | |||
| 79 | auto map = MapAddress(block, gpu_addr, cpu_addr, size); | 82 | auto map = MapAddress(block, gpu_addr, cpu_addr, size); |
| 80 | if (is_written) { | 83 | if (is_written) { |
| 81 | map->MarkAsModified(true, GetModifiedTicks()); | 84 | map->MarkAsModified(true, GetModifiedTicks()); |
| 85 | if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { | ||
| 86 | MarkForAsyncFlush(map); | ||
| 87 | } | ||
| 82 | if (!map->IsWritten()) { | 88 | if (!map->IsWritten()) { |
| 83 | map->MarkAsWritten(true); | 89 | map->MarkAsWritten(true); |
| 84 | MarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); | 90 | MarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); |
| @@ -137,11 +143,22 @@ public: | |||
| 137 | }); | 143 | }); |
| 138 | for (auto& object : objects) { | 144 | for (auto& object : objects) { |
| 139 | if (object->IsModified() && object->IsRegistered()) { | 145 | if (object->IsModified() && object->IsRegistered()) { |
| 146 | mutex.unlock(); | ||
| 140 | FlushMap(object); | 147 | FlushMap(object); |
| 148 | mutex.lock(); | ||
| 141 | } | 149 | } |
| 142 | } | 150 | } |
| 143 | } | 151 | } |
| 144 | 152 | ||
| 153 | bool MustFlushRegion(VAddr addr, std::size_t size) { | ||
| 154 | std::lock_guard lock{mutex}; | ||
| 155 | |||
| 156 | const std::vector<MapInterval> objects = GetMapsInRange(addr, size); | ||
| 157 | return std::any_of(objects.cbegin(), objects.cend(), [](const MapInterval& map) { | ||
| 158 | return map->IsModified() && map->IsRegistered(); | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | |||
| 145 | /// Mark the specified region as being invalidated | 162 | /// Mark the specified region as being invalidated |
| 146 | void InvalidateRegion(VAddr addr, u64 size) { | 163 | void InvalidateRegion(VAddr addr, u64 size) { |
| 147 | std::lock_guard lock{mutex}; | 164 | std::lock_guard lock{mutex}; |
| @@ -154,6 +171,77 @@ public: | |||
| 154 | } | 171 | } |
| 155 | } | 172 | } |
| 156 | 173 | ||
| 174 | void OnCPUWrite(VAddr addr, std::size_t size) { | ||
| 175 | std::lock_guard lock{mutex}; | ||
| 176 | |||
| 177 | for (const auto& object : GetMapsInRange(addr, size)) { | ||
| 178 | if (object->IsMemoryMarked() && object->IsRegistered()) { | ||
| 179 | UnmarkMemory(object); | ||
| 180 | object->SetSyncPending(true); | ||
| 181 | marked_for_unregister.emplace_back(object); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | void SyncGuestHost() { | ||
| 187 | std::lock_guard lock{mutex}; | ||
| 188 | |||
| 189 | for (const auto& object : marked_for_unregister) { | ||
| 190 | if (object->IsRegistered()) { | ||
| 191 | object->SetSyncPending(false); | ||
| 192 | Unregister(object); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | marked_for_unregister.clear(); | ||
| 196 | } | ||
| 197 | |||
| 198 | void CommitAsyncFlushes() { | ||
| 199 | if (uncommitted_flushes) { | ||
| 200 | auto commit_list = std::make_shared<std::list<MapInterval>>(); | ||
| 201 | for (auto& map : *uncommitted_flushes) { | ||
| 202 | if (map->IsRegistered() && map->IsModified()) { | ||
| 203 | // TODO(Blinkhawk): Implement backend asynchronous flushing | ||
| 204 | // AsyncFlushMap(map) | ||
| 205 | commit_list->push_back(map); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | if (!commit_list->empty()) { | ||
| 209 | committed_flushes.push_back(commit_list); | ||
| 210 | } else { | ||
| 211 | committed_flushes.emplace_back(); | ||
| 212 | } | ||
| 213 | } else { | ||
| 214 | committed_flushes.emplace_back(); | ||
| 215 | } | ||
| 216 | uncommitted_flushes.reset(); | ||
| 217 | } | ||
| 218 | |||
| 219 | bool ShouldWaitAsyncFlushes() const { | ||
| 220 | return !committed_flushes.empty() && committed_flushes.front() != nullptr; | ||
| 221 | } | ||
| 222 | |||
| 223 | bool HasUncommittedFlushes() const { | ||
| 224 | return uncommitted_flushes != nullptr; | ||
| 225 | } | ||
| 226 | |||
| 227 | void PopAsyncFlushes() { | ||
| 228 | if (committed_flushes.empty()) { | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | auto& flush_list = committed_flushes.front(); | ||
| 232 | if (!flush_list) { | ||
| 233 | committed_flushes.pop_front(); | ||
| 234 | return; | ||
| 235 | } | ||
| 236 | for (MapInterval& map : *flush_list) { | ||
| 237 | if (map->IsRegistered()) { | ||
| 238 | // TODO(Blinkhawk): Replace this for reading the asynchronous flush | ||
| 239 | FlushMap(map); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | committed_flushes.pop_front(); | ||
| 243 | } | ||
| 244 | |||
| 157 | virtual BufferType GetEmptyBuffer(std::size_t size) = 0; | 245 | virtual BufferType GetEmptyBuffer(std::size_t size) = 0; |
| 158 | 246 | ||
| 159 | protected: | 247 | protected: |
| @@ -196,17 +284,30 @@ protected: | |||
| 196 | const IntervalType interval{new_map->GetStart(), new_map->GetEnd()}; | 284 | const IntervalType interval{new_map->GetStart(), new_map->GetEnd()}; |
| 197 | mapped_addresses.insert({interval, new_map}); | 285 | mapped_addresses.insert({interval, new_map}); |
| 198 | rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); | 286 | rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); |
| 287 | new_map->SetMemoryMarked(true); | ||
| 199 | if (inherit_written) { | 288 | if (inherit_written) { |
| 200 | MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1); | 289 | MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1); |
| 201 | new_map->MarkAsWritten(true); | 290 | new_map->MarkAsWritten(true); |
| 202 | } | 291 | } |
| 203 | } | 292 | } |
| 204 | 293 | ||
| 205 | /// Unregisters an object from the cache | 294 | void UnmarkMemory(const MapInterval& map) { |
| 206 | void Unregister(MapInterval& map) { | 295 | if (!map->IsMemoryMarked()) { |
| 296 | return; | ||
| 297 | } | ||
| 207 | const std::size_t size = map->GetEnd() - map->GetStart(); | 298 | const std::size_t size = map->GetEnd() - map->GetStart(); |
| 208 | rasterizer.UpdatePagesCachedCount(map->GetStart(), size, -1); | 299 | rasterizer.UpdatePagesCachedCount(map->GetStart(), size, -1); |
| 300 | map->SetMemoryMarked(false); | ||
| 301 | } | ||
| 302 | |||
| 303 | /// Unregisters an object from the cache | ||
| 304 | void Unregister(const MapInterval& map) { | ||
| 305 | UnmarkMemory(map); | ||
| 209 | map->MarkAsRegistered(false); | 306 | map->MarkAsRegistered(false); |
| 307 | if (map->IsSyncPending()) { | ||
| 308 | marked_for_unregister.remove(map); | ||
| 309 | map->SetSyncPending(false); | ||
| 310 | } | ||
| 210 | if (map->IsWritten()) { | 311 | if (map->IsWritten()) { |
| 211 | UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); | 312 | UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); |
| 212 | } | 313 | } |
| @@ -264,6 +365,9 @@ private: | |||
| 264 | MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr); | 365 | MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr); |
| 265 | if (modified_inheritance) { | 366 | if (modified_inheritance) { |
| 266 | new_map->MarkAsModified(true, GetModifiedTicks()); | 367 | new_map->MarkAsModified(true, GetModifiedTicks()); |
| 368 | if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { | ||
| 369 | MarkForAsyncFlush(new_map); | ||
| 370 | } | ||
| 267 | } | 371 | } |
| 268 | Register(new_map, write_inheritance); | 372 | Register(new_map, write_inheritance); |
| 269 | return new_map; | 373 | return new_map; |
| @@ -450,6 +554,13 @@ private: | |||
| 450 | return false; | 554 | return false; |
| 451 | } | 555 | } |
| 452 | 556 | ||
| 557 | void MarkForAsyncFlush(MapInterval& map) { | ||
| 558 | if (!uncommitted_flushes) { | ||
| 559 | uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval>>(); | ||
| 560 | } | ||
| 561 | uncommitted_flushes->insert(map); | ||
| 562 | } | ||
| 563 | |||
| 453 | VideoCore::RasterizerInterface& rasterizer; | 564 | VideoCore::RasterizerInterface& rasterizer; |
| 454 | Core::System& system; | 565 | Core::System& system; |
| 455 | 566 | ||
| @@ -479,6 +590,10 @@ private: | |||
| 479 | u64 modified_ticks = 0; | 590 | u64 modified_ticks = 0; |
| 480 | 591 | ||
| 481 | std::vector<u8> staging_buffer; | 592 | std::vector<u8> staging_buffer; |
| 593 | std::list<MapInterval> marked_for_unregister; | ||
| 594 | |||
| 595 | std::shared_ptr<std::unordered_set<MapInterval>> uncommitted_flushes{}; | ||
| 596 | std::list<std::shared_ptr<std::list<MapInterval>>> committed_flushes; | ||
| 482 | 597 | ||
| 483 | std::recursive_mutex mutex; | 598 | std::recursive_mutex mutex; |
| 484 | }; | 599 | }; |
diff --git a/src/video_core/buffer_cache/map_interval.h b/src/video_core/buffer_cache/map_interval.h index b0956029d..29d8b26f3 100644 --- a/src/video_core/buffer_cache/map_interval.h +++ b/src/video_core/buffer_cache/map_interval.h | |||
| @@ -46,6 +46,22 @@ public: | |||
| 46 | return is_registered; | 46 | return is_registered; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | void SetMemoryMarked(bool is_memory_marked_) { | ||
| 50 | is_memory_marked = is_memory_marked_; | ||
| 51 | } | ||
| 52 | |||
| 53 | bool IsMemoryMarked() const { | ||
| 54 | return is_memory_marked; | ||
| 55 | } | ||
| 56 | |||
| 57 | void SetSyncPending(bool is_sync_pending_) { | ||
| 58 | is_sync_pending = is_sync_pending_; | ||
| 59 | } | ||
| 60 | |||
| 61 | bool IsSyncPending() const { | ||
| 62 | return is_sync_pending; | ||
| 63 | } | ||
| 64 | |||
| 49 | VAddr GetStart() const { | 65 | VAddr GetStart() const { |
| 50 | return start; | 66 | return start; |
| 51 | } | 67 | } |
| @@ -83,6 +99,8 @@ private: | |||
| 83 | bool is_written{}; | 99 | bool is_written{}; |
| 84 | bool is_modified{}; | 100 | bool is_modified{}; |
| 85 | bool is_registered{}; | 101 | bool is_registered{}; |
| 102 | bool is_memory_marked{}; | ||
| 103 | bool is_sync_pending{}; | ||
| 86 | u64 ticks{}; | 104 | u64 ticks{}; |
| 87 | }; | 105 | }; |
| 88 | 106 | ||
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 0b77afc71..16311f05e 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -21,6 +21,7 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128, | |||
| 21 | void DmaPusher::DispatchCalls() { | 21 | void DmaPusher::DispatchCalls() { |
| 22 | MICROPROFILE_SCOPE(DispatchCalls); | 22 | MICROPROFILE_SCOPE(DispatchCalls); |
| 23 | 23 | ||
| 24 | gpu.SyncGuestHost(); | ||
| 24 | // On entering GPU code, assume all memory may be touched by the ARM core. | 25 | // On entering GPU code, assume all memory may be touched by the ARM core. |
| 25 | gpu.Maxwell3D().OnMemoryWrite(); | 26 | gpu.Maxwell3D().OnMemoryWrite(); |
| 26 | 27 | ||
| @@ -32,6 +33,8 @@ void DmaPusher::DispatchCalls() { | |||
| 32 | } | 33 | } |
| 33 | } | 34 | } |
| 34 | gpu.FlushCommands(); | 35 | gpu.FlushCommands(); |
| 36 | gpu.SyncGuestHost(); | ||
| 37 | gpu.OnCommandListEnd(); | ||
| 35 | } | 38 | } |
| 36 | 39 | ||
| 37 | bool DmaPusher::Step() { | 40 | bool DmaPusher::Step() { |
| @@ -68,16 +71,22 @@ bool DmaPusher::Step() { | |||
| 68 | gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), | 71 | gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), |
| 69 | command_list_header.size * sizeof(u32)); | 72 | command_list_header.size * sizeof(u32)); |
| 70 | 73 | ||
| 71 | for (const CommandHeader& command_header : command_headers) { | 74 | for (std::size_t index = 0; index < command_headers.size();) { |
| 75 | const CommandHeader& command_header = command_headers[index]; | ||
| 72 | 76 | ||
| 73 | // now, see if we're in the middle of a command | 77 | if (dma_state.method_count) { |
| 74 | if (dma_state.length_pending) { | ||
| 75 | // Second word of long non-inc methods command - method count | ||
| 76 | dma_state.length_pending = 0; | ||
| 77 | dma_state.method_count = command_header.method_count_; | ||
| 78 | } else if (dma_state.method_count) { | ||
| 79 | // Data word of methods command | 78 | // Data word of methods command |
| 80 | CallMethod(command_header.argument); | 79 | if (dma_state.non_incrementing) { |
| 80 | const u32 max_write = static_cast<u32>( | ||
| 81 | std::min<std::size_t>(index + dma_state.method_count, command_headers.size()) - | ||
| 82 | index); | ||
| 83 | CallMultiMethod(&command_header.argument, max_write); | ||
| 84 | dma_state.method_count -= max_write; | ||
| 85 | index += max_write; | ||
| 86 | continue; | ||
| 87 | } else { | ||
| 88 | CallMethod(command_header.argument); | ||
| 89 | } | ||
| 81 | 90 | ||
| 82 | if (!dma_state.non_incrementing) { | 91 | if (!dma_state.non_incrementing) { |
| 83 | dma_state.method++; | 92 | dma_state.method++; |
| @@ -117,6 +126,7 @@ bool DmaPusher::Step() { | |||
| 117 | break; | 126 | break; |
| 118 | } | 127 | } |
| 119 | } | 128 | } |
| 129 | index++; | ||
| 120 | } | 130 | } |
| 121 | 131 | ||
| 122 | if (!non_main) { | 132 | if (!non_main) { |
| @@ -137,4 +147,9 @@ void DmaPusher::CallMethod(u32 argument) const { | |||
| 137 | gpu.CallMethod({dma_state.method, argument, dma_state.subchannel, dma_state.method_count}); | 147 | gpu.CallMethod({dma_state.method, argument, dma_state.subchannel, dma_state.method_count}); |
| 138 | } | 148 | } |
| 139 | 149 | ||
| 150 | void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const { | ||
| 151 | gpu.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, | ||
| 152 | dma_state.method_count); | ||
| 153 | } | ||
| 154 | |||
| 140 | } // namespace Tegra | 155 | } // namespace Tegra |
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index d6188614a..6cef71306 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h | |||
| @@ -75,6 +75,7 @@ private: | |||
| 75 | void SetState(const CommandHeader& command_header); | 75 | void SetState(const CommandHeader& command_header); |
| 76 | 76 | ||
| 77 | void CallMethod(u32 argument) const; | 77 | void CallMethod(u32 argument) const; |
| 78 | void CallMultiMethod(const u32* base_start, u32 num_methods) const; | ||
| 78 | 79 | ||
| 79 | std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once | 80 | std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once |
| 80 | 81 | ||
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 85d308e26..8a47614d2 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp | |||
| @@ -28,7 +28,13 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) { | 31 | void Fermi2D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending) { |
| 32 | for (std::size_t i = 0; i < amount; i++) { | ||
| 33 | CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)}); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | static std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) { | ||
| 32 | const u32 line_a = src_2 - src_1; | 38 | const u32 line_a = src_2 - src_1; |
| 33 | const u32 line_b = dst_2 - dst_1; | 39 | const u32 line_b = dst_2 - dst_1; |
| 34 | const u32 excess = std::max<s32>(0, line_a - src_line + src_1); | 40 | const u32 excess = std::max<s32>(0, line_a - src_line + src_1); |
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index dba342c70..939a5966d 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -39,6 +39,9 @@ public: | |||
| 39 | /// Write the value to the register identified by method. | 39 | /// Write the value to the register identified by method. |
| 40 | void CallMethod(const GPU::MethodCall& method_call); | 40 | void CallMethod(const GPU::MethodCall& method_call); |
| 41 | 41 | ||
| 42 | /// Write multiple values to the register identified by method. | ||
| 43 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending); | ||
| 44 | |||
| 42 | enum class Origin : u32 { | 45 | enum class Origin : u32 { |
| 43 | Center = 0, | 46 | Center = 0, |
| 44 | Corner = 1, | 47 | Corner = 1, |
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 368c75a66..00a12175f 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -51,6 +51,13 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) { | |||
| 51 | } | 51 | } |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | ||
| 55 | u32 methods_pending) { | ||
| 56 | for (std::size_t i = 0; i < amount; i++) { | ||
| 57 | CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)}); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 54 | Texture::FullTextureInfo KeplerCompute::GetTexture(std::size_t offset) const { | 61 | Texture::FullTextureInfo KeplerCompute::GetTexture(std::size_t offset) const { |
| 55 | const std::bitset<8> cbuf_mask = launch_description.const_buffer_enable_mask.Value(); | 62 | const std::bitset<8> cbuf_mask = launch_description.const_buffer_enable_mask.Value(); |
| 56 | ASSERT(cbuf_mask[regs.tex_cb_index]); | 63 | ASSERT(cbuf_mask[regs.tex_cb_index]); |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index eeb79c56f..fe55fdfd0 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -202,6 +202,9 @@ public: | |||
| 202 | /// Write the value to the register identified by method. | 202 | /// Write the value to the register identified by method. |
| 203 | void CallMethod(const GPU::MethodCall& method_call); | 203 | void CallMethod(const GPU::MethodCall& method_call); |
| 204 | 204 | ||
| 205 | /// Write multiple values to the register identified by method. | ||
| 206 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending); | ||
| 207 | |||
| 205 | Texture::FullTextureInfo GetTexture(std::size_t offset) const; | 208 | Texture::FullTextureInfo GetTexture(std::size_t offset) const; |
| 206 | 209 | ||
| 207 | /// Given a texture handle, returns the TSC and TIC entries. | 210 | /// Given a texture handle, returns the TSC and TIC entries. |
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 597872e43..586ff15dc 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -41,4 +41,11 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) { | |||
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | void KeplerMemory::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | ||
| 45 | u32 methods_pending) { | ||
| 46 | for (std::size_t i = 0; i < amount; i++) { | ||
| 47 | CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)}); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 44 | } // namespace Tegra::Engines | 51 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 396fb6e86..bb26fb030 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -40,6 +40,9 @@ public: | |||
| 40 | /// Write the value to the register identified by method. | 40 | /// Write the value to the register identified by method. |
| 41 | void CallMethod(const GPU::MethodCall& method_call); | 41 | void CallMethod(const GPU::MethodCall& method_call); |
| 42 | 42 | ||
| 43 | /// Write multiple values to the register identified by method. | ||
| 44 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending); | ||
| 45 | |||
| 43 | struct Regs { | 46 | struct Regs { |
| 44 | static constexpr size_t NUM_REGS = 0x7F; | 47 | static constexpr size_t NUM_REGS = 0x7F; |
| 45 | 48 | ||
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index baa74ad4c..39e3b66a2 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -280,6 +280,58 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 280 | } | 280 | } |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | ||
| 284 | u32 methods_pending) { | ||
| 285 | // Methods after 0xE00 are special, they're actually triggers for some microcode that was | ||
| 286 | // uploaded to the GPU during initialization. | ||
| 287 | if (method >= MacroRegistersStart) { | ||
| 288 | // We're trying to execute a macro | ||
| 289 | if (executing_macro == 0) { | ||
| 290 | // A macro call must begin by writing the macro method's register, not its argument. | ||
| 291 | ASSERT_MSG((method % 2) == 0, | ||
| 292 | "Can't start macro execution by writing to the ARGS register"); | ||
| 293 | executing_macro = method; | ||
| 294 | } | ||
| 295 | |||
| 296 | for (std::size_t i = 0; i < amount; i++) { | ||
| 297 | macro_params.push_back(base_start[i]); | ||
| 298 | } | ||
| 299 | |||
| 300 | // Call the macro when there are no more parameters in the command buffer | ||
| 301 | if (amount == methods_pending) { | ||
| 302 | CallMacroMethod(executing_macro, macro_params.size(), macro_params.data()); | ||
| 303 | macro_params.clear(); | ||
| 304 | } | ||
| 305 | return; | ||
| 306 | } | ||
| 307 | switch (method) { | ||
| 308 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): | ||
| 309 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): | ||
| 310 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): | ||
| 311 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]): | ||
| 312 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]): | ||
| 313 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]): | ||
| 314 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]): | ||
| 315 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]): | ||
| 316 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]): | ||
| 317 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]): | ||
| 318 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]): | ||
| 319 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]): | ||
| 320 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]): | ||
| 321 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]): | ||
| 322 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]): | ||
| 323 | case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): { | ||
| 324 | ProcessCBMultiData(method, base_start, amount); | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | default: { | ||
| 328 | for (std::size_t i = 0; i < amount; i++) { | ||
| 329 | CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)}); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 283 | void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { | 335 | void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) { |
| 284 | if (mme_draw.current_mode == MMEDrawMode::Undefined) { | 336 | if (mme_draw.current_mode == MMEDrawMode::Undefined) { |
| 285 | if (mme_draw.gl_begin_consume) { | 337 | if (mme_draw.gl_begin_consume) { |
| @@ -404,7 +456,11 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 404 | 456 | ||
| 405 | switch (regs.query.query_get.operation) { | 457 | switch (regs.query.query_get.operation) { |
| 406 | case Regs::QueryOperation::Release: | 458 | case Regs::QueryOperation::Release: |
| 407 | StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); | 459 | if (regs.query.query_get.fence == 1) { |
| 460 | rasterizer.SignalSemaphore(regs.query.QueryAddress(), regs.query.query_sequence); | ||
| 461 | } else { | ||
| 462 | StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); | ||
| 463 | } | ||
| 408 | break; | 464 | break; |
| 409 | case Regs::QueryOperation::Acquire: | 465 | case Regs::QueryOperation::Acquire: |
| 410 | // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that | 466 | // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that |
| @@ -483,7 +539,7 @@ void Maxwell3D::ProcessSyncPoint() { | |||
| 483 | const u32 increment = regs.sync_info.increment.Value(); | 539 | const u32 increment = regs.sync_info.increment.Value(); |
| 484 | [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); | 540 | [[maybe_unused]] const u32 cache_flush = regs.sync_info.unknown.Value(); |
| 485 | if (increment) { | 541 | if (increment) { |
| 486 | system.GPU().IncrementSyncPoint(sync_point); | 542 | rasterizer.SignalSyncPoint(sync_point); |
| 487 | } | 543 | } |
| 488 | } | 544 | } |
| 489 | 545 | ||
| @@ -566,6 +622,28 @@ void Maxwell3D::StartCBData(u32 method) { | |||
| 566 | ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]); | 622 | ProcessCBData(regs.const_buffer.cb_data[cb_data_state.id]); |
| 567 | } | 623 | } |
| 568 | 624 | ||
| 625 | void Maxwell3D::ProcessCBMultiData(u32 method, const u32* start_base, u32 amount) { | ||
| 626 | if (cb_data_state.current != method) { | ||
| 627 | if (cb_data_state.current != null_cb_data) { | ||
| 628 | FinishCBData(); | ||
| 629 | } | ||
| 630 | constexpr u32 first_cb_data = MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]); | ||
| 631 | cb_data_state.start_pos = regs.const_buffer.cb_pos; | ||
| 632 | cb_data_state.id = method - first_cb_data; | ||
| 633 | cb_data_state.current = method; | ||
| 634 | cb_data_state.counter = 0; | ||
| 635 | } | ||
| 636 | const std::size_t id = cb_data_state.id; | ||
| 637 | const std::size_t size = amount; | ||
| 638 | std::size_t i = 0; | ||
| 639 | for (; i < size; i++) { | ||
| 640 | cb_data_state.buffer[id][cb_data_state.counter] = start_base[i]; | ||
| 641 | cb_data_state.counter++; | ||
| 642 | } | ||
| 643 | // Increment the current buffer position. | ||
| 644 | regs.const_buffer.cb_pos = regs.const_buffer.cb_pos + 4 * amount; | ||
| 645 | } | ||
| 646 | |||
| 569 | void Maxwell3D::FinishCBData() { | 647 | void Maxwell3D::FinishCBData() { |
| 570 | // Write the input value to the current const buffer at the current position. | 648 | // Write the input value to the current const buffer at the current position. |
| 571 | const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); | 649 | const GPUVAddr buffer_address = regs.const_buffer.BufferAddress(); |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 59d5752d2..3dfba8197 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -1259,7 +1259,8 @@ public: | |||
| 1259 | 1259 | ||
| 1260 | GPUVAddr LimitAddress() const { | 1260 | GPUVAddr LimitAddress() const { |
| 1261 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) | | 1261 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(limit_high) << 32) | |
| 1262 | limit_low); | 1262 | limit_low) + |
| 1263 | 1; | ||
| 1263 | } | 1264 | } |
| 1264 | } vertex_array_limit[NumVertexArrays]; | 1265 | } vertex_array_limit[NumVertexArrays]; |
| 1265 | 1266 | ||
| @@ -1358,6 +1359,9 @@ public: | |||
| 1358 | /// Write the value to the register identified by method. | 1359 | /// Write the value to the register identified by method. |
| 1359 | void CallMethod(const GPU::MethodCall& method_call); | 1360 | void CallMethod(const GPU::MethodCall& method_call); |
| 1360 | 1361 | ||
| 1362 | /// Write multiple values to the register identified by method. | ||
| 1363 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending); | ||
| 1364 | |||
| 1361 | /// Write the value to the register identified by method. | 1365 | /// Write the value to the register identified by method. |
| 1362 | void CallMethodFromMME(const GPU::MethodCall& method_call); | 1366 | void CallMethodFromMME(const GPU::MethodCall& method_call); |
| 1363 | 1367 | ||
| @@ -1511,6 +1515,7 @@ private: | |||
| 1511 | /// Handles a write to the CB_DATA[i] register. | 1515 | /// Handles a write to the CB_DATA[i] register. |
| 1512 | void StartCBData(u32 method); | 1516 | void StartCBData(u32 method); |
| 1513 | void ProcessCBData(u32 value); | 1517 | void ProcessCBData(u32 value); |
| 1518 | void ProcessCBMultiData(u32 method, const u32* start_base, u32 amount); | ||
| 1514 | void FinishCBData(); | 1519 | void FinishCBData(); |
| 1515 | 1520 | ||
| 1516 | /// Handles a write to the CB_BIND register. | 1521 | /// Handles a write to the CB_BIND register. |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index c2610f992..6630005b0 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -36,6 +36,13 @@ void MaxwellDMA::CallMethod(const GPU::MethodCall& method_call) { | |||
| 36 | #undef MAXWELLDMA_REG_INDEX | 36 | #undef MAXWELLDMA_REG_INDEX |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount, | ||
| 40 | u32 methods_pending) { | ||
| 41 | for (std::size_t i = 0; i < amount; i++) { | ||
| 42 | CallMethod({method, base_start[i], 0, methods_pending - static_cast<u32>(i)}); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 39 | void MaxwellDMA::HandleCopy() { | 46 | void MaxwellDMA::HandleCopy() { |
| 40 | LOG_TRACE(HW_GPU, "Requested a DMA copy"); | 47 | LOG_TRACE(HW_GPU, "Requested a DMA copy"); |
| 41 | 48 | ||
| @@ -104,8 +111,13 @@ void MaxwellDMA::HandleCopy() { | |||
| 104 | write_buffer.resize(dst_size); | 111 | write_buffer.resize(dst_size); |
| 105 | } | 112 | } |
| 106 | 113 | ||
| 107 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); | 114 | if (Settings::IsGPULevelExtreme()) { |
| 108 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); | 115 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); |
| 116 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); | ||
| 117 | } else { | ||
| 118 | memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size); | ||
| 119 | memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size); | ||
| 120 | } | ||
| 109 | 121 | ||
| 110 | Texture::UnswizzleSubrect( | 122 | Texture::UnswizzleSubrect( |
| 111 | regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel, | 123 | regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel, |
| @@ -136,7 +148,7 @@ void MaxwellDMA::HandleCopy() { | |||
| 136 | write_buffer.resize(dst_size); | 148 | write_buffer.resize(dst_size); |
| 137 | } | 149 | } |
| 138 | 150 | ||
| 139 | if (Settings::values.use_accurate_gpu_emulation) { | 151 | if (Settings::IsGPULevelExtreme()) { |
| 140 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); | 152 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); |
| 141 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); | 153 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); |
| 142 | } else { | 154 | } else { |
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 4f40d1d1f..c43ed8194 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -35,6 +35,9 @@ public: | |||
| 35 | /// Write the value to the register identified by method. | 35 | /// Write the value to the register identified by method. |
| 36 | void CallMethod(const GPU::MethodCall& method_call); | 36 | void CallMethod(const GPU::MethodCall& method_call); |
| 37 | 37 | ||
| 38 | /// Write multiple values to the register identified by method. | ||
| 39 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, u32 methods_pending); | ||
| 40 | |||
| 38 | struct Regs { | 41 | struct Regs { |
| 39 | static constexpr std::size_t NUM_REGS = 0x1D6; | 42 | static constexpr std::size_t NUM_REGS = 0x1D6; |
| 40 | 43 | ||
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 7231597d4..cde3a26b9 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -655,6 +655,7 @@ union Instruction { | |||
| 655 | } | 655 | } |
| 656 | 656 | ||
| 657 | constexpr Instruction(u64 value) : value{value} {} | 657 | constexpr Instruction(u64 value) : value{value} {} |
| 658 | constexpr Instruction(const Instruction& instr) : value(instr.value) {} | ||
| 658 | 659 | ||
| 659 | BitField<0, 8, Register> gpr0; | 660 | BitField<0, 8, Register> gpr0; |
| 660 | BitField<8, 8, Register> gpr8; | 661 | BitField<8, 8, Register> gpr8; |
| @@ -817,11 +818,9 @@ union Instruction { | |||
| 817 | BitField<32, 1, u64> saturate; | 818 | BitField<32, 1, u64> saturate; |
| 818 | BitField<49, 2, HalfMerge> merge; | 819 | BitField<49, 2, HalfMerge> merge; |
| 819 | 820 | ||
| 820 | BitField<43, 1, u64> negate_a; | ||
| 821 | BitField<44, 1, u64> abs_a; | 821 | BitField<44, 1, u64> abs_a; |
| 822 | BitField<47, 2, HalfType> type_a; | 822 | BitField<47, 2, HalfType> type_a; |
| 823 | 823 | ||
| 824 | BitField<31, 1, u64> negate_b; | ||
| 825 | BitField<30, 1, u64> abs_b; | 824 | BitField<30, 1, u64> abs_b; |
| 826 | BitField<28, 2, HalfType> type_b; | 825 | BitField<28, 2, HalfType> type_b; |
| 827 | 826 | ||
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h new file mode 100644 index 000000000..dabd1588c --- /dev/null +++ b/src/video_core/fence_manager.h | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | #include <memory> | ||
| 10 | #include <queue> | ||
| 11 | |||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/memory.h" | ||
| 16 | #include "core/settings.h" | ||
| 17 | #include "video_core/gpu.h" | ||
| 18 | #include "video_core/memory_manager.h" | ||
| 19 | #include "video_core/rasterizer_interface.h" | ||
| 20 | |||
| 21 | namespace VideoCommon { | ||
| 22 | |||
| 23 | class FenceBase { | ||
| 24 | public: | ||
| 25 | FenceBase(u32 payload, bool is_stubbed) | ||
| 26 | : address{}, payload{payload}, is_semaphore{false}, is_stubbed{is_stubbed} {} | ||
| 27 | |||
| 28 | FenceBase(GPUVAddr address, u32 payload, bool is_stubbed) | ||
| 29 | : address{address}, payload{payload}, is_semaphore{true}, is_stubbed{is_stubbed} {} | ||
| 30 | |||
| 31 | GPUVAddr GetAddress() const { | ||
| 32 | return address; | ||
| 33 | } | ||
| 34 | |||
| 35 | u32 GetPayload() const { | ||
| 36 | return payload; | ||
| 37 | } | ||
| 38 | |||
| 39 | bool IsSemaphore() const { | ||
| 40 | return is_semaphore; | ||
| 41 | } | ||
| 42 | |||
| 43 | private: | ||
| 44 | GPUVAddr address; | ||
| 45 | u32 payload; | ||
| 46 | bool is_semaphore; | ||
| 47 | |||
| 48 | protected: | ||
| 49 | bool is_stubbed; | ||
| 50 | }; | ||
| 51 | |||
| 52 | template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache> | ||
| 53 | class FenceManager { | ||
| 54 | public: | ||
| 55 | void SignalSemaphore(GPUVAddr addr, u32 value) { | ||
| 56 | TryReleasePendingFences(); | ||
| 57 | const bool should_flush = ShouldFlush(); | ||
| 58 | CommitAsyncFlushes(); | ||
| 59 | TFence new_fence = CreateFence(addr, value, !should_flush); | ||
| 60 | fences.push(new_fence); | ||
| 61 | QueueFence(new_fence); | ||
| 62 | if (should_flush) { | ||
| 63 | rasterizer.FlushCommands(); | ||
| 64 | } | ||
| 65 | rasterizer.SyncGuestHost(); | ||
| 66 | } | ||
| 67 | |||
| 68 | void SignalSyncPoint(u32 value) { | ||
| 69 | TryReleasePendingFences(); | ||
| 70 | const bool should_flush = ShouldFlush(); | ||
| 71 | CommitAsyncFlushes(); | ||
| 72 | TFence new_fence = CreateFence(value, !should_flush); | ||
| 73 | fences.push(new_fence); | ||
| 74 | QueueFence(new_fence); | ||
| 75 | if (should_flush) { | ||
| 76 | rasterizer.FlushCommands(); | ||
| 77 | } | ||
| 78 | rasterizer.SyncGuestHost(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void WaitPendingFences() { | ||
| 82 | auto& gpu{system.GPU()}; | ||
| 83 | auto& memory_manager{gpu.MemoryManager()}; | ||
| 84 | while (!fences.empty()) { | ||
| 85 | TFence& current_fence = fences.front(); | ||
| 86 | if (ShouldWait()) { | ||
| 87 | WaitFence(current_fence); | ||
| 88 | } | ||
| 89 | PopAsyncFlushes(); | ||
| 90 | if (current_fence->IsSemaphore()) { | ||
| 91 | memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload()); | ||
| 92 | } else { | ||
| 93 | gpu.IncrementSyncPoint(current_fence->GetPayload()); | ||
| 94 | } | ||
| 95 | fences.pop(); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | protected: | ||
| 100 | FenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | ||
| 101 | TTextureCache& texture_cache, TTBufferCache& buffer_cache, | ||
| 102 | TQueryCache& query_cache) | ||
| 103 | : system{system}, rasterizer{rasterizer}, texture_cache{texture_cache}, | ||
| 104 | buffer_cache{buffer_cache}, query_cache{query_cache} {} | ||
| 105 | |||
| 106 | virtual ~FenceManager() {} | ||
| 107 | |||
| 108 | /// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is | ||
| 109 | /// true | ||
| 110 | virtual TFence CreateFence(u32 value, bool is_stubbed) = 0; | ||
| 111 | /// Creates a Semaphore Fence Interface, does not create a backend fence if 'is_stubbed' is true | ||
| 112 | virtual TFence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) = 0; | ||
| 113 | /// Queues a fence into the backend if the fence isn't stubbed. | ||
| 114 | virtual void QueueFence(TFence& fence) = 0; | ||
| 115 | /// Notifies that the backend fence has been signaled/reached in host GPU. | ||
| 116 | virtual bool IsFenceSignaled(TFence& fence) const = 0; | ||
| 117 | /// Waits until a fence has been signalled by the host GPU. | ||
| 118 | virtual void WaitFence(TFence& fence) = 0; | ||
| 119 | |||
| 120 | Core::System& system; | ||
| 121 | VideoCore::RasterizerInterface& rasterizer; | ||
| 122 | TTextureCache& texture_cache; | ||
| 123 | TTBufferCache& buffer_cache; | ||
| 124 | TQueryCache& query_cache; | ||
| 125 | |||
| 126 | private: | ||
| 127 | void TryReleasePendingFences() { | ||
| 128 | auto& gpu{system.GPU()}; | ||
| 129 | auto& memory_manager{gpu.MemoryManager()}; | ||
| 130 | while (!fences.empty()) { | ||
| 131 | TFence& current_fence = fences.front(); | ||
| 132 | if (ShouldWait() && !IsFenceSignaled(current_fence)) { | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | PopAsyncFlushes(); | ||
| 136 | if (current_fence->IsSemaphore()) { | ||
| 137 | memory_manager.Write<u32>(current_fence->GetAddress(), current_fence->GetPayload()); | ||
| 138 | } else { | ||
| 139 | gpu.IncrementSyncPoint(current_fence->GetPayload()); | ||
| 140 | } | ||
| 141 | fences.pop(); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | bool ShouldWait() const { | ||
| 146 | return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() || | ||
| 147 | query_cache.ShouldWaitAsyncFlushes(); | ||
| 148 | } | ||
| 149 | |||
| 150 | bool ShouldFlush() const { | ||
| 151 | return texture_cache.HasUncommittedFlushes() || buffer_cache.HasUncommittedFlushes() || | ||
| 152 | query_cache.HasUncommittedFlushes(); | ||
| 153 | } | ||
| 154 | |||
| 155 | void PopAsyncFlushes() { | ||
| 156 | texture_cache.PopAsyncFlushes(); | ||
| 157 | buffer_cache.PopAsyncFlushes(); | ||
| 158 | query_cache.PopAsyncFlushes(); | ||
| 159 | } | ||
| 160 | |||
| 161 | void CommitAsyncFlushes() { | ||
| 162 | texture_cache.CommitAsyncFlushes(); | ||
| 163 | buffer_cache.CommitAsyncFlushes(); | ||
| 164 | query_cache.CommitAsyncFlushes(); | ||
| 165 | } | ||
| 166 | |||
| 167 | std::queue<TFence> fences; | ||
| 168 | }; | ||
| 169 | |||
| 170 | } // namespace VideoCommon | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index a606f4abd..b87fd873d 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/core_timing_util.h" | 9 | #include "core/core_timing_util.h" |
| 10 | #include "core/frontend/emu_window.h" | 10 | #include "core/frontend/emu_window.h" |
| 11 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 12 | #include "core/settings.h" | ||
| 12 | #include "video_core/engines/fermi_2d.h" | 13 | #include "video_core/engines/fermi_2d.h" |
| 13 | #include "video_core/engines/kepler_compute.h" | 14 | #include "video_core/engines/kepler_compute.h" |
| 14 | #include "video_core/engines/kepler_memory.h" | 15 | #include "video_core/engines/kepler_memory.h" |
| @@ -125,6 +126,28 @@ bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) { | |||
| 125 | return true; | 126 | return true; |
| 126 | } | 127 | } |
| 127 | 128 | ||
| 129 | u64 GPU::RequestFlush(VAddr addr, std::size_t size) { | ||
| 130 | std::unique_lock lck{flush_request_mutex}; | ||
| 131 | const u64 fence = ++last_flush_fence; | ||
| 132 | flush_requests.emplace_back(fence, addr, size); | ||
| 133 | return fence; | ||
| 134 | } | ||
| 135 | |||
| 136 | void GPU::TickWork() { | ||
| 137 | std::unique_lock lck{flush_request_mutex}; | ||
| 138 | while (!flush_requests.empty()) { | ||
| 139 | auto& request = flush_requests.front(); | ||
| 140 | const u64 fence = request.fence; | ||
| 141 | const VAddr addr = request.addr; | ||
| 142 | const std::size_t size = request.size; | ||
| 143 | flush_requests.pop_front(); | ||
| 144 | flush_request_mutex.unlock(); | ||
| 145 | renderer->Rasterizer().FlushRegion(addr, size); | ||
| 146 | current_flush_fence.store(fence); | ||
| 147 | flush_request_mutex.lock(); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 128 | u64 GPU::GetTicks() const { | 151 | u64 GPU::GetTicks() const { |
| 129 | // This values were reversed engineered by fincs from NVN | 152 | // This values were reversed engineered by fincs from NVN |
| 130 | // The gpu clock is reported in units of 385/625 nanoseconds | 153 | // The gpu clock is reported in units of 385/625 nanoseconds |
| @@ -132,7 +155,10 @@ u64 GPU::GetTicks() const { | |||
| 132 | constexpr u64 gpu_ticks_den = 625; | 155 | constexpr u64 gpu_ticks_den = 625; |
| 133 | 156 | ||
| 134 | const u64 cpu_ticks = system.CoreTiming().GetTicks(); | 157 | const u64 cpu_ticks = system.CoreTiming().GetTicks(); |
| 135 | const u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); | 158 | u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); |
| 159 | if (Settings::values.use_fast_gpu_time) { | ||
| 160 | nanoseconds /= 256; | ||
| 161 | } | ||
| 136 | const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; | 162 | const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; |
| 137 | const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; | 163 | const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; |
| 138 | return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; | 164 | return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; |
| @@ -142,6 +168,13 @@ void GPU::FlushCommands() { | |||
| 142 | renderer->Rasterizer().FlushCommands(); | 168 | renderer->Rasterizer().FlushCommands(); |
| 143 | } | 169 | } |
| 144 | 170 | ||
| 171 | void GPU::SyncGuestHost() { | ||
| 172 | renderer->Rasterizer().SyncGuestHost(); | ||
| 173 | } | ||
| 174 | |||
| 175 | void GPU::OnCommandListEnd() { | ||
| 176 | renderer->Rasterizer().ReleaseFences(); | ||
| 177 | } | ||
| 145 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence | 178 | // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence |
| 146 | // their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. | 179 | // their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4. |
| 147 | // So the values you see in docs might be multiplied by 4. | 180 | // So the values you see in docs might be multiplied by 4. |
| @@ -180,16 +213,32 @@ void GPU::CallMethod(const MethodCall& method_call) { | |||
| 180 | 213 | ||
| 181 | ASSERT(method_call.subchannel < bound_engines.size()); | 214 | ASSERT(method_call.subchannel < bound_engines.size()); |
| 182 | 215 | ||
| 183 | if (ExecuteMethodOnEngine(method_call)) { | 216 | if (ExecuteMethodOnEngine(method_call.method)) { |
| 184 | CallEngineMethod(method_call); | 217 | CallEngineMethod(method_call); |
| 185 | } else { | 218 | } else { |
| 186 | CallPullerMethod(method_call); | 219 | CallPullerMethod(method_call); |
| 187 | } | 220 | } |
| 188 | } | 221 | } |
| 189 | 222 | ||
| 190 | bool GPU::ExecuteMethodOnEngine(const MethodCall& method_call) { | 223 | void GPU::CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, |
| 191 | const auto method = static_cast<BufferMethods>(method_call.method); | 224 | u32 methods_pending) { |
| 192 | return method >= BufferMethods::NonPullerMethods; | 225 | LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method, subchannel); |
| 226 | |||
| 227 | ASSERT(subchannel < bound_engines.size()); | ||
| 228 | |||
| 229 | if (ExecuteMethodOnEngine(method)) { | ||
| 230 | CallEngineMultiMethod(method, subchannel, base_start, amount, methods_pending); | ||
| 231 | } else { | ||
| 232 | for (std::size_t i = 0; i < amount; i++) { | ||
| 233 | CallPullerMethod( | ||
| 234 | {method, base_start[i], subchannel, methods_pending - static_cast<u32>(i)}); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | bool GPU::ExecuteMethodOnEngine(u32 method) { | ||
| 240 | const auto buffer_method = static_cast<BufferMethods>(method); | ||
| 241 | return buffer_method >= BufferMethods::NonPullerMethods; | ||
| 193 | } | 242 | } |
| 194 | 243 | ||
| 195 | void GPU::CallPullerMethod(const MethodCall& method_call) { | 244 | void GPU::CallPullerMethod(const MethodCall& method_call) { |
| @@ -269,6 +318,31 @@ void GPU::CallEngineMethod(const MethodCall& method_call) { | |||
| 269 | } | 318 | } |
| 270 | } | 319 | } |
| 271 | 320 | ||
| 321 | void GPU::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, | ||
| 322 | u32 methods_pending) { | ||
| 323 | const EngineID engine = bound_engines[subchannel]; | ||
| 324 | |||
| 325 | switch (engine) { | ||
| 326 | case EngineID::FERMI_TWOD_A: | ||
| 327 | fermi_2d->CallMultiMethod(method, base_start, amount, methods_pending); | ||
| 328 | break; | ||
| 329 | case EngineID::MAXWELL_B: | ||
| 330 | maxwell_3d->CallMultiMethod(method, base_start, amount, methods_pending); | ||
| 331 | break; | ||
| 332 | case EngineID::KEPLER_COMPUTE_B: | ||
| 333 | kepler_compute->CallMultiMethod(method, base_start, amount, methods_pending); | ||
| 334 | break; | ||
| 335 | case EngineID::MAXWELL_DMA_COPY_A: | ||
| 336 | maxwell_dma->CallMultiMethod(method, base_start, amount, methods_pending); | ||
| 337 | break; | ||
| 338 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: | ||
| 339 | kepler_memory->CallMultiMethod(method, base_start, amount, methods_pending); | ||
| 340 | break; | ||
| 341 | default: | ||
| 342 | UNIMPLEMENTED_MSG("Unimplemented engine"); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 272 | void GPU::ProcessBindMethod(const MethodCall& method_call) { | 346 | void GPU::ProcessBindMethod(const MethodCall& method_call) { |
| 273 | // Bind the current subchannel to the desired engine id. | 347 | // Bind the current subchannel to the desired engine id. |
| 274 | LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, | 348 | LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 1a2d747be..dd51c95b7 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -155,7 +155,27 @@ public: | |||
| 155 | /// Calls a GPU method. | 155 | /// Calls a GPU method. |
| 156 | void CallMethod(const MethodCall& method_call); | 156 | void CallMethod(const MethodCall& method_call); |
| 157 | 157 | ||
| 158 | /// Calls a GPU multivalue method. | ||
| 159 | void CallMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, | ||
| 160 | u32 methods_pending); | ||
| 161 | |||
| 162 | /// Flush all current written commands into the host GPU for execution. | ||
| 158 | void FlushCommands(); | 163 | void FlushCommands(); |
| 164 | /// Synchronizes CPU writes with Host GPU memory. | ||
| 165 | void SyncGuestHost(); | ||
| 166 | /// Signal the ending of command list. | ||
| 167 | virtual void OnCommandListEnd(); | ||
| 168 | |||
| 169 | /// Request a host GPU memory flush from the CPU. | ||
| 170 | u64 RequestFlush(VAddr addr, std::size_t size); | ||
| 171 | |||
| 172 | /// Obtains current flush request fence id. | ||
| 173 | u64 CurrentFlushRequestFence() const { | ||
| 174 | return current_flush_fence.load(std::memory_order_relaxed); | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Tick pending requests within the GPU. | ||
| 178 | void TickWork(); | ||
| 159 | 179 | ||
| 160 | /// Returns a reference to the Maxwell3D GPU engine. | 180 | /// Returns a reference to the Maxwell3D GPU engine. |
| 161 | Engines::Maxwell3D& Maxwell3D(); | 181 | Engines::Maxwell3D& Maxwell3D(); |
| @@ -293,8 +313,12 @@ private: | |||
| 293 | /// Calls a GPU engine method. | 313 | /// Calls a GPU engine method. |
| 294 | void CallEngineMethod(const MethodCall& method_call); | 314 | void CallEngineMethod(const MethodCall& method_call); |
| 295 | 315 | ||
| 316 | /// Calls a GPU engine multivalue method. | ||
| 317 | void CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_start, u32 amount, | ||
| 318 | u32 methods_pending); | ||
| 319 | |||
| 296 | /// Determines where the method should be executed. | 320 | /// Determines where the method should be executed. |
| 297 | bool ExecuteMethodOnEngine(const MethodCall& method_call); | 321 | bool ExecuteMethodOnEngine(u32 method); |
| 298 | 322 | ||
| 299 | protected: | 323 | protected: |
| 300 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; | 324 | std::unique_ptr<Tegra::DmaPusher> dma_pusher; |
| @@ -325,6 +349,19 @@ private: | |||
| 325 | 349 | ||
| 326 | std::condition_variable sync_cv; | 350 | std::condition_variable sync_cv; |
| 327 | 351 | ||
| 352 | struct FlushRequest { | ||
| 353 | FlushRequest(u64 fence, VAddr addr, std::size_t size) | ||
| 354 | : fence{fence}, addr{addr}, size{size} {} | ||
| 355 | u64 fence; | ||
| 356 | VAddr addr; | ||
| 357 | std::size_t size; | ||
| 358 | }; | ||
| 359 | |||
| 360 | std::list<FlushRequest> flush_requests; | ||
| 361 | std::atomic<u64> current_flush_fence{}; | ||
| 362 | u64 last_flush_fence{}; | ||
| 363 | std::mutex flush_request_mutex; | ||
| 364 | |||
| 328 | const bool is_async; | 365 | const bool is_async; |
| 329 | }; | 366 | }; |
| 330 | 367 | ||
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 20e73a37e..53305ab43 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp | |||
| @@ -52,4 +52,8 @@ void GPUAsynch::WaitIdle() const { | |||
| 52 | gpu_thread.WaitIdle(); | 52 | gpu_thread.WaitIdle(); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | void GPUAsynch::OnCommandListEnd() { | ||
| 56 | gpu_thread.OnCommandListEnd(); | ||
| 57 | } | ||
| 58 | |||
| 55 | } // namespace VideoCommon | 59 | } // namespace VideoCommon |
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 03fd0eef0..517658612 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h | |||
| @@ -32,6 +32,8 @@ public: | |||
| 32 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | 32 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 33 | void WaitIdle() const override; | 33 | void WaitIdle() const override; |
| 34 | 34 | ||
| 35 | void OnCommandListEnd() override; | ||
| 36 | |||
| 35 | protected: | 37 | protected: |
| 36 | void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override; | 38 | void TriggerCpuInterrupt(u32 syncpoint_id, u32 value) const override; |
| 37 | 39 | ||
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 10cda686b..c3bb4fe06 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/frontend/emu_window.h" | 8 | #include "core/frontend/emu_window.h" |
| 9 | #include "core/settings.h" | ||
| 9 | #include "video_core/dma_pusher.h" | 10 | #include "video_core/dma_pusher.h" |
| 10 | #include "video_core/gpu.h" | 11 | #include "video_core/gpu.h" |
| 11 | #include "video_core/gpu_thread.h" | 12 | #include "video_core/gpu_thread.h" |
| @@ -14,8 +15,9 @@ | |||
| 14 | namespace VideoCommon::GPUThread { | 15 | namespace VideoCommon::GPUThread { |
| 15 | 16 | ||
| 16 | /// Runs the GPU thread | 17 | /// Runs the GPU thread |
| 17 | static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, | 18 | static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, |
| 18 | Tegra::DmaPusher& dma_pusher, SynchState& state) { | 19 | Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, |
| 20 | SynchState& state) { | ||
| 19 | MicroProfileOnThreadCreate("GpuThread"); | 21 | MicroProfileOnThreadCreate("GpuThread"); |
| 20 | 22 | ||
| 21 | // Wait for first GPU command before acquiring the window context | 23 | // Wait for first GPU command before acquiring the window context |
| @@ -37,10 +39,14 @@ static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::Graphic | |||
| 37 | dma_pusher.DispatchCalls(); | 39 | dma_pusher.DispatchCalls(); |
| 38 | } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { | 40 | } else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) { |
| 39 | renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); | 41 | renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); |
| 42 | } else if (const auto data = std::get_if<OnCommandListEndCommand>(&next.data)) { | ||
| 43 | renderer.Rasterizer().ReleaseFences(); | ||
| 44 | } else if (const auto data = std::get_if<GPUTickCommand>(&next.data)) { | ||
| 45 | system.GPU().TickWork(); | ||
| 40 | } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { | 46 | } else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) { |
| 41 | renderer.Rasterizer().FlushRegion(data->addr, data->size); | 47 | renderer.Rasterizer().FlushRegion(data->addr, data->size); |
| 42 | } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) { | 48 | } else if (const auto data = std::get_if<InvalidateRegionCommand>(&next.data)) { |
| 43 | renderer.Rasterizer().InvalidateRegion(data->addr, data->size); | 49 | renderer.Rasterizer().OnCPUWrite(data->addr, data->size); |
| 44 | } else if (std::holds_alternative<EndProcessingCommand>(next.data)) { | 50 | } else if (std::holds_alternative<EndProcessingCommand>(next.data)) { |
| 45 | return; | 51 | return; |
| 46 | } else { | 52 | } else { |
| @@ -65,8 +71,8 @@ ThreadManager::~ThreadManager() { | |||
| 65 | void ThreadManager::StartThread(VideoCore::RendererBase& renderer, | 71 | void ThreadManager::StartThread(VideoCore::RendererBase& renderer, |
| 66 | Core::Frontend::GraphicsContext& context, | 72 | Core::Frontend::GraphicsContext& context, |
| 67 | Tegra::DmaPusher& dma_pusher) { | 73 | Tegra::DmaPusher& dma_pusher) { |
| 68 | thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher), | 74 | thread = std::thread{RunThread, std::ref(system), std::ref(renderer), |
| 69 | std::ref(state)}; | 75 | std::ref(context), std::ref(dma_pusher), std::ref(state)}; |
| 70 | } | 76 | } |
| 71 | 77 | ||
| 72 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { | 78 | void ThreadManager::SubmitList(Tegra::CommandList&& entries) { |
| @@ -78,16 +84,29 @@ void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 78 | } | 84 | } |
| 79 | 85 | ||
| 80 | void ThreadManager::FlushRegion(VAddr addr, u64 size) { | 86 | void ThreadManager::FlushRegion(VAddr addr, u64 size) { |
| 81 | PushCommand(FlushRegionCommand(addr, size)); | 87 | if (!Settings::IsGPULevelHigh()) { |
| 88 | PushCommand(FlushRegionCommand(addr, size)); | ||
| 89 | return; | ||
| 90 | } | ||
| 91 | if (!Settings::IsGPULevelExtreme()) { | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | if (system.Renderer().Rasterizer().MustFlushRegion(addr, size)) { | ||
| 95 | auto& gpu = system.GPU(); | ||
| 96 | u64 fence = gpu.RequestFlush(addr, size); | ||
| 97 | PushCommand(GPUTickCommand()); | ||
| 98 | while (fence > gpu.CurrentFlushRequestFence()) { | ||
| 99 | } | ||
| 100 | } | ||
| 82 | } | 101 | } |
| 83 | 102 | ||
| 84 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { | 103 | void ThreadManager::InvalidateRegion(VAddr addr, u64 size) { |
| 85 | system.Renderer().Rasterizer().InvalidateRegion(addr, size); | 104 | system.Renderer().Rasterizer().OnCPUWrite(addr, size); |
| 86 | } | 105 | } |
| 87 | 106 | ||
| 88 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 107 | void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 89 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important | 108 | // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important |
| 90 | InvalidateRegion(addr, size); | 109 | system.Renderer().Rasterizer().OnCPUWrite(addr, size); |
| 91 | } | 110 | } |
| 92 | 111 | ||
| 93 | void ThreadManager::WaitIdle() const { | 112 | void ThreadManager::WaitIdle() const { |
| @@ -95,6 +114,10 @@ void ThreadManager::WaitIdle() const { | |||
| 95 | } | 114 | } |
| 96 | } | 115 | } |
| 97 | 116 | ||
| 117 | void ThreadManager::OnCommandListEnd() { | ||
| 118 | PushCommand(OnCommandListEndCommand()); | ||
| 119 | } | ||
| 120 | |||
| 98 | u64 ThreadManager::PushCommand(CommandData&& command_data) { | 121 | u64 ThreadManager::PushCommand(CommandData&& command_data) { |
| 99 | const u64 fence{++state.last_fence}; | 122 | const u64 fence{++state.last_fence}; |
| 100 | state.queue.Push(CommandDataContainer(std::move(command_data), fence)); | 123 | state.queue.Push(CommandDataContainer(std::move(command_data), fence)); |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index cd74ad330..5a28335d6 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -70,9 +70,16 @@ struct FlushAndInvalidateRegionCommand final { | |||
| 70 | u64 size; | 70 | u64 size; |
| 71 | }; | 71 | }; |
| 72 | 72 | ||
| 73 | /// Command called within the gpu, to schedule actions after a command list end | ||
| 74 | struct OnCommandListEndCommand final {}; | ||
| 75 | |||
| 76 | /// Command to make the gpu look into pending requests | ||
| 77 | struct GPUTickCommand final {}; | ||
| 78 | |||
| 73 | using CommandData = | 79 | using CommandData = |
| 74 | std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, | 80 | std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, |
| 75 | InvalidateRegionCommand, FlushAndInvalidateRegionCommand>; | 81 | InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand, |
| 82 | GPUTickCommand>; | ||
| 76 | 83 | ||
| 77 | struct CommandDataContainer { | 84 | struct CommandDataContainer { |
| 78 | CommandDataContainer() = default; | 85 | CommandDataContainer() = default; |
| @@ -122,6 +129,8 @@ public: | |||
| 122 | // Wait until the gpu thread is idle. | 129 | // Wait until the gpu thread is idle. |
| 123 | void WaitIdle() const; | 130 | void WaitIdle() const; |
| 124 | 131 | ||
| 132 | void OnCommandListEnd(); | ||
| 133 | |||
| 125 | private: | 134 | private: |
| 126 | /// Pushes a command to be executed by the GPU thread | 135 | /// Pushes a command to be executed by the GPU thread |
| 127 | u64 PushCommand(CommandData&& command_data); | 136 | u64 PushCommand(CommandData&& command_data); |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index fd49bc2a9..dbee9f634 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -51,11 +51,8 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { | |||
| 51 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; | 51 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; |
| 52 | 52 | ||
| 53 | MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); | 53 | MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); |
| 54 | ASSERT(system.CurrentProcess() | 54 | ASSERT( |
| 55 | ->PageTable() | 55 | system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess()); |
| 56 | .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared, | ||
| 57 | Kernel::Memory::MemoryAttribute::DeviceShared) | ||
| 58 | .IsSuccess()); | ||
| 59 | 56 | ||
| 60 | return gpu_addr; | 57 | return gpu_addr; |
| 61 | } | 58 | } |
| @@ -66,11 +63,8 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) | |||
| 66 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 63 | const u64 aligned_size{Common::AlignUp(size, page_size)}; |
| 67 | 64 | ||
| 68 | MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); | 65 | MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); |
| 69 | ASSERT(system.CurrentProcess() | 66 | ASSERT( |
| 70 | ->PageTable() | 67 | system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess()); |
| 71 | .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared, | ||
| 72 | Kernel::Memory::MemoryAttribute::DeviceShared) | ||
| 73 | .IsSuccess()); | ||
| 74 | return gpu_addr; | 68 | return gpu_addr; |
| 75 | } | 69 | } |
| 76 | 70 | ||
| @@ -87,9 +81,7 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | |||
| 87 | UnmapRange(gpu_addr, aligned_size); | 81 | UnmapRange(gpu_addr, aligned_size); |
| 88 | ASSERT(system.CurrentProcess() | 82 | ASSERT(system.CurrentProcess() |
| 89 | ->PageTable() | 83 | ->PageTable() |
| 90 | .SetMemoryAttribute(cpu_addr.value(), size, | 84 | .UnlockForDeviceAddressSpace(cpu_addr.value(), size) |
| 91 | Kernel::Memory::MemoryAttribute::DeviceShared, | ||
| 92 | Kernel::Memory::MemoryAttribute::None) | ||
| 93 | .IsSuccess()); | 85 | .IsSuccess()); |
| 94 | 86 | ||
| 95 | return gpu_addr; | 87 | return gpu_addr; |
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h index 5ea2b01f2..2f75f8801 100644 --- a/src/video_core/query_cache.h +++ b/src/video_core/query_cache.h | |||
| @@ -12,10 +12,12 @@ | |||
| 12 | #include <mutex> | 12 | #include <mutex> |
| 13 | #include <optional> | 13 | #include <optional> |
| 14 | #include <unordered_map> | 14 | #include <unordered_map> |
| 15 | #include <unordered_set> | ||
| 15 | #include <vector> | 16 | #include <vector> |
| 16 | 17 | ||
| 17 | #include "common/assert.h" | 18 | #include "common/assert.h" |
| 18 | #include "core/core.h" | 19 | #include "core/core.h" |
| 20 | #include "core/settings.h" | ||
| 19 | #include "video_core/engines/maxwell_3d.h" | 21 | #include "video_core/engines/maxwell_3d.h" |
| 20 | #include "video_core/gpu.h" | 22 | #include "video_core/gpu.h" |
| 21 | #include "video_core/memory_manager.h" | 23 | #include "video_core/memory_manager.h" |
| @@ -130,6 +132,9 @@ public: | |||
| 130 | } | 132 | } |
| 131 | 133 | ||
| 132 | query->BindCounter(Stream(type).Current(), timestamp); | 134 | query->BindCounter(Stream(type).Current(), timestamp); |
| 135 | if (Settings::values.use_asynchronous_gpu_emulation) { | ||
| 136 | AsyncFlushQuery(cpu_addr); | ||
| 137 | } | ||
| 133 | } | 138 | } |
| 134 | 139 | ||
| 135 | /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. | 140 | /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. |
| @@ -170,6 +175,37 @@ public: | |||
| 170 | return streams[static_cast<std::size_t>(type)]; | 175 | return streams[static_cast<std::size_t>(type)]; |
| 171 | } | 176 | } |
| 172 | 177 | ||
| 178 | void CommitAsyncFlushes() { | ||
| 179 | committed_flushes.push_back(uncommitted_flushes); | ||
| 180 | uncommitted_flushes.reset(); | ||
| 181 | } | ||
| 182 | |||
| 183 | bool HasUncommittedFlushes() const { | ||
| 184 | return uncommitted_flushes != nullptr; | ||
| 185 | } | ||
| 186 | |||
| 187 | bool ShouldWaitAsyncFlushes() const { | ||
| 188 | if (committed_flushes.empty()) { | ||
| 189 | return false; | ||
| 190 | } | ||
| 191 | return committed_flushes.front() != nullptr; | ||
| 192 | } | ||
| 193 | |||
| 194 | void PopAsyncFlushes() { | ||
| 195 | if (committed_flushes.empty()) { | ||
| 196 | return; | ||
| 197 | } | ||
| 198 | auto& flush_list = committed_flushes.front(); | ||
| 199 | if (!flush_list) { | ||
| 200 | committed_flushes.pop_front(); | ||
| 201 | return; | ||
| 202 | } | ||
| 203 | for (VAddr query_address : *flush_list) { | ||
| 204 | FlushAndRemoveRegion(query_address, 4); | ||
| 205 | } | ||
| 206 | committed_flushes.pop_front(); | ||
| 207 | } | ||
| 208 | |||
| 173 | protected: | 209 | protected: |
| 174 | std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; | 210 | std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; |
| 175 | 211 | ||
| @@ -224,6 +260,13 @@ private: | |||
| 224 | return found != std::end(contents) ? &*found : nullptr; | 260 | return found != std::end(contents) ? &*found : nullptr; |
| 225 | } | 261 | } |
| 226 | 262 | ||
| 263 | void AsyncFlushQuery(VAddr addr) { | ||
| 264 | if (!uncommitted_flushes) { | ||
| 265 | uncommitted_flushes = std::make_shared<std::unordered_set<VAddr>>(); | ||
| 266 | } | ||
| 267 | uncommitted_flushes->insert(addr); | ||
| 268 | } | ||
| 269 | |||
| 227 | static constexpr std::uintptr_t PAGE_SIZE = 4096; | 270 | static constexpr std::uintptr_t PAGE_SIZE = 4096; |
| 228 | static constexpr unsigned PAGE_SHIFT = 12; | 271 | static constexpr unsigned PAGE_SHIFT = 12; |
| 229 | 272 | ||
| @@ -235,6 +278,9 @@ private: | |||
| 235 | std::unordered_map<u64, std::vector<CachedQuery>> cached_queries; | 278 | std::unordered_map<u64, std::vector<CachedQuery>> cached_queries; |
| 236 | 279 | ||
| 237 | std::array<CounterStream, VideoCore::NumQueryTypes> streams; | 280 | std::array<CounterStream, VideoCore::NumQueryTypes> streams; |
| 281 | |||
| 282 | std::shared_ptr<std::unordered_set<VAddr>> uncommitted_flushes{}; | ||
| 283 | std::list<std::shared_ptr<std::unordered_set<VAddr>>> committed_flushes; | ||
| 238 | }; | 284 | }; |
| 239 | 285 | ||
| 240 | template <class QueryCache, class HostCounter> | 286 | template <class QueryCache, class HostCounter> |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 8ae5b9c4e..603f61952 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -49,15 +49,33 @@ public: | |||
| 49 | /// Records a GPU query and caches it | 49 | /// Records a GPU query and caches it |
| 50 | virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0; | 50 | virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0; |
| 51 | 51 | ||
| 52 | /// Signal a GPU based semaphore as a fence | ||
| 53 | virtual void SignalSemaphore(GPUVAddr addr, u32 value) = 0; | ||
| 54 | |||
| 55 | /// Signal a GPU based syncpoint as a fence | ||
| 56 | virtual void SignalSyncPoint(u32 value) = 0; | ||
| 57 | |||
| 58 | /// Release all pending fences. | ||
| 59 | virtual void ReleaseFences() = 0; | ||
| 60 | |||
| 52 | /// Notify rasterizer that all caches should be flushed to Switch memory | 61 | /// Notify rasterizer that all caches should be flushed to Switch memory |
| 53 | virtual void FlushAll() = 0; | 62 | virtual void FlushAll() = 0; |
| 54 | 63 | ||
| 55 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 64 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 56 | virtual void FlushRegion(VAddr addr, u64 size) = 0; | 65 | virtual void FlushRegion(VAddr addr, u64 size) = 0; |
| 57 | 66 | ||
| 67 | /// Check if the the specified memory area requires flushing to CPU Memory. | ||
| 68 | virtual bool MustFlushRegion(VAddr addr, u64 size) = 0; | ||
| 69 | |||
| 58 | /// Notify rasterizer that any caches of the specified region should be invalidated | 70 | /// Notify rasterizer that any caches of the specified region should be invalidated |
| 59 | virtual void InvalidateRegion(VAddr addr, u64 size) = 0; | 71 | virtual void InvalidateRegion(VAddr addr, u64 size) = 0; |
| 60 | 72 | ||
| 73 | /// Notify rasterizer that any caches of the specified region are desync with guest | ||
| 74 | virtual void OnCPUWrite(VAddr addr, u64 size) = 0; | ||
| 75 | |||
| 76 | /// Sync memory between guest and host. | ||
| 77 | virtual void SyncGuestHost() = 0; | ||
| 78 | |||
| 61 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 79 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 62 | /// and invalidated | 80 | /// and invalidated |
| 63 | virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; | 81 | virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index cb5792407..4efce0de7 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -52,7 +52,7 @@ Buffer OGLBufferCache::CreateBlock(VAddr cpu_addr, std::size_t size) { | |||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | void OGLBufferCache::WriteBarrier() { | 54 | void OGLBufferCache::WriteBarrier() { |
| 55 | glMemoryBarrier(GL_ALL_BARRIER_BITS); | 55 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | GLuint OGLBufferCache::ToHandle(const Buffer& buffer) { | 58 | GLuint OGLBufferCache::ToHandle(const Buffer& buffer) { |
| @@ -72,6 +72,7 @@ void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, s | |||
| 72 | void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, | 72 | void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size, |
| 73 | u8* data) { | 73 | u8* data) { |
| 74 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); | 74 | MICROPROFILE_SCOPE(OpenGL_Buffer_Download); |
| 75 | glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); | ||
| 75 | glGetNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset), | 76 | glGetNamedBufferSubData(buffer->GetHandle(), static_cast<GLintptr>(offset), |
| 76 | static_cast<GLsizeiptr>(size), data); | 77 | static_cast<GLsizeiptr>(size), data); |
| 77 | } | 78 | } |
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp new file mode 100644 index 000000000..99ddcb3f8 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | |||
| 7 | #include "video_core/renderer_opengl/gl_fence_manager.h" | ||
| 8 | |||
| 9 | namespace OpenGL { | ||
| 10 | |||
| 11 | GLInnerFence::GLInnerFence(u32 payload, bool is_stubbed) | ||
| 12 | : VideoCommon::FenceBase(payload, is_stubbed), sync_object{} {} | ||
| 13 | |||
| 14 | GLInnerFence::GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed) | ||
| 15 | : VideoCommon::FenceBase(address, payload, is_stubbed), sync_object{} {} | ||
| 16 | |||
| 17 | GLInnerFence::~GLInnerFence() = default; | ||
| 18 | |||
| 19 | void GLInnerFence::Queue() { | ||
| 20 | if (is_stubbed) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | ASSERT(sync_object.handle == 0); | ||
| 24 | sync_object.Create(); | ||
| 25 | } | ||
| 26 | |||
| 27 | bool GLInnerFence::IsSignaled() const { | ||
| 28 | if (is_stubbed) { | ||
| 29 | return true; | ||
| 30 | } | ||
| 31 | ASSERT(sync_object.handle != 0); | ||
| 32 | GLsizei length; | ||
| 33 | GLint sync_status; | ||
| 34 | glGetSynciv(sync_object.handle, GL_SYNC_STATUS, sizeof(GLint), &length, &sync_status); | ||
| 35 | return sync_status == GL_SIGNALED; | ||
| 36 | } | ||
| 37 | |||
| 38 | void GLInnerFence::Wait() { | ||
| 39 | if (is_stubbed) { | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | ASSERT(sync_object.handle != 0); | ||
| 43 | glClientWaitSync(sync_object.handle, 0, GL_TIMEOUT_IGNORED); | ||
| 44 | } | ||
| 45 | |||
| 46 | FenceManagerOpenGL::FenceManagerOpenGL(Core::System& system, | ||
| 47 | VideoCore::RasterizerInterface& rasterizer, | ||
| 48 | TextureCacheOpenGL& texture_cache, | ||
| 49 | OGLBufferCache& buffer_cache, QueryCache& query_cache) | ||
| 50 | : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache) {} | ||
| 51 | |||
| 52 | Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) { | ||
| 53 | return std::make_shared<GLInnerFence>(value, is_stubbed); | ||
| 54 | } | ||
| 55 | |||
| 56 | Fence FenceManagerOpenGL::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { | ||
| 57 | return std::make_shared<GLInnerFence>(addr, value, is_stubbed); | ||
| 58 | } | ||
| 59 | |||
| 60 | void FenceManagerOpenGL::QueueFence(Fence& fence) { | ||
| 61 | fence->Queue(); | ||
| 62 | } | ||
| 63 | |||
| 64 | bool FenceManagerOpenGL::IsFenceSignaled(Fence& fence) const { | ||
| 65 | return fence->IsSignaled(); | ||
| 66 | } | ||
| 67 | |||
| 68 | void FenceManagerOpenGL::WaitFence(Fence& fence) { | ||
| 69 | fence->Wait(); | ||
| 70 | } | ||
| 71 | |||
| 72 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h new file mode 100644 index 000000000..c917b3343 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_fence_manager.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <glad/glad.h> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "video_core/fence_manager.h" | ||
| 12 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | ||
| 13 | #include "video_core/renderer_opengl/gl_query_cache.h" | ||
| 14 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 15 | #include "video_core/renderer_opengl/gl_texture_cache.h" | ||
| 16 | |||
| 17 | namespace OpenGL { | ||
| 18 | |||
| 19 | class GLInnerFence : public VideoCommon::FenceBase { | ||
| 20 | public: | ||
| 21 | GLInnerFence(u32 payload, bool is_stubbed); | ||
| 22 | GLInnerFence(GPUVAddr address, u32 payload, bool is_stubbed); | ||
| 23 | ~GLInnerFence(); | ||
| 24 | |||
| 25 | void Queue(); | ||
| 26 | |||
| 27 | bool IsSignaled() const; | ||
| 28 | |||
| 29 | void Wait(); | ||
| 30 | |||
| 31 | private: | ||
| 32 | OGLSync sync_object; | ||
| 33 | }; | ||
| 34 | |||
| 35 | using Fence = std::shared_ptr<GLInnerFence>; | ||
| 36 | using GenericFenceManager = | ||
| 37 | VideoCommon::FenceManager<Fence, TextureCacheOpenGL, OGLBufferCache, QueryCache>; | ||
| 38 | |||
| 39 | class FenceManagerOpenGL final : public GenericFenceManager { | ||
| 40 | public: | ||
| 41 | FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | ||
| 42 | TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache, | ||
| 43 | QueryCache& query_cache); | ||
| 44 | |||
| 45 | protected: | ||
| 46 | Fence CreateFence(u32 value, bool is_stubbed) override; | ||
| 47 | Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override; | ||
| 48 | void QueueFence(Fence& fence) override; | ||
| 49 | bool IsFenceSignaled(Fence& fence) const override; | ||
| 50 | void WaitFence(Fence& fence) override; | ||
| 51 | }; | ||
| 52 | |||
| 53 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 175374f0d..6fe155bcc 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -99,9 +99,10 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind | |||
| 99 | ScreenInfo& info, GLShader::ProgramManager& program_manager, | 99 | ScreenInfo& info, GLShader::ProgramManager& program_manager, |
| 100 | StateTracker& state_tracker) | 100 | StateTracker& state_tracker) |
| 101 | : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device, state_tracker}, | 101 | : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device, state_tracker}, |
| 102 | shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system}, | 102 | shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, |
| 103 | screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker}, | 103 | buffer_cache{*this, system, device, STREAM_BUFFER_SIZE}, |
| 104 | buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { | 104 | fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system}, |
| 105 | screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker} { | ||
| 105 | CheckExtensions(); | 106 | CheckExtensions(); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| @@ -185,8 +186,12 @@ void RasterizerOpenGL::SetupVertexBuffer() { | |||
| 185 | const GPUVAddr start = vertex_array.StartAddress(); | 186 | const GPUVAddr start = vertex_array.StartAddress(); |
| 186 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); | 187 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); |
| 187 | 188 | ||
| 188 | ASSERT(end > start); | 189 | ASSERT(end >= start); |
| 189 | const u64 size = end - start + 1; | 190 | const u64 size = end - start; |
| 191 | if (size == 0) { | ||
| 192 | glBindVertexBuffer(static_cast<GLuint>(index), 0, 0, vertex_array.stride); | ||
| 193 | continue; | ||
| 194 | } | ||
| 190 | const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); | 195 | const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); |
| 191 | glBindVertexBuffer(static_cast<GLuint>(index), vertex_buffer, vertex_buffer_offset, | 196 | glBindVertexBuffer(static_cast<GLuint>(index), vertex_buffer, vertex_buffer_offset, |
| 192 | vertex_array.stride); | 197 | vertex_array.stride); |
| @@ -310,8 +315,8 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | |||
| 310 | const GPUVAddr start = regs.vertex_array[index].StartAddress(); | 315 | const GPUVAddr start = regs.vertex_array[index].StartAddress(); |
| 311 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); | 316 | const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); |
| 312 | 317 | ||
| 313 | ASSERT(end > start); | 318 | size += end - start; |
| 314 | size += end - start + 1; | 319 | ASSERT(end >= start); |
| 315 | } | 320 | } |
| 316 | 321 | ||
| 317 | return size; | 322 | return size; |
| @@ -599,6 +604,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 599 | EndTransformFeedback(); | 604 | EndTransformFeedback(); |
| 600 | 605 | ||
| 601 | ++num_queued_commands; | 606 | ++num_queued_commands; |
| 607 | |||
| 608 | system.GPU().TickWork(); | ||
| 602 | } | 609 | } |
| 603 | 610 | ||
| 604 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { | 611 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { |
| @@ -649,6 +656,13 @@ void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { | |||
| 649 | query_cache.FlushRegion(addr, size); | 656 | query_cache.FlushRegion(addr, size); |
| 650 | } | 657 | } |
| 651 | 658 | ||
| 659 | bool RasterizerOpenGL::MustFlushRegion(VAddr addr, u64 size) { | ||
| 660 | if (!Settings::IsGPULevelHigh()) { | ||
| 661 | return buffer_cache.MustFlushRegion(addr, size); | ||
| 662 | } | ||
| 663 | return texture_cache.MustFlushRegion(addr, size) || buffer_cache.MustFlushRegion(addr, size); | ||
| 664 | } | ||
| 665 | |||
| 652 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { | 666 | void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { |
| 653 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 667 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 654 | if (addr == 0 || size == 0) { | 668 | if (addr == 0 || size == 0) { |
| @@ -660,8 +674,52 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { | |||
| 660 | query_cache.InvalidateRegion(addr, size); | 674 | query_cache.InvalidateRegion(addr, size); |
| 661 | } | 675 | } |
| 662 | 676 | ||
| 677 | void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) { | ||
| 678 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | ||
| 679 | if (addr == 0 || size == 0) { | ||
| 680 | return; | ||
| 681 | } | ||
| 682 | texture_cache.OnCPUWrite(addr, size); | ||
| 683 | shader_cache.InvalidateRegion(addr, size); | ||
| 684 | buffer_cache.OnCPUWrite(addr, size); | ||
| 685 | query_cache.InvalidateRegion(addr, size); | ||
| 686 | } | ||
| 687 | |||
| 688 | void RasterizerOpenGL::SyncGuestHost() { | ||
| 689 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | ||
| 690 | texture_cache.SyncGuestHost(); | ||
| 691 | buffer_cache.SyncGuestHost(); | ||
| 692 | } | ||
| 693 | |||
| 694 | void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { | ||
| 695 | auto& gpu{system.GPU()}; | ||
| 696 | if (!gpu.IsAsync()) { | ||
| 697 | auto& memory_manager{gpu.MemoryManager()}; | ||
| 698 | memory_manager.Write<u32>(addr, value); | ||
| 699 | return; | ||
| 700 | } | ||
| 701 | fence_manager.SignalSemaphore(addr, value); | ||
| 702 | } | ||
| 703 | |||
| 704 | void RasterizerOpenGL::SignalSyncPoint(u32 value) { | ||
| 705 | auto& gpu{system.GPU()}; | ||
| 706 | if (!gpu.IsAsync()) { | ||
| 707 | gpu.IncrementSyncPoint(value); | ||
| 708 | return; | ||
| 709 | } | ||
| 710 | fence_manager.SignalSyncPoint(value); | ||
| 711 | } | ||
| 712 | |||
| 713 | void RasterizerOpenGL::ReleaseFences() { | ||
| 714 | auto& gpu{system.GPU()}; | ||
| 715 | if (!gpu.IsAsync()) { | ||
| 716 | return; | ||
| 717 | } | ||
| 718 | fence_manager.WaitPendingFences(); | ||
| 719 | } | ||
| 720 | |||
| 663 | void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 721 | void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 664 | if (Settings::values.use_accurate_gpu_emulation) { | 722 | if (Settings::IsGPULevelExtreme()) { |
| 665 | FlushRegion(addr, size); | 723 | FlushRegion(addr, size); |
| 666 | } | 724 | } |
| 667 | InvalidateRegion(addr, size); | 725 | InvalidateRegion(addr, size); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index caea174d2..ebd2173eb 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "video_core/rasterizer_interface.h" | 23 | #include "video_core/rasterizer_interface.h" |
| 24 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 24 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 25 | #include "video_core/renderer_opengl/gl_device.h" | 25 | #include "video_core/renderer_opengl/gl_device.h" |
| 26 | #include "video_core/renderer_opengl/gl_fence_manager.h" | ||
| 26 | #include "video_core/renderer_opengl/gl_framebuffer_cache.h" | 27 | #include "video_core/renderer_opengl/gl_framebuffer_cache.h" |
| 27 | #include "video_core/renderer_opengl/gl_query_cache.h" | 28 | #include "video_core/renderer_opengl/gl_query_cache.h" |
| 28 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 29 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| @@ -66,7 +67,13 @@ public: | |||
| 66 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; | 67 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; |
| 67 | void FlushAll() override; | 68 | void FlushAll() override; |
| 68 | void FlushRegion(VAddr addr, u64 size) override; | 69 | void FlushRegion(VAddr addr, u64 size) override; |
| 70 | bool MustFlushRegion(VAddr addr, u64 size) override; | ||
| 69 | void InvalidateRegion(VAddr addr, u64 size) override; | 71 | void InvalidateRegion(VAddr addr, u64 size) override; |
| 72 | void OnCPUWrite(VAddr addr, u64 size) override; | ||
| 73 | void SyncGuestHost() override; | ||
| 74 | void SignalSemaphore(GPUVAddr addr, u32 value) override; | ||
| 75 | void SignalSyncPoint(u32 value) override; | ||
| 76 | void ReleaseFences() override; | ||
| 70 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | 77 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 71 | void FlushCommands() override; | 78 | void FlushCommands() override; |
| 72 | void TickFrame() override; | 79 | void TickFrame() override; |
| @@ -222,6 +229,8 @@ private: | |||
| 222 | SamplerCacheOpenGL sampler_cache; | 229 | SamplerCacheOpenGL sampler_cache; |
| 223 | FramebufferCacheOpenGL framebuffer_cache; | 230 | FramebufferCacheOpenGL framebuffer_cache; |
| 224 | QueryCache query_cache; | 231 | QueryCache query_cache; |
| 232 | OGLBufferCache buffer_cache; | ||
| 233 | FenceManagerOpenGL fence_manager; | ||
| 225 | 234 | ||
| 226 | Core::System& system; | 235 | Core::System& system; |
| 227 | ScreenInfo& screen_info; | 236 | ScreenInfo& screen_info; |
| @@ -229,7 +238,6 @@ private: | |||
| 229 | StateTracker& state_tracker; | 238 | StateTracker& state_tracker; |
| 230 | 239 | ||
| 231 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; | 240 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; |
| 232 | OGLBufferCache buffer_cache; | ||
| 233 | 241 | ||
| 234 | GLint vertex_binding = 0; | 242 | GLint vertex_binding = 0; |
| 235 | 243 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6d2ff20f9..f63156b8d 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -448,7 +448,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 448 | 448 | ||
| 449 | // Look up shader in the cache based on address | 449 | // Look up shader in the cache based on address |
| 450 | const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; | 450 | const auto cpu_addr{memory_manager.GpuToCpuAddress(address)}; |
| 451 | Shader shader{cpu_addr ? TryGet(*cpu_addr) : nullptr}; | 451 | Shader shader{cpu_addr ? TryGet(*cpu_addr) : null_shader}; |
| 452 | if (shader) { | 452 | if (shader) { |
| 453 | return last_shaders[static_cast<std::size_t>(program)] = shader; | 453 | return last_shaders[static_cast<std::size_t>(program)] = shader; |
| 454 | } | 454 | } |
| @@ -477,7 +477,12 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 477 | const std::size_t size_in_bytes = code.size() * sizeof(u64); | 477 | const std::size_t size_in_bytes = code.size() * sizeof(u64); |
| 478 | shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes); | 478 | shader = CachedShader::CreateFromCache(params, found->second, size_in_bytes); |
| 479 | } | 479 | } |
| 480 | Register(shader); | 480 | |
| 481 | if (cpu_addr) { | ||
| 482 | Register(shader); | ||
| 483 | } else { | ||
| 484 | null_shader = shader; | ||
| 485 | } | ||
| 481 | 486 | ||
| 482 | return last_shaders[static_cast<std::size_t>(program)] = shader; | 487 | return last_shaders[static_cast<std::size_t>(program)] = shader; |
| 483 | } | 488 | } |
| @@ -486,7 +491,7 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { | |||
| 486 | auto& memory_manager{system.GPU().MemoryManager()}; | 491 | auto& memory_manager{system.GPU().MemoryManager()}; |
| 487 | const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)}; | 492 | const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)}; |
| 488 | 493 | ||
| 489 | auto kernel = cpu_addr ? TryGet(*cpu_addr) : nullptr; | 494 | auto kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel; |
| 490 | if (kernel) { | 495 | if (kernel) { |
| 491 | return kernel; | 496 | return kernel; |
| 492 | } | 497 | } |
| @@ -507,7 +512,11 @@ Shader ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) { | |||
| 507 | kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes); | 512 | kernel = CachedShader::CreateFromCache(params, found->second, size_in_bytes); |
| 508 | } | 513 | } |
| 509 | 514 | ||
| 510 | Register(kernel); | 515 | if (cpu_addr) { |
| 516 | Register(kernel); | ||
| 517 | } else { | ||
| 518 | null_kernel = kernel; | ||
| 519 | } | ||
| 511 | return kernel; | 520 | return kernel; |
| 512 | } | 521 | } |
| 513 | 522 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index c836df5bd..91690b470 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -125,6 +125,9 @@ private: | |||
| 125 | ShaderDiskCacheOpenGL disk_cache; | 125 | ShaderDiskCacheOpenGL disk_cache; |
| 126 | std::unordered_map<u64, PrecompiledShader> runtime_cache; | 126 | std::unordered_map<u64, PrecompiledShader> runtime_cache; |
| 127 | 127 | ||
| 128 | Shader null_shader{}; | ||
| 129 | Shader null_kernel{}; | ||
| 130 | |||
| 128 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | 131 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; |
| 129 | }; | 132 | }; |
| 130 | 133 | ||
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 9fe6bdbf9..9a950f4de 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h | |||
| @@ -129,7 +129,7 @@ struct alignas(32) FixedPipelineState { | |||
| 129 | auto& binding = bindings[index]; | 129 | auto& binding = bindings[index]; |
| 130 | binding.raw = 0; | 130 | binding.raw = 0; |
| 131 | binding.enabled.Assign(enabled ? 1 : 0); | 131 | binding.enabled.Assign(enabled ? 1 : 0); |
| 132 | binding.stride.Assign(stride); | 132 | binding.stride.Assign(static_cast<u16>(stride)); |
| 133 | binding_divisors[index] = divisor; | 133 | binding_divisors[index] = divisor; |
| 134 | } | 134 | } |
| 135 | 135 | ||
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp new file mode 100644 index 000000000..435c8c1b8 --- /dev/null +++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.cpp | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifdef HAS_NSIGHT_AFTERMATH | ||
| 6 | |||
| 7 | #include <mutex> | ||
| 8 | #include <string> | ||
| 9 | #include <string_view> | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include <fmt/format.h> | ||
| 14 | |||
| 15 | #define VK_NO_PROTOTYPES | ||
| 16 | #include <vulkan/vulkan.h> | ||
| 17 | |||
| 18 | #include <GFSDK_Aftermath.h> | ||
| 19 | #include <GFSDK_Aftermath_Defines.h> | ||
| 20 | #include <GFSDK_Aftermath_GpuCrashDump.h> | ||
| 21 | #include <GFSDK_Aftermath_GpuCrashDumpDecoding.h> | ||
| 22 | |||
| 23 | #include "common/common_paths.h" | ||
| 24 | #include "common/common_types.h" | ||
| 25 | #include "common/file_util.h" | ||
| 26 | #include "common/logging/log.h" | ||
| 27 | #include "common/scope_exit.h" | ||
| 28 | |||
| 29 | #include "video_core/renderer_vulkan/nsight_aftermath_tracker.h" | ||
| 30 | |||
| 31 | namespace Vulkan { | ||
| 32 | |||
| 33 | static constexpr char AFTERMATH_LIB_NAME[] = "GFSDK_Aftermath_Lib.x64.dll"; | ||
| 34 | |||
| 35 | NsightAftermathTracker::NsightAftermathTracker() = default; | ||
| 36 | |||
| 37 | NsightAftermathTracker::~NsightAftermathTracker() { | ||
| 38 | if (initialized) { | ||
| 39 | (void)GFSDK_Aftermath_DisableGpuCrashDumps(); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | bool NsightAftermathTracker::Initialize() { | ||
| 44 | if (!dl.Open(AFTERMATH_LIB_NAME)) { | ||
| 45 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath DLL"); | ||
| 46 | return false; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (!dl.GetSymbol("GFSDK_Aftermath_DisableGpuCrashDumps", | ||
| 50 | &GFSDK_Aftermath_DisableGpuCrashDumps) || | ||
| 51 | !dl.GetSymbol("GFSDK_Aftermath_EnableGpuCrashDumps", | ||
| 52 | &GFSDK_Aftermath_EnableGpuCrashDumps) || | ||
| 53 | !dl.GetSymbol("GFSDK_Aftermath_GetShaderDebugInfoIdentifier", | ||
| 54 | &GFSDK_Aftermath_GetShaderDebugInfoIdentifier) || | ||
| 55 | !dl.GetSymbol("GFSDK_Aftermath_GetShaderHashSpirv", &GFSDK_Aftermath_GetShaderHashSpirv) || | ||
| 56 | !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_CreateDecoder", | ||
| 57 | &GFSDK_Aftermath_GpuCrashDump_CreateDecoder) || | ||
| 58 | !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_DestroyDecoder", | ||
| 59 | &GFSDK_Aftermath_GpuCrashDump_DestroyDecoder) || | ||
| 60 | !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GenerateJSON", | ||
| 61 | &GFSDK_Aftermath_GpuCrashDump_GenerateJSON) || | ||
| 62 | !dl.GetSymbol("GFSDK_Aftermath_GpuCrashDump_GetJSON", | ||
| 63 | &GFSDK_Aftermath_GpuCrashDump_GetJSON)) { | ||
| 64 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); | ||
| 65 | return false; | ||
| 66 | } | ||
| 67 | |||
| 68 | dump_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "gpucrash"; | ||
| 69 | |||
| 70 | (void)FileUtil::DeleteDirRecursively(dump_dir); | ||
| 71 | if (!FileUtil::CreateDir(dump_dir)) { | ||
| 72 | LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); | ||
| 73 | return false; | ||
| 74 | } | ||
| 75 | |||
| 76 | if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( | ||
| 77 | GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, | ||
| 78 | GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, | ||
| 79 | ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { | ||
| 80 | LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); | ||
| 81 | return false; | ||
| 82 | } | ||
| 83 | |||
| 84 | LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir); | ||
| 85 | |||
| 86 | initialized = true; | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | |||
| 90 | void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const { | ||
| 91 | if (!initialized) { | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | |||
| 95 | std::vector<u32> spirv_copy = spirv; | ||
| 96 | GFSDK_Aftermath_SpirvCode shader; | ||
| 97 | shader.pData = spirv_copy.data(); | ||
| 98 | shader.size = static_cast<u32>(spirv_copy.size() * 4); | ||
| 99 | |||
| 100 | std::scoped_lock lock{mutex}; | ||
| 101 | |||
| 102 | GFSDK_Aftermath_ShaderHash hash; | ||
| 103 | if (!GFSDK_Aftermath_SUCCEED( | ||
| 104 | GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) { | ||
| 105 | LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module"); | ||
| 106 | return; | ||
| 107 | } | ||
| 108 | |||
| 109 | FileUtil::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb"); | ||
| 110 | if (!file.IsOpen()) { | ||
| 111 | LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash); | ||
| 112 | return; | ||
| 113 | } | ||
| 114 | if (file.WriteArray(spirv.data(), spirv.size()) != spirv.size()) { | ||
| 115 | LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash); | ||
| 116 | return; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump, | ||
| 121 | u32 gpu_crash_dump_size) { | ||
| 122 | std::scoped_lock lock{mutex}; | ||
| 123 | |||
| 124 | LOG_CRITICAL(Render_Vulkan, "called"); | ||
| 125 | |||
| 126 | GFSDK_Aftermath_GpuCrashDump_Decoder decoder; | ||
| 127 | if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_CreateDecoder( | ||
| 128 | GFSDK_Aftermath_Version_API, gpu_crash_dump, gpu_crash_dump_size, &decoder))) { | ||
| 129 | LOG_ERROR(Render_Vulkan, "Failed to create decoder"); | ||
| 130 | return; | ||
| 131 | } | ||
| 132 | SCOPE_EXIT({ GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder); }); | ||
| 133 | |||
| 134 | u32 json_size = 0; | ||
| 135 | if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( | ||
| 136 | decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, | ||
| 137 | GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr, | ||
| 138 | this, &json_size))) { | ||
| 139 | LOG_ERROR(Render_Vulkan, "Failed to generate JSON"); | ||
| 140 | return; | ||
| 141 | } | ||
| 142 | std::vector<char> json(json_size); | ||
| 143 | if (!GFSDK_Aftermath_SUCCEED( | ||
| 144 | GFSDK_Aftermath_GpuCrashDump_GetJSON(decoder, json_size, json.data()))) { | ||
| 145 | LOG_ERROR(Render_Vulkan, "Failed to query JSON"); | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | |||
| 149 | const std::string base_name = [this] { | ||
| 150 | const int id = dump_id++; | ||
| 151 | if (id == 0) { | ||
| 152 | return fmt::format("{}/crash.nv-gpudmp", dump_dir); | ||
| 153 | } else { | ||
| 154 | return fmt::format("{}/crash_{}.nv-gpudmp", dump_dir, id); | ||
| 155 | } | ||
| 156 | }(); | ||
| 157 | |||
| 158 | std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size); | ||
| 159 | if (FileUtil::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) { | ||
| 160 | LOG_ERROR(Render_Vulkan, "Failed to write dump file"); | ||
| 161 | return; | ||
| 162 | } | ||
| 163 | const std::string_view json_view(json.data(), json.size()); | ||
| 164 | if (FileUtil::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) { | ||
| 165 | LOG_ERROR(Render_Vulkan, "Failed to write JSON"); | ||
| 166 | return; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_info, | ||
| 171 | u32 shader_debug_info_size) { | ||
| 172 | std::scoped_lock lock{mutex}; | ||
| 173 | |||
| 174 | GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier; | ||
| 175 | if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GetShaderDebugInfoIdentifier( | ||
| 176 | GFSDK_Aftermath_Version_API, shader_debug_info, shader_debug_info_size, &identifier))) { | ||
| 177 | LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_GetShaderDebugInfoIdentifier failed"); | ||
| 178 | return; | ||
| 179 | } | ||
| 180 | |||
| 181 | const std::string path = | ||
| 182 | fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]); | ||
| 183 | FileUtil::IOFile file(path, "wb"); | ||
| 184 | if (!file.IsOpen()) { | ||
| 185 | LOG_ERROR(Render_Vulkan, "Failed to create file {}", path); | ||
| 186 | return; | ||
| 187 | } | ||
| 188 | if (file.WriteBytes(static_cast<const u8*>(shader_debug_info), shader_debug_info_size) != | ||
| 189 | shader_debug_info_size) { | ||
| 190 | LOG_ERROR(Render_Vulkan, "Failed to write file {}", path); | ||
| 191 | return; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | void NsightAftermathTracker::OnCrashDumpDescriptionCallback( | ||
| 196 | PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description) { | ||
| 197 | add_description(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "yuzu"); | ||
| 198 | } | ||
| 199 | |||
| 200 | void NsightAftermathTracker::GpuCrashDumpCallback(const void* gpu_crash_dump, | ||
| 201 | u32 gpu_crash_dump_size, void* user_data) { | ||
| 202 | static_cast<NsightAftermathTracker*>(user_data)->OnGpuCrashDumpCallback(gpu_crash_dump, | ||
| 203 | gpu_crash_dump_size); | ||
| 204 | } | ||
| 205 | |||
| 206 | void NsightAftermathTracker::ShaderDebugInfoCallback(const void* shader_debug_info, | ||
| 207 | u32 shader_debug_info_size, void* user_data) { | ||
| 208 | static_cast<NsightAftermathTracker*>(user_data)->OnShaderDebugInfoCallback( | ||
| 209 | shader_debug_info, shader_debug_info_size); | ||
| 210 | } | ||
| 211 | |||
| 212 | void NsightAftermathTracker::CrashDumpDescriptionCallback( | ||
| 213 | PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data) { | ||
| 214 | static_cast<NsightAftermathTracker*>(user_data)->OnCrashDumpDescriptionCallback( | ||
| 215 | add_description); | ||
| 216 | } | ||
| 217 | |||
| 218 | } // namespace Vulkan | ||
| 219 | |||
| 220 | #endif // HAS_NSIGHT_AFTERMATH | ||
diff --git a/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h new file mode 100644 index 000000000..afe7ae99e --- /dev/null +++ b/src/video_core/renderer_vulkan/nsight_aftermath_tracker.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <mutex> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #define VK_NO_PROTOTYPES | ||
| 12 | #include <vulkan/vulkan.h> | ||
| 13 | |||
| 14 | #ifdef HAS_NSIGHT_AFTERMATH | ||
| 15 | #include <GFSDK_Aftermath_Defines.h> | ||
| 16 | #include <GFSDK_Aftermath_GpuCrashDump.h> | ||
| 17 | #include <GFSDK_Aftermath_GpuCrashDumpDecoding.h> | ||
| 18 | #endif | ||
| 19 | |||
| 20 | #include "common/common_types.h" | ||
| 21 | #include "common/dynamic_library.h" | ||
| 22 | |||
| 23 | namespace Vulkan { | ||
| 24 | |||
| 25 | class NsightAftermathTracker { | ||
| 26 | public: | ||
| 27 | NsightAftermathTracker(); | ||
| 28 | ~NsightAftermathTracker(); | ||
| 29 | |||
| 30 | NsightAftermathTracker(const NsightAftermathTracker&) = delete; | ||
| 31 | NsightAftermathTracker& operator=(const NsightAftermathTracker&) = delete; | ||
| 32 | |||
| 33 | // Delete move semantics because Aftermath initialization uses a pointer to this. | ||
| 34 | NsightAftermathTracker(NsightAftermathTracker&&) = delete; | ||
| 35 | NsightAftermathTracker& operator=(NsightAftermathTracker&&) = delete; | ||
| 36 | |||
| 37 | bool Initialize(); | ||
| 38 | |||
| 39 | void SaveShader(const std::vector<u32>& spirv) const; | ||
| 40 | |||
| 41 | private: | ||
| 42 | #ifdef HAS_NSIGHT_AFTERMATH | ||
| 43 | static void GpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size, | ||
| 44 | void* user_data); | ||
| 45 | |||
| 46 | static void ShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size, | ||
| 47 | void* user_data); | ||
| 48 | |||
| 49 | static void CrashDumpDescriptionCallback( | ||
| 50 | PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description, void* user_data); | ||
| 51 | |||
| 52 | void OnGpuCrashDumpCallback(const void* gpu_crash_dump, u32 gpu_crash_dump_size); | ||
| 53 | |||
| 54 | void OnShaderDebugInfoCallback(const void* shader_debug_info, u32 shader_debug_info_size); | ||
| 55 | |||
| 56 | void OnCrashDumpDescriptionCallback( | ||
| 57 | PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription add_description); | ||
| 58 | |||
| 59 | mutable std::mutex mutex; | ||
| 60 | |||
| 61 | std::string dump_dir; | ||
| 62 | int dump_id = 0; | ||
| 63 | |||
| 64 | bool initialized = false; | ||
| 65 | |||
| 66 | Common::DynamicLibrary dl; | ||
| 67 | PFN_GFSDK_Aftermath_DisableGpuCrashDumps GFSDK_Aftermath_DisableGpuCrashDumps; | ||
| 68 | PFN_GFSDK_Aftermath_EnableGpuCrashDumps GFSDK_Aftermath_EnableGpuCrashDumps; | ||
| 69 | PFN_GFSDK_Aftermath_GetShaderDebugInfoIdentifier GFSDK_Aftermath_GetShaderDebugInfoIdentifier; | ||
| 70 | PFN_GFSDK_Aftermath_GetShaderHashSpirv GFSDK_Aftermath_GetShaderHashSpirv; | ||
| 71 | PFN_GFSDK_Aftermath_GpuCrashDump_CreateDecoder GFSDK_Aftermath_GpuCrashDump_CreateDecoder; | ||
| 72 | PFN_GFSDK_Aftermath_GpuCrashDump_DestroyDecoder GFSDK_Aftermath_GpuCrashDump_DestroyDecoder; | ||
| 73 | PFN_GFSDK_Aftermath_GpuCrashDump_GenerateJSON GFSDK_Aftermath_GpuCrashDump_GenerateJSON; | ||
| 74 | PFN_GFSDK_Aftermath_GpuCrashDump_GetJSON GFSDK_Aftermath_GpuCrashDump_GetJSON; | ||
| 75 | #endif | ||
| 76 | }; | ||
| 77 | |||
| 78 | #ifndef HAS_NSIGHT_AFTERMATH | ||
| 79 | inline NsightAftermathTracker::NsightAftermathTracker() = default; | ||
| 80 | inline NsightAftermathTracker::~NsightAftermathTracker() = default; | ||
| 81 | inline bool NsightAftermathTracker::Initialize() { | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | inline void NsightAftermathTracker::SaveShader(const std::vector<u32>&) const {} | ||
| 85 | #endif | ||
| 86 | |||
| 87 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 23beafa4f..52566bb79 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp | |||
| @@ -105,6 +105,8 @@ vk::DescriptorUpdateTemplateKHR VKComputePipeline::CreateDescriptorUpdateTemplat | |||
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const { | 107 | vk::ShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const { |
| 108 | device.SaveShader(code); | ||
| 109 | |||
| 108 | VkShaderModuleCreateInfo ci; | 110 | VkShaderModuleCreateInfo ci; |
| 109 | ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; | 111 | ci.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
| 110 | ci.pNext = nullptr; | 112 | ci.pNext = nullptr; |
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 52d29e49d..e90c76492 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <string_view> | 9 | #include <string_view> |
| 10 | #include <thread> | 10 | #include <thread> |
| 11 | #include <unordered_set> | 11 | #include <unordered_set> |
| 12 | #include <utility> | ||
| 12 | #include <vector> | 13 | #include <vector> |
| 13 | 14 | ||
| 14 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| @@ -167,6 +168,7 @@ bool VKDevice::Create() { | |||
| 167 | VkPhysicalDeviceFeatures2 features2; | 168 | VkPhysicalDeviceFeatures2 features2; |
| 168 | features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; | 169 | features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; |
| 169 | features2.pNext = nullptr; | 170 | features2.pNext = nullptr; |
| 171 | const void* first_next = &features2; | ||
| 170 | void** next = &features2.pNext; | 172 | void** next = &features2.pNext; |
| 171 | 173 | ||
| 172 | auto& features = features2.features; | 174 | auto& features = features2.features; |
| @@ -296,7 +298,19 @@ bool VKDevice::Create() { | |||
| 296 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); | 298 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); |
| 297 | } | 299 | } |
| 298 | 300 | ||
| 299 | logical = vk::Device::Create(physical, queue_cis, extensions, features2, dld); | 301 | VkDeviceDiagnosticsConfigCreateInfoNV diagnostics_nv; |
| 302 | if (nv_device_diagnostics_config) { | ||
| 303 | nsight_aftermath_tracker.Initialize(); | ||
| 304 | |||
| 305 | diagnostics_nv.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV; | ||
| 306 | diagnostics_nv.pNext = &features2; | ||
| 307 | diagnostics_nv.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV | | ||
| 308 | VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV | | ||
| 309 | VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV; | ||
| 310 | first_next = &diagnostics_nv; | ||
| 311 | } | ||
| 312 | |||
| 313 | logical = vk::Device::Create(physical, queue_cis, extensions, first_next, dld); | ||
| 300 | if (!logical) { | 314 | if (!logical) { |
| 301 | LOG_ERROR(Render_Vulkan, "Failed to create logical device"); | 315 | LOG_ERROR(Render_Vulkan, "Failed to create logical device"); |
| 302 | return false; | 316 | return false; |
| @@ -344,17 +358,12 @@ VkFormat VKDevice::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFla | |||
| 344 | void VKDevice::ReportLoss() const { | 358 | void VKDevice::ReportLoss() const { |
| 345 | LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); | 359 | LOG_CRITICAL(Render_Vulkan, "Device loss occured!"); |
| 346 | 360 | ||
| 347 | // Wait some time to let the log flush | 361 | // Wait for the log to flush and for Nsight Aftermath to dump the results |
| 348 | std::this_thread::sleep_for(std::chrono::seconds{1}); | 362 | std::this_thread::sleep_for(std::chrono::seconds{3}); |
| 349 | 363 | } | |
| 350 | if (!nv_device_diagnostic_checkpoints) { | ||
| 351 | return; | ||
| 352 | } | ||
| 353 | 364 | ||
| 354 | [[maybe_unused]] const std::vector data = graphics_queue.GetCheckpointDataNV(dld); | 365 | void VKDevice::SaveShader(const std::vector<u32>& spirv) const { |
| 355 | // Catch here in debug builds (or with optimizations disabled) the last graphics pipeline to be | 366 | nsight_aftermath_tracker.SaveShader(spirv); |
| 356 | // executed. It can be done on a debugger by evaluating the expression: | ||
| 357 | // *(VKGraphicsPipeline*)data[0] | ||
| 358 | } | 367 | } |
| 359 | 368 | ||
| 360 | bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { | 369 | bool VKDevice::IsOptimalAstcSupported(const VkPhysicalDeviceFeatures& features) const { |
| @@ -527,8 +536,8 @@ std::vector<const char*> VKDevice::LoadExtensions() { | |||
| 527 | Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, | 536 | Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, |
| 528 | false); | 537 | false); |
| 529 | if (Settings::values.renderer_debug) { | 538 | if (Settings::values.renderer_debug) { |
| 530 | Test(extension, nv_device_diagnostic_checkpoints, | 539 | Test(extension, nv_device_diagnostics_config, |
| 531 | VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); | 540 | VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); |
| 532 | } | 541 | } |
| 533 | } | 542 | } |
| 534 | 543 | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index bac58d385..c8640762d 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | 11 | ||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "video_core/renderer_vulkan/nsight_aftermath_tracker.h" | ||
| 13 | #include "video_core/renderer_vulkan/wrapper.h" | 14 | #include "video_core/renderer_vulkan/wrapper.h" |
| 14 | 15 | ||
| 15 | namespace Vulkan { | 16 | namespace Vulkan { |
| @@ -43,6 +44,9 @@ public: | |||
| 43 | /// Reports a device loss. | 44 | /// Reports a device loss. |
| 44 | void ReportLoss() const; | 45 | void ReportLoss() const; |
| 45 | 46 | ||
| 47 | /// Reports a shader to Nsight Aftermath. | ||
| 48 | void SaveShader(const std::vector<u32>& spirv) const; | ||
| 49 | |||
| 46 | /// Returns the dispatch loader with direct function pointers of the device. | 50 | /// Returns the dispatch loader with direct function pointers of the device. |
| 47 | const vk::DeviceDispatch& GetDispatchLoader() const { | 51 | const vk::DeviceDispatch& GetDispatchLoader() const { |
| 48 | return dld; | 52 | return dld; |
| @@ -168,11 +172,6 @@ public: | |||
| 168 | return ext_transform_feedback; | 172 | return ext_transform_feedback; |
| 169 | } | 173 | } |
| 170 | 174 | ||
| 171 | /// Returns true if the device supports VK_NV_device_diagnostic_checkpoints. | ||
| 172 | bool IsNvDeviceDiagnosticCheckpoints() const { | ||
| 173 | return nv_device_diagnostic_checkpoints; | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Returns the vendor name reported from Vulkan. | 175 | /// Returns the vendor name reported from Vulkan. |
| 177 | std::string_view GetVendorName() const { | 176 | std::string_view GetVendorName() const { |
| 178 | return vendor_name; | 177 | return vendor_name; |
| @@ -228,7 +227,7 @@ private: | |||
| 228 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. | 227 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. |
| 229 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 228 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 230 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. | 229 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. |
| 231 | bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints. | 230 | bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. |
| 232 | 231 | ||
| 233 | // Telemetry parameters | 232 | // Telemetry parameters |
| 234 | std::string vendor_name; ///< Device's driver name. | 233 | std::string vendor_name; ///< Device's driver name. |
| @@ -236,6 +235,9 @@ private: | |||
| 236 | 235 | ||
| 237 | /// Format properties dictionary. | 236 | /// Format properties dictionary. |
| 238 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; | 237 | std::unordered_map<VkFormat, VkFormatProperties> format_properties; |
| 238 | |||
| 239 | /// Nsight Aftermath GPU crash tracker | ||
| 240 | NsightAftermathTracker nsight_aftermath_tracker; | ||
| 239 | }; | 241 | }; |
| 240 | 242 | ||
| 241 | } // namespace Vulkan | 243 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp new file mode 100644 index 000000000..a02be5487 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <thread> | ||
| 7 | |||
| 8 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | ||
| 9 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 10 | #include "video_core/renderer_vulkan/vk_fence_manager.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_texture_cache.h" | ||
| 13 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 14 | |||
| 15 | namespace Vulkan { | ||
| 16 | |||
| 17 | InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload, bool is_stubbed) | ||
| 18 | : VideoCommon::FenceBase(payload, is_stubbed), device{device}, scheduler{scheduler} {} | ||
| 19 | |||
| 20 | InnerFence::InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address, | ||
| 21 | u32 payload, bool is_stubbed) | ||
| 22 | : VideoCommon::FenceBase(address, payload, is_stubbed), device{device}, scheduler{scheduler} {} | ||
| 23 | |||
| 24 | InnerFence::~InnerFence() = default; | ||
| 25 | |||
| 26 | void InnerFence::Queue() { | ||
| 27 | if (is_stubbed) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | ASSERT(!event); | ||
| 31 | |||
| 32 | event = device.GetLogical().CreateEvent(); | ||
| 33 | ticks = scheduler.Ticks(); | ||
| 34 | |||
| 35 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 36 | scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) { | ||
| 37 | cmdbuf.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); | ||
| 38 | }); | ||
| 39 | } | ||
| 40 | |||
| 41 | bool InnerFence::IsSignaled() const { | ||
| 42 | if (is_stubbed) { | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | ASSERT(event); | ||
| 46 | return IsEventSignalled(); | ||
| 47 | } | ||
| 48 | |||
| 49 | void InnerFence::Wait() { | ||
| 50 | if (is_stubbed) { | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | ASSERT(event); | ||
| 54 | |||
| 55 | if (ticks >= scheduler.Ticks()) { | ||
| 56 | scheduler.Flush(); | ||
| 57 | } | ||
| 58 | while (!IsEventSignalled()) { | ||
| 59 | std::this_thread::yield(); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | bool InnerFence::IsEventSignalled() const { | ||
| 64 | switch (const VkResult result = event.GetStatus()) { | ||
| 65 | case VK_EVENT_SET: | ||
| 66 | return true; | ||
| 67 | case VK_EVENT_RESET: | ||
| 68 | return false; | ||
| 69 | default: | ||
| 70 | throw vk::Exception(result); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | VKFenceManager::VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | ||
| 75 | const VKDevice& device, VKScheduler& scheduler, | ||
| 76 | VKTextureCache& texture_cache, VKBufferCache& buffer_cache, | ||
| 77 | VKQueryCache& query_cache) | ||
| 78 | : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache), | ||
| 79 | device{device}, scheduler{scheduler} {} | ||
| 80 | |||
| 81 | Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) { | ||
| 82 | return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed); | ||
| 83 | } | ||
| 84 | |||
| 85 | Fence VKFenceManager::CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) { | ||
| 86 | return std::make_shared<InnerFence>(device, scheduler, addr, value, is_stubbed); | ||
| 87 | } | ||
| 88 | |||
| 89 | void VKFenceManager::QueueFence(Fence& fence) { | ||
| 90 | fence->Queue(); | ||
| 91 | } | ||
| 92 | |||
| 93 | bool VKFenceManager::IsFenceSignaled(Fence& fence) const { | ||
| 94 | return fence->IsSignaled(); | ||
| 95 | } | ||
| 96 | |||
| 97 | void VKFenceManager::WaitFence(Fence& fence) { | ||
| 98 | fence->Wait(); | ||
| 99 | } | ||
| 100 | |||
| 101 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h new file mode 100644 index 000000000..04d07fe6a --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_fence_manager.h | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include "video_core/fence_manager.h" | ||
| 10 | #include "video_core/renderer_vulkan/wrapper.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace VideoCore { | ||
| 17 | class RasterizerInterface; | ||
| 18 | } | ||
| 19 | |||
| 20 | namespace Vulkan { | ||
| 21 | |||
| 22 | class VKBufferCache; | ||
| 23 | class VKDevice; | ||
| 24 | class VKQueryCache; | ||
| 25 | class VKScheduler; | ||
| 26 | class VKTextureCache; | ||
| 27 | |||
| 28 | class InnerFence : public VideoCommon::FenceBase { | ||
| 29 | public: | ||
| 30 | explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, u32 payload, | ||
| 31 | bool is_stubbed); | ||
| 32 | explicit InnerFence(const VKDevice& device, VKScheduler& scheduler, GPUVAddr address, | ||
| 33 | u32 payload, bool is_stubbed); | ||
| 34 | ~InnerFence(); | ||
| 35 | |||
| 36 | void Queue(); | ||
| 37 | |||
| 38 | bool IsSignaled() const; | ||
| 39 | |||
| 40 | void Wait(); | ||
| 41 | |||
| 42 | private: | ||
| 43 | bool IsEventSignalled() const; | ||
| 44 | |||
| 45 | const VKDevice& device; | ||
| 46 | VKScheduler& scheduler; | ||
| 47 | vk::Event event; | ||
| 48 | u64 ticks = 0; | ||
| 49 | }; | ||
| 50 | using Fence = std::shared_ptr<InnerFence>; | ||
| 51 | |||
| 52 | using GenericFenceManager = | ||
| 53 | VideoCommon::FenceManager<Fence, VKTextureCache, VKBufferCache, VKQueryCache>; | ||
| 54 | |||
| 55 | class VKFenceManager final : public GenericFenceManager { | ||
| 56 | public: | ||
| 57 | explicit VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | ||
| 58 | const VKDevice& device, VKScheduler& scheduler, | ||
| 59 | VKTextureCache& texture_cache, VKBufferCache& buffer_cache, | ||
| 60 | VKQueryCache& query_cache); | ||
| 61 | |||
| 62 | protected: | ||
| 63 | Fence CreateFence(u32 value, bool is_stubbed) override; | ||
| 64 | Fence CreateFence(GPUVAddr addr, u32 value, bool is_stubbed) override; | ||
| 65 | void QueueFence(Fence& fence) override; | ||
| 66 | bool IsFenceSignaled(Fence& fence) const override; | ||
| 67 | void WaitFence(Fence& fence) override; | ||
| 68 | |||
| 69 | private: | ||
| 70 | const VKDevice& device; | ||
| 71 | VKScheduler& scheduler; | ||
| 72 | }; | ||
| 73 | |||
| 74 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 343999cf5..8332b42aa 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | |||
| @@ -148,6 +148,8 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules( | |||
| 148 | continue; | 148 | continue; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | device.SaveShader(stage->code); | ||
| 152 | |||
| 151 | ci.codeSize = stage->code.size() * sizeof(u32); | 153 | ci.codeSize = stage->code.size() * sizeof(u32); |
| 152 | ci.pCode = stage->code.data(); | 154 | ci.pCode = stage->code.data(); |
| 153 | modules.push_back(device.GetLogical().CreateShaderModule(ci)); | 155 | modules.push_back(device.GetLogical().CreateShaderModule(ci)); |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 8fdc6400d..91b1b16a5 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -207,7 +207,7 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { | |||
| 207 | const GPUVAddr program_addr{GetShaderAddress(system, program)}; | 207 | const GPUVAddr program_addr{GetShaderAddress(system, program)}; |
| 208 | const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); | 208 | const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr); |
| 209 | ASSERT(cpu_addr); | 209 | ASSERT(cpu_addr); |
| 210 | auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr; | 210 | auto shader = cpu_addr ? TryGet(*cpu_addr) : null_shader; |
| 211 | if (!shader) { | 211 | if (!shader) { |
| 212 | const auto host_ptr{memory_manager.GetPointer(program_addr)}; | 212 | const auto host_ptr{memory_manager.GetPointer(program_addr)}; |
| 213 | 213 | ||
| @@ -218,7 +218,11 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() { | |||
| 218 | 218 | ||
| 219 | shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr, | 219 | shader = std::make_shared<CachedShader>(system, stage, program_addr, *cpu_addr, |
| 220 | std::move(code), stage_offset); | 220 | std::move(code), stage_offset); |
| 221 | Register(shader); | 221 | if (cpu_addr) { |
| 222 | Register(shader); | ||
| 223 | } else { | ||
| 224 | null_shader = shader; | ||
| 225 | } | ||
| 222 | } | 226 | } |
| 223 | shaders[index] = std::move(shader); | 227 | shaders[index] = std::move(shader); |
| 224 | } | 228 | } |
| @@ -261,7 +265,7 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach | |||
| 261 | const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); | 265 | const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr); |
| 262 | ASSERT(cpu_addr); | 266 | ASSERT(cpu_addr); |
| 263 | 267 | ||
| 264 | auto shader = cpu_addr ? TryGet(*cpu_addr) : nullptr; | 268 | auto shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel; |
| 265 | if (!shader) { | 269 | if (!shader) { |
| 266 | // No shader found - create a new one | 270 | // No shader found - create a new one |
| 267 | const auto host_ptr = memory_manager.GetPointer(program_addr); | 271 | const auto host_ptr = memory_manager.GetPointer(program_addr); |
| @@ -271,7 +275,11 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach | |||
| 271 | shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, | 275 | shader = std::make_shared<CachedShader>(system, Tegra::Engines::ShaderType::Compute, |
| 272 | program_addr, *cpu_addr, std::move(code), | 276 | program_addr, *cpu_addr, std::move(code), |
| 273 | kernel_main_offset); | 277 | kernel_main_offset); |
| 274 | Register(shader); | 278 | if (cpu_addr) { |
| 279 | Register(shader); | ||
| 280 | } else { | ||
| 281 | null_kernel = shader; | ||
| 282 | } | ||
| 275 | } | 283 | } |
| 276 | 284 | ||
| 277 | Specialization specialization; | 285 | Specialization specialization; |
| @@ -330,8 +338,10 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 330 | 338 | ||
| 331 | Specialization specialization; | 339 | Specialization specialization; |
| 332 | if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { | 340 | if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { |
| 333 | ASSERT(fixed_state.rasterizer.point_size != 0); | 341 | float point_size; |
| 334 | std::memcpy(&specialization.point_size, &fixed_state.rasterizer.point_size, sizeof(u32)); | 342 | std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float)); |
| 343 | specialization.point_size = point_size; | ||
| 344 | ASSERT(point_size != 0.0f); | ||
| 335 | } | 345 | } |
| 336 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { | 346 | for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { |
| 337 | specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type(); | 347 | specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type(); |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 7ccdb7083..602a0a340 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -182,6 +182,9 @@ private: | |||
| 182 | VKUpdateDescriptorQueue& update_descriptor_queue; | 182 | VKUpdateDescriptorQueue& update_descriptor_queue; |
| 183 | VKRenderPassCache& renderpass_cache; | 183 | VKRenderPassCache& renderpass_cache; |
| 184 | 184 | ||
| 185 | Shader null_shader{}; | ||
| 186 | Shader null_kernel{}; | ||
| 187 | |||
| 185 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | 188 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; |
| 186 | 189 | ||
| 187 | GraphicsPipelineCacheKey last_graphics_key; | 190 | GraphicsPipelineCacheKey last_graphics_key; |
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 0966c7ff7..813f7c162 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp | |||
| @@ -113,8 +113,19 @@ u64 HostCounter::BlockingQuery() const { | |||
| 113 | if (ticks >= cache.Scheduler().Ticks()) { | 113 | if (ticks >= cache.Scheduler().Ticks()) { |
| 114 | cache.Scheduler().Flush(); | 114 | cache.Scheduler().Flush(); |
| 115 | } | 115 | } |
| 116 | return cache.Device().GetLogical().GetQueryResult<u64>( | 116 | u64 data; |
| 117 | query.first, query.second, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); | 117 | const VkResult result = cache.Device().GetLogical().GetQueryResults( |
| 118 | query.first, query.second, 1, sizeof(data), &data, sizeof(data), | ||
| 119 | VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); | ||
| 120 | switch (result) { | ||
| 121 | case VK_SUCCESS: | ||
| 122 | return data; | ||
| 123 | case VK_ERROR_DEVICE_LOST: | ||
| 124 | cache.Device().ReportLoss(); | ||
| 125 | [[fallthrough]]; | ||
| 126 | default: | ||
| 127 | throw vk::Exception(result); | ||
| 128 | } | ||
| 118 | } | 129 | } |
| 119 | 130 | ||
| 120 | } // namespace Vulkan | 131 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 71007bbe8..68464e637 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "common/microprofile.h" | 17 | #include "common/microprofile.h" |
| 18 | #include "core/core.h" | 18 | #include "core/core.h" |
| 19 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 20 | #include "core/settings.h" | ||
| 20 | #include "video_core/engines/kepler_compute.h" | 21 | #include "video_core/engines/kepler_compute.h" |
| 21 | #include "video_core/engines/maxwell_3d.h" | 22 | #include "video_core/engines/maxwell_3d.h" |
| 22 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" | 23 | #include "video_core/renderer_vulkan/fixed_pipeline_state.h" |
| @@ -299,7 +300,9 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind | |||
| 299 | pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue, | 300 | pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue, |
| 300 | renderpass_cache), | 301 | renderpass_cache), |
| 301 | buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), | 302 | buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), |
| 302 | sampler_cache(device), query_cache(system, *this, device, scheduler) { | 303 | sampler_cache(device), |
| 304 | fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache), | ||
| 305 | query_cache(system, *this, device, scheduler) { | ||
| 303 | scheduler.SetQueryCache(query_cache); | 306 | scheduler.SetQueryCache(query_cache); |
| 304 | } | 307 | } |
| 305 | 308 | ||
| @@ -347,11 +350,6 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 347 | 350 | ||
| 348 | buffer_bindings.Bind(scheduler); | 351 | buffer_bindings.Bind(scheduler); |
| 349 | 352 | ||
| 350 | if (device.IsNvDeviceDiagnosticCheckpoints()) { | ||
| 351 | scheduler.Record( | ||
| 352 | [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(&pipeline); }); | ||
| 353 | } | ||
| 354 | |||
| 355 | BeginTransformFeedback(); | 353 | BeginTransformFeedback(); |
| 356 | 354 | ||
| 357 | const auto pipeline_layout = pipeline.GetLayout(); | 355 | const auto pipeline_layout = pipeline.GetLayout(); |
| @@ -365,6 +363,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 365 | }); | 363 | }); |
| 366 | 364 | ||
| 367 | EndTransformFeedback(); | 365 | EndTransformFeedback(); |
| 366 | |||
| 367 | system.GPU().TickWork(); | ||
| 368 | } | 368 | } |
| 369 | 369 | ||
| 370 | void RasterizerVulkan::Clear() { | 370 | void RasterizerVulkan::Clear() { |
| @@ -478,11 +478,6 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) { | |||
| 478 | TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | 478 | TransitionImages(image_views, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| 479 | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); | 479 | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); |
| 480 | 480 | ||
| 481 | if (device.IsNvDeviceDiagnosticCheckpoints()) { | ||
| 482 | scheduler.Record( | ||
| 483 | [&pipeline](vk::CommandBuffer cmdbuf) { cmdbuf.SetCheckpointNV(nullptr); }); | ||
| 484 | } | ||
| 485 | |||
| 486 | scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y, | 481 | scheduler.Record([grid_x = launch_desc.grid_dim_x, grid_y = launch_desc.grid_dim_y, |
| 487 | grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(), | 482 | grid_z = launch_desc.grid_dim_z, pipeline_handle = pipeline.GetHandle(), |
| 488 | layout = pipeline.GetLayout(), | 483 | layout = pipeline.GetLayout(), |
| @@ -514,6 +509,13 @@ void RasterizerVulkan::FlushRegion(VAddr addr, u64 size) { | |||
| 514 | query_cache.FlushRegion(addr, size); | 509 | query_cache.FlushRegion(addr, size); |
| 515 | } | 510 | } |
| 516 | 511 | ||
| 512 | bool RasterizerVulkan::MustFlushRegion(VAddr addr, u64 size) { | ||
| 513 | if (!Settings::IsGPULevelHigh()) { | ||
| 514 | return buffer_cache.MustFlushRegion(addr, size); | ||
| 515 | } | ||
| 516 | return texture_cache.MustFlushRegion(addr, size) || buffer_cache.MustFlushRegion(addr, size); | ||
| 517 | } | ||
| 518 | |||
| 517 | void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { | 519 | void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { |
| 518 | if (addr == 0 || size == 0) { | 520 | if (addr == 0 || size == 0) { |
| 519 | return; | 521 | return; |
| @@ -524,6 +526,47 @@ void RasterizerVulkan::InvalidateRegion(VAddr addr, u64 size) { | |||
| 524 | query_cache.InvalidateRegion(addr, size); | 526 | query_cache.InvalidateRegion(addr, size); |
| 525 | } | 527 | } |
| 526 | 528 | ||
| 529 | void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) { | ||
| 530 | if (addr == 0 || size == 0) { | ||
| 531 | return; | ||
| 532 | } | ||
| 533 | texture_cache.OnCPUWrite(addr, size); | ||
| 534 | pipeline_cache.InvalidateRegion(addr, size); | ||
| 535 | buffer_cache.OnCPUWrite(addr, size); | ||
| 536 | query_cache.InvalidateRegion(addr, size); | ||
| 537 | } | ||
| 538 | |||
| 539 | void RasterizerVulkan::SyncGuestHost() { | ||
| 540 | texture_cache.SyncGuestHost(); | ||
| 541 | buffer_cache.SyncGuestHost(); | ||
| 542 | } | ||
| 543 | |||
| 544 | void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) { | ||
| 545 | auto& gpu{system.GPU()}; | ||
| 546 | if (!gpu.IsAsync()) { | ||
| 547 | gpu.MemoryManager().Write<u32>(addr, value); | ||
| 548 | return; | ||
| 549 | } | ||
| 550 | fence_manager.SignalSemaphore(addr, value); | ||
| 551 | } | ||
| 552 | |||
| 553 | void RasterizerVulkan::SignalSyncPoint(u32 value) { | ||
| 554 | auto& gpu{system.GPU()}; | ||
| 555 | if (!gpu.IsAsync()) { | ||
| 556 | gpu.IncrementSyncPoint(value); | ||
| 557 | return; | ||
| 558 | } | ||
| 559 | fence_manager.SignalSyncPoint(value); | ||
| 560 | } | ||
| 561 | |||
| 562 | void RasterizerVulkan::ReleaseFences() { | ||
| 563 | auto& gpu{system.GPU()}; | ||
| 564 | if (!gpu.IsAsync()) { | ||
| 565 | return; | ||
| 566 | } | ||
| 567 | fence_manager.WaitPendingFences(); | ||
| 568 | } | ||
| 569 | |||
| 527 | void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { | 570 | void RasterizerVulkan::FlushAndInvalidateRegion(VAddr addr, u64 size) { |
| 528 | FlushRegion(addr, size); | 571 | FlushRegion(addr, size); |
| 529 | InvalidateRegion(addr, size); | 572 | InvalidateRegion(addr, size); |
| @@ -834,8 +877,12 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex | |||
| 834 | const GPUVAddr start{vertex_array.StartAddress()}; | 877 | const GPUVAddr start{vertex_array.StartAddress()}; |
| 835 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; | 878 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; |
| 836 | 879 | ||
| 837 | ASSERT(end > start); | 880 | ASSERT(end >= start); |
| 838 | const std::size_t size{end - start + 1}; | 881 | const std::size_t size{end - start}; |
| 882 | if (size == 0) { | ||
| 883 | buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); | ||
| 884 | continue; | ||
| 885 | } | ||
| 839 | const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); | 886 | const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); |
| 840 | buffer_bindings.AddVertexBinding(buffer, offset); | 887 | buffer_bindings.AddVertexBinding(buffer, offset); |
| 841 | } | 888 | } |
| @@ -990,8 +1037,7 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry, | |||
| 990 | const Tegra::Engines::ConstBufferInfo& buffer) { | 1037 | const Tegra::Engines::ConstBufferInfo& buffer) { |
| 991 | if (!buffer.enabled) { | 1038 | if (!buffer.enabled) { |
| 992 | // Set values to zero to unbind buffers | 1039 | // Set values to zero to unbind buffers |
| 993 | update_descriptor_queue.AddBuffer(buffer_cache.GetEmptyBuffer(sizeof(float)), 0, | 1040 | update_descriptor_queue.AddBuffer(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE); |
| 994 | sizeof(float)); | ||
| 995 | return; | 1041 | return; |
| 996 | } | 1042 | } |
| 997 | 1043 | ||
| @@ -1014,7 +1060,9 @@ void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAdd | |||
| 1014 | if (size == 0) { | 1060 | if (size == 0) { |
| 1015 | // Sometimes global memory pointers don't have a proper size. Upload a dummy entry | 1061 | // Sometimes global memory pointers don't have a proper size. Upload a dummy entry |
| 1016 | // because Vulkan doesn't like empty buffers. | 1062 | // because Vulkan doesn't like empty buffers. |
| 1017 | constexpr std::size_t dummy_size = 4; | 1063 | // Note: Do *not* use DefaultBuffer() here, storage buffers can be written breaking the |
| 1064 | // default buffer. | ||
| 1065 | static constexpr std::size_t dummy_size = 4; | ||
| 1018 | const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size); | 1066 | const auto buffer = buffer_cache.GetEmptyBuffer(dummy_size); |
| 1019 | update_descriptor_queue.AddBuffer(buffer, 0, dummy_size); | 1067 | update_descriptor_queue.AddBuffer(buffer, 0, dummy_size); |
| 1020 | return; | 1068 | return; |
| @@ -1179,7 +1227,7 @@ std::size_t RasterizerVulkan::CalculateVertexArraysSize() const { | |||
| 1179 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; | 1227 | const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; |
| 1180 | DEBUG_ASSERT(end >= start); | 1228 | DEBUG_ASSERT(end >= start); |
| 1181 | 1229 | ||
| 1182 | size += (end - start + 1) * regs.vertex_array[index].enable; | 1230 | size += (end - start) * regs.vertex_array[index].enable; |
| 1183 | } | 1231 | } |
| 1184 | return size; | 1232 | return size; |
| 1185 | } | 1233 | } |
| @@ -1226,4 +1274,29 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions) | |||
| 1226 | return renderpass_params; | 1274 | return renderpass_params; |
| 1227 | } | 1275 | } |
| 1228 | 1276 | ||
| 1277 | VkBuffer RasterizerVulkan::DefaultBuffer() { | ||
| 1278 | if (default_buffer) { | ||
| 1279 | return *default_buffer; | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | VkBufferCreateInfo ci; | ||
| 1283 | ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; | ||
| 1284 | ci.pNext = nullptr; | ||
| 1285 | ci.flags = 0; | ||
| 1286 | ci.size = DEFAULT_BUFFER_SIZE; | ||
| 1287 | ci.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | | ||
| 1288 | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; | ||
| 1289 | ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | ||
| 1290 | ci.queueFamilyIndexCount = 0; | ||
| 1291 | ci.pQueueFamilyIndices = nullptr; | ||
| 1292 | default_buffer = device.GetLogical().CreateBuffer(ci); | ||
| 1293 | default_buffer_commit = memory_manager.Commit(default_buffer, false); | ||
| 1294 | |||
| 1295 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 1296 | scheduler.Record([buffer = *default_buffer](vk::CommandBuffer cmdbuf) { | ||
| 1297 | cmdbuf.FillBuffer(buffer, 0, DEFAULT_BUFFER_SIZE, 0); | ||
| 1298 | }); | ||
| 1299 | return *default_buffer; | ||
| 1300 | } | ||
| 1301 | |||
| 1229 | } // namespace Vulkan | 1302 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index d9108f862..d41a7929e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" | 21 | #include "video_core/renderer_vulkan/vk_buffer_cache.h" |
| 22 | #include "video_core/renderer_vulkan/vk_compute_pass.h" | 22 | #include "video_core/renderer_vulkan/vk_compute_pass.h" |
| 23 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 23 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 24 | #include "video_core/renderer_vulkan/vk_fence_manager.h" | ||
| 24 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | 25 | #include "video_core/renderer_vulkan/vk_memory_manager.h" |
| 25 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 26 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 26 | #include "video_core/renderer_vulkan/vk_query_cache.h" | 27 | #include "video_core/renderer_vulkan/vk_query_cache.h" |
| @@ -118,7 +119,13 @@ public: | |||
| 118 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; | 119 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; |
| 119 | void FlushAll() override; | 120 | void FlushAll() override; |
| 120 | void FlushRegion(VAddr addr, u64 size) override; | 121 | void FlushRegion(VAddr addr, u64 size) override; |
| 122 | bool MustFlushRegion(VAddr addr, u64 size) override; | ||
| 121 | void InvalidateRegion(VAddr addr, u64 size) override; | 123 | void InvalidateRegion(VAddr addr, u64 size) override; |
| 124 | void OnCPUWrite(VAddr addr, u64 size) override; | ||
| 125 | void SyncGuestHost() override; | ||
| 126 | void SignalSemaphore(GPUVAddr addr, u32 value) override; | ||
| 127 | void SignalSyncPoint(u32 value) override; | ||
| 128 | void ReleaseFences() override; | ||
| 122 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; | 129 | void FlushAndInvalidateRegion(VAddr addr, u64 size) override; |
| 123 | void FlushCommands() override; | 130 | void FlushCommands() override; |
| 124 | void TickFrame() override; | 131 | void TickFrame() override; |
| @@ -148,6 +155,7 @@ private: | |||
| 148 | using Texceptions = std::bitset<Maxwell::NumRenderTargets + 1>; | 155 | using Texceptions = std::bitset<Maxwell::NumRenderTargets + 1>; |
| 149 | 156 | ||
| 150 | static constexpr std::size_t ZETA_TEXCEPTION_INDEX = 8; | 157 | static constexpr std::size_t ZETA_TEXCEPTION_INDEX = 8; |
| 158 | static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float); | ||
| 151 | 159 | ||
| 152 | void FlushWork(); | 160 | void FlushWork(); |
| 153 | 161 | ||
| @@ -240,6 +248,8 @@ private: | |||
| 240 | 248 | ||
| 241 | RenderPassParams GetRenderPassParams(Texceptions texceptions) const; | 249 | RenderPassParams GetRenderPassParams(Texceptions texceptions) const; |
| 242 | 250 | ||
| 251 | VkBuffer DefaultBuffer(); | ||
| 252 | |||
| 243 | Core::System& system; | 253 | Core::System& system; |
| 244 | Core::Frontend::EmuWindow& render_window; | 254 | Core::Frontend::EmuWindow& render_window; |
| 245 | VKScreenInfo& screen_info; | 255 | VKScreenInfo& screen_info; |
| @@ -261,8 +271,12 @@ private: | |||
| 261 | VKPipelineCache pipeline_cache; | 271 | VKPipelineCache pipeline_cache; |
| 262 | VKBufferCache buffer_cache; | 272 | VKBufferCache buffer_cache; |
| 263 | VKSamplerCache sampler_cache; | 273 | VKSamplerCache sampler_cache; |
| 274 | VKFenceManager fence_manager; | ||
| 264 | VKQueryCache query_cache; | 275 | VKQueryCache query_cache; |
| 265 | 276 | ||
| 277 | vk::Buffer default_buffer; | ||
| 278 | VKMemoryCommit default_buffer_commit; | ||
| 279 | |||
| 266 | std::array<View, Maxwell::NumRenderTargets> color_attachments; | 280 | std::array<View, Maxwell::NumRenderTargets> color_attachments; |
| 267 | View zeta_attachment; | 281 | View zeta_attachment; |
| 268 | 282 | ||
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 900f551b3..ae7ba3eb5 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -166,7 +166,15 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) { | |||
| 166 | submit_info.pCommandBuffers = current_cmdbuf.address(); | 166 | submit_info.pCommandBuffers = current_cmdbuf.address(); |
| 167 | submit_info.signalSemaphoreCount = semaphore ? 1 : 0; | 167 | submit_info.signalSemaphoreCount = semaphore ? 1 : 0; |
| 168 | submit_info.pSignalSemaphores = &semaphore; | 168 | submit_info.pSignalSemaphores = &semaphore; |
| 169 | device.GetGraphicsQueue().Submit(submit_info, *current_fence); | 169 | switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) { |
| 170 | case VK_SUCCESS: | ||
| 171 | break; | ||
| 172 | case VK_ERROR_DEVICE_LOST: | ||
| 173 | device.ReportLoss(); | ||
| 174 | [[fallthrough]]; | ||
| 175 | default: | ||
| 176 | vk::Check(result); | ||
| 177 | } | ||
| 170 | } | 178 | } |
| 171 | 179 | ||
| 172 | void VKScheduler::AllocateNewContext() { | 180 | void VKScheduler::AllocateNewContext() { |
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index abd836efe..45c180221 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp | |||
| @@ -78,7 +78,7 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v | |||
| 78 | ci.size = 1ULL << log2; | 78 | ci.size = 1ULL << log2; |
| 79 | ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | | 79 | ci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | |
| 80 | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | | 80 | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | |
| 81 | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; | 81 | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; |
| 82 | ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; | 82 | ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| 83 | ci.queueFamilyIndexCount = 0; | 83 | ci.queueFamilyIndexCount = 0; |
| 84 | ci.pQueueFamilyIndices = nullptr; | 84 | ci.pQueueFamilyIndices = nullptr; |
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp index 9b94dfff1..7f5bc1404 100644 --- a/src/video_core/renderer_vulkan/wrapper.cpp +++ b/src/video_core/renderer_vulkan/wrapper.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <exception> | 6 | #include <exception> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <optional> | 8 | #include <optional> |
| @@ -16,6 +17,23 @@ namespace Vulkan::vk { | |||
| 16 | 17 | ||
| 17 | namespace { | 18 | namespace { |
| 18 | 19 | ||
| 20 | void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) { | ||
| 21 | std::stable_sort(devices.begin(), devices.end(), [&](auto lhs, auto rhs) { | ||
| 22 | // This will call Vulkan more than needed, but these calls are cheap. | ||
| 23 | const auto lhs_properties = vk::PhysicalDevice(lhs, dld).GetProperties(); | ||
| 24 | const auto rhs_properties = vk::PhysicalDevice(rhs, dld).GetProperties(); | ||
| 25 | |||
| 26 | // Prefer discrete GPUs, Nvidia over AMD, AMD over Intel, Intel over the rest. | ||
| 27 | const bool preferred = | ||
| 28 | (lhs_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && | ||
| 29 | rhs_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) || | ||
| 30 | (lhs_properties.vendorID == 0x10DE && rhs_properties.vendorID != 0x10DE) || | ||
| 31 | (lhs_properties.vendorID == 0x1002 && rhs_properties.vendorID != 0x1002) || | ||
| 32 | (lhs_properties.vendorID == 0x8086 && rhs_properties.vendorID != 0x8086); | ||
| 33 | return !preferred; | ||
| 34 | }); | ||
| 35 | } | ||
| 36 | |||
| 19 | template <typename T> | 37 | template <typename T> |
| 20 | bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name, | 38 | bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name, |
| 21 | VkInstance instance = nullptr) noexcept { | 39 | VkInstance instance = nullptr) noexcept { |
| @@ -61,9 +79,9 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 61 | X(vkCmdPipelineBarrier); | 79 | X(vkCmdPipelineBarrier); |
| 62 | X(vkCmdPushConstants); | 80 | X(vkCmdPushConstants); |
| 63 | X(vkCmdSetBlendConstants); | 81 | X(vkCmdSetBlendConstants); |
| 64 | X(vkCmdSetCheckpointNV); | ||
| 65 | X(vkCmdSetDepthBias); | 82 | X(vkCmdSetDepthBias); |
| 66 | X(vkCmdSetDepthBounds); | 83 | X(vkCmdSetDepthBounds); |
| 84 | X(vkCmdSetEvent); | ||
| 67 | X(vkCmdSetScissor); | 85 | X(vkCmdSetScissor); |
| 68 | X(vkCmdSetStencilCompareMask); | 86 | X(vkCmdSetStencilCompareMask); |
| 69 | X(vkCmdSetStencilReference); | 87 | X(vkCmdSetStencilReference); |
| @@ -76,6 +94,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 76 | X(vkCreateDescriptorPool); | 94 | X(vkCreateDescriptorPool); |
| 77 | X(vkCreateDescriptorSetLayout); | 95 | X(vkCreateDescriptorSetLayout); |
| 78 | X(vkCreateDescriptorUpdateTemplateKHR); | 96 | X(vkCreateDescriptorUpdateTemplateKHR); |
| 97 | X(vkCreateEvent); | ||
| 79 | X(vkCreateFence); | 98 | X(vkCreateFence); |
| 80 | X(vkCreateFramebuffer); | 99 | X(vkCreateFramebuffer); |
| 81 | X(vkCreateGraphicsPipelines); | 100 | X(vkCreateGraphicsPipelines); |
| @@ -94,6 +113,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 94 | X(vkDestroyDescriptorPool); | 113 | X(vkDestroyDescriptorPool); |
| 95 | X(vkDestroyDescriptorSetLayout); | 114 | X(vkDestroyDescriptorSetLayout); |
| 96 | X(vkDestroyDescriptorUpdateTemplateKHR); | 115 | X(vkDestroyDescriptorUpdateTemplateKHR); |
| 116 | X(vkDestroyEvent); | ||
| 97 | X(vkDestroyFence); | 117 | X(vkDestroyFence); |
| 98 | X(vkDestroyFramebuffer); | 118 | X(vkDestroyFramebuffer); |
| 99 | X(vkDestroyImage); | 119 | X(vkDestroyImage); |
| @@ -113,10 +133,10 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 113 | X(vkFreeMemory); | 133 | X(vkFreeMemory); |
| 114 | X(vkGetBufferMemoryRequirements); | 134 | X(vkGetBufferMemoryRequirements); |
| 115 | X(vkGetDeviceQueue); | 135 | X(vkGetDeviceQueue); |
| 136 | X(vkGetEventStatus); | ||
| 116 | X(vkGetFenceStatus); | 137 | X(vkGetFenceStatus); |
| 117 | X(vkGetImageMemoryRequirements); | 138 | X(vkGetImageMemoryRequirements); |
| 118 | X(vkGetQueryPoolResults); | 139 | X(vkGetQueryPoolResults); |
| 119 | X(vkGetQueueCheckpointDataNV); | ||
| 120 | X(vkMapMemory); | 140 | X(vkMapMemory); |
| 121 | X(vkQueueSubmit); | 141 | X(vkQueueSubmit); |
| 122 | X(vkResetFences); | 142 | X(vkResetFences); |
| @@ -271,6 +291,10 @@ void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) | |||
| 271 | dld.vkFreeMemory(device, handle, nullptr); | 291 | dld.vkFreeMemory(device, handle, nullptr); |
| 272 | } | 292 | } |
| 273 | 293 | ||
| 294 | void Destroy(VkDevice device, VkEvent handle, const DeviceDispatch& dld) noexcept { | ||
| 295 | dld.vkDestroyEvent(device, handle, nullptr); | ||
| 296 | } | ||
| 297 | |||
| 274 | void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept { | 298 | void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept { |
| 275 | dld.vkDestroyFence(device, handle, nullptr); | 299 | dld.vkDestroyFence(device, handle, nullptr); |
| 276 | } | 300 | } |
| @@ -383,7 +407,8 @@ std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices( | |||
| 383 | if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) { | 407 | if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) { |
| 384 | return std::nullopt; | 408 | return std::nullopt; |
| 385 | } | 409 | } |
| 386 | return physical_devices; | 410 | SortPhysicalDevices(physical_devices, *dld); |
| 411 | return std::make_optional(std::move(physical_devices)); | ||
| 387 | } | 412 | } |
| 388 | 413 | ||
| 389 | DebugCallback Instance::TryCreateDebugCallback( | 414 | DebugCallback Instance::TryCreateDebugCallback( |
| @@ -409,17 +434,6 @@ DebugCallback Instance::TryCreateDebugCallback( | |||
| 409 | return DebugCallback(messenger, handle, *dld); | 434 | return DebugCallback(messenger, handle, *dld); |
| 410 | } | 435 | } |
| 411 | 436 | ||
| 412 | std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const { | ||
| 413 | if (!dld.vkGetQueueCheckpointDataNV) { | ||
| 414 | return {}; | ||
| 415 | } | ||
| 416 | u32 num; | ||
| 417 | dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr); | ||
| 418 | std::vector<VkCheckpointDataNV> checkpoints(num); | ||
| 419 | dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data()); | ||
| 420 | return checkpoints; | ||
| 421 | } | ||
| 422 | |||
| 423 | void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { | 437 | void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { |
| 424 | Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); | 438 | Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); |
| 425 | } | 439 | } |
| @@ -469,12 +483,11 @@ std::vector<VkImage> SwapchainKHR::GetImages() const { | |||
| 469 | } | 483 | } |
| 470 | 484 | ||
| 471 | Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, | 485 | Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, |
| 472 | Span<const char*> enabled_extensions, | 486 | Span<const char*> enabled_extensions, const void* next, |
| 473 | const VkPhysicalDeviceFeatures2& enabled_features, | ||
| 474 | DeviceDispatch& dld) noexcept { | 487 | DeviceDispatch& dld) noexcept { |
| 475 | VkDeviceCreateInfo ci; | 488 | VkDeviceCreateInfo ci; |
| 476 | ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | 489 | ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
| 477 | ci.pNext = &enabled_features; | 490 | ci.pNext = next; |
| 478 | ci.flags = 0; | 491 | ci.flags = 0; |
| 479 | ci.queueCreateInfoCount = queues_ci.size(); | 492 | ci.queueCreateInfoCount = queues_ci.size(); |
| 480 | ci.pQueueCreateInfos = queues_ci.data(); | 493 | ci.pQueueCreateInfos = queues_ci.data(); |
| @@ -613,6 +626,16 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons | |||
| 613 | return ShaderModule(object, handle, *dld); | 626 | return ShaderModule(object, handle, *dld); |
| 614 | } | 627 | } |
| 615 | 628 | ||
| 629 | Event Device::CreateEvent() const { | ||
| 630 | VkEventCreateInfo ci; | ||
| 631 | ci.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO; | ||
| 632 | ci.pNext = nullptr; | ||
| 633 | ci.flags = 0; | ||
| 634 | VkEvent object; | ||
| 635 | Check(dld->vkCreateEvent(handle, &ci, nullptr, &object)); | ||
| 636 | return Event(object, handle, *dld); | ||
| 637 | } | ||
| 638 | |||
| 616 | SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const { | 639 | SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const { |
| 617 | VkSwapchainKHR object; | 640 | VkSwapchainKHR object; |
| 618 | Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object)); | 641 | Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object)); |
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h index fb3657819..bda16a2cb 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/renderer_vulkan/wrapper.h | |||
| @@ -197,9 +197,9 @@ struct DeviceDispatch : public InstanceDispatch { | |||
| 197 | PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; | 197 | PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; |
| 198 | PFN_vkCmdPushConstants vkCmdPushConstants; | 198 | PFN_vkCmdPushConstants vkCmdPushConstants; |
| 199 | PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; | 199 | PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; |
| 200 | PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV; | ||
| 201 | PFN_vkCmdSetDepthBias vkCmdSetDepthBias; | 200 | PFN_vkCmdSetDepthBias vkCmdSetDepthBias; |
| 202 | PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; | 201 | PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; |
| 202 | PFN_vkCmdSetEvent vkCmdSetEvent; | ||
| 203 | PFN_vkCmdSetScissor vkCmdSetScissor; | 203 | PFN_vkCmdSetScissor vkCmdSetScissor; |
| 204 | PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; | 204 | PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; |
| 205 | PFN_vkCmdSetStencilReference vkCmdSetStencilReference; | 205 | PFN_vkCmdSetStencilReference vkCmdSetStencilReference; |
| @@ -212,6 +212,7 @@ struct DeviceDispatch : public InstanceDispatch { | |||
| 212 | PFN_vkCreateDescriptorPool vkCreateDescriptorPool; | 212 | PFN_vkCreateDescriptorPool vkCreateDescriptorPool; |
| 213 | PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; | 213 | PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; |
| 214 | PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; | 214 | PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; |
| 215 | PFN_vkCreateEvent vkCreateEvent; | ||
| 215 | PFN_vkCreateFence vkCreateFence; | 216 | PFN_vkCreateFence vkCreateFence; |
| 216 | PFN_vkCreateFramebuffer vkCreateFramebuffer; | 217 | PFN_vkCreateFramebuffer vkCreateFramebuffer; |
| 217 | PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; | 218 | PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; |
| @@ -230,6 +231,7 @@ struct DeviceDispatch : public InstanceDispatch { | |||
| 230 | PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; | 231 | PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; |
| 231 | PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; | 232 | PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; |
| 232 | PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; | 233 | PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; |
| 234 | PFN_vkDestroyEvent vkDestroyEvent; | ||
| 233 | PFN_vkDestroyFence vkDestroyFence; | 235 | PFN_vkDestroyFence vkDestroyFence; |
| 234 | PFN_vkDestroyFramebuffer vkDestroyFramebuffer; | 236 | PFN_vkDestroyFramebuffer vkDestroyFramebuffer; |
| 235 | PFN_vkDestroyImage vkDestroyImage; | 237 | PFN_vkDestroyImage vkDestroyImage; |
| @@ -249,10 +251,10 @@ struct DeviceDispatch : public InstanceDispatch { | |||
| 249 | PFN_vkFreeMemory vkFreeMemory; | 251 | PFN_vkFreeMemory vkFreeMemory; |
| 250 | PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; | 252 | PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; |
| 251 | PFN_vkGetDeviceQueue vkGetDeviceQueue; | 253 | PFN_vkGetDeviceQueue vkGetDeviceQueue; |
| 254 | PFN_vkGetEventStatus vkGetEventStatus; | ||
| 252 | PFN_vkGetFenceStatus vkGetFenceStatus; | 255 | PFN_vkGetFenceStatus vkGetFenceStatus; |
| 253 | PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; | 256 | PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; |
| 254 | PFN_vkGetQueryPoolResults vkGetQueryPoolResults; | 257 | PFN_vkGetQueryPoolResults vkGetQueryPoolResults; |
| 255 | PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV; | ||
| 256 | PFN_vkMapMemory vkMapMemory; | 258 | PFN_vkMapMemory vkMapMemory; |
| 257 | PFN_vkQueueSubmit vkQueueSubmit; | 259 | PFN_vkQueueSubmit vkQueueSubmit; |
| 258 | PFN_vkResetFences vkResetFences; | 260 | PFN_vkResetFences vkResetFences; |
| @@ -281,6 +283,7 @@ void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept; | |||
| 281 | void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; | 283 | void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; |
| 282 | void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; | 284 | void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; |
| 283 | void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; | 285 | void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; |
| 286 | void Destroy(VkDevice, VkEvent, const DeviceDispatch&) noexcept; | ||
| 284 | void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; | 287 | void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; |
| 285 | void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; | 288 | void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; |
| 286 | void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; | 289 | void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; |
| @@ -567,12 +570,8 @@ public: | |||
| 567 | /// Construct a queue handle. | 570 | /// Construct a queue handle. |
| 568 | constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} | 571 | constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} |
| 569 | 572 | ||
| 570 | /// Returns the checkpoint data. | 573 | VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept { |
| 571 | /// @note Returns an empty vector when the function pointer is not present. | 574 | return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence); |
| 572 | std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const; | ||
| 573 | |||
| 574 | void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const { | ||
| 575 | Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence)); | ||
| 576 | } | 575 | } |
| 577 | 576 | ||
| 578 | VkResult Present(const VkPresentInfoKHR& present_info) const noexcept { | 577 | VkResult Present(const VkPresentInfoKHR& present_info) const noexcept { |
| @@ -654,13 +653,21 @@ public: | |||
| 654 | std::vector<VkImage> GetImages() const; | 653 | std::vector<VkImage> GetImages() const; |
| 655 | }; | 654 | }; |
| 656 | 655 | ||
| 656 | class Event : public Handle<VkEvent, VkDevice, DeviceDispatch> { | ||
| 657 | using Handle<VkEvent, VkDevice, DeviceDispatch>::Handle; | ||
| 658 | |||
| 659 | public: | ||
| 660 | VkResult GetStatus() const noexcept { | ||
| 661 | return dld->vkGetEventStatus(owner, handle); | ||
| 662 | } | ||
| 663 | }; | ||
| 664 | |||
| 657 | class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { | 665 | class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { |
| 658 | using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle; | 666 | using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle; |
| 659 | 667 | ||
| 660 | public: | 668 | public: |
| 661 | static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, | 669 | static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, |
| 662 | Span<const char*> enabled_extensions, | 670 | Span<const char*> enabled_extensions, const void* next, |
| 663 | const VkPhysicalDeviceFeatures2& enabled_features, | ||
| 664 | DeviceDispatch& dld) noexcept; | 671 | DeviceDispatch& dld) noexcept; |
| 665 | 672 | ||
| 666 | Queue GetQueue(u32 family_index) const noexcept; | 673 | Queue GetQueue(u32 family_index) const noexcept; |
| @@ -702,6 +709,8 @@ public: | |||
| 702 | 709 | ||
| 703 | ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; | 710 | ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; |
| 704 | 711 | ||
| 712 | Event CreateEvent() const; | ||
| 713 | |||
| 705 | SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; | 714 | SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; |
| 706 | 715 | ||
| 707 | DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; | 716 | DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; |
| @@ -734,18 +743,11 @@ public: | |||
| 734 | dld->vkResetQueryPoolEXT(handle, query_pool, first, count); | 743 | dld->vkResetQueryPoolEXT(handle, query_pool, first, count); |
| 735 | } | 744 | } |
| 736 | 745 | ||
| 737 | void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, | 746 | VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, |
| 738 | void* data, VkDeviceSize stride, VkQueryResultFlags flags) const { | 747 | void* data, VkDeviceSize stride, VkQueryResultFlags flags) const |
| 739 | Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, | 748 | noexcept { |
| 740 | flags)); | 749 | return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, |
| 741 | } | 750 | flags); |
| 742 | |||
| 743 | template <typename T> | ||
| 744 | T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const { | ||
| 745 | static_assert(std::is_trivially_copyable_v<T>); | ||
| 746 | T value; | ||
| 747 | GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags); | ||
| 748 | return value; | ||
| 749 | } | 751 | } |
| 750 | }; | 752 | }; |
| 751 | 753 | ||
| @@ -920,10 +922,6 @@ public: | |||
| 920 | dld->vkCmdPushConstants(handle, layout, flags, offset, size, values); | 922 | dld->vkCmdPushConstants(handle, layout, flags, offset, size, values); |
| 921 | } | 923 | } |
| 922 | 924 | ||
| 923 | void SetCheckpointNV(const void* checkpoint_marker) const noexcept { | ||
| 924 | dld->vkCmdSetCheckpointNV(handle, checkpoint_marker); | ||
| 925 | } | ||
| 926 | |||
| 927 | void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept { | 925 | void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept { |
| 928 | dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data()); | 926 | dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data()); |
| 929 | } | 927 | } |
| @@ -956,6 +954,10 @@ public: | |||
| 956 | dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds); | 954 | dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds); |
| 957 | } | 955 | } |
| 958 | 956 | ||
| 957 | void SetEvent(VkEvent event, VkPipelineStageFlags stage_flags) const noexcept { | ||
| 958 | dld->vkCmdSetEvent(handle, event, stage_flags); | ||
| 959 | } | ||
| 960 | |||
| 959 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, | 961 | void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, |
| 960 | const VkDeviceSize* offsets, | 962 | const VkDeviceSize* offsets, |
| 961 | const VkDeviceSize* sizes) const noexcept { | 963 | const VkDeviceSize* sizes) const noexcept { |
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index 6d313963a..e00a3fb70 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp | |||
| @@ -587,8 +587,6 @@ bool TryQuery(CFGRebuildState& state) { | |||
| 587 | return true; | 587 | return true; |
| 588 | } | 588 | } |
| 589 | 589 | ||
| 590 | } // Anonymous namespace | ||
| 591 | |||
| 592 | void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) { | 590 | void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) { |
| 593 | const auto get_expr = ([&](const Condition& cond) -> Expr { | 591 | const auto get_expr = ([&](const Condition& cond) -> Expr { |
| 594 | Expr result{}; | 592 | Expr result{}; |
| @@ -655,6 +653,8 @@ void DecompileShader(CFGRebuildState& state) { | |||
| 655 | state.manager->Decompile(); | 653 | state.manager->Decompile(); |
| 656 | } | 654 | } |
| 657 | 655 | ||
| 656 | } // Anonymous namespace | ||
| 657 | |||
| 658 | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address, | 658 | std::unique_ptr<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, u32 start_address, |
| 659 | const CompilerSettings& settings, | 659 | const CompilerSettings& settings, |
| 660 | Registry& registry) { | 660 | Registry& registry) { |
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp index ee7d9a29d..a276aee44 100644 --- a/src/video_core/shader/decode/arithmetic_half.cpp +++ b/src/video_core/shader/decode/arithmetic_half.cpp | |||
| @@ -19,22 +19,46 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { | |||
| 19 | const Instruction instr = {program_code[pc]}; | 19 | const Instruction instr = {program_code[pc]}; |
| 20 | const auto opcode = OpCode::Decode(instr); | 20 | const auto opcode = OpCode::Decode(instr); |
| 21 | 21 | ||
| 22 | if (opcode->get().GetId() == OpCode::Id::HADD2_C || | 22 | bool negate_a = false; |
| 23 | opcode->get().GetId() == OpCode::Id::HADD2_R) { | 23 | bool negate_b = false; |
| 24 | bool absolute_a = false; | ||
| 25 | bool absolute_b = false; | ||
| 26 | |||
| 27 | switch (opcode->get().GetId()) { | ||
| 28 | case OpCode::Id::HADD2_R: | ||
| 24 | if (instr.alu_half.ftz == 0) { | 29 | if (instr.alu_half.ftz == 0) { |
| 25 | LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); | 30 | LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); |
| 26 | } | 31 | } |
| 32 | negate_a = ((instr.value >> 43) & 1) != 0; | ||
| 33 | negate_b = ((instr.value >> 31) & 1) != 0; | ||
| 34 | absolute_a = ((instr.value >> 44) & 1) != 0; | ||
| 35 | absolute_b = ((instr.value >> 30) & 1) != 0; | ||
| 36 | break; | ||
| 37 | case OpCode::Id::HADD2_C: | ||
| 38 | if (instr.alu_half.ftz == 0) { | ||
| 39 | LOG_DEBUG(HW_GPU, "{} without FTZ is not implemented", opcode->get().GetName()); | ||
| 40 | } | ||
| 41 | negate_a = ((instr.value >> 43) & 1) != 0; | ||
| 42 | negate_b = ((instr.value >> 56) & 1) != 0; | ||
| 43 | absolute_a = ((instr.value >> 44) & 1) != 0; | ||
| 44 | absolute_b = ((instr.value >> 54) & 1) != 0; | ||
| 45 | break; | ||
| 46 | case OpCode::Id::HMUL2_R: | ||
| 47 | negate_a = ((instr.value >> 43) & 1) != 0; | ||
| 48 | absolute_a = ((instr.value >> 44) & 1) != 0; | ||
| 49 | absolute_b = ((instr.value >> 30) & 1) != 0; | ||
| 50 | break; | ||
| 51 | case OpCode::Id::HMUL2_C: | ||
| 52 | negate_b = ((instr.value >> 31) & 1) != 0; | ||
| 53 | absolute_a = ((instr.value >> 44) & 1) != 0; | ||
| 54 | absolute_b = ((instr.value >> 54) & 1) != 0; | ||
| 55 | break; | ||
| 27 | } | 56 | } |
| 28 | 57 | ||
| 29 | const bool negate_a = | ||
| 30 | opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; | ||
| 31 | const bool negate_b = | ||
| 32 | opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; | ||
| 33 | |||
| 34 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a); | 58 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a); |
| 35 | op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a); | 59 | op_a = GetOperandAbsNegHalf(op_a, absolute_a, negate_a); |
| 36 | 60 | ||
| 37 | auto [type_b, op_b] = [&]() -> std::tuple<HalfType, Node> { | 61 | auto [type_b, op_b] = [this, instr, opcode]() -> std::pair<HalfType, Node> { |
| 38 | switch (opcode->get().GetId()) { | 62 | switch (opcode->get().GetId()) { |
| 39 | case OpCode::Id::HADD2_C: | 63 | case OpCode::Id::HADD2_C: |
| 40 | case OpCode::Id::HMUL2_C: | 64 | case OpCode::Id::HMUL2_C: |
| @@ -48,17 +72,16 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { | |||
| 48 | } | 72 | } |
| 49 | }(); | 73 | }(); |
| 50 | op_b = UnpackHalfFloat(op_b, type_b); | 74 | op_b = UnpackHalfFloat(op_b, type_b); |
| 51 | // redeclaration to avoid a bug in clang with reusing local bindings in lambdas | 75 | op_b = GetOperandAbsNegHalf(op_b, absolute_b, negate_b); |
| 52 | Node op_b_alt = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b); | ||
| 53 | 76 | ||
| 54 | Node value = [&]() { | 77 | Node value = [this, opcode, op_a, op_b = op_b] { |
| 55 | switch (opcode->get().GetId()) { | 78 | switch (opcode->get().GetId()) { |
| 56 | case OpCode::Id::HADD2_C: | 79 | case OpCode::Id::HADD2_C: |
| 57 | case OpCode::Id::HADD2_R: | 80 | case OpCode::Id::HADD2_R: |
| 58 | return Operation(OperationCode::HAdd, PRECISE, op_a, op_b_alt); | 81 | return Operation(OperationCode::HAdd, PRECISE, op_a, op_b); |
| 59 | case OpCode::Id::HMUL2_C: | 82 | case OpCode::Id::HMUL2_C: |
| 60 | case OpCode::Id::HMUL2_R: | 83 | case OpCode::Id::HMUL2_R: |
| 61 | return Operation(OperationCode::HMul, PRECISE, op_a, op_b_alt); | 84 | return Operation(OperationCode::HMul, PRECISE, op_a, op_b); |
| 62 | default: | 85 | default: |
| 63 | UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); | 86 | UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); |
| 64 | return Immediate(0); | 87 | return Immediate(0); |
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp index 0f4c3103a..9af8c606d 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp | |||
| @@ -249,8 +249,8 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) { | |||
| 249 | } | 249 | } |
| 250 | case OpCode::Id::LEA_IMM: { | 250 | case OpCode::Id::LEA_IMM: { |
| 251 | const bool neg = instr.lea.imm.neg != 0; | 251 | const bool neg = instr.lea.imm.neg != 0; |
| 252 | return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)), | 252 | return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), |
| 253 | GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true), | 253 | Immediate(static_cast<u32>(instr.lea.imm.entry_a)), |
| 254 | Immediate(static_cast<u32>(instr.lea.imm.entry_b))}; | 254 | Immediate(static_cast<u32>(instr.lea.imm.entry_b))}; |
| 255 | } | 255 | } |
| 256 | case OpCode::Id::LEA_RZ: { | 256 | case OpCode::Id::LEA_RZ: { |
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h index c5ab21f56..79e10ffbb 100644 --- a/src/video_core/texture_cache/surface_base.h +++ b/src/video_core/texture_cache/surface_base.h | |||
| @@ -192,6 +192,22 @@ public: | |||
| 192 | index = index_; | 192 | index = index_; |
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | void SetMemoryMarked(bool is_memory_marked_) { | ||
| 196 | is_memory_marked = is_memory_marked_; | ||
| 197 | } | ||
| 198 | |||
| 199 | bool IsMemoryMarked() const { | ||
| 200 | return is_memory_marked; | ||
| 201 | } | ||
| 202 | |||
| 203 | void SetSyncPending(bool is_sync_pending_) { | ||
| 204 | is_sync_pending = is_sync_pending_; | ||
| 205 | } | ||
| 206 | |||
| 207 | bool IsSyncPending() const { | ||
| 208 | return is_sync_pending; | ||
| 209 | } | ||
| 210 | |||
| 195 | void MarkAsPicked(bool is_picked_) { | 211 | void MarkAsPicked(bool is_picked_) { |
| 196 | is_picked = is_picked_; | 212 | is_picked = is_picked_; |
| 197 | } | 213 | } |
| @@ -303,6 +319,8 @@ private: | |||
| 303 | bool is_target{}; | 319 | bool is_target{}; |
| 304 | bool is_registered{}; | 320 | bool is_registered{}; |
| 305 | bool is_picked{}; | 321 | bool is_picked{}; |
| 322 | bool is_memory_marked{}; | ||
| 323 | bool is_sync_pending{}; | ||
| 306 | u32 index{NO_RT}; | 324 | u32 index{NO_RT}; |
| 307 | u64 modification_tick{}; | 325 | u64 modification_tick{}; |
| 308 | }; | 326 | }; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 69ca08fd1..cf6bd005a 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <algorithm> | 7 | #include <algorithm> |
| 8 | #include <array> | 8 | #include <array> |
| 9 | #include <list> | ||
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <mutex> | 11 | #include <mutex> |
| 11 | #include <set> | 12 | #include <set> |
| @@ -62,6 +63,30 @@ public: | |||
| 62 | } | 63 | } |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 66 | void OnCPUWrite(VAddr addr, std::size_t size) { | ||
| 67 | std::lock_guard lock{mutex}; | ||
| 68 | |||
| 69 | for (const auto& surface : GetSurfacesInRegion(addr, size)) { | ||
| 70 | if (surface->IsMemoryMarked()) { | ||
| 71 | UnmarkMemory(surface); | ||
| 72 | surface->SetSyncPending(true); | ||
| 73 | marked_for_unregister.emplace_back(surface); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | void SyncGuestHost() { | ||
| 79 | std::lock_guard lock{mutex}; | ||
| 80 | |||
| 81 | for (const auto& surface : marked_for_unregister) { | ||
| 82 | if (surface->IsRegistered()) { | ||
| 83 | surface->SetSyncPending(false); | ||
| 84 | Unregister(surface); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | marked_for_unregister.clear(); | ||
| 88 | } | ||
| 89 | |||
| 65 | /** | 90 | /** |
| 66 | * Guarantees that rendertargets don't unregister themselves if the | 91 | * Guarantees that rendertargets don't unregister themselves if the |
| 67 | * collide. Protection is currently only done on 3D slices. | 92 | * collide. Protection is currently only done on 3D slices. |
| @@ -85,10 +110,20 @@ public: | |||
| 85 | return a->GetModificationTick() < b->GetModificationTick(); | 110 | return a->GetModificationTick() < b->GetModificationTick(); |
| 86 | }); | 111 | }); |
| 87 | for (const auto& surface : surfaces) { | 112 | for (const auto& surface : surfaces) { |
| 113 | mutex.unlock(); | ||
| 88 | FlushSurface(surface); | 114 | FlushSurface(surface); |
| 115 | mutex.lock(); | ||
| 89 | } | 116 | } |
| 90 | } | 117 | } |
| 91 | 118 | ||
| 119 | bool MustFlushRegion(VAddr addr, std::size_t size) { | ||
| 120 | std::lock_guard lock{mutex}; | ||
| 121 | |||
| 122 | const auto surfaces = GetSurfacesInRegion(addr, size); | ||
| 123 | return std::any_of(surfaces.cbegin(), surfaces.cend(), | ||
| 124 | [](const TSurface& surface) { return surface->IsModified(); }); | ||
| 125 | } | ||
| 126 | |||
| 92 | TView GetTextureSurface(const Tegra::Texture::TICEntry& tic, | 127 | TView GetTextureSurface(const Tegra::Texture::TICEntry& tic, |
| 93 | const VideoCommon::Shader::Sampler& entry) { | 128 | const VideoCommon::Shader::Sampler& entry) { |
| 94 | std::lock_guard lock{mutex}; | 129 | std::lock_guard lock{mutex}; |
| @@ -206,8 +241,14 @@ public: | |||
| 206 | 241 | ||
| 207 | auto surface_view = GetSurface(gpu_addr, *cpu_addr, | 242 | auto surface_view = GetSurface(gpu_addr, *cpu_addr, |
| 208 | SurfaceParams::CreateForFramebuffer(system, index), true); | 243 | SurfaceParams::CreateForFramebuffer(system, index), true); |
| 209 | if (render_targets[index].target) | 244 | if (render_targets[index].target) { |
| 210 | render_targets[index].target->MarkAsRenderTarget(false, NO_RT); | 245 | auto& surface = render_targets[index].target; |
| 246 | surface->MarkAsRenderTarget(false, NO_RT); | ||
| 247 | const auto& cr_params = surface->GetSurfaceParams(); | ||
| 248 | if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) { | ||
| 249 | AsyncFlushSurface(surface); | ||
| 250 | } | ||
| 251 | } | ||
| 211 | render_targets[index].target = surface_view.first; | 252 | render_targets[index].target = surface_view.first; |
| 212 | render_targets[index].view = surface_view.second; | 253 | render_targets[index].view = surface_view.second; |
| 213 | if (render_targets[index].target) | 254 | if (render_targets[index].target) |
| @@ -284,6 +325,34 @@ public: | |||
| 284 | return ++ticks; | 325 | return ++ticks; |
| 285 | } | 326 | } |
| 286 | 327 | ||
| 328 | void CommitAsyncFlushes() { | ||
| 329 | committed_flushes.push_back(uncommitted_flushes); | ||
| 330 | uncommitted_flushes.reset(); | ||
| 331 | } | ||
| 332 | |||
| 333 | bool HasUncommittedFlushes() const { | ||
| 334 | return uncommitted_flushes != nullptr; | ||
| 335 | } | ||
| 336 | |||
| 337 | bool ShouldWaitAsyncFlushes() const { | ||
| 338 | return !committed_flushes.empty() && committed_flushes.front() != nullptr; | ||
| 339 | } | ||
| 340 | |||
| 341 | void PopAsyncFlushes() { | ||
| 342 | if (committed_flushes.empty()) { | ||
| 343 | return; | ||
| 344 | } | ||
| 345 | auto& flush_list = committed_flushes.front(); | ||
| 346 | if (!flush_list) { | ||
| 347 | committed_flushes.pop_front(); | ||
| 348 | return; | ||
| 349 | } | ||
| 350 | for (TSurface& surface : *flush_list) { | ||
| 351 | FlushSurface(surface); | ||
| 352 | } | ||
| 353 | committed_flushes.pop_front(); | ||
| 354 | } | ||
| 355 | |||
| 287 | protected: | 356 | protected: |
| 288 | explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 357 | explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 289 | bool is_astc_supported) | 358 | bool is_astc_supported) |
| @@ -345,9 +414,20 @@ protected: | |||
| 345 | surface->SetCpuAddr(*cpu_addr); | 414 | surface->SetCpuAddr(*cpu_addr); |
| 346 | RegisterInnerCache(surface); | 415 | RegisterInnerCache(surface); |
| 347 | surface->MarkAsRegistered(true); | 416 | surface->MarkAsRegistered(true); |
| 417 | surface->SetMemoryMarked(true); | ||
| 348 | rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1); | 418 | rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1); |
| 349 | } | 419 | } |
| 350 | 420 | ||
| 421 | void UnmarkMemory(TSurface surface) { | ||
| 422 | if (!surface->IsMemoryMarked()) { | ||
| 423 | return; | ||
| 424 | } | ||
| 425 | const std::size_t size = surface->GetSizeInBytes(); | ||
| 426 | const VAddr cpu_addr = surface->GetCpuAddr(); | ||
| 427 | rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1); | ||
| 428 | surface->SetMemoryMarked(false); | ||
| 429 | } | ||
| 430 | |||
| 351 | void Unregister(TSurface surface) { | 431 | void Unregister(TSurface surface) { |
| 352 | if (guard_render_targets && surface->IsProtected()) { | 432 | if (guard_render_targets && surface->IsProtected()) { |
| 353 | return; | 433 | return; |
| @@ -355,9 +435,11 @@ protected: | |||
| 355 | if (!guard_render_targets && surface->IsRenderTarget()) { | 435 | if (!guard_render_targets && surface->IsRenderTarget()) { |
| 356 | ManageRenderTargetUnregister(surface); | 436 | ManageRenderTargetUnregister(surface); |
| 357 | } | 437 | } |
| 358 | const std::size_t size = surface->GetSizeInBytes(); | 438 | UnmarkMemory(surface); |
| 359 | const VAddr cpu_addr = surface->GetCpuAddr(); | 439 | if (surface->IsSyncPending()) { |
| 360 | rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1); | 440 | marked_for_unregister.remove(surface); |
| 441 | surface->SetSyncPending(false); | ||
| 442 | } | ||
| 361 | UnregisterInnerCache(surface); | 443 | UnregisterInnerCache(surface); |
| 362 | surface->MarkAsRegistered(false); | 444 | surface->MarkAsRegistered(false); |
| 363 | ReserveSurface(surface->GetSurfaceParams(), surface); | 445 | ReserveSurface(surface->GetSurfaceParams(), surface); |
| @@ -417,7 +499,7 @@ private: | |||
| 417 | **/ | 499 | **/ |
| 418 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, | 500 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, |
| 419 | const GPUVAddr gpu_addr, const MatchTopologyResult untopological) { | 501 | const GPUVAddr gpu_addr, const MatchTopologyResult untopological) { |
| 420 | if (Settings::values.use_accurate_gpu_emulation) { | 502 | if (Settings::IsGPULevelExtreme()) { |
| 421 | return RecycleStrategy::Flush; | 503 | return RecycleStrategy::Flush; |
| 422 | } | 504 | } |
| 423 | // 3D Textures decision | 505 | // 3D Textures decision |
| @@ -461,7 +543,7 @@ private: | |||
| 461 | } | 543 | } |
| 462 | switch (PickStrategy(overlaps, params, gpu_addr, untopological)) { | 544 | switch (PickStrategy(overlaps, params, gpu_addr, untopological)) { |
| 463 | case RecycleStrategy::Ignore: { | 545 | case RecycleStrategy::Ignore: { |
| 464 | return InitializeSurface(gpu_addr, params, Settings::values.use_accurate_gpu_emulation); | 546 | return InitializeSurface(gpu_addr, params, Settings::IsGPULevelExtreme()); |
| 465 | } | 547 | } |
| 466 | case RecycleStrategy::Flush: { | 548 | case RecycleStrategy::Flush: { |
| 467 | std::sort(overlaps.begin(), overlaps.end(), | 549 | std::sort(overlaps.begin(), overlaps.end(), |
| @@ -509,7 +591,7 @@ private: | |||
| 509 | } | 591 | } |
| 510 | const auto& final_params = new_surface->GetSurfaceParams(); | 592 | const auto& final_params = new_surface->GetSurfaceParams(); |
| 511 | if (cr_params.type != final_params.type) { | 593 | if (cr_params.type != final_params.type) { |
| 512 | if (Settings::values.use_accurate_gpu_emulation) { | 594 | if (Settings::IsGPULevelExtreme()) { |
| 513 | BufferCopy(current_surface, new_surface); | 595 | BufferCopy(current_surface, new_surface); |
| 514 | } | 596 | } |
| 515 | } else { | 597 | } else { |
| @@ -598,7 +680,7 @@ private: | |||
| 598 | if (passed_tests == 0) { | 680 | if (passed_tests == 0) { |
| 599 | return {}; | 681 | return {}; |
| 600 | // In Accurate GPU all tests should pass, else we recycle | 682 | // In Accurate GPU all tests should pass, else we recycle |
| 601 | } else if (Settings::values.use_accurate_gpu_emulation && passed_tests != overlaps.size()) { | 683 | } else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) { |
| 602 | return {}; | 684 | return {}; |
| 603 | } | 685 | } |
| 604 | for (const auto& surface : overlaps) { | 686 | for (const auto& surface : overlaps) { |
| @@ -668,7 +750,7 @@ private: | |||
| 668 | for (const auto& surface : overlaps) { | 750 | for (const auto& surface : overlaps) { |
| 669 | if (!surface->MatchTarget(params.target)) { | 751 | if (!surface->MatchTarget(params.target)) { |
| 670 | if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { | 752 | if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { |
| 671 | if (Settings::values.use_accurate_gpu_emulation) { | 753 | if (Settings::IsGPULevelExtreme()) { |
| 672 | return std::nullopt; | 754 | return std::nullopt; |
| 673 | } | 755 | } |
| 674 | Unregister(surface); | 756 | Unregister(surface); |
| @@ -1106,6 +1188,13 @@ private: | |||
| 1106 | TView view; | 1188 | TView view; |
| 1107 | }; | 1189 | }; |
| 1108 | 1190 | ||
| 1191 | void AsyncFlushSurface(TSurface& surface) { | ||
| 1192 | if (!uncommitted_flushes) { | ||
| 1193 | uncommitted_flushes = std::make_shared<std::list<TSurface>>(); | ||
| 1194 | } | ||
| 1195 | uncommitted_flushes->push_back(surface); | ||
| 1196 | } | ||
| 1197 | |||
| 1109 | VideoCore::RasterizerInterface& rasterizer; | 1198 | VideoCore::RasterizerInterface& rasterizer; |
| 1110 | 1199 | ||
| 1111 | FormatLookupTable format_lookup_table; | 1200 | FormatLookupTable format_lookup_table; |
| @@ -1150,6 +1239,11 @@ private: | |||
| 1150 | std::unordered_map<u32, TSurface> invalid_cache; | 1239 | std::unordered_map<u32, TSurface> invalid_cache; |
| 1151 | std::vector<u8> invalid_memory; | 1240 | std::vector<u8> invalid_memory; |
| 1152 | 1241 | ||
| 1242 | std::list<TSurface> marked_for_unregister; | ||
| 1243 | |||
| 1244 | std::shared_ptr<std::list<TSurface>> uncommitted_flushes{}; | ||
| 1245 | std::list<std::shared_ptr<std::list<TSurface>>> committed_flushes; | ||
| 1246 | |||
| 1153 | StagingCache staging_cache; | 1247 | StagingCache staging_cache; |
| 1154 | std::recursive_mutex mutex; | 1248 | std::recursive_mutex mutex; |
| 1155 | }; | 1249 | }; |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 7df5f1452..fae8638ec 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "video_core/textures/texture.h" | 11 | #include "video_core/textures/texture.h" |
| 12 | 12 | ||
| 13 | namespace Tegra::Texture { | 13 | namespace Tegra::Texture { |
| 14 | namespace { | ||
| 14 | 15 | ||
| 15 | /** | 16 | /** |
| 16 | * This table represents the internal swizzle of a gob, | 17 | * This table represents the internal swizzle of a gob, |
| @@ -174,6 +175,8 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool | |||
| 174 | } | 175 | } |
| 175 | } | 176 | } |
| 176 | 177 | ||
| 178 | } // Anonymous namespace | ||
| 179 | |||
| 177 | void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, | 180 | void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, |
| 178 | u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data, | 181 | u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data, |
| 179 | bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing) { | 182 | bool unswizzle, u32 block_height, u32 block_depth, u32 width_spacing) { |
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index e5eac3f3b..9f2d6d308 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h | |||
| @@ -56,8 +56,7 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 | |||
| 56 | u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, | 56 | u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, |
| 57 | u32 offset_x, u32 offset_y); | 57 | u32 offset_x, u32 offset_y); |
| 58 | 58 | ||
| 59 | void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, | 59 | void SwizzleKepler(u32 width, u32 height, u32 dst_x, u32 dst_y, u32 block_height, |
| 60 | const u32 block_height, const std::size_t copy_size, const u8* source_data, | 60 | std::size_t copy_size, const u8* source_data, u8* swizzle_data); |
| 61 | u8* swizzle_data); | ||
| 62 | 61 | ||
| 63 | } // namespace Tegra::Texture | 62 | } // namespace Tegra::Texture |
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 6aff38735..4bc8ee726 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "yuzu/applets/profile_select.h" | 17 | #include "yuzu/applets/profile_select.h" |
| 18 | #include "yuzu/main.h" | 18 | #include "yuzu/main.h" |
| 19 | 19 | ||
| 20 | namespace { | ||
| 20 | QString FormatUserEntryText(const QString& username, Common::UUID uuid) { | 21 | QString FormatUserEntryText(const QString& username, Common::UUID uuid) { |
| 21 | return QtProfileSelectionDialog::tr( | 22 | return QtProfileSelectionDialog::tr( |
| 22 | "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " | 23 | "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " |
| @@ -41,6 +42,7 @@ QPixmap GetIcon(Common::UUID uuid) { | |||
| 41 | 42 | ||
| 42 | return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); | 43 | return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
| 43 | } | 44 | } |
| 45 | } // Anonymous namespace | ||
| 44 | 46 | ||
| 45 | QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | 47 | QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) |
| 46 | : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | 48 | : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 7f6dfac84..a44eed047 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | #include "input_common/main.h" | 12 | #include "input_common/main.h" |
| 13 | #include "input_common/udp/client.h" | 13 | #include "input_common/udp/client.h" |
| 14 | #include "yuzu/configuration/config.h" | 14 | #include "yuzu/configuration/config.h" |
| 15 | #include "yuzu/uisettings.h" | ||
| 16 | 15 | ||
| 17 | Config::Config() { | 16 | Config::Config() { |
| 18 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 17 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| @@ -212,12 +211,13 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default | |||
| 212 | // This must be in alphabetical order according to action name as it must have the same order as | 211 | // This must be in alphabetical order according to action name as it must have the same order as |
| 213 | // UISetting::values.shortcuts, which is alphabetically ordered. | 212 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 214 | // clang-format off | 213 | // clang-format off |
| 215 | const std::array<UISettings::Shortcut, 15> default_hotkeys{{ | 214 | const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{ |
| 216 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, | 215 | {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, |
| 216 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | ||
| 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, | 217 | {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, |
| 218 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, | 218 | {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, |
| 219 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, | ||
| 220 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, | 219 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}}, |
| 220 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, | ||
| 221 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, | 221 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, |
| 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, | 222 | {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, |
| 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, | 223 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, |
| @@ -227,7 +227,6 @@ const std::array<UISettings::Shortcut, 15> default_hotkeys{{ | |||
| 227 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, | 227 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, |
| 228 | {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, | 228 | {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, |
| 229 | {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, | 229 | {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, |
| 230 | {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, | ||
| 231 | }}; | 230 | }}; |
| 232 | // clang-format on | 231 | // clang-format on |
| 233 | 232 | ||
| @@ -639,11 +638,13 @@ void Config::ReadRendererValues() { | |||
| 639 | Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); | 638 | Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); |
| 640 | Settings::values.use_disk_shader_cache = | 639 | Settings::values.use_disk_shader_cache = |
| 641 | ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); | 640 | ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); |
| 642 | Settings::values.use_accurate_gpu_emulation = | 641 | const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt(); |
| 643 | ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); | 642 | Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); |
| 644 | Settings::values.use_asynchronous_gpu_emulation = | 643 | Settings::values.use_asynchronous_gpu_emulation = |
| 645 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); | 644 | ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); |
| 646 | Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); | 645 | Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); |
| 646 | Settings::values.use_fast_gpu_time = | ||
| 647 | ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool(); | ||
| 647 | Settings::values.force_30fps_mode = | 648 | Settings::values.force_30fps_mode = |
| 648 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); | 649 | ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); |
| 649 | 650 | ||
| @@ -1080,11 +1081,12 @@ void Config::SaveRendererValues() { | |||
| 1080 | WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); | 1081 | WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); |
| 1081 | WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, | 1082 | WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, |
| 1082 | true); | 1083 | true); |
| 1083 | WriteSetting(QStringLiteral("use_accurate_gpu_emulation"), | 1084 | WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy), |
| 1084 | Settings::values.use_accurate_gpu_emulation, false); | 1085 | 0); |
| 1085 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), | 1086 | WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), |
| 1086 | Settings::values.use_asynchronous_gpu_emulation, false); | 1087 | Settings::values.use_asynchronous_gpu_emulation, false); |
| 1087 | WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); | 1088 | WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); |
| 1089 | WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true); | ||
| 1088 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); | 1090 | WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); |
| 1089 | 1091 | ||
| 1090 | // Cast to double because Qt's written float values are not human-readable | 1092 | // Cast to double because Qt's written float values are not human-readable |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index ba6888004..5cd2a5feb 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <QVariant> | 10 | #include <QVariant> |
| 11 | #include "core/settings.h" | 11 | #include "core/settings.h" |
| 12 | #include "yuzu/uisettings.h" | ||
| 12 | 13 | ||
| 13 | class QSettings; | 14 | class QSettings; |
| 14 | 15 | ||
| @@ -26,6 +27,7 @@ public: | |||
| 26 | default_mouse_buttons; | 27 | default_mouse_buttons; |
| 27 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; | 28 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; |
| 28 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; | 29 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; |
| 30 | static const std::array<UISettings::Shortcut, 15> default_hotkeys; | ||
| 29 | 31 | ||
| 30 | private: | 32 | private: |
| 31 | void ReadValues(); | 33 | void ReadValues(); |
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 29f540eb7..835ee821c 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp | |||
| @@ -138,7 +138,7 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) | |||
| 138 | str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(), | 138 | str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(), |
| 139 | QStringLiteral("NX Gamecard;*.xci")); | 139 | QStringLiteral("NX Gamecard;*.xci")); |
| 140 | } else { | 140 | } else { |
| 141 | str = QFileDialog::getExistingDirectory(this, caption, edit->text()); | 141 | str = QFileDialog::getExistingDirectory(this, caption, edit->text()) + QDir::separator(); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | if (str.isEmpty()) | 144 | if (str.isEmpty()) |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index b9f429f84..5bb2ae555 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp | |||
| @@ -19,9 +19,10 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default; | |||
| 19 | 19 | ||
| 20 | void ConfigureGraphicsAdvanced::SetConfiguration() { | 20 | void ConfigureGraphicsAdvanced::SetConfiguration() { |
| 21 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | 21 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); |
| 22 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 22 | ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy)); |
| 23 | ui->use_vsync->setEnabled(runtime_lock); | 23 | ui->use_vsync->setEnabled(runtime_lock); |
| 24 | ui->use_vsync->setChecked(Settings::values.use_vsync); | 24 | ui->use_vsync->setChecked(Settings::values.use_vsync); |
| 25 | ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time); | ||
| 25 | ui->force_30fps_mode->setEnabled(runtime_lock); | 26 | ui->force_30fps_mode->setEnabled(runtime_lock); |
| 26 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | 27 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); |
| 27 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); | 28 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); |
| @@ -29,8 +30,10 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { | |||
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | void ConfigureGraphicsAdvanced::ApplyConfiguration() { | 32 | void ConfigureGraphicsAdvanced::ApplyConfiguration() { |
| 32 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 33 | auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex()); |
| 34 | Settings::values.gpu_accuracy = gpu_accuracy; | ||
| 33 | Settings::values.use_vsync = ui->use_vsync->isChecked(); | 35 | Settings::values.use_vsync = ui->use_vsync->isChecked(); |
| 36 | Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked(); | ||
| 34 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); | 37 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); |
| 35 | Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex(); | 38 | Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex(); |
| 36 | } | 39 | } |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 42eec278e..770b80c50 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui | |||
| @@ -23,11 +23,34 @@ | |||
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QVBoxLayout" name="verticalLayout_3"> | 24 | <layout class="QVBoxLayout" name="verticalLayout_3"> |
| 25 | <item> | 25 | <item> |
| 26 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> | 26 | <layout class="QHBoxLayout" name="horizontalLayout_2"> |
| 27 | <property name="text"> | 27 | <item> |
| 28 | <string>Use accurate GPU emulation (slow)</string> | 28 | <widget class="QLabel" name="label_gpu_accuracy"> |
| 29 | </property> | 29 | <property name="text"> |
| 30 | </widget> | 30 | <string>Accuracy Level:</string> |
| 31 | </property> | ||
| 32 | </widget> | ||
| 33 | </item> | ||
| 34 | <item> | ||
| 35 | <widget class="QComboBox" name="gpu_accuracy"> | ||
| 36 | <item> | ||
| 37 | <property name="text"> | ||
| 38 | <string notr="true">Normal</string> | ||
| 39 | </property> | ||
| 40 | </item> | ||
| 41 | <item> | ||
| 42 | <property name="text"> | ||
| 43 | <string notr="true">High</string> | ||
| 44 | </property> | ||
| 45 | </item> | ||
| 46 | <item> | ||
| 47 | <property name="text"> | ||
| 48 | <string notr="true">Extreme(very slow)</string> | ||
| 49 | </property> | ||
| 50 | </item> | ||
| 51 | </widget> | ||
| 52 | </item> | ||
| 53 | </layout> | ||
| 31 | </item> | 54 | </item> |
| 32 | <item> | 55 | <item> |
| 33 | <widget class="QCheckBox" name="use_vsync"> | 56 | <widget class="QCheckBox" name="use_vsync"> |
| @@ -47,6 +70,13 @@ | |||
| 47 | </widget> | 70 | </widget> |
| 48 | </item> | 71 | </item> |
| 49 | <item> | 72 | <item> |
| 73 | <widget class="QCheckBox" name="use_fast_gpu_time"> | ||
| 74 | <property name="text"> | ||
| 75 | <string>Use Fast GPU Time</string> | ||
| 76 | </property> | ||
| 77 | </widget> | ||
| 78 | </item> | ||
| 79 | <item> | ||
| 50 | <layout class="QHBoxLayout" name="horizontalLayout_1"> | 80 | <layout class="QHBoxLayout" name="horizontalLayout_1"> |
| 51 | <item> | 81 | <item> |
| 52 | <widget class="QLabel" name="af_label"> | 82 | <widget class="QLabel" name="af_label"> |
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index fa9052136..6f7fd4414 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp | |||
| @@ -2,10 +2,12 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QMenu> | ||
| 5 | #include <QMessageBox> | 6 | #include <QMessageBox> |
| 6 | #include <QStandardItemModel> | 7 | #include <QStandardItemModel> |
| 7 | #include "core/settings.h" | 8 | #include "core/settings.h" |
| 8 | #include "ui_configure_hotkeys.h" | 9 | #include "ui_configure_hotkeys.h" |
| 10 | #include "yuzu/configuration/config.h" | ||
| 9 | #include "yuzu/configuration/configure_hotkeys.h" | 11 | #include "yuzu/configuration/configure_hotkeys.h" |
| 10 | #include "yuzu/hotkeys.h" | 12 | #include "yuzu/hotkeys.h" |
| 11 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" | 13 | #include "yuzu/util/sequence_dialog/sequence_dialog.h" |
| @@ -19,6 +21,9 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) | |||
| 19 | model->setColumnCount(3); | 21 | model->setColumnCount(3); |
| 20 | 22 | ||
| 21 | connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure); | 23 | connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure); |
| 24 | connect(ui->hotkey_list, &QTreeView::customContextMenuRequested, this, | ||
| 25 | &ConfigureHotkeys::PopupContextMenu); | ||
| 26 | ui->hotkey_list->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 22 | ui->hotkey_list->setModel(model); | 27 | ui->hotkey_list->setModel(model); |
| 23 | 28 | ||
| 24 | // TODO(Kloen): Make context configurable as well (hiding the column for now) | 29 | // TODO(Kloen): Make context configurable as well (hiding the column for now) |
| @@ -27,6 +32,10 @@ ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) | |||
| 27 | ui->hotkey_list->setColumnWidth(0, 200); | 32 | ui->hotkey_list->setColumnWidth(0, 200); |
| 28 | ui->hotkey_list->resizeColumnToContents(1); | 33 | ui->hotkey_list->resizeColumnToContents(1); |
| 29 | 34 | ||
| 35 | connect(ui->button_restore_defaults, &QPushButton::clicked, this, | ||
| 36 | &ConfigureHotkeys::RestoreDefaults); | ||
| 37 | connect(ui->button_clear_all, &QPushButton::clicked, this, &ConfigureHotkeys::ClearAll); | ||
| 38 | |||
| 30 | RetranslateUI(); | 39 | RetranslateUI(); |
| 31 | } | 40 | } |
| 32 | 41 | ||
| @@ -71,7 +80,6 @@ void ConfigureHotkeys::Configure(QModelIndex index) { | |||
| 71 | } | 80 | } |
| 72 | 81 | ||
| 73 | index = index.sibling(index.row(), 1); | 82 | index = index.sibling(index.row(), 1); |
| 74 | auto* const model = ui->hotkey_list->model(); | ||
| 75 | const auto previous_key = model->data(index); | 83 | const auto previous_key = model->data(index); |
| 76 | 84 | ||
| 77 | SequenceDialog hotkey_dialog{this}; | 85 | SequenceDialog hotkey_dialog{this}; |
| @@ -81,31 +89,33 @@ void ConfigureHotkeys::Configure(QModelIndex index) { | |||
| 81 | if (return_code == QDialog::Rejected || key_sequence.isEmpty()) { | 89 | if (return_code == QDialog::Rejected || key_sequence.isEmpty()) { |
| 82 | return; | 90 | return; |
| 83 | } | 91 | } |
| 92 | const auto [key_sequence_used, used_action] = IsUsedKey(key_sequence); | ||
| 84 | 93 | ||
| 85 | if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { | 94 | if (key_sequence_used && key_sequence != QKeySequence(previous_key.toString())) { |
| 86 | QMessageBox::warning(this, tr("Conflicting Key Sequence"), | 95 | QMessageBox::warning( |
| 87 | tr("The entered key sequence is already assigned to another hotkey.")); | 96 | this, tr("Conflicting Key Sequence"), |
| 97 | tr("The entered key sequence is already assigned to: %1").arg(used_action)); | ||
| 88 | } else { | 98 | } else { |
| 89 | model->setData(index, key_sequence.toString(QKeySequence::NativeText)); | 99 | model->setData(index, key_sequence.toString(QKeySequence::NativeText)); |
| 90 | } | 100 | } |
| 91 | } | 101 | } |
| 92 | 102 | ||
| 93 | bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { | 103 | std::pair<bool, QString> ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { |
| 94 | for (int r = 0; r < model->rowCount(); r++) { | 104 | for (int r = 0; r < model->rowCount(); ++r) { |
| 95 | const QStandardItem* const parent = model->item(r, 0); | 105 | const QStandardItem* const parent = model->item(r, 0); |
| 96 | 106 | ||
| 97 | for (int r2 = 0; r2 < parent->rowCount(); r2++) { | 107 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { |
| 98 | const QStandardItem* const key_seq_item = parent->child(r2, 1); | 108 | const QStandardItem* const key_seq_item = parent->child(r2, 1); |
| 99 | const auto key_seq_str = key_seq_item->text(); | 109 | const auto key_seq_str = key_seq_item->text(); |
| 100 | const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); | 110 | const auto key_seq = QKeySequence::fromString(key_seq_str, QKeySequence::NativeText); |
| 101 | 111 | ||
| 102 | if (key_sequence == key_seq) { | 112 | if (key_sequence == key_seq) { |
| 103 | return true; | 113 | return std::make_pair(true, parent->child(r2, 0)->text()); |
| 104 | } | 114 | } |
| 105 | } | 115 | } |
| 106 | } | 116 | } |
| 107 | 117 | ||
| 108 | return false; | 118 | return std::make_pair(false, QString()); |
| 109 | } | 119 | } |
| 110 | 120 | ||
| 111 | void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { | 121 | void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { |
| @@ -128,3 +138,55 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { | |||
| 128 | 138 | ||
| 129 | registry.SaveHotkeys(); | 139 | registry.SaveHotkeys(); |
| 130 | } | 140 | } |
| 141 | |||
| 142 | void ConfigureHotkeys::RestoreDefaults() { | ||
| 143 | for (int r = 0; r < model->rowCount(); ++r) { | ||
| 144 | const QStandardItem* parent = model->item(r, 0); | ||
| 145 | |||
| 146 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { | ||
| 147 | model->item(r, 0)->child(r2, 1)->setText(Config::default_hotkeys[r2].shortcut.first); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | void ConfigureHotkeys::ClearAll() { | ||
| 153 | for (int r = 0; r < model->rowCount(); ++r) { | ||
| 154 | const QStandardItem* parent = model->item(r, 0); | ||
| 155 | |||
| 156 | for (int r2 = 0; r2 < parent->rowCount(); ++r2) { | ||
| 157 | model->item(r, 0)->child(r2, 1)->setText(tr("")); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) { | ||
| 163 | QModelIndex index = ui->hotkey_list->indexAt(menu_location); | ||
| 164 | if (!index.parent().isValid()) { | ||
| 165 | return; | ||
| 166 | } | ||
| 167 | |||
| 168 | const auto selected = index.sibling(index.row(), 1); | ||
| 169 | QMenu context_menu; | ||
| 170 | |||
| 171 | QAction* restore_default = context_menu.addAction(tr("Restore Default")); | ||
| 172 | QAction* clear = context_menu.addAction(tr("Clear")); | ||
| 173 | |||
| 174 | connect(restore_default, &QAction::triggered, [this, selected] { | ||
| 175 | const QKeySequence& default_key_sequence = QKeySequence::fromString( | ||
| 176 | Config::default_hotkeys[selected.row()].shortcut.first, QKeySequence::NativeText); | ||
| 177 | const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); | ||
| 178 | |||
| 179 | if (key_sequence_used && | ||
| 180 | default_key_sequence != QKeySequence(model->data(selected).toString())) { | ||
| 181 | |||
| 182 | QMessageBox::warning( | ||
| 183 | this, tr("Conflicting Key Sequence"), | ||
| 184 | tr("The default key sequence is already assigned to: %1").arg(used_action)); | ||
| 185 | } else { | ||
| 186 | model->setData(selected, default_key_sequence.toString(QKeySequence::NativeText)); | ||
| 187 | } | ||
| 188 | }); | ||
| 189 | connect(clear, &QAction::triggered, [this, selected] { model->setData(selected, tr("")); }); | ||
| 190 | |||
| 191 | context_menu.exec(ui->hotkey_list->viewport()->mapToGlobal(menu_location)); | ||
| 192 | } | ||
diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h index 8f8c6173b..a2ec3323e 100644 --- a/src/yuzu/configuration/configure_hotkeys.h +++ b/src/yuzu/configuration/configure_hotkeys.h | |||
| @@ -35,7 +35,11 @@ private: | |||
| 35 | void RetranslateUI(); | 35 | void RetranslateUI(); |
| 36 | 36 | ||
| 37 | void Configure(QModelIndex index); | 37 | void Configure(QModelIndex index); |
| 38 | bool IsUsedKey(QKeySequence key_sequence) const; | 38 | std::pair<bool, QString> IsUsedKey(QKeySequence key_sequence) const; |
| 39 | |||
| 40 | void RestoreDefaults(); | ||
| 41 | void ClearAll(); | ||
| 42 | void PopupContextMenu(const QPoint& menu_location); | ||
| 39 | 43 | ||
| 40 | std::unique_ptr<Ui::ConfigureHotkeys> ui; | 44 | std::unique_ptr<Ui::ConfigureHotkeys> ui; |
| 41 | 45 | ||
diff --git a/src/yuzu/configuration/configure_hotkeys.ui b/src/yuzu/configuration/configure_hotkeys.ui index 0d0b70f38..6d9f861e3 100644 --- a/src/yuzu/configuration/configure_hotkeys.ui +++ b/src/yuzu/configuration/configure_hotkeys.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>363</width> | 9 | <width>439</width> |
| 10 | <height>388</height> | 10 | <height>510</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -15,7 +15,7 @@ | |||
| 15 | </property> | 15 | </property> |
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | 16 | <layout class="QVBoxLayout" name="verticalLayout"> |
| 17 | <item> | 17 | <item> |
| 18 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 18 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 19 | <item> | 19 | <item> |
| 20 | <widget class="QLabel" name="label_2"> | 20 | <widget class="QLabel" name="label_2"> |
| 21 | <property name="text"> | 21 | <property name="text"> |
| @@ -24,6 +24,37 @@ | |||
| 24 | </widget> | 24 | </widget> |
| 25 | </item> | 25 | </item> |
| 26 | <item> | 26 | <item> |
| 27 | <spacer name="horizontalSpacer"> | ||
| 28 | <property name="orientation"> | ||
| 29 | <enum>Qt::Horizontal</enum> | ||
| 30 | </property> | ||
| 31 | <property name="sizeHint" stdset="0"> | ||
| 32 | <size> | ||
| 33 | <width>40</width> | ||
| 34 | <height>20</height> | ||
| 35 | </size> | ||
| 36 | </property> | ||
| 37 | </spacer> | ||
| 38 | </item> | ||
| 39 | <item> | ||
| 40 | <widget class="QPushButton" name="button_clear_all"> | ||
| 41 | <property name="text"> | ||
| 42 | <string>Clear All</string> | ||
| 43 | </property> | ||
| 44 | </widget> | ||
| 45 | </item> | ||
| 46 | <item> | ||
| 47 | <widget class="QPushButton" name="button_restore_defaults"> | ||
| 48 | <property name="text"> | ||
| 49 | <string>Restore Defaults</string> | ||
| 50 | </property> | ||
| 51 | </widget> | ||
| 52 | </item> | ||
| 53 | </layout> | ||
| 54 | </item> | ||
| 55 | <item> | ||
| 56 | <layout class="QVBoxLayout" name="verticalLayout_2"> | ||
| 57 | <item> | ||
| 27 | <widget class="QTreeView" name="hotkey_list"> | 58 | <widget class="QTreeView" name="hotkey_list"> |
| 28 | <property name="editTriggers"> | 59 | <property name="editTriggers"> |
| 29 | <set>QAbstractItemView::NoEditTriggers</set> | 60 | <set>QAbstractItemView::NoEditTriggers</set> |
| @@ -39,4 +70,4 @@ | |||
| 39 | </widget> | 70 | </widget> |
| 40 | <resources/> | 71 | <resources/> |
| 41 | <connections/> | 72 | <connections/> |
| 42 | </ui> \ No newline at end of file | 73 | </ui> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 15ac30f12..e4eb5594b 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -56,7 +56,6 @@ static void SetAnalogButton(const Common::ParamPackage& input_param, | |||
| 56 | if (analog_param.Get("engine", "") != "analog_from_button") { | 56 | if (analog_param.Get("engine", "") != "analog_from_button") { |
| 57 | analog_param = { | 57 | analog_param = { |
| 58 | {"engine", "analog_from_button"}, | 58 | {"engine", "analog_from_button"}, |
| 59 | {"modifier_scale", "0.5"}, | ||
| 60 | }; | 59 | }; |
| 61 | } | 60 | } |
| 62 | analog_param.Set(button_name, input_param.Serialize()); | 61 | analog_param.Set(button_name, input_param.Serialize()); |
| @@ -236,8 +235,10 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 236 | widget->setVisible(false); | 235 | widget->setVisible(false); |
| 237 | 236 | ||
| 238 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; | 237 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; |
| 239 | analog_map_deadzone = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; | 238 | analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier, |
| 240 | analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; | 239 | ui->sliderRStickDeadzoneAndModifier}; |
| 240 | analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier, | ||
| 241 | ui->labelRStickDeadzoneAndModifier}; | ||
| 241 | 242 | ||
| 242 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | 243 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { |
| 243 | auto* const button = button_map[button_id]; | 244 | auto* const button = button_map[button_id]; |
| @@ -328,10 +329,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 328 | InputCommon::Polling::DeviceType::Analog); | 329 | InputCommon::Polling::DeviceType::Analog); |
| 329 | } | 330 | } |
| 330 | }); | 331 | }); |
| 331 | connect(analog_map_deadzone[analog_id], &QSlider::valueChanged, [=] { | 332 | |
| 332 | const float deadzone = analog_map_deadzone[analog_id]->value() / 100.0f; | 333 | connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { |
| 333 | analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1").arg(deadzone)); | 334 | const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); |
| 334 | analogs_param[analog_id].Set("deadzone", deadzone); | 335 | if (analogs_param[analog_id].Get("engine", "") == "sdl") { |
| 336 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( | ||
| 337 | tr("Deadzone: %1%").arg(slider_value)); | ||
| 338 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); | ||
| 339 | } else { | ||
| 340 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( | ||
| 341 | tr("Modifier Scale: %1%").arg(slider_value)); | ||
| 342 | analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); | ||
| 343 | } | ||
| 335 | }); | 344 | }); |
| 336 | } | 345 | } |
| 337 | 346 | ||
| @@ -517,20 +526,31 @@ void ConfigureInputPlayer::UpdateButtonLabels() { | |||
| 517 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); | 526 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); |
| 518 | 527 | ||
| 519 | auto& param = analogs_param[analog_id]; | 528 | auto& param = analogs_param[analog_id]; |
| 520 | auto* const analog_deadzone_slider = analog_map_deadzone[analog_id]; | 529 | auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id]; |
| 521 | auto* const analog_deadzone_label = analog_map_deadzone_label[analog_id]; | 530 | auto* const analog_stick_slider_label = |
| 522 | 531 | analog_map_deadzone_and_modifier_slider_label[analog_id]; | |
| 523 | if (param.Has("engine") && param.Get("engine", "") == "sdl") { | 532 | |
| 524 | if (!param.Has("deadzone")) { | 533 | if (param.Has("engine")) { |
| 525 | param.Set("deadzone", 0.1f); | 534 | if (param.Get("engine", "") == "sdl") { |
| 535 | if (!param.Has("deadzone")) { | ||
| 536 | param.Set("deadzone", 0.1f); | ||
| 537 | } | ||
| 538 | |||
| 539 | analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); | ||
| 540 | if (analog_stick_slider->value() == 0) { | ||
| 541 | analog_stick_slider_label->setText(tr("Deadzone: 0%")); | ||
| 542 | } | ||
| 543 | } else { | ||
| 544 | if (!param.Has("modifier_scale")) { | ||
| 545 | param.Set("modifier_scale", 0.5f); | ||
| 546 | } | ||
| 547 | |||
| 548 | analog_stick_slider->setValue( | ||
| 549 | static_cast<int>(param.Get("modifier_scale", 0.5f) * 100)); | ||
| 550 | if (analog_stick_slider->value() == 0) { | ||
| 551 | analog_stick_slider_label->setText(tr("Modifier Scale: 0%")); | ||
| 552 | } | ||
| 526 | } | 553 | } |
| 527 | |||
| 528 | analog_deadzone_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); | ||
| 529 | analog_deadzone_slider->setVisible(true); | ||
| 530 | analog_deadzone_label->setVisible(true); | ||
| 531 | } else { | ||
| 532 | analog_deadzone_slider->setVisible(false); | ||
| 533 | analog_deadzone_label->setVisible(false); | ||
| 534 | } | 554 | } |
| 535 | } | 555 | } |
| 536 | } | 556 | } |
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 045704e47..95afa5375 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -97,8 +97,10 @@ private: | |||
| 97 | /// Analog inputs are also represented each with a single button, used to configure with an | 97 | /// Analog inputs are also represented each with a single button, used to configure with an |
| 98 | /// actual analog stick | 98 | /// actual analog stick |
| 99 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; | 99 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; |
| 100 | std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone; | 100 | std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> |
| 101 | std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; | 101 | analog_map_deadzone_and_modifier_slider; |
| 102 | std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> | ||
| 103 | analog_map_deadzone_and_modifier_slider_label; | ||
| 102 | 104 | ||
| 103 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; | 105 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; |
| 104 | 106 | ||
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 4b37746a1..f27a77180 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -171,11 +171,11 @@ | |||
| 171 | </layout> | 171 | </layout> |
| 172 | </item> | 172 | </item> |
| 173 | <item row="4" column="0" colspan="2"> | 173 | <item row="4" column="0" colspan="2"> |
| 174 | <layout class="QVBoxLayout" name="sliderRStickDeadzoneVerticalLayout"> | 174 | <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout"> |
| 175 | <item> | 175 | <item> |
| 176 | <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout"> | 176 | <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout"> |
| 177 | <item> | 177 | <item> |
| 178 | <widget class="QLabel" name="labelRStickDeadzone"> | 178 | <widget class="QLabel" name="labelRStickDeadzoneAndModifier"> |
| 179 | <property name="text"> | 179 | <property name="text"> |
| 180 | <string>Deadzone: 0</string> | 180 | <string>Deadzone: 0</string> |
| 181 | </property> | 181 | </property> |
| @@ -187,7 +187,7 @@ | |||
| 187 | </layout> | 187 | </layout> |
| 188 | </item> | 188 | </item> |
| 189 | <item> | 189 | <item> |
| 190 | <widget class="QSlider" name="sliderRStickDeadzone"> | 190 | <widget class="QSlider" name="sliderRStickDeadzoneAndModifier"> |
| 191 | <property name="orientation"> | 191 | <property name="orientation"> |
| 192 | <enum>Qt::Horizontal</enum> | 192 | <enum>Qt::Horizontal</enum> |
| 193 | </property> | 193 | </property> |
| @@ -784,14 +784,14 @@ | |||
| 784 | </layout> | 784 | </layout> |
| 785 | </item> | 785 | </item> |
| 786 | <item row="5" column="1" colspan="2"> | 786 | <item row="5" column="1" colspan="2"> |
| 787 | <layout class="QVBoxLayout" name="sliderLStickDeadzoneVerticalLayout"> | 787 | <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout"> |
| 788 | <property name="sizeConstraint"> | 788 | <property name="sizeConstraint"> |
| 789 | <enum>QLayout::SetDefaultConstraint</enum> | 789 | <enum>QLayout::SetDefaultConstraint</enum> |
| 790 | </property> | 790 | </property> |
| 791 | <item> | 791 | <item> |
| 792 | <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout"> | 792 | <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout"> |
| 793 | <item> | 793 | <item> |
| 794 | <widget class="QLabel" name="labelLStickDeadzone"> | 794 | <widget class="QLabel" name="labelLStickDeadzoneAndModifier"> |
| 795 | <property name="text"> | 795 | <property name="text"> |
| 796 | <string>Deadzone: 0</string> | 796 | <string>Deadzone: 0</string> |
| 797 | </property> | 797 | </property> |
| @@ -803,7 +803,7 @@ | |||
| 803 | </layout> | 803 | </layout> |
| 804 | </item> | 804 | </item> |
| 805 | <item> | 805 | <item> |
| 806 | <widget class="QSlider" name="sliderLStickDeadzone"> | 806 | <widget class="QSlider" name="sliderLStickDeadzoneAndModifier"> |
| 807 | <property name="orientation"> | 807 | <property name="orientation"> |
| 808 | <enum>Qt::Horizontal</enum> | 808 | <enum>Qt::Horizontal</enum> |
| 809 | </property> | 809 | </property> |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 3e6d5a7cd..0cd0054c8 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -126,13 +126,6 @@ public: | |||
| 126 | 126 | ||
| 127 | return GameListItem::data(role); | 127 | return GameListItem::data(role); |
| 128 | } | 128 | } |
| 129 | |||
| 130 | /** | ||
| 131 | * Override to prevent automatic sorting. | ||
| 132 | */ | ||
| 133 | bool operator<(const QStandardItem& other) const override { | ||
| 134 | return false; | ||
| 135 | } | ||
| 136 | }; | 129 | }; |
| 137 | 130 | ||
| 138 | class GameListItemCompat : public GameListItem { | 131 | class GameListItemCompat : public GameListItem { |
| @@ -279,6 +272,13 @@ public: | |||
| 279 | return static_cast<int>(dir_type); | 272 | return static_cast<int>(dir_type); |
| 280 | } | 273 | } |
| 281 | 274 | ||
| 275 | /** | ||
| 276 | * Override to prevent automatic sorting between folders and the addDir button. | ||
| 277 | */ | ||
| 278 | bool operator<(const QStandardItem& other) const override { | ||
| 279 | return false; | ||
| 280 | } | ||
| 281 | |||
| 282 | private: | 282 | private: |
| 283 | GameListItemType dir_type; | 283 | GameListItemType dir_type; |
| 284 | }; | 284 | }; |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 80341747f..8476a5a16 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -388,12 +388,14 @@ void Config::ReadValues() { | |||
| 388 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); | 388 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); |
| 389 | Settings::values.use_disk_shader_cache = | 389 | Settings::values.use_disk_shader_cache = |
| 390 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | 390 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); |
| 391 | Settings::values.use_accurate_gpu_emulation = | 391 | const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); |
| 392 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | 392 | Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); |
| 393 | Settings::values.use_asynchronous_gpu_emulation = | 393 | Settings::values.use_asynchronous_gpu_emulation = |
| 394 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | 394 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); |
| 395 | Settings::values.use_vsync = | 395 | Settings::values.use_vsync = |
| 396 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); | 396 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)); |
| 397 | Settings::values.use_fast_gpu_time = | ||
| 398 | sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); | ||
| 397 | 399 | ||
| 398 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); | 400 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); |
| 399 | Settings::values.bg_green = | 401 | Settings::values.bg_green = |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 171d16fa0..60b1a62fa 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -146,9 +146,9 @@ frame_limit = | |||
| 146 | # 0 (default): Off, 1 : On | 146 | # 0 (default): Off, 1 : On |
| 147 | use_disk_shader_cache = | 147 | use_disk_shader_cache = |
| 148 | 148 | ||
| 149 | # Whether to use accurate GPU emulation | 149 | # Which gpu accuracy level to use |
| 150 | # 0 (default): Off (fast), 1 : On (slow) | 150 | # 0 (Normal), 1 (High), 2 (Extreme) |
| 151 | use_accurate_gpu_emulation = | 151 | gpu_accuracy = |
| 152 | 152 | ||
| 153 | # Whether to use asynchronous GPU emulation | 153 | # Whether to use asynchronous GPU emulation |
| 154 | # 0 : Off (slow), 1 (default): On (fast) | 154 | # 0 : Off (slow), 1 (default): On (fast) |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 19584360c..e5e684206 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -181,9 +181,10 @@ void EmuWindow_SDL2::PollEvents() { | |||
| 181 | const u32 current_time = SDL_GetTicks(); | 181 | const u32 current_time = SDL_GetTicks(); |
| 182 | if (current_time > last_time + 2000) { | 182 | if (current_time > last_time + 2000) { |
| 183 | const auto results = Core::System::GetInstance().GetAndResetPerfStats(); | 183 | const auto results = Core::System::GetInstance().GetAndResetPerfStats(); |
| 184 | const auto title = fmt::format( | 184 | const auto title = |
| 185 | "yuzu {} | {}-{} | FPS: {:.0f} ({:.0%})", Common::g_build_fullname, | 185 | fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, |
| 186 | Common::g_scm_branch, Common::g_scm_desc, results.game_fps, results.emulation_speed); | 186 | Common::g_scm_branch, Common::g_scm_desc, results.game_fps, |
| 187 | results.emulation_speed * 100.0); | ||
| 187 | SDL_SetWindowTitle(render_window, title.c_str()); | 188 | SDL_SetWindowTitle(render_window, title.c_str()); |
| 188 | last_time = current_time; | 189 | last_time = current_time; |
| 189 | } | 190 | } |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index f2990910e..cb8e68a39 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -29,6 +29,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) | |||
| 29 | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | 29 | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |
| 30 | 30 | ||
| 31 | SDL_SysWMinfo wm; | 31 | SDL_SysWMinfo wm; |
| 32 | SDL_VERSION(&wm.version); | ||
| 32 | if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { | 33 | if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { |
| 33 | LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); | 34 | LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); |
| 34 | std::exit(EXIT_FAILURE); | 35 | std::exit(EXIT_FAILURE); |
| @@ -70,7 +71,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen) | |||
| 70 | EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; | 71 | EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; |
| 71 | 72 | ||
| 72 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { | 73 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { |
| 73 | return nullptr; | 74 | return std::make_unique<DummyContext>(); |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | void EmuWindow_SDL2_VK::Present() { | 77 | void EmuWindow_SDL2_VK::Present() { |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index b8021ebea..77a6ca72b 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -22,3 +22,5 @@ public: | |||
| 22 | 22 | ||
| 23 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 23 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 24 | }; | 24 | }; |
| 25 | |||
| 26 | class DummyContext : public Core::Frontend::GraphicsContext {}; | ||
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index ee2591c8f..3be58b15d 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp | |||
| @@ -126,10 +126,12 @@ void Config::ReadValues() { | |||
| 126 | Settings::values.frame_limit = 100; | 126 | Settings::values.frame_limit = 100; |
| 127 | Settings::values.use_disk_shader_cache = | 127 | Settings::values.use_disk_shader_cache = |
| 128 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | 128 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); |
| 129 | Settings::values.use_accurate_gpu_emulation = | 129 | const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0); |
| 130 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | 130 | Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); |
| 131 | Settings::values.use_asynchronous_gpu_emulation = | 131 | Settings::values.use_asynchronous_gpu_emulation = |
| 132 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); | 132 | sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); |
| 133 | Settings::values.use_fast_gpu_time = | ||
| 134 | sdl2_config->GetBoolean("Renderer", "use_fast_gpu_time", true); | ||
| 133 | 135 | ||
| 134 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); | 136 | Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); |
| 135 | Settings::values.bg_green = | 137 | Settings::values.bg_green = |